@react-x11/components 0.1.0 → 0.2.0

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 (783) hide show
  1. package/README.md +979 -21
  2. package/dist/ansi/cast.d.ts +48 -0
  3. package/dist/ansi/cast.d.ts.map +1 -0
  4. package/dist/ansi/cast.js +135 -0
  5. package/dist/ansi/cast.js.map +1 -0
  6. package/dist/ansi/index.d.ts +10 -0
  7. package/dist/ansi/index.d.ts.map +1 -0
  8. package/dist/ansi/index.js +27 -0
  9. package/dist/ansi/index.js.map +1 -0
  10. package/dist/ansi/palette.d.ts +79 -0
  11. package/dist/ansi/palette.d.ts.map +1 -0
  12. package/dist/ansi/palette.js +155 -0
  13. package/dist/ansi/palette.js.map +1 -0
  14. package/dist/ansi/parse.d.ts +114 -0
  15. package/dist/ansi/parse.d.ts.map +1 -0
  16. package/dist/ansi/parse.js +687 -0
  17. package/dist/ansi/parse.js.map +1 -0
  18. package/dist/ansi/sgr.d.ts +87 -0
  19. package/dist/ansi/sgr.d.ts.map +1 -0
  20. package/dist/ansi/sgr.js +302 -0
  21. package/dist/ansi/sgr.js.map +1 -0
  22. package/dist/ansi/utf8.d.ts +15 -0
  23. package/dist/ansi/utf8.d.ts.map +1 -0
  24. package/dist/ansi/utf8.js +82 -0
  25. package/dist/ansi/utf8.js.map +1 -0
  26. package/dist/calendar/Calendar.d.ts +136 -0
  27. package/dist/calendar/Calendar.d.ts.map +1 -0
  28. package/dist/calendar/Calendar.js +460 -0
  29. package/dist/calendar/Calendar.js.map +1 -0
  30. package/dist/calendar/DatePicker.d.ts +52 -0
  31. package/dist/calendar/DatePicker.d.ts.map +1 -0
  32. package/dist/calendar/DatePicker.js +257 -0
  33. package/dist/calendar/DatePicker.js.map +1 -0
  34. package/dist/calendar/dates.d.ts +90 -0
  35. package/dist/calendar/dates.d.ts.map +1 -0
  36. package/dist/calendar/dates.js +236 -0
  37. package/dist/calendar/dates.js.map +1 -0
  38. package/dist/calendar/index.d.ts +8 -0
  39. package/dist/calendar/index.d.ts.map +1 -0
  40. package/dist/calendar/index.js +15 -0
  41. package/dist/calendar/index.js.map +1 -0
  42. package/dist/charts/components.d.ts +141 -0
  43. package/dist/charts/components.d.ts.map +1 -0
  44. package/dist/charts/components.js +580 -0
  45. package/dist/charts/components.js.map +1 -0
  46. package/dist/charts/data.d.ts +198 -0
  47. package/dist/charts/data.d.ts.map +1 -0
  48. package/dist/charts/data.js +616 -0
  49. package/dist/charts/data.js.map +1 -0
  50. package/dist/charts/hx.d.ts +21 -0
  51. package/dist/charts/hx.d.ts.map +1 -0
  52. package/dist/charts/hx.js +15 -0
  53. package/dist/charts/hx.js.map +1 -0
  54. package/dist/charts/index.d.ts +27 -0
  55. package/dist/charts/index.d.ts.map +1 -0
  56. package/dist/charts/index.js +38 -0
  57. package/dist/charts/index.js.map +1 -0
  58. package/dist/charts/node.d.ts +96 -0
  59. package/dist/charts/node.d.ts.map +1 -0
  60. package/dist/charts/node.js +720 -0
  61. package/dist/charts/node.js.map +1 -0
  62. package/dist/charts/render.d.ts +153 -0
  63. package/dist/charts/render.d.ts.map +1 -0
  64. package/dist/charts/render.js +762 -0
  65. package/dist/charts/render.js.map +1 -0
  66. package/dist/charts/scale.d.ts +42 -0
  67. package/dist/charts/scale.d.ts.map +1 -0
  68. package/dist/charts/scale.js +173 -0
  69. package/dist/charts/scale.js.map +1 -0
  70. package/dist/charts/spec.d.ts +91 -0
  71. package/dist/charts/spec.d.ts.map +1 -0
  72. package/dist/charts/spec.js +33 -0
  73. package/dist/charts/spec.js.map +1 -0
  74. package/dist/charts/timers.d.ts +6 -0
  75. package/dist/charts/timers.d.ts.map +1 -0
  76. package/dist/charts/timers.js +14 -0
  77. package/dist/charts/timers.js.map +1 -0
  78. package/dist/code/hx.d.ts +24 -0
  79. package/dist/code/hx.d.ts.map +1 -0
  80. package/dist/code/hx.js +25 -0
  81. package/dist/code/hx.js.map +1 -0
  82. package/dist/code/index.d.ts +45 -0
  83. package/dist/code/index.d.ts.map +1 -0
  84. package/dist/code/index.js +93 -0
  85. package/dist/code/index.js.map +1 -0
  86. package/dist/code-editor/completion.d.ts +26 -0
  87. package/dist/code-editor/completion.d.ts.map +1 -0
  88. package/dist/code-editor/completion.js +129 -0
  89. package/dist/code-editor/completion.js.map +1 -0
  90. package/dist/code-editor/doc.d.ts +35 -0
  91. package/dist/code-editor/doc.d.ts.map +1 -0
  92. package/dist/code-editor/doc.js +162 -0
  93. package/dist/code-editor/doc.js.map +1 -0
  94. package/dist/code-editor/hx.d.ts +13 -0
  95. package/dist/code-editor/hx.d.ts.map +1 -0
  96. package/dist/code-editor/hx.js +10 -0
  97. package/dist/code-editor/hx.js.map +1 -0
  98. package/dist/code-editor/index.d.ts +88 -0
  99. package/dist/code-editor/index.d.ts.map +1 -0
  100. package/dist/code-editor/index.js +401 -0
  101. package/dist/code-editor/index.js.map +1 -0
  102. package/dist/code-editor/node.d.ts +343 -0
  103. package/dist/code-editor/node.d.ts.map +1 -0
  104. package/dist/code-editor/node.js +1521 -0
  105. package/dist/code-editor/node.js.map +1 -0
  106. package/dist/code-language/hljs.d.ts +47 -0
  107. package/dist/code-language/hljs.d.ts.map +1 -0
  108. package/dist/code-language/hljs.js +211 -0
  109. package/dist/code-language/hljs.js.map +1 -0
  110. package/dist/code-language/index.d.ts +22 -0
  111. package/dist/code-language/index.d.ts.map +1 -0
  112. package/dist/code-language/index.js +20 -0
  113. package/dist/code-language/index.js.map +1 -0
  114. package/dist/code-language/languages/glsl.d.ts +4 -0
  115. package/dist/code-language/languages/glsl.d.ts.map +1 -0
  116. package/dist/code-language/languages/glsl.js +99 -0
  117. package/dist/code-language/languages/glsl.js.map +1 -0
  118. package/dist/code-language/languages/javascript.d.ts +10 -0
  119. package/dist/code-language/languages/javascript.d.ts.map +1 -0
  120. package/dist/code-language/languages/javascript.js +360 -0
  121. package/dist/code-language/languages/javascript.js.map +1 -0
  122. package/dist/code-language/languages/json.d.ts +4 -0
  123. package/dist/code-language/languages/json.d.ts.map +1 -0
  124. package/dist/code-language/languages/json.js +68 -0
  125. package/dist/code-language/languages/json.js.map +1 -0
  126. package/dist/code-language/languages/shell.d.ts +4 -0
  127. package/dist/code-language/languages/shell.d.ts.map +1 -0
  128. package/dist/code-language/languages/shell.js +189 -0
  129. package/dist/code-language/languages/shell.js.map +1 -0
  130. package/dist/code-language/languages/sql.d.ts +11 -0
  131. package/dist/code-language/languages/sql.d.ts.map +1 -0
  132. package/dist/code-language/languages/sql.js +133 -0
  133. package/dist/code-language/languages/sql.js.map +1 -0
  134. package/dist/code-language/lezer.d.ts +24 -0
  135. package/dist/code-language/lezer.d.ts.map +1 -0
  136. package/dist/code-language/lezer.js +148 -0
  137. package/dist/code-language/lezer.js.map +1 -0
  138. package/dist/code-language/registry.d.ts +19 -0
  139. package/dist/code-language/registry.d.ts.map +1 -0
  140. package/dist/code-language/registry.js +56 -0
  141. package/dist/code-language/registry.js.map +1 -0
  142. package/dist/code-language/runs.d.ts +35 -0
  143. package/dist/code-language/runs.d.ts.map +1 -0
  144. package/dist/code-language/runs.js +88 -0
  145. package/dist/code-language/runs.js.map +1 -0
  146. package/dist/code-language/stream.d.ts +70 -0
  147. package/dist/code-language/stream.d.ts.map +1 -0
  148. package/dist/code-language/stream.js +243 -0
  149. package/dist/code-language/stream.js.map +1 -0
  150. package/dist/code-language/textmate.d.ts +27 -0
  151. package/dist/code-language/textmate.d.ts.map +1 -0
  152. package/dist/code-language/textmate.js +86 -0
  153. package/dist/code-language/textmate.js.map +1 -0
  154. package/dist/code-language/theme.d.ts +26 -0
  155. package/dist/code-language/theme.d.ts.map +1 -0
  156. package/dist/code-language/theme.js +108 -0
  157. package/dist/code-language/theme.js.map +1 -0
  158. package/dist/code-language/timers.d.ts +7 -0
  159. package/dist/code-language/timers.d.ts.map +1 -0
  160. package/dist/code-language/timers.js +22 -0
  161. package/dist/code-language/timers.js.map +1 -0
  162. package/dist/code-language/types.d.ts +173 -0
  163. package/dist/code-language/types.d.ts.map +1 -0
  164. package/dist/code-language/types.js +41 -0
  165. package/dist/code-language/types.js.map +1 -0
  166. package/dist/codeblock/index.d.ts +69 -0
  167. package/dist/codeblock/index.d.ts.map +1 -0
  168. package/dist/codeblock/index.js +85 -0
  169. package/dist/codeblock/index.js.map +1 -0
  170. package/dist/color-picker/ColorField.d.ts +39 -0
  171. package/dist/color-picker/ColorField.d.ts.map +1 -0
  172. package/dist/color-picker/ColorField.js +239 -0
  173. package/dist/color-picker/ColorField.js.map +1 -0
  174. package/dist/color-picker/ColorPicker.d.ts +109 -0
  175. package/dist/color-picker/ColorPicker.d.ts.map +1 -0
  176. package/dist/color-picker/ColorPicker.js +757 -0
  177. package/dist/color-picker/ColorPicker.js.map +1 -0
  178. package/dist/color-picker/color.d.ts +105 -0
  179. package/dist/color-picker/color.d.ts.map +1 -0
  180. package/dist/color-picker/color.js +313 -0
  181. package/dist/color-picker/color.js.map +1 -0
  182. package/dist/color-picker/index.d.ts +8 -0
  183. package/dist/color-picker/index.d.ts.map +1 -0
  184. package/dist/color-picker/index.js +19 -0
  185. package/dist/color-picker/index.js.map +1 -0
  186. package/dist/desktop-calendar/eds.d.ts +102 -0
  187. package/dist/desktop-calendar/eds.d.ts.map +1 -0
  188. package/dist/desktop-calendar/eds.js +356 -0
  189. package/dist/desktop-calendar/eds.js.map +1 -0
  190. package/dist/desktop-calendar/ical.d.ts +63 -0
  191. package/dist/desktop-calendar/ical.d.ts.map +1 -0
  192. package/dist/desktop-calendar/ical.js +57 -0
  193. package/dist/desktop-calendar/ical.js.map +1 -0
  194. package/dist/desktop-calendar/index.d.ts +72 -0
  195. package/dist/desktop-calendar/index.d.ts.map +1 -0
  196. package/dist/desktop-calendar/index.js +213 -0
  197. package/dist/desktop-calendar/index.js.map +1 -0
  198. package/dist/embed/client.d.ts +121 -0
  199. package/dist/embed/client.d.ts.map +1 -0
  200. package/dist/embed/client.js +203 -0
  201. package/dist/embed/client.js.map +1 -0
  202. package/dist/embed/host.d.ts +96 -0
  203. package/dist/embed/host.d.ts.map +1 -0
  204. package/dist/embed/host.js +266 -0
  205. package/dist/embed/host.js.map +1 -0
  206. package/dist/embed/index.d.ts +5 -0
  207. package/dist/embed/index.d.ts.map +1 -0
  208. package/dist/embed/index.js +13 -0
  209. package/dist/embed/index.js.map +1 -0
  210. package/dist/embed/timers.d.ts +6 -0
  211. package/dist/embed/timers.d.ts.map +1 -0
  212. package/dist/embed/timers.js +16 -0
  213. package/dist/embed/timers.js.map +1 -0
  214. package/dist/flow/draw.d.ts +45 -0
  215. package/dist/flow/draw.d.ts.map +1 -0
  216. package/dist/flow/draw.js +295 -0
  217. package/dist/flow/draw.js.map +1 -0
  218. package/dist/flow/index.d.ts +64 -0
  219. package/dist/flow/index.d.ts.map +1 -0
  220. package/dist/flow/index.js +306 -0
  221. package/dist/flow/index.js.map +1 -0
  222. package/dist/flow/model.d.ts +158 -0
  223. package/dist/flow/model.d.ts.map +1 -0
  224. package/dist/flow/model.js +566 -0
  225. package/dist/flow/model.js.map +1 -0
  226. package/dist/flow/node.d.ts +429 -0
  227. package/dist/flow/node.d.ts.map +1 -0
  228. package/dist/flow/node.js +3025 -0
  229. package/dist/flow/node.js.map +1 -0
  230. package/dist/flow/paths.d.ts +43 -0
  231. package/dist/flow/paths.d.ts.map +1 -0
  232. package/dist/flow/paths.js +327 -0
  233. package/dist/flow/paths.js.map +1 -0
  234. package/dist/flow/types.d.ts +561 -0
  235. package/dist/flow/types.d.ts.map +1 -0
  236. package/dist/flow/types.js +2 -0
  237. package/dist/flow/types.js.map +1 -0
  238. package/dist/formula/hx.d.ts +24 -0
  239. package/dist/formula/hx.d.ts.map +1 -0
  240. package/dist/formula/hx.js +25 -0
  241. package/dist/formula/hx.js.map +1 -0
  242. package/dist/formula/index.d.ts +74 -0
  243. package/dist/formula/index.d.ts.map +1 -0
  244. package/dist/formula/index.js +155 -0
  245. package/dist/formula/index.js.map +1 -0
  246. package/dist/formula/katex.d.ts +78 -0
  247. package/dist/formula/katex.d.ts.map +1 -0
  248. package/dist/formula/katex.js +130 -0
  249. package/dist/formula/katex.js.map +1 -0
  250. package/dist/formula/layout.d.ts +103 -0
  251. package/dist/formula/layout.d.ts.map +1 -0
  252. package/dist/formula/layout.js +716 -0
  253. package/dist/formula/layout.js.map +1 -0
  254. package/dist/formula/node.d.ts +50 -0
  255. package/dist/formula/node.d.ts.map +1 -0
  256. package/dist/formula/node.js +351 -0
  257. package/dist/formula/node.js.map +1 -0
  258. package/dist/html/controls.d.ts +48 -0
  259. package/dist/html/controls.d.ts.map +1 -0
  260. package/dist/html/controls.js +195 -0
  261. package/dist/html/controls.js.map +1 -0
  262. package/dist/html/css/cascade.d.ts +51 -0
  263. package/dist/html/css/cascade.d.ts.map +1 -0
  264. package/dist/html/css/cascade.js +552 -0
  265. package/dist/html/css/cascade.js.map +1 -0
  266. package/dist/html/css/parse.d.ts +74 -0
  267. package/dist/html/css/parse.d.ts.map +1 -0
  268. package/dist/html/css/parse.js +464 -0
  269. package/dist/html/css/parse.js.map +1 -0
  270. package/dist/html/css/style.d.ts +142 -0
  271. package/dist/html/css/style.d.ts.map +1 -0
  272. package/dist/html/css/style.js +1145 -0
  273. package/dist/html/css/style.js.map +1 -0
  274. package/dist/html/css/ua.d.ts +5 -0
  275. package/dist/html/css/ua.d.ts.map +1 -0
  276. package/dist/html/css/ua.js +171 -0
  277. package/dist/html/css/ua.js.map +1 -0
  278. package/dist/html/css/values.d.ts +78 -0
  279. package/dist/html/css/values.d.ts.map +1 -0
  280. package/dist/html/css/values.js +330 -0
  281. package/dist/html/css/values.js.map +1 -0
  282. package/dist/html/dom.d.ts +112 -0
  283. package/dist/html/dom.d.ts.map +1 -0
  284. package/dist/html/dom.js +374 -0
  285. package/dist/html/dom.js.map +1 -0
  286. package/dist/html/hx.d.ts +24 -0
  287. package/dist/html/hx.d.ts.map +1 -0
  288. package/dist/html/hx.js +29 -0
  289. package/dist/html/hx.js.map +1 -0
  290. package/dist/html/index.d.ts +137 -0
  291. package/dist/html/index.d.ts.map +1 -0
  292. package/dist/html/index.js +339 -0
  293. package/dist/html/index.js.map +1 -0
  294. package/dist/html/layout/block.d.ts +58 -0
  295. package/dist/html/layout/block.d.ts.map +1 -0
  296. package/dist/html/layout/block.js +724 -0
  297. package/dist/html/layout/block.js.map +1 -0
  298. package/dist/html/layout/boxes.d.ts +261 -0
  299. package/dist/html/layout/boxes.d.ts.map +1 -0
  300. package/dist/html/layout/boxes.js +725 -0
  301. package/dist/html/layout/boxes.js.map +1 -0
  302. package/dist/html/layout/flex.d.ts +16 -0
  303. package/dist/html/layout/flex.d.ts.map +1 -0
  304. package/dist/html/layout/flex.js +242 -0
  305. package/dist/html/layout/flex.js.map +1 -0
  306. package/dist/html/layout/floats.d.ts +50 -0
  307. package/dist/html/layout/floats.d.ts.map +1 -0
  308. package/dist/html/layout/floats.js +110 -0
  309. package/dist/html/layout/floats.js.map +1 -0
  310. package/dist/html/layout/inline.d.ts +44 -0
  311. package/dist/html/layout/inline.d.ts.map +1 -0
  312. package/dist/html/layout/inline.js +700 -0
  313. package/dist/html/layout/inline.js.map +1 -0
  314. package/dist/html/layout/table.d.ts +5 -0
  315. package/dist/html/layout/table.d.ts.map +1 -0
  316. package/dist/html/layout/table.js +340 -0
  317. package/dist/html/layout/table.js.map +1 -0
  318. package/dist/html/node.d.ts +149 -0
  319. package/dist/html/node.d.ts.map +1 -0
  320. package/dist/html/node.js +861 -0
  321. package/dist/html/node.js.map +1 -0
  322. package/dist/html/paint.d.ts +48 -0
  323. package/dist/html/paint.d.ts.map +1 -0
  324. package/dist/html/paint.js +495 -0
  325. package/dist/html/paint.js.map +1 -0
  326. package/dist/html/resources.d.ts +48 -0
  327. package/dist/html/resources.d.ts.map +1 -0
  328. package/dist/html/resources.js +156 -0
  329. package/dist/html/resources.js.map +1 -0
  330. package/dist/index.d.ts +48 -0
  331. package/dist/index.d.ts.map +1 -0
  332. package/dist/index.js +92 -0
  333. package/dist/index.js.map +1 -0
  334. package/dist/internal/heights.d.ts +81 -0
  335. package/dist/internal/heights.d.ts.map +1 -0
  336. package/dist/internal/heights.js +193 -0
  337. package/dist/internal/heights.js.map +1 -0
  338. package/dist/internal/hx.d.ts +24 -0
  339. package/dist/internal/hx.d.ts.map +1 -0
  340. package/dist/internal/hx.js +27 -0
  341. package/dist/internal/hx.js.map +1 -0
  342. package/dist/internal/text.d.ts +6 -0
  343. package/dist/internal/text.d.ts.map +1 -0
  344. package/dist/internal/text.js +44 -0
  345. package/dist/internal/text.js.map +1 -0
  346. package/dist/internal/timers.d.ts +8 -0
  347. package/dist/internal/timers.d.ts.map +1 -0
  348. package/dist/internal/timers.js +22 -0
  349. package/dist/internal/timers.js.map +1 -0
  350. package/dist/internal/widget.d.ts +40 -0
  351. package/dist/internal/widget.d.ts.map +1 -0
  352. package/dist/internal/widget.js +44 -0
  353. package/dist/internal/widget.js.map +1 -0
  354. package/dist/markdown/ast.d.ts +126 -0
  355. package/dist/markdown/ast.d.ts.map +1 -0
  356. package/dist/markdown/ast.js +8 -0
  357. package/dist/markdown/ast.js.map +1 -0
  358. package/dist/markdown/hx.d.ts +24 -0
  359. package/dist/markdown/hx.d.ts.map +1 -0
  360. package/dist/markdown/hx.js +25 -0
  361. package/dist/markdown/hx.js.map +1 -0
  362. package/dist/markdown/index.d.ts +90 -0
  363. package/dist/markdown/index.d.ts.map +1 -0
  364. package/dist/markdown/index.js +413 -0
  365. package/dist/markdown/index.js.map +1 -0
  366. package/dist/markdown/parse.d.ts +8 -0
  367. package/dist/markdown/parse.d.ts.map +1 -0
  368. package/dist/markdown/parse.js +911 -0
  369. package/dist/markdown/parse.js.map +1 -0
  370. package/dist/markdown/spans.d.ts +20 -0
  371. package/dist/markdown/spans.d.ts.map +1 -0
  372. package/dist/markdown/spans.js +96 -0
  373. package/dist/markdown/spans.js.map +1 -0
  374. package/dist/media-player/backends.d.ts +64 -0
  375. package/dist/media-player/backends.d.ts.map +1 -0
  376. package/dist/media-player/backends.js +110 -0
  377. package/dist/media-player/backends.js.map +1 -0
  378. package/dist/media-player/control.d.ts +40 -0
  379. package/dist/media-player/control.d.ts.map +1 -0
  380. package/dist/media-player/control.js +44 -0
  381. package/dist/media-player/control.js.map +1 -0
  382. package/dist/media-player/hx.d.ts +24 -0
  383. package/dist/media-player/hx.d.ts.map +1 -0
  384. package/dist/media-player/hx.js +25 -0
  385. package/dist/media-player/hx.js.map +1 -0
  386. package/dist/media-player/index.d.ts +107 -0
  387. package/dist/media-player/index.d.ts.map +1 -0
  388. package/dist/media-player/index.js +219 -0
  389. package/dist/media-player/index.js.map +1 -0
  390. package/dist/media-player/mpv.d.ts +4 -0
  391. package/dist/media-player/mpv.d.ts.map +1 -0
  392. package/dist/media-player/mpv.js +120 -0
  393. package/dist/media-player/mpv.js.map +1 -0
  394. package/dist/media-player/vlc.d.ts +8 -0
  395. package/dist/media-player/vlc.d.ts.map +1 -0
  396. package/dist/media-player/vlc.js +83 -0
  397. package/dist/media-player/vlc.js.map +1 -0
  398. package/dist/richtext/index.d.ts +19 -0
  399. package/dist/richtext/index.d.ts.map +1 -0
  400. package/dist/richtext/index.js +4 -0
  401. package/dist/richtext/index.js.map +1 -0
  402. package/dist/richtext/links.d.ts +12 -0
  403. package/dist/richtext/links.d.ts.map +1 -0
  404. package/dist/richtext/links.js +64 -0
  405. package/dist/richtext/links.js.map +1 -0
  406. package/dist/richtext/menu.d.ts +11 -0
  407. package/dist/richtext/menu.d.ts.map +1 -0
  408. package/dist/richtext/menu.js +43 -0
  409. package/dist/richtext/menu.js.map +1 -0
  410. package/dist/richtext/node.d.ts +151 -0
  411. package/dist/richtext/node.d.ts.map +1 -0
  412. package/dist/richtext/node.js +255 -0
  413. package/dist/richtext/node.js.map +1 -0
  414. package/dist/richtext/runs.d.ts +92 -0
  415. package/dist/richtext/runs.d.ts.map +1 -0
  416. package/dist/richtext/runs.js +158 -0
  417. package/dist/richtext/runs.js.map +1 -0
  418. package/dist/table/hx.d.ts +24 -0
  419. package/dist/table/hx.d.ts.map +1 -0
  420. package/dist/table/hx.js +25 -0
  421. package/dist/table/hx.js.map +1 -0
  422. package/dist/table/index.d.ts +169 -0
  423. package/dist/table/index.d.ts.map +1 -0
  424. package/dist/table/index.js +844 -0
  425. package/dist/table/index.js.map +1 -0
  426. package/dist/table/rows.d.ts +136 -0
  427. package/dist/table/rows.d.ts.map +1 -0
  428. package/dist/table/rows.js +146 -0
  429. package/dist/table/rows.js.map +1 -0
  430. package/dist/terminal/backends.d.ts +105 -0
  431. package/dist/terminal/backends.d.ts.map +1 -0
  432. package/dist/terminal/backends.js +185 -0
  433. package/dist/terminal/backends.js.map +1 -0
  434. package/dist/terminal/hx.d.ts +24 -0
  435. package/dist/terminal/hx.d.ts.map +1 -0
  436. package/dist/terminal/hx.js +25 -0
  437. package/dist/terminal/hx.js.map +1 -0
  438. package/dist/terminal/index.d.ts +160 -0
  439. package/dist/terminal/index.d.ts.map +1 -0
  440. package/dist/terminal/index.js +344 -0
  441. package/dist/terminal/index.js.map +1 -0
  442. package/dist/terminal/title.d.ts +24 -0
  443. package/dist/terminal/title.d.ts.map +1 -0
  444. package/dist/terminal/title.js +129 -0
  445. package/dist/terminal/title.js.map +1 -0
  446. package/dist/terminal/vt/colors.d.ts +63 -0
  447. package/dist/terminal/vt/colors.d.ts.map +1 -0
  448. package/dist/terminal/vt/colors.js +203 -0
  449. package/dist/terminal/vt/colors.js.map +1 -0
  450. package/dist/terminal/vt/diff.d.ts +117 -0
  451. package/dist/terminal/vt/diff.d.ts.map +1 -0
  452. package/dist/terminal/vt/diff.js +358 -0
  453. package/dist/terminal/vt/diff.js.map +1 -0
  454. package/dist/terminal/vt/fonts.d.ts +92 -0
  455. package/dist/terminal/vt/fonts.d.ts.map +1 -0
  456. package/dist/terminal/vt/fonts.js +178 -0
  457. package/dist/terminal/vt/fonts.js.map +1 -0
  458. package/dist/terminal/vt/index.d.ts +78 -0
  459. package/dist/terminal/vt/index.d.ts.map +1 -0
  460. package/dist/terminal/vt/index.js +312 -0
  461. package/dist/terminal/vt/index.js.map +1 -0
  462. package/dist/terminal/vt/keys.d.ts +74 -0
  463. package/dist/terminal/vt/keys.d.ts.map +1 -0
  464. package/dist/terminal/vt/keys.js +309 -0
  465. package/dist/terminal/vt/keys.js.map +1 -0
  466. package/dist/terminal/vt/mouse.d.ts +44 -0
  467. package/dist/terminal/vt/mouse.d.ts.map +1 -0
  468. package/dist/terminal/vt/mouse.js +106 -0
  469. package/dist/terminal/vt/mouse.js.map +1 -0
  470. package/dist/terminal/vt/node.d.ts +246 -0
  471. package/dist/terminal/vt/node.d.ts.map +1 -0
  472. package/dist/terminal/vt/node.js +1068 -0
  473. package/dist/terminal/vt/node.js.map +1 -0
  474. package/dist/terminal/vt/pty.d.ts +81 -0
  475. package/dist/terminal/vt/pty.d.ts.map +1 -0
  476. package/dist/terminal/vt/pty.js +201 -0
  477. package/dist/terminal/vt/pty.js.map +1 -0
  478. package/dist/terminal/vt/renderer.d.ts +179 -0
  479. package/dist/terminal/vt/renderer.d.ts.map +1 -0
  480. package/dist/terminal/vt/renderer.js +357 -0
  481. package/dist/terminal/vt/renderer.js.map +1 -0
  482. package/dist/terminal/vt/timers.d.ts +6 -0
  483. package/dist/terminal/vt/timers.d.ts.map +1 -0
  484. package/dist/terminal/vt/timers.js +16 -0
  485. package/dist/terminal/vt/timers.js.map +1 -0
  486. package/dist/terminal/vt/xterm.d.ts +142 -0
  487. package/dist/terminal/vt/xterm.d.ts.map +1 -0
  488. package/dist/terminal/vt/xterm.js +32 -0
  489. package/dist/terminal/vt/xterm.js.map +1 -0
  490. package/dist/terminal-output/hx.d.ts +24 -0
  491. package/dist/terminal-output/hx.d.ts.map +1 -0
  492. package/dist/terminal-output/hx.js +25 -0
  493. package/dist/terminal-output/hx.js.map +1 -0
  494. package/dist/terminal-output/index.d.ts +92 -0
  495. package/dist/terminal-output/index.d.ts.map +1 -0
  496. package/dist/terminal-output/index.js +179 -0
  497. package/dist/terminal-output/index.js.map +1 -0
  498. package/dist/terminal-output/runs.d.ts +27 -0
  499. package/dist/terminal-output/runs.d.ts.map +1 -0
  500. package/dist/terminal-output/runs.js +66 -0
  501. package/dist/terminal-output/runs.js.map +1 -0
  502. package/dist/three/canvas.d.ts +46 -0
  503. package/dist/three/canvas.d.ts.map +1 -0
  504. package/dist/three/canvas.js +305 -0
  505. package/dist/three/canvas.js.map +1 -0
  506. package/dist/three/events.d.ts +64 -0
  507. package/dist/three/events.d.ts.map +1 -0
  508. package/dist/three/events.js +146 -0
  509. package/dist/three/events.js.map +1 -0
  510. package/dist/three/geometries.d.ts +89 -0
  511. package/dist/three/geometries.d.ts.map +1 -0
  512. package/dist/three/geometries.js +345 -0
  513. package/dist/three/geometries.js.map +1 -0
  514. package/dist/three/globals.d.ts +7 -0
  515. package/dist/three/globals.d.ts.map +1 -0
  516. package/dist/three/globals.js +20 -0
  517. package/dist/three/globals.js.map +1 -0
  518. package/dist/three/hx.d.ts +9 -0
  519. package/dist/three/hx.d.ts.map +1 -0
  520. package/dist/three/hx.js +10 -0
  521. package/dist/three/hx.js.map +1 -0
  522. package/dist/three/index.d.ts +22 -0
  523. package/dist/three/index.d.ts.map +1 -0
  524. package/dist/three/index.js +24 -0
  525. package/dist/three/index.js.map +1 -0
  526. package/dist/three/jsx-dev-runtime.d.ts +19 -0
  527. package/dist/three/jsx-dev-runtime.d.ts.map +1 -0
  528. package/dist/three/jsx-dev-runtime.js +2 -0
  529. package/dist/three/jsx-dev-runtime.js.map +1 -0
  530. package/dist/three/jsx-runtime.d.ts +19 -0
  531. package/dist/three/jsx-runtime.d.ts.map +1 -0
  532. package/dist/three/jsx-runtime.js +2 -0
  533. package/dist/three/jsx-runtime.js.map +1 -0
  534. package/dist/three/jsx.d.ts +198 -0
  535. package/dist/three/jsx.d.ts.map +1 -0
  536. package/dist/three/jsx.js +2 -0
  537. package/dist/three/jsx.js.map +1 -0
  538. package/dist/three/materials.d.ts +125 -0
  539. package/dist/three/materials.d.ts.map +1 -0
  540. package/dist/three/materials.js +229 -0
  541. package/dist/three/materials.js.map +1 -0
  542. package/dist/three/math.d.ts +146 -0
  543. package/dist/three/math.d.ts.map +1 -0
  544. package/dist/three/math.js +464 -0
  545. package/dist/three/math.js.map +1 -0
  546. package/dist/three/objects.d.ts +185 -0
  547. package/dist/three/objects.d.ts.map +1 -0
  548. package/dist/three/objects.js +309 -0
  549. package/dist/three/objects.js.map +1 -0
  550. package/dist/three/passes.d.ts +54 -0
  551. package/dist/three/passes.d.ts.map +1 -0
  552. package/dist/three/passes.js +78 -0
  553. package/dist/three/passes.js.map +1 -0
  554. package/dist/three/postprocess.d.ts +85 -0
  555. package/dist/three/postprocess.d.ts.map +1 -0
  556. package/dist/three/postprocess.js +503 -0
  557. package/dist/three/postprocess.js.map +1 -0
  558. package/dist/three/raycast.d.ts +24 -0
  559. package/dist/three/raycast.d.ts.map +1 -0
  560. package/dist/three/raycast.js +138 -0
  561. package/dist/three/raycast.js.map +1 -0
  562. package/dist/three/reconciler.d.ts +48 -0
  563. package/dist/three/reconciler.d.ts.map +1 -0
  564. package/dist/three/reconciler.js +416 -0
  565. package/dist/three/reconciler.js.map +1 -0
  566. package/dist/three/renderer-direct.d.ts +102 -0
  567. package/dist/three/renderer-direct.d.ts.map +1 -0
  568. package/dist/three/renderer-direct.js +782 -0
  569. package/dist/three/renderer-direct.js.map +1 -0
  570. package/dist/three/renderer-indirect.d.ts +68 -0
  571. package/dist/three/renderer-indirect.d.ts.map +1 -0
  572. package/dist/three/renderer-indirect.js +438 -0
  573. package/dist/three/renderer-indirect.js.map +1 -0
  574. package/dist/three/store.d.ts +81 -0
  575. package/dist/three/store.d.ts.map +1 -0
  576. package/dist/three/store.js +110 -0
  577. package/dist/three/store.js.map +1 -0
  578. package/dist/timeline/hx.d.ts +18 -0
  579. package/dist/timeline/hx.d.ts.map +1 -0
  580. package/dist/timeline/hx.js +10 -0
  581. package/dist/timeline/hx.js.map +1 -0
  582. package/dist/timeline/index.d.ts +152 -0
  583. package/dist/timeline/index.d.ts.map +1 -0
  584. package/dist/timeline/index.js +383 -0
  585. package/dist/timeline/index.js.map +1 -0
  586. package/dist/tray-host/hx.d.ts +24 -0
  587. package/dist/tray-host/hx.d.ts.map +1 -0
  588. package/dist/tray-host/hx.js +25 -0
  589. package/dist/tray-host/hx.js.map +1 -0
  590. package/dist/tray-host/index.d.ts +110 -0
  591. package/dist/tray-host/index.d.ts.map +1 -0
  592. package/dist/tray-host/index.js +191 -0
  593. package/dist/tray-host/index.js.map +1 -0
  594. package/dist/tray-host/manager.d.ts +137 -0
  595. package/dist/tray-host/manager.d.ts.map +1 -0
  596. package/dist/tray-host/manager.js +462 -0
  597. package/dist/tray-host/manager.js.map +1 -0
  598. package/dist/tray-host/notify.d.ts +22 -0
  599. package/dist/tray-host/notify.d.ts.map +1 -0
  600. package/dist/tray-host/notify.js +74 -0
  601. package/dist/tray-host/notify.js.map +1 -0
  602. package/dist/tray-host/protocol.d.ts +106 -0
  603. package/dist/tray-host/protocol.d.ts.map +1 -0
  604. package/dist/tray-host/protocol.js +191 -0
  605. package/dist/tray-host/protocol.js.map +1 -0
  606. package/dist/tree/hx.d.ts +24 -0
  607. package/dist/tree/hx.d.ts.map +1 -0
  608. package/dist/tree/hx.js +25 -0
  609. package/dist/tree/hx.js.map +1 -0
  610. package/dist/tree/index.d.ts +221 -0
  611. package/dist/tree/index.d.ts.map +1 -0
  612. package/dist/tree/index.js +716 -0
  613. package/dist/tree/index.js.map +1 -0
  614. package/dist/tree/internal.d.ts +18 -0
  615. package/dist/tree/internal.d.ts.map +1 -0
  616. package/dist/tree/internal.js +66 -0
  617. package/dist/tree/internal.js.map +1 -0
  618. package/dist/tree/rows.d.ts +177 -0
  619. package/dist/tree/rows.d.ts.map +1 -0
  620. package/dist/tree/rows.js +179 -0
  621. package/dist/tree/rows.js.map +1 -0
  622. package/package.json +166 -12
  623. package/src/ansi/cast.ts +159 -0
  624. package/src/ansi/index.ts +53 -0
  625. package/src/ansi/palette.ts +226 -0
  626. package/src/ansi/parse.ts +780 -0
  627. package/src/ansi/sgr.ts +380 -0
  628. package/src/ansi/utf8.ts +84 -0
  629. package/src/calendar/Calendar.ts +744 -0
  630. package/src/calendar/DatePicker.ts +368 -0
  631. package/src/calendar/dates.ts +307 -0
  632. package/src/calendar/index.ts +51 -0
  633. package/src/charts/components.ts +864 -0
  634. package/src/charts/data.ts +776 -0
  635. package/src/charts/hx.ts +39 -0
  636. package/src/charts/index.ts +99 -0
  637. package/src/charts/node.ts +932 -0
  638. package/src/charts/render.ts +982 -0
  639. package/src/charts/scale.ts +214 -0
  640. package/src/charts/spec.ts +122 -0
  641. package/src/charts/timers.ts +24 -0
  642. package/src/code/hx.ts +51 -0
  643. package/src/code/index.ts +174 -0
  644. package/src/code-editor/completion.ts +161 -0
  645. package/src/code-editor/doc.ts +198 -0
  646. package/src/code-editor/hx.ts +26 -0
  647. package/src/code-editor/index.ts +615 -0
  648. package/src/code-editor/node.ts +1926 -0
  649. package/src/code-language/hljs.ts +302 -0
  650. package/src/code-language/index.ts +51 -0
  651. package/src/code-language/languages/glsl.ts +120 -0
  652. package/src/code-language/languages/javascript.ts +419 -0
  653. package/src/code-language/languages/json.ts +72 -0
  654. package/src/code-language/languages/shell.ts +221 -0
  655. package/src/code-language/languages/sql.ts +165 -0
  656. package/src/code-language/lezer.ts +190 -0
  657. package/src/code-language/registry.ts +66 -0
  658. package/src/code-language/runs.ts +128 -0
  659. package/src/code-language/stream.ts +306 -0
  660. package/src/code-language/textmate.ts +134 -0
  661. package/src/code-language/theme.ts +124 -0
  662. package/src/code-language/timers.ts +36 -0
  663. package/src/code-language/types.ts +252 -0
  664. package/src/codeblock/index.ts +146 -0
  665. package/src/color-picker/ColorField.ts +333 -0
  666. package/src/color-picker/ColorPicker.ts +1069 -0
  667. package/src/color-picker/color.ts +399 -0
  668. package/src/color-picker/index.ts +50 -0
  669. package/src/desktop-calendar/eds.ts +484 -0
  670. package/src/desktop-calendar/ical.ts +119 -0
  671. package/src/desktop-calendar/index.ts +287 -0
  672. package/src/embed/client.ts +311 -0
  673. package/src/embed/host.ts +432 -0
  674. package/src/embed/index.ts +33 -0
  675. package/src/embed/timers.ts +32 -0
  676. package/src/flow/draw.ts +418 -0
  677. package/src/flow/index.ts +473 -0
  678. package/src/flow/model.ts +687 -0
  679. package/src/flow/node.ts +3694 -0
  680. package/src/flow/paths.ts +403 -0
  681. package/src/flow/types.ts +634 -0
  682. package/src/formula/hx.ts +51 -0
  683. package/src/formula/index.ts +243 -0
  684. package/src/formula/katex.ts +209 -0
  685. package/src/formula/layout.ts +876 -0
  686. package/src/formula/node.ts +467 -0
  687. package/src/html/controls.ts +244 -0
  688. package/src/html/css/cascade.ts +606 -0
  689. package/src/html/css/parse.ts +500 -0
  690. package/src/html/css/style.ts +1371 -0
  691. package/src/html/css/ua.ts +174 -0
  692. package/src/html/css/values.ts +358 -0
  693. package/src/html/dom.ts +410 -0
  694. package/src/html/hx.ts +55 -0
  695. package/src/html/index.ts +545 -0
  696. package/src/html/layout/block.ts +876 -0
  697. package/src/html/layout/boxes.ts +981 -0
  698. package/src/html/layout/flex.ts +297 -0
  699. package/src/html/layout/floats.ts +125 -0
  700. package/src/html/layout/inline.ts +870 -0
  701. package/src/html/layout/table.ts +380 -0
  702. package/src/html/node.ts +967 -0
  703. package/src/html/paint.ts +605 -0
  704. package/src/html/resources.ts +214 -0
  705. package/src/index.ts +638 -0
  706. package/src/internal/heights.ts +211 -0
  707. package/src/internal/hx.ts +53 -0
  708. package/src/internal/text.ts +44 -0
  709. package/src/internal/timers.ts +44 -0
  710. package/src/internal/widget.ts +92 -0
  711. package/src/markdown/ast.ts +163 -0
  712. package/src/markdown/hx.ts +51 -0
  713. package/src/markdown/index.ts +686 -0
  714. package/src/markdown/parse.ts +1037 -0
  715. package/src/markdown/spans.ts +143 -0
  716. package/src/media-player/backends.ts +155 -0
  717. package/src/media-player/control.ts +70 -0
  718. package/src/media-player/hx.ts +51 -0
  719. package/src/media-player/index.ts +378 -0
  720. package/src/media-player/mpv.ts +146 -0
  721. package/src/media-player/vlc.ts +97 -0
  722. package/src/richtext/index.ts +42 -0
  723. package/src/richtext/links.ts +78 -0
  724. package/src/richtext/menu.ts +49 -0
  725. package/src/richtext/node.ts +406 -0
  726. package/src/richtext/runs.ts +253 -0
  727. package/src/table/hx.ts +51 -0
  728. package/src/table/index.ts +1219 -0
  729. package/src/table/rows.ts +275 -0
  730. package/src/terminal/backends.ts +242 -0
  731. package/src/terminal/hx.ts +51 -0
  732. package/src/terminal/index.ts +574 -0
  733. package/src/terminal/title.ts +172 -0
  734. package/src/terminal/vt/colors.ts +247 -0
  735. package/src/terminal/vt/diff.ts +467 -0
  736. package/src/terminal/vt/fonts.ts +233 -0
  737. package/src/terminal/vt/index.ts +443 -0
  738. package/src/terminal/vt/keys.ts +369 -0
  739. package/src/terminal/vt/mouse.ts +144 -0
  740. package/src/terminal/vt/node.ts +1242 -0
  741. package/src/terminal/vt/pty.ts +334 -0
  742. package/src/terminal/vt/renderer.ts +555 -0
  743. package/src/terminal/vt/timers.ts +31 -0
  744. package/src/terminal/vt/xterm.ts +193 -0
  745. package/src/terminal-output/hx.ts +51 -0
  746. package/src/terminal-output/index.ts +340 -0
  747. package/src/terminal-output/runs.ts +79 -0
  748. package/src/three/canvas.ts +451 -0
  749. package/src/three/events.ts +197 -0
  750. package/src/three/geometries.ts +429 -0
  751. package/src/three/globals.ts +34 -0
  752. package/src/three/hx.ts +22 -0
  753. package/src/three/index.ts +98 -0
  754. package/src/three/jsx-dev-runtime.ts +27 -0
  755. package/src/three/jsx-runtime.ts +38 -0
  756. package/src/three/jsx.ts +275 -0
  757. package/src/three/materials.ts +270 -0
  758. package/src/three/math.ts +585 -0
  759. package/src/three/objects.ts +397 -0
  760. package/src/three/passes.ts +90 -0
  761. package/src/three/postprocess.ts +677 -0
  762. package/src/three/raycast.ts +189 -0
  763. package/src/three/react-reconciler.d.ts +50 -0
  764. package/src/three/reconciler.ts +624 -0
  765. package/src/three/renderer-direct.ts +1055 -0
  766. package/src/three/renderer-indirect.ts +570 -0
  767. package/src/three/store.ts +207 -0
  768. package/src/timeline/hx.ts +31 -0
  769. package/src/timeline/index.ts +603 -0
  770. package/src/tray-host/hx.ts +51 -0
  771. package/src/tray-host/index.ts +331 -0
  772. package/src/tray-host/manager.ts +639 -0
  773. package/src/tray-host/notify.ts +102 -0
  774. package/src/tray-host/protocol.ts +235 -0
  775. package/src/tree/hx.ts +51 -0
  776. package/src/tree/index.ts +1087 -0
  777. package/src/tree/internal.ts +87 -0
  778. package/src/tree/rows.ts +334 -0
  779. package/src/index.d.ts +0 -2
  780. package/src/index.js +0 -9
  781. package/src/sparkline/index.d.ts +0 -29
  782. package/src/sparkline/index.js +0 -50
  783. package/src/sparkline/node.js +0 -72
@@ -0,0 +1,1069 @@
1
+ // <ColorPicker> — a saturation/value field over a hue slider, and whatever
2
+ // else the app asked for.
3
+ //
4
+ // Nothing here registers an element. The question `src/flow/` established is
5
+ // whether the feature's viewport is a *transform*; a picker's is not, so this
6
+ // is the `<Calendar>` shape — `<box>`, `<text>`, `<textinput>` and three
7
+ // `<canvas>` panes — and importing it has no side effect at all.
8
+ //
9
+ // The three panes are pictures, and they are cheap ones: ntk's 2d context
10
+ // hands out real XRender gradients, so the field is a hue fill plus two
11
+ // gradients rather than 36 000 pixels of JavaScript, and each pane carries a
12
+ // `cacheKey` naming everything its drawing reads. The **thumbs are not part
13
+ // of the drawing** — each is an absolutely positioned `<box>` — so a drag
14
+ // moves two numbers in a style and repaints a 12px node, instead of
15
+ // re-running the gradients on every pointer motion.
16
+ //
17
+ // The panes are square-cornered on purpose: ntk's rounded-rect fast path
18
+ // bails to polygon rasterization when the fill is not a plain colour (the
19
+ // `gradient` reason in `stats.shapes`), and a picker is the surface most
20
+ // likely to hit that cliff.
21
+ import React, { useImperativeHandle, useRef, useState } from 'react';
22
+ import type { ReactElement, ReactNode, Ref } from 'react';
23
+ import { createStyles } from 'react-x11/style';
24
+ import type { Style } from 'react-x11/style';
25
+ import { useEyedropper, useTheme } from 'react-x11';
26
+ import type {
27
+ ChangeEvent,
28
+ DrawnNode,
29
+ KeyboardEvent,
30
+ MouseEvent,
31
+ TextInputNode,
32
+ } from 'react-x11';
33
+ import {
34
+ XK_DOWN,
35
+ XK_END,
36
+ XK_ESCAPE,
37
+ XK_HOME,
38
+ XK_LEFT,
39
+ XK_PAGE_DOWN,
40
+ XK_PAGE_UP,
41
+ XK_RETURN,
42
+ XK_RIGHT,
43
+ XK_TAB,
44
+ XK_UP,
45
+ } from 'react-x11/keysyms';
46
+
47
+ import { hx } from '../internal/hx.js';
48
+ import { changeEvent } from '../internal/widget.js';
49
+ import type { WidgetChangeEvent } from '../internal/widget.js';
50
+ import {
51
+ channelsFromHsv,
52
+ channelsFromRgb,
53
+ contrastGrade,
54
+ contrastRatio,
55
+ formatColor,
56
+ formatOf,
57
+ opaqueHex,
58
+ parseColor,
59
+ } from './color.js';
60
+ import type { ColorChannels, ColorFormat } from './color.js';
61
+
62
+ // The geometry. Fixed rather than fluid, for the reason `CALENDAR_WIDTH`
63
+ // exists: `<ColorField>` has to size a real X window around this *before* it
64
+ // is laid out, and it is also what lets the panes carry a `cacheKey` — a key
65
+ // naming a width the pane might not have is a stale picture.
66
+ const PAD = 10;
67
+ const GAP = 8;
68
+ const CONTENT = 240;
69
+ const AREA_H = 150;
70
+ const STRIP_H = 12;
71
+ const ROW_H = 26;
72
+ const SWATCH = 20;
73
+ const SWATCH_GAP = 6;
74
+ const THUMB = 12;
75
+ /** The checker under anything translucent, in device pixels. */
76
+ const CHECKER = 6;
77
+
78
+ /** How wide a `<ColorPicker>` lays out. */
79
+ export const COLOR_PICKER_WIDTH = CONTENT + PAD * 2;
80
+
81
+ const s = createStyles({
82
+ root: {
83
+ padding: PAD,
84
+ gap: GAP,
85
+ alignItems: 'stretch',
86
+ width: COLOR_PICKER_WIDTH,
87
+ // The geometry is a promise the panes' `cacheKey`s and the thumbs'
88
+ // positions are both written against, so the panel does not shrink: a
89
+ // squashed field would put the thumb somewhere the colour is not.
90
+ flexShrink: 0,
91
+ },
92
+ area: {
93
+ width: CONTENT,
94
+ height: AREA_H,
95
+ flexShrink: 0,
96
+ position: 'relative',
97
+ cursor: 'crosshair',
98
+ },
99
+ strip: {
100
+ width: CONTENT,
101
+ height: STRIP_H,
102
+ flexShrink: 0,
103
+ position: 'relative',
104
+ cursor: 'pointer',
105
+ },
106
+ pane: { position: 'absolute', left: 0, top: 0, right: 0, bottom: 0 },
107
+ areaThumb: {
108
+ position: 'absolute',
109
+ width: THUMB,
110
+ height: THUMB,
111
+ borderRadius: THUMB / 2,
112
+ borderWidth: 2,
113
+ borderColor: 'white',
114
+ },
115
+ stripThumb: {
116
+ position: 'absolute',
117
+ top: -2,
118
+ width: 8,
119
+ height: STRIP_H + 4,
120
+ borderRadius: 3,
121
+ borderWidth: 2,
122
+ borderColor: 'white',
123
+ },
124
+ row: {
125
+ flexDirection: 'row',
126
+ alignItems: 'center',
127
+ gap: 6,
128
+ height: ROW_H,
129
+ flexShrink: 0,
130
+ },
131
+ preview: { width: ROW_H, height: ROW_H, flexShrink: 0 },
132
+ field: { flexGrow: 1, height: ROW_H, paddingLeft: 6, paddingRight: 6 },
133
+ readout: { flexGrow: 1 },
134
+ button: {
135
+ height: ROW_H,
136
+ paddingLeft: 8,
137
+ paddingRight: 8,
138
+ alignItems: 'center',
139
+ justifyContent: 'center',
140
+ cursor: 'pointer',
141
+ flexShrink: 0,
142
+ transition: { backgroundColor: 80 },
143
+ },
144
+ glyph: { width: 14, height: 14 },
145
+ swatches: { flexDirection: 'row', flexWrap: 'wrap', gap: SWATCH_GAP },
146
+ swatch: { width: SWATCH, height: SWATCH, cursor: 'pointer' },
147
+ contrast: {
148
+ flexDirection: 'row',
149
+ alignItems: 'center',
150
+ gap: 6,
151
+ height: ROW_H,
152
+ },
153
+ });
154
+
155
+ /** The parts a picker is made of, in the order they are stacked. */
156
+ export type ColorPickerPart =
157
+ 'area' | 'hue' | 'alpha' | 'fields' | 'swatches' | 'recent' | 'contrast';
158
+
159
+ /** A preset. The bare string is the common case; the object is for a preset
160
+ * whose name is worth reading out ("Brand primary"). */
161
+ export type ColorSwatch = string | { value: string; label?: string };
162
+
163
+ const swatchValue = (sw: ColorSwatch): string =>
164
+ typeof sw === 'string' ? sw : sw.value;
165
+ const swatchLabel = (sw: ColorSwatch): string =>
166
+ typeof sw === 'string' ? sw : (sw.label ?? sw.value);
167
+
168
+ /**
169
+ * What `onChange` is handed.
170
+ *
171
+ * A {@link WidgetChangeEvent} — `{ type, target: { type, name, value } }`,
172
+ * the shape a form library destructures — **plus** `color`. `ev.value` is the
173
+ * string, and `ev.color` is what it was built from, which is how an app that
174
+ * wants channels gets them without a second callback or an object-valued
175
+ * `value`. `ev.color.h` is the live hue, which the string cannot always carry:
176
+ * every black is hue 0 once it is written down.
177
+ */
178
+ export interface ColorChangeEvent extends WidgetChangeEvent<string> {
179
+ color: ColorChannels;
180
+ }
181
+
182
+ function colorChangeEvent(
183
+ name: string | undefined,
184
+ value: string,
185
+ color: ColorChannels,
186
+ ): ColorChangeEvent {
187
+ return { ...changeEvent('color', name, value), color };
188
+ }
189
+
190
+ export interface ColorPickerHandle {
191
+ /** Focus the first axis. */
192
+ focus: () => void;
193
+ /** Feed the picker a key it did not receive itself — what `<ColorField>`
194
+ * uses, the way `<DatePicker>` feeds `<Calendar>`. Answers whether the
195
+ * picker took it. */
196
+ handleKey: (ev: KeyboardEvent) => boolean;
197
+ readonly value: string;
198
+ readonly channels: ColorChannels;
199
+ }
200
+
201
+ export interface ColorPickerProps {
202
+ /** The colour, controlled. Any CSS colour this package can parse — which is
203
+ * more than the renderer can paint; see `formatColor` for what comes out. */
204
+ value?: string;
205
+ /** The colour, uncontrolled. Defaults to `#000000`. */
206
+ defaultValue?: string;
207
+ /** Every step of a drag, every arrow key, every accepted edit of the field. */
208
+ onChange?: (ev: ColorChangeEvent) => void;
209
+ /** The same event, on pointer release and on each keyboard commit — for an
210
+ * app whose re-render is too expensive to run per pointer step. */
211
+ onChangeEnd?: (ev: ColorChangeEvent) => void;
212
+ /** Carried on `ev.target.name`, for form libraries. */
213
+ name?: string;
214
+ /** Show the alpha strip, and emit alpha. Off by default: a picker without
215
+ * the strip must not start emitting `rgba()` because the value it was
216
+ * handed had one. */
217
+ alpha?: boolean;
218
+ /** The output spelling. `'auto'` (the default) follows the spelling the
219
+ * incoming value used, and hex when it used none. */
220
+ format?: ColorFormat | 'auto';
221
+ /** Presets, as a row under the panel. */
222
+ swatches?: readonly ColorSwatch[];
223
+ /** The app's recently-used colours, as a second row. Nothing is stored
224
+ * here — a picker that remembered would be a store. */
225
+ recent?: readonly ColorSwatch[];
226
+ /** Which parts appear, and in what order. Defaults to the parts the other
227
+ * props imply: the field and the hue slider, alpha when `alpha`, the
228
+ * fields row, and a row per non-empty swatch list. */
229
+ parts?: readonly ColorPickerPart[];
230
+ /** The eyedropper button. `true` (the default) shows it wherever core can
231
+ * sample the screen — `useEyedropper().supported`. A function replaces the
232
+ * sampler with your own, which is also how a test drives it. */
233
+ eyedropper?: boolean | (() => Promise<string | null>);
234
+ /** A colour to measure the value against: the `'contrast'` part shows the
235
+ * WCAG ratio and its grade. */
236
+ contrast?: string;
237
+ disabled?: boolean;
238
+ /** Whether the panel takes the keyboard itself. `<ColorField>` sets this
239
+ * false and feeds keys through the handle, because an override-redirect
240
+ * popup never takes focus. */
241
+ focusable?: boolean;
242
+ /** Draw the axis cursor even without the focus — the other half of
243
+ * `focusable={false}`. */
244
+ focusVisible?: boolean;
245
+ style?: Style | Style[];
246
+ 'data-testname'?: string;
247
+ ref?: Ref<ColorPickerHandle>;
248
+ children?: ReactNode;
249
+ }
250
+
251
+ /** The rows a set of parts adds up to — `<ColorField>` needs the height of
252
+ * the panel before the panel exists. */
253
+ export function colorPickerHeight(
254
+ parts: readonly ColorPickerPart[],
255
+ counts: { swatches?: number; recent?: number } = {},
256
+ ): number {
257
+ const perRow = Math.floor((CONTENT + SWATCH_GAP) / (SWATCH + SWATCH_GAP));
258
+ const rows = (n: number): number =>
259
+ n
260
+ ? Math.ceil(n / perRow) * SWATCH +
261
+ (Math.ceil(n / perRow) - 1) * SWATCH_GAP
262
+ : 0;
263
+ const heightOf = (part: ColorPickerPart): number => {
264
+ switch (part) {
265
+ case 'area':
266
+ return AREA_H;
267
+ case 'hue':
268
+ case 'alpha':
269
+ return STRIP_H;
270
+ case 'fields':
271
+ case 'contrast':
272
+ return ROW_H;
273
+ case 'swatches':
274
+ return rows(counts.swatches ?? 0);
275
+ case 'recent':
276
+ return rows(counts.recent ?? 0);
277
+ default:
278
+ return 0;
279
+ }
280
+ };
281
+ const heights = parts.map(heightOf).filter((h) => h > 0);
282
+ return (
283
+ PAD * 2 +
284
+ heights.reduce((sum, h) => sum + h, 0) +
285
+ Math.max(0, heights.length - 1) * GAP
286
+ );
287
+ }
288
+
289
+ const clamp01 = (n: number): number => (n < 0 ? 0 : n > 1 ? 1 : n);
290
+
291
+ /** The checkerboard under anything translucent. Two greys rather than a
292
+ * theme colour: it is a *hole*, and a hole looks the same on every palette. */
293
+ function drawChecker(ctx: any, width: number, height: number): void {
294
+ ctx.fillStyle = '#ffffff';
295
+ ctx.fillRect(0, 0, width, height);
296
+ ctx.fillStyle = '#c8c8c8';
297
+ for (let y = 0; y < height; y += CHECKER) {
298
+ for (let x = 0; x < width; x += CHECKER) {
299
+ if (((x / CHECKER) & 1) === ((y / CHECKER) & 1)) continue;
300
+ ctx.fillRect(
301
+ x,
302
+ y,
303
+ Math.min(CHECKER, width - x),
304
+ Math.min(CHECKER, height - y),
305
+ );
306
+ }
307
+ }
308
+ }
309
+
310
+ /**
311
+ * The dropper glyph: the bulb at the top right, the stem running down to the
312
+ * left, and the tip it draws with. Three shapes rather than one clever path,
313
+ * because at 14 pixels a pipette reads only if the bulb and the tip are both
314
+ * solid — the first draft was a single stroke and looked like a slash.
315
+ */
316
+ function EyedropperGlyph({ color }: { color: string }): ReactElement {
317
+ return hx('canvas', {
318
+ style: s.glyph,
319
+ cacheKey: `eyedropper:${color}`,
320
+ onDraw: (ctx, { width, height }) => {
321
+ ctx.fillStyle = color;
322
+ ctx.strokeStyle = color;
323
+ // the bulb
324
+ ctx.beginPath();
325
+ ctx.arc(width - 3.5, 3.5, 3, 0, Math.PI * 2);
326
+ ctx.fill();
327
+ // the stem
328
+ ctx.lineWidth = 2;
329
+ ctx.beginPath();
330
+ ctx.moveTo(width - 5, 5);
331
+ ctx.lineTo(4.5, height - 4.5);
332
+ ctx.stroke();
333
+ // the tip, pointing at what it will sample
334
+ ctx.beginPath();
335
+ ctx.moveTo(1, height - 1);
336
+ ctx.lineTo(5.5, height - 2.5);
337
+ ctx.lineTo(2.5, height - 5.5);
338
+ ctx.closePath();
339
+ ctx.fill();
340
+ },
341
+ });
342
+ }
343
+
344
+ /**
345
+ * `<ColorPicker value onChange>` — the colour panel.
346
+ *
347
+ * <ColorPicker value={accent} onChange={(ev) => setAccent(ev.value)} />
348
+ * <ColorPicker … alpha swatches={brand.colors} />
349
+ * <ColorPicker … parts={['swatches']} /> // the palette-only shape
350
+ *
351
+ * The value is a **CSS colour string**, the way a `<Calendar>`'s day is a
352
+ * `'YYYY-MM-DD'` string: it drops straight into `backgroundColor`, survives
353
+ * JSON, and needs no constructor. What it cannot carry is the hue behind a
354
+ * black or a grey, so the picker holds HSVA of its own and adopts the `value`
355
+ * prop only when that prop is not the string it last emitted. Drag the
356
+ * brightness to zero and back and the hue is still where it was.
357
+ */
358
+ export function ColorPicker(props: ColorPickerProps): ReactElement {
359
+ const {
360
+ value,
361
+ defaultValue,
362
+ onChange,
363
+ onChangeEnd,
364
+ name,
365
+ alpha = false,
366
+ format = 'auto',
367
+ swatches,
368
+ recent,
369
+ parts,
370
+ eyedropper = true,
371
+ contrast,
372
+ disabled = false,
373
+ focusable = true,
374
+ focusVisible,
375
+ style,
376
+ children,
377
+ ref,
378
+ } = props;
379
+
380
+ const theme = useTheme();
381
+ const [model, setModel] = useState<ColorChannels>(
382
+ () =>
383
+ parseColor(value ?? defaultValue ?? '') ?? channelsFromHsv(0, 0, 0, 1),
384
+ );
385
+ // What the last emitted string was, so that an app echoing it back is a
386
+ // no-op rather than a re-parse that loses the hue.
387
+ const emitted = useRef<string | null>(null);
388
+ // The spelling `format="auto"` follows: whatever the last string that came
389
+ // *in* was written in.
390
+ const [sourceFormat, setSourceFormat] = useState<ColorFormat>(
391
+ () => formatOf(value ?? defaultValue ?? '') ?? 'hex',
392
+ );
393
+ const [prevValue, setPrevValue] = useState(value);
394
+ const [dragging, setDragging] = useState<'area' | 'hue' | 'alpha' | null>(
395
+ null,
396
+ );
397
+ const [active, setActive] = useState<'area' | 'hue' | 'alpha'>('area');
398
+ const [focused, setFocused] = useState(false);
399
+ const [typed, setTyped] = useState<string | null>(null);
400
+ const [textModel, setTextModel] = useState<ColorFormat>('hex');
401
+ const areaRef = useRef<DrawnNode | null>(null);
402
+ const hueRef = useRef<DrawnNode | null>(null);
403
+ const alphaRef = useRef<DrawnNode | null>(null);
404
+
405
+ // Controlled reconciliation, at render — React's own "adjusting state when
406
+ // a prop changes" pattern. The hue of the model survives a value that has
407
+ // none of its own, which is what makes dragging through black work.
408
+ if (value !== undefined && value !== prevValue) {
409
+ setPrevValue(value);
410
+ if (value !== emitted.current) {
411
+ const parsed = parseColor(value);
412
+ if (parsed) {
413
+ setModel(
414
+ channelsFromRgb(parsed.r, parsed.g, parsed.b, parsed.a, model.h),
415
+ );
416
+ setSourceFormat(formatOf(value) ?? sourceFormat);
417
+ setTyped(null);
418
+ }
419
+ }
420
+ }
421
+
422
+ const outputFormat: ColorFormat = format === 'auto' ? sourceFormat : format;
423
+ const text = formatColor(model, outputFormat, alpha);
424
+ const dropper = useEyedropper();
425
+
426
+ const emit = (
427
+ next: ColorChannels,
428
+ done: boolean,
429
+ // The spelling to emit in, when the caller already knows it: `setState`
430
+ // does not land until the next render, so a swatch or a typed value that
431
+ // brings its own format has to hand it over rather than set it and read
432
+ // it back in the same handler.
433
+ spelling: ColorFormat = outputFormat,
434
+ ): void => {
435
+ const out = formatColor(next, spelling, alpha);
436
+ emitted.current = out;
437
+ setModel(next);
438
+ setTyped(null);
439
+ const ev = colorChangeEvent(name, out, next);
440
+ onChange?.(ev);
441
+ if (done) onChangeEnd?.(ev);
442
+ };
443
+
444
+ /** Commit whatever the current model is — the release of a drag, and the
445
+ * end of a keyboard step. */
446
+ const commit = (next: ColorChannels): void => emit(next, true);
447
+
448
+ /** Take a colour from somewhere that is not this widget — the screen, or a
449
+ * swatch. The hue comes along when the new colour has none of its own, and
450
+ * the alpha only when there is a strip to show it on. */
451
+ const adopt = (colour: string): void => {
452
+ const parsed = parseColor(colour);
453
+ if (!parsed) return;
454
+ const spelling = formatOf(colour) ?? sourceFormat;
455
+ setSourceFormat(spelling);
456
+ emit(
457
+ channelsFromRgb(
458
+ parsed.r,
459
+ parsed.g,
460
+ parsed.b,
461
+ alpha ? parsed.a : 1,
462
+ model.h,
463
+ ),
464
+ true,
465
+ format === 'auto' ? spelling : outputFormat,
466
+ );
467
+ };
468
+
469
+ // `eyedropper` is a switch and a seam at once: `true` takes core's sampler
470
+ // — `useEyedropper()` answers `supported: false` where there is none, which
471
+ // is the button's existence — and a function replaces it, which is both the
472
+ // kiosk case and how a test drives a pick without a server.
473
+ const ownDropper = typeof eyedropper === 'function' ? eyedropper : null;
474
+ const showDropper =
475
+ !disabled &&
476
+ (ownDropper !== null || (eyedropper !== false && dropper.supported));
477
+ const pickFromScreen = (): void => {
478
+ const pick = ownDropper ?? dropper.pick;
479
+ void Promise.resolve(pick()).then((hex) => {
480
+ // null is a cancel, and a cancel leaves the colour alone.
481
+ if (hex) adopt(hex);
482
+ });
483
+ };
484
+
485
+ const setHsv = (h: number, sat: number, v: number, done = false): void =>
486
+ emit(channelsFromHsv(h, sat, v, model.a), done);
487
+
488
+ // --- pointer ------------------------------------------------------------
489
+
490
+ const fractionIn = (
491
+ node: DrawnNode | null,
492
+ ev: MouseEvent,
493
+ ): { x: number; y: number } | null => {
494
+ const box = node?.abs;
495
+ if (!box?.width || !box.height) return null;
496
+ return {
497
+ x: clamp01((ev.x - box.x) / box.width),
498
+ y: clamp01((ev.y - box.y) / box.height),
499
+ };
500
+ };
501
+
502
+ const areaTo = (ev: MouseEvent, done: boolean): void => {
503
+ const at = fractionIn(areaRef.current, ev);
504
+ if (at) setHsv(model.h, at.x, 1 - at.y, done);
505
+ };
506
+ const hueTo = (ev: MouseEvent, done: boolean): void => {
507
+ const at = fractionIn(hueRef.current, ev);
508
+ if (at) setHsv(at.x * 360, model.s, model.v, done);
509
+ };
510
+ const alphaTo = (ev: MouseEvent, done: boolean): void => {
511
+ const at = fractionIn(alphaRef.current, ev);
512
+ if (at) emit(channelsFromHsv(model.h, model.s, model.v, at.x), done);
513
+ };
514
+
515
+ const track = (
516
+ axis: 'area' | 'hue' | 'alpha',
517
+ to: (ev: MouseEvent, done: boolean) => void,
518
+ ): Record<string, (ev: MouseEvent) => void> =>
519
+ disabled
520
+ ? {}
521
+ : {
522
+ onMouseDown: (ev: MouseEvent) => {
523
+ ev.capturePointer();
524
+ setDragging(axis);
525
+ setActive(axis);
526
+ to(ev, false);
527
+ },
528
+ onMouseMove: (ev: MouseEvent) => {
529
+ if (dragging === axis) to(ev, false);
530
+ },
531
+ onMouseUp: (ev: MouseEvent) => {
532
+ if (dragging !== axis) return;
533
+ setDragging(null);
534
+ to(ev, true);
535
+ },
536
+ };
537
+
538
+ // --- keyboard -----------------------------------------------------------
539
+
540
+ const axes = (): ('area' | 'hue' | 'alpha')[] =>
541
+ (['area', 'hue', 'alpha'] as const).filter((part) => shown.includes(part));
542
+
543
+ const step = (ev: KeyboardEvent, coarse: number, fine: number): number =>
544
+ ev.shiftKey ? coarse : fine;
545
+
546
+ const handleKey = (ev: KeyboardEvent): boolean => {
547
+ if (disabled) return false;
548
+ const axis = axes().includes(active) ? active : (axes()[0] ?? 'area');
549
+ const dx = step(ev, 0.1, 0.01);
550
+ switch (ev.keysym) {
551
+ case XK_LEFT:
552
+ case XK_RIGHT: {
553
+ const dir = ev.keysym === XK_LEFT ? -1 : 1;
554
+ if (axis === 'hue')
555
+ setHsv(model.h + dir * step(ev, 10, 1), model.s, model.v, true);
556
+ else if (axis === 'alpha')
557
+ commit(
558
+ channelsFromHsv(model.h, model.s, model.v, model.a + dir * dx),
559
+ );
560
+ else setHsv(model.h, model.s + dir * dx, model.v, true);
561
+ return true;
562
+ }
563
+ case XK_UP:
564
+ case XK_DOWN: {
565
+ if (axis !== 'area') return false;
566
+ const dir = ev.keysym === XK_UP ? 1 : -1;
567
+ setHsv(model.h, model.s, model.v + dir * dx, true);
568
+ return true;
569
+ }
570
+ case XK_HOME:
571
+ case XK_END: {
572
+ const end = ev.keysym === XK_END;
573
+ if (axis === 'hue') setHsv(end ? 360 : 0, model.s, model.v, true);
574
+ else if (axis === 'alpha')
575
+ commit(channelsFromHsv(model.h, model.s, model.v, end ? 1 : 0));
576
+ else setHsv(model.h, end ? 1 : 0, model.v, true);
577
+ return true;
578
+ }
579
+ case XK_PAGE_UP:
580
+ case XK_PAGE_DOWN: {
581
+ if (axis !== 'area') return false;
582
+ setHsv(model.h, model.s, ev.keysym === XK_PAGE_UP ? 1 : 0, true);
583
+ return true;
584
+ }
585
+ case XK_TAB: {
586
+ // Only ours when the panel does not own the focus: a picker on a
587
+ // popup is fed its keys by the trigger, and without this its hue
588
+ // slider would be unreachable from the keyboard. Answering `false` at
589
+ // the ends is what lets Tab leave the picker.
590
+ if (focusable) return false;
591
+ const list = axes();
592
+ const at = list.indexOf(axis);
593
+ const next = at + (ev.shiftKey ? -1 : 1);
594
+ if (next < 0 || next >= list.length) return false;
595
+ setActive(list[next]);
596
+ return true;
597
+ }
598
+ default:
599
+ return false;
600
+ }
601
+ };
602
+
603
+ useImperativeHandle(ref, () => ({
604
+ focus: () => {
605
+ areaRef.current?.focus() ?? hueRef.current?.focus();
606
+ },
607
+ handleKey,
608
+ get value() {
609
+ return text;
610
+ },
611
+ get channels() {
612
+ return model;
613
+ },
614
+ }));
615
+
616
+ // --- the parts ----------------------------------------------------------
617
+
618
+ const shown: ColorPickerPart[] =
619
+ parts !== undefined
620
+ ? [...parts]
621
+ : [
622
+ 'area',
623
+ 'hue',
624
+ ...(alpha ? (['alpha'] as const) : []),
625
+ 'fields',
626
+ ...(swatches?.length ? (['swatches'] as const) : []),
627
+ ...(recent?.length ? (['recent'] as const) : []),
628
+ ...(contrast ? (['contrast'] as const) : []),
629
+ ];
630
+
631
+ const cursorShown = focusVisible ?? focused;
632
+ const hue = opaqueHex(channelsFromHsv(model.h, 1, 1));
633
+ const ink = disabled ? theme.textMuted : theme.text;
634
+
635
+ const axisProps = (
636
+ axis: 'area' | 'hue' | 'alpha',
637
+ label: string,
638
+ valueText: string,
639
+ now: number,
640
+ max: number,
641
+ ): Record<string, unknown> => ({
642
+ role: 'slider',
643
+ 'aria-label': label,
644
+ // The number alone means nothing read out loud, so every axis says what
645
+ // it is in words as well — the one part of React Aria's colour grammar
646
+ // that is pure gain.
647
+ 'aria-valuetext': valueText,
648
+ 'aria-valuenow': Math.round(now),
649
+ 'aria-valuemin': 0,
650
+ 'aria-valuemax': max,
651
+ 'aria-disabled': disabled || undefined,
652
+ focusable: focusable && !disabled,
653
+ onFocus: () => {
654
+ setFocused(true);
655
+ setActive(axis);
656
+ },
657
+ onBlur: () => setFocused(false),
658
+ onKeyDown: focusable ? handleKey : undefined,
659
+ });
660
+
661
+ const renderArea = (): ReactElement =>
662
+ hx(
663
+ 'box',
664
+ {
665
+ key: 'area',
666
+ ref: areaRef,
667
+ ...axisProps(
668
+ 'area',
669
+ 'Saturation and brightness',
670
+ `saturation ${Math.round(model.s * 100)}%, brightness ${Math.round(model.v * 100)}%`,
671
+ model.s * 100,
672
+ 100,
673
+ ),
674
+ style: [
675
+ s.area,
676
+ {
677
+ borderWidth: theme.borderWidth,
678
+ borderColor:
679
+ cursorShown && active === 'area'
680
+ ? theme.borderFocus
681
+ : theme.border,
682
+ },
683
+ ],
684
+ ...track('area', areaTo),
685
+ },
686
+ hx('canvas', {
687
+ style: s.pane,
688
+ // Everything the drawing reads: the hue, and the size, which is a
689
+ // constant here precisely so this key can name it.
690
+ cacheKey: `sv:${Math.round(model.h)}:${CONTENT}x${AREA_H}`,
691
+ onDraw: (ctx, { width, height }) => {
692
+ ctx.fillStyle = hue;
693
+ ctx.fillRect(0, 0, width, height);
694
+ // Legacy comma spelling in the stops, for the same reason
695
+ // `formatColor` emits it: it is what ntk's parser understands.
696
+ const white = ctx.createLinearGradient(0, 0, width, 0);
697
+ white.addColorStop(0, 'rgb(255, 255, 255)');
698
+ white.addColorStop(1, 'rgba(255, 255, 255, 0)');
699
+ ctx.fillStyle = white;
700
+ ctx.fillRect(0, 0, width, height);
701
+ const black = ctx.createLinearGradient(0, 0, 0, height);
702
+ black.addColorStop(0, 'rgba(0, 0, 0, 0)');
703
+ black.addColorStop(1, 'rgb(0, 0, 0)');
704
+ ctx.fillStyle = black;
705
+ ctx.fillRect(0, 0, width, height);
706
+ },
707
+ }),
708
+ hx('box', {
709
+ style: [
710
+ s.areaThumb,
711
+ {
712
+ left: model.s * CONTENT - THUMB / 2,
713
+ top: (1 - model.v) * AREA_H - THUMB / 2,
714
+ backgroundColor: opaqueHex(model),
715
+ },
716
+ ],
717
+ }),
718
+ );
719
+
720
+ const renderHue = (): ReactElement =>
721
+ hx(
722
+ 'box',
723
+ {
724
+ key: 'hue',
725
+ ref: hueRef,
726
+ ...axisProps(
727
+ 'hue',
728
+ 'Hue',
729
+ `hue ${Math.round(model.h)} degrees`,
730
+ model.h,
731
+ 360,
732
+ ),
733
+ style: [
734
+ s.strip,
735
+ {
736
+ borderWidth: theme.borderWidth,
737
+ borderColor:
738
+ cursorShown && active === 'hue'
739
+ ? theme.borderFocus
740
+ : theme.border,
741
+ },
742
+ ],
743
+ ...track('hue', hueTo),
744
+ },
745
+ hx('canvas', {
746
+ style: s.pane,
747
+ cacheKey: `hue:${CONTENT}x${STRIP_H}`,
748
+ onDraw: (ctx, { width, height }) => {
749
+ const ramp = ctx.createLinearGradient(0, 0, width, 0);
750
+ for (let stop = 0; stop <= 6; stop += 1) {
751
+ ramp.addColorStop(
752
+ stop / 6,
753
+ opaqueHex(channelsFromHsv(stop * 60, 1, 1)),
754
+ );
755
+ }
756
+ ctx.fillStyle = ramp;
757
+ ctx.fillRect(0, 0, width, height);
758
+ },
759
+ }),
760
+ hx('box', {
761
+ style: [
762
+ s.stripThumb,
763
+ { left: (model.h / 360) * CONTENT - 4, backgroundColor: hue },
764
+ ],
765
+ }),
766
+ );
767
+
768
+ const renderAlpha = (): ReactElement =>
769
+ hx(
770
+ 'box',
771
+ {
772
+ key: 'alpha',
773
+ ref: alphaRef,
774
+ ...axisProps(
775
+ 'alpha',
776
+ 'Opacity',
777
+ `opacity ${Math.round(model.a * 100)}%`,
778
+ model.a * 100,
779
+ 100,
780
+ ),
781
+ style: [
782
+ s.strip,
783
+ {
784
+ borderWidth: theme.borderWidth,
785
+ borderColor:
786
+ cursorShown && active === 'alpha'
787
+ ? theme.borderFocus
788
+ : theme.border,
789
+ },
790
+ ],
791
+ ...track('alpha', alphaTo),
792
+ },
793
+ hx('canvas', {
794
+ style: s.pane,
795
+ cacheKey: `alpha:${opaqueHex(model)}:${CONTENT}x${STRIP_H}`,
796
+ onDraw: (ctx, { width, height }) => {
797
+ drawChecker(ctx, width, height);
798
+ const ramp = ctx.createLinearGradient(0, 0, width, 0);
799
+ const { r, g, b } = model;
800
+ ramp.addColorStop(0, `rgba(${r}, ${g}, ${b}, 0)`);
801
+ ramp.addColorStop(1, `rgb(${r}, ${g}, ${b})`);
802
+ ctx.fillStyle = ramp;
803
+ ctx.fillRect(0, 0, width, height);
804
+ },
805
+ }),
806
+ hx('box', {
807
+ style: [
808
+ s.stripThumb,
809
+ { left: model.a * CONTENT - 4, backgroundColor: opaqueHex(model) },
810
+ ],
811
+ }),
812
+ );
813
+
814
+ /** A colour chip: the checkerboard, then the colour over it. Used for the
815
+ * preview and for every swatch, so a translucent preset reads as one. */
816
+ const chip = (
817
+ // `where` is part of the cache key, not decoration: every chip is the
818
+ // same drawing at a different size, and a key that named only the colour
819
+ // would let a 16px contrast chip composite a 26px preview's surface. The
820
+ // sizes are constants per call site, so the name is enough to separate
821
+ // them — this is the "a key that leaves an input out shows stale pixels"
822
+ // trap, found by looking at a real window.
823
+ where: 'preview' | 'swatch' | 'contrast',
824
+ colour: ColorChannels,
825
+ style: Style | Style[],
826
+ key?: string,
827
+ ): ReactElement =>
828
+ hx('canvas', {
829
+ key,
830
+ style,
831
+ cacheKey: `chip:${where}:${formatColor(colour, 'hex', true)}`,
832
+ onDraw: (ctx, { width, height }) => {
833
+ if (colour.a < 1) drawChecker(ctx, width, height);
834
+ const { r, g, b, a } = colour;
835
+ ctx.fillStyle =
836
+ a < 1 ? `rgba(${r}, ${g}, ${b}, ${a})` : opaqueHex(colour);
837
+ ctx.fillRect(0, 0, width, height);
838
+ },
839
+ });
840
+
841
+ /** What the field accepted, or a revert. A typed value that is not a colour
842
+ * is not an error state — the field simply goes back to the model, which
843
+ * is what every other picker does and what an escape key would have. */
844
+ const applyText = (raw: string): void => {
845
+ if (parseColor(raw)) adopt(raw);
846
+ else setTyped(null);
847
+ };
848
+
849
+ const modelText = (): string => {
850
+ if (textModel === 'hex') return formatColor(model, 'hex', alpha);
851
+ if (textModel === 'rgb') return formatColor(model, 'rgb', alpha);
852
+ return formatColor(model, 'hsl', alpha);
853
+ };
854
+
855
+ const renderFields = (): ReactElement =>
856
+ hx(
857
+ 'box',
858
+ { key: 'fields', style: s.row },
859
+ chip('preview', model, [
860
+ s.preview,
861
+ { borderWidth: theme.borderWidth, borderColor: theme.border },
862
+ ]),
863
+ // An override-redirect popup never takes the focus, so a `<textinput>`
864
+ // inside one could never be typed into — `<ColorField>` renders the
865
+ // value as a readout instead, and its own trigger is where a typed
866
+ // colour goes.
867
+ focusable
868
+ ? hx('textinput', {
869
+ value: typed ?? modelText(),
870
+ 'aria-label': 'Colour value',
871
+ disabled: disabled || undefined,
872
+ style: [
873
+ s.field,
874
+ {
875
+ borderWidth: theme.borderWidth,
876
+ borderRadius: theme.radius,
877
+ borderColor: theme.border,
878
+ backgroundColor: theme.surface,
879
+ fontFamily: theme.monoFamily,
880
+ },
881
+ ],
882
+ onChange: (ev: ChangeEvent<TextInputNode>) => setTyped(ev.value),
883
+ onSubmit: () => applyText(typed ?? ''),
884
+ onBlur: () => (typed === null ? undefined : applyText(typed)),
885
+ onKeyDown: (ev: KeyboardEvent) => {
886
+ if (ev.keysym === XK_ESCAPE) setTyped(null);
887
+ else if (ev.keysym === XK_RETURN) applyText(typed ?? '');
888
+ },
889
+ })
890
+ : hx(
891
+ 'text',
892
+ {
893
+ style: [s.readout, { color: ink, fontFamily: theme.monoFamily }],
894
+ },
895
+ modelText(),
896
+ ),
897
+ hx(
898
+ 'box',
899
+ {
900
+ role: 'button',
901
+ 'aria-label': `Value as ${textModel.toUpperCase()} — press to change`,
902
+ focusable: focusable && !disabled,
903
+ style: [
904
+ s.button,
905
+ {
906
+ borderRadius: theme.radius,
907
+ ':hover': { backgroundColor: theme.surfaceHover },
908
+ ':active': { backgroundColor: theme.surfaceActive },
909
+ },
910
+ ],
911
+ onClick: disabled
912
+ ? undefined
913
+ : () => {
914
+ setTyped(null);
915
+ setTextModel(
916
+ textModel === 'hex'
917
+ ? 'rgb'
918
+ : textModel === 'rgb'
919
+ ? 'hsl'
920
+ : 'hex',
921
+ );
922
+ },
923
+ },
924
+ hx(
925
+ 'text',
926
+ { style: { color: ink, fontSize: 11 } },
927
+ textModel.toUpperCase(),
928
+ ),
929
+ ),
930
+ showDropper &&
931
+ hx(
932
+ 'box',
933
+ {
934
+ role: 'button',
935
+ 'aria-label': 'Pick a colour from the screen',
936
+ focusable: focusable && !disabled,
937
+ disabled: disabled || dropper.picking || undefined,
938
+ style: [
939
+ s.button,
940
+ {
941
+ borderRadius: theme.radius,
942
+ ':hover': { backgroundColor: theme.surfaceHover },
943
+ ':active': { backgroundColor: theme.surfaceActive },
944
+ },
945
+ ],
946
+ onClick: disabled || dropper.picking ? undefined : pickFromScreen,
947
+ },
948
+ React.createElement(EyedropperGlyph, { color: ink }),
949
+ ),
950
+ );
951
+
952
+ const renderSwatches = (
953
+ key: 'swatches' | 'recent',
954
+ list: readonly ColorSwatch[],
955
+ ): ReactElement =>
956
+ hx(
957
+ 'box',
958
+ {
959
+ key,
960
+ role: 'group',
961
+ 'aria-label': key === 'recent' ? 'Recent colours' : 'Swatches',
962
+ style: s.swatches,
963
+ },
964
+ ...list.map((sw, i) => {
965
+ const parsed = parseColor(swatchValue(sw));
966
+ const chosen =
967
+ parsed &&
968
+ formatColor(parsed, 'hex', true) === formatColor(model, 'hex', true);
969
+ return hx(
970
+ 'box',
971
+ {
972
+ key: `${key}:${i}`,
973
+ role: 'button',
974
+ 'aria-label': swatchLabel(sw),
975
+ 'aria-pressed': Boolean(chosen),
976
+ focusable: focusable && !disabled,
977
+ style: [
978
+ s.swatch,
979
+ {
980
+ borderRadius: theme.radius,
981
+ borderWidth: chosen ? 2 : theme.borderWidth,
982
+ borderColor: chosen ? theme.accent : theme.border,
983
+ },
984
+ ],
985
+ onClick: disabled ? undefined : () => adopt(swatchValue(sw)),
986
+ onKeyDown: (ev: KeyboardEvent) => {
987
+ if (disabled) return;
988
+ if (ev.keysym === XK_RETURN || ev.codepoint === 32)
989
+ adopt(swatchValue(sw));
990
+ },
991
+ },
992
+ parsed ? chip('swatch', parsed, s.pane) : null,
993
+ );
994
+ }),
995
+ );
996
+
997
+ const renderContrast = (): ReactElement | null => {
998
+ if (!contrast) return null;
999
+ const ratio = contrastRatio(model, contrast);
1000
+ if (ratio === null) return null;
1001
+ const grade = contrastGrade(ratio);
1002
+ const colour =
1003
+ grade === 'fail'
1004
+ ? theme.danger
1005
+ : grade === 'AA'
1006
+ ? theme.warning
1007
+ : theme.success;
1008
+ return hx(
1009
+ 'box',
1010
+ { key: 'contrast', style: s.contrast },
1011
+ chip('contrast', parseColor(contrast) ?? model, [
1012
+ {
1013
+ width: 16,
1014
+ height: 16,
1015
+ borderWidth: theme.borderWidth,
1016
+ borderColor: theme.border,
1017
+ },
1018
+ ]),
1019
+ hx(
1020
+ 'text',
1021
+ { style: { color: ink, fontSize: 11, flexGrow: 1 } },
1022
+ `${ratio.toFixed(2)}:1`,
1023
+ ),
1024
+ hx(
1025
+ 'text',
1026
+ { style: { color: colour, fontSize: 11, fontWeight: 'bold' } },
1027
+ grade,
1028
+ ),
1029
+ );
1030
+ };
1031
+
1032
+ const renderPart = (part: ColorPickerPart): ReactElement | null => {
1033
+ switch (part) {
1034
+ case 'area':
1035
+ return renderArea();
1036
+ case 'hue':
1037
+ return renderHue();
1038
+ case 'alpha':
1039
+ return renderAlpha();
1040
+ case 'fields':
1041
+ return renderFields();
1042
+ case 'swatches':
1043
+ return swatches?.length ? renderSwatches('swatches', swatches) : null;
1044
+ case 'recent':
1045
+ return recent?.length ? renderSwatches('recent', recent) : null;
1046
+ case 'contrast':
1047
+ return renderContrast();
1048
+ default:
1049
+ return null;
1050
+ }
1051
+ };
1052
+
1053
+ // `data-testname` is a runtime convention — react-x11's queries read it off
1054
+ // `props` — and its element declarations do not carry it, so it is attached
1055
+ // past the type the way `<Terminal>` does.
1056
+ return hx(
1057
+ 'box',
1058
+ {
1059
+ theme,
1060
+ role: 'group',
1061
+ 'aria-label': 'Colour picker',
1062
+ 'aria-disabled': disabled || undefined,
1063
+ style: [s.root, style],
1064
+ 'data-testname': props['data-testname'],
1065
+ } as Parameters<typeof hx<'box'>>[1],
1066
+ ...shown.map(renderPart),
1067
+ children,
1068
+ );
1069
+ }