@orz/ui 0.0.0-stage → 0.0.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 (270) hide show
  1. package/LICENSE +29 -0
  2. package/README.md +541 -2
  3. package/dist/components/avatar.d.ts +14 -0
  4. package/dist/components/avatar.d.ts.map +1 -0
  5. package/dist/components/avatar.js +23 -0
  6. package/dist/components/avatar.js.map +1 -0
  7. package/dist/components/badge.d.ts +10 -0
  8. package/dist/components/badge.d.ts.map +1 -0
  9. package/dist/components/badge.js +26 -0
  10. package/dist/components/badge.js.map +1 -0
  11. package/dist/components/button.d.ts +11 -0
  12. package/dist/components/button.d.ts.map +1 -0
  13. package/dist/components/button.js +40 -0
  14. package/dist/components/button.js.map +1 -0
  15. package/dist/components/card.d.ts +12 -0
  16. package/dist/components/card.d.ts.map +1 -0
  17. package/dist/components/card.js +25 -0
  18. package/dist/components/card.js.map +1 -0
  19. package/dist/components/color-picker.d.ts +58 -0
  20. package/dist/components/color-picker.d.ts.map +1 -0
  21. package/dist/components/color-picker.js +950 -0
  22. package/dist/components/color-picker.js.map +1 -0
  23. package/dist/components/confirm-dialog.d.ts +26 -0
  24. package/dist/components/confirm-dialog.d.ts.map +1 -0
  25. package/dist/components/confirm-dialog.js +18 -0
  26. package/dist/components/confirm-dialog.js.map +1 -0
  27. package/dist/components/dialog.d.ts +26 -0
  28. package/dist/components/dialog.d.ts.map +1 -0
  29. package/dist/components/dialog.js +149 -0
  30. package/dist/components/dialog.js.map +1 -0
  31. package/dist/components/dropdown-menu.d.ts +30 -0
  32. package/dist/components/dropdown-menu.d.ts.map +1 -0
  33. package/dist/components/dropdown-menu.js +100 -0
  34. package/dist/components/dropdown-menu.js.map +1 -0
  35. package/dist/components/filter-menu.d.ts +98 -0
  36. package/dist/components/filter-menu.d.ts.map +1 -0
  37. package/dist/components/filter-menu.js +745 -0
  38. package/dist/components/filter-menu.js.map +1 -0
  39. package/dist/components/highlighted-input.d.ts +40 -0
  40. package/dist/components/highlighted-input.d.ts.map +1 -0
  41. package/dist/components/highlighted-input.js +95 -0
  42. package/dist/components/highlighted-input.js.map +1 -0
  43. package/dist/components/hover-card.d.ts +7 -0
  44. package/dist/components/hover-card.d.ts.map +1 -0
  45. package/dist/components/hover-card.js +22 -0
  46. package/dist/components/hover-card.js.map +1 -0
  47. package/dist/components/input.d.ts +4 -0
  48. package/dist/components/input.d.ts.map +1 -0
  49. package/dist/components/input.js +7 -0
  50. package/dist/components/input.js.map +1 -0
  51. package/dist/components/label.d.ts +5 -0
  52. package/dist/components/label.d.ts.map +1 -0
  53. package/dist/components/label.js +8 -0
  54. package/dist/components/label.js.map +1 -0
  55. package/dist/components/link-chip.d.ts +67 -0
  56. package/dist/components/link-chip.d.ts.map +1 -0
  57. package/dist/components/link-chip.js +71 -0
  58. package/dist/components/link-chip.js.map +1 -0
  59. package/dist/components/load-failure.d.ts +24 -0
  60. package/dist/components/load-failure.d.ts.map +1 -0
  61. package/dist/components/load-failure.js +27 -0
  62. package/dist/components/load-failure.js.map +1 -0
  63. package/dist/components/load-more.d.ts +15 -0
  64. package/dist/components/load-more.d.ts.map +1 -0
  65. package/dist/components/load-more.js +25 -0
  66. package/dist/components/load-more.js.map +1 -0
  67. package/dist/components/media-controls.d.ts +50 -0
  68. package/dist/components/media-controls.d.ts.map +1 -0
  69. package/dist/components/media-controls.js +71 -0
  70. package/dist/components/media-controls.js.map +1 -0
  71. package/dist/components/popover.d.ts +9 -0
  72. package/dist/components/popover.d.ts.map +1 -0
  73. package/dist/components/popover.js +82 -0
  74. package/dist/components/popover.js.map +1 -0
  75. package/dist/components/query-box.d.ts +78 -0
  76. package/dist/components/query-box.d.ts.map +1 -0
  77. package/dist/components/query-box.js +332 -0
  78. package/dist/components/query-box.js.map +1 -0
  79. package/dist/components/query-highlight.d.ts +14 -0
  80. package/dist/components/query-highlight.d.ts.map +1 -0
  81. package/dist/components/query-highlight.js +132 -0
  82. package/dist/components/query-highlight.js.map +1 -0
  83. package/dist/components/select.d.ts +19 -0
  84. package/dist/components/select.d.ts.map +1 -0
  85. package/dist/components/select.js +38 -0
  86. package/dist/components/select.js.map +1 -0
  87. package/dist/components/separator.d.ts +5 -0
  88. package/dist/components/separator.d.ts.map +1 -0
  89. package/dist/components/separator.js +9 -0
  90. package/dist/components/separator.js.map +1 -0
  91. package/dist/components/sheet.d.ts +16 -0
  92. package/dist/components/sheet.d.ts.map +1 -0
  93. package/dist/components/sheet.js +41 -0
  94. package/dist/components/sheet.js.map +1 -0
  95. package/dist/components/skeleton.d.ts +3 -0
  96. package/dist/components/skeleton.d.ts.map +1 -0
  97. package/dist/components/skeleton.js +7 -0
  98. package/dist/components/skeleton.js.map +1 -0
  99. package/dist/components/slider.d.ts +8 -0
  100. package/dist/components/slider.d.ts.map +1 -0
  101. package/dist/components/slider.js +14 -0
  102. package/dist/components/slider.js.map +1 -0
  103. package/dist/components/sonner.d.ts +4 -0
  104. package/dist/components/sonner.d.ts.map +1 -0
  105. package/dist/components/sonner.js +26 -0
  106. package/dist/components/sonner.js.map +1 -0
  107. package/dist/components/switch.d.ts +5 -0
  108. package/dist/components/switch.d.ts.map +1 -0
  109. package/dist/components/switch.js +8 -0
  110. package/dist/components/switch.js.map +1 -0
  111. package/dist/components/table.d.ts +11 -0
  112. package/dist/components/table.d.ts.map +1 -0
  113. package/dist/components/table.js +28 -0
  114. package/dist/components/table.js.map +1 -0
  115. package/dist/components/tabs.d.ts +8 -0
  116. package/dist/components/tabs.d.ts.map +1 -0
  117. package/dist/components/tabs.js +18 -0
  118. package/dist/components/tabs.js.map +1 -0
  119. package/dist/components/textarea.d.ts +4 -0
  120. package/dist/components/textarea.d.ts.map +1 -0
  121. package/dist/components/textarea.js +7 -0
  122. package/dist/components/textarea.js.map +1 -0
  123. package/dist/components/theme-editor/draft.d.ts +235 -0
  124. package/dist/components/theme-editor/draft.d.ts.map +1 -0
  125. package/dist/components/theme-editor/draft.js +638 -0
  126. package/dist/components/theme-editor/draft.js.map +1 -0
  127. package/dist/components/theme-editor/export-panel.d.ts +14 -0
  128. package/dist/components/theme-editor/export-panel.d.ts.map +1 -0
  129. package/dist/components/theme-editor/export-panel.js +39 -0
  130. package/dist/components/theme-editor/export-panel.js.map +1 -0
  131. package/dist/components/theme-editor/form.d.ts +69 -0
  132. package/dist/components/theme-editor/form.d.ts.map +1 -0
  133. package/dist/components/theme-editor/form.js +350 -0
  134. package/dist/components/theme-editor/form.js.map +1 -0
  135. package/dist/components/theme-editor/import-panel.d.ts +21 -0
  136. package/dist/components/theme-editor/import-panel.d.ts.map +1 -0
  137. package/dist/components/theme-editor/import-panel.js +117 -0
  138. package/dist/components/theme-editor/import-panel.js.map +1 -0
  139. package/dist/components/theme-editor/theme-list.d.ts +23 -0
  140. package/dist/components/theme-editor/theme-list.d.ts.map +1 -0
  141. package/dist/components/theme-editor/theme-list.js +42 -0
  142. package/dist/components/theme-editor/theme-list.js.map +1 -0
  143. package/dist/components/theme-editor/token-list.d.ts +30 -0
  144. package/dist/components/theme-editor/token-list.d.ts.map +1 -0
  145. package/dist/components/theme-editor/token-list.js +124 -0
  146. package/dist/components/theme-editor/token-list.js.map +1 -0
  147. package/dist/components/theme-editor/tokens.d.ts +37 -0
  148. package/dist/components/theme-editor/tokens.d.ts.map +1 -0
  149. package/dist/components/theme-editor/tokens.js +155 -0
  150. package/dist/components/theme-editor/tokens.js.map +1 -0
  151. package/dist/components/theme-editor.d.ts +26 -0
  152. package/dist/components/theme-editor.d.ts.map +1 -0
  153. package/dist/components/theme-editor.js +467 -0
  154. package/dist/components/theme-editor.js.map +1 -0
  155. package/dist/components/theme-menu.d.ts +28 -0
  156. package/dist/components/theme-menu.d.ts.map +1 -0
  157. package/dist/components/theme-menu.js +308 -0
  158. package/dist/components/theme-menu.js.map +1 -0
  159. package/dist/components/tooltip.d.ts +11 -0
  160. package/dist/components/tooltip.d.ts.map +1 -0
  161. package/dist/components/tooltip.js +18 -0
  162. package/dist/components/tooltip.js.map +1 -0
  163. package/dist/components/unsaved-changes-dialog.d.ts +39 -0
  164. package/dist/components/unsaved-changes-dialog.d.ts.map +1 -0
  165. package/dist/components/unsaved-changes-dialog.js +46 -0
  166. package/dist/components/unsaved-changes-dialog.js.map +1 -0
  167. package/dist/hooks/scroll-insets.d.ts +89 -0
  168. package/dist/hooks/scroll-insets.d.ts.map +1 -0
  169. package/dist/hooks/scroll-insets.js +265 -0
  170. package/dist/hooks/scroll-insets.js.map +1 -0
  171. package/dist/hooks/use-element-height.d.ts +11 -0
  172. package/dist/hooks/use-element-height.d.ts.map +1 -0
  173. package/dist/hooks/use-element-height.js +33 -0
  174. package/dist/hooks/use-element-height.js.map +1 -0
  175. package/dist/hooks/use-media-query.d.ts +22 -0
  176. package/dist/hooks/use-media-query.d.ts.map +1 -0
  177. package/dist/hooks/use-media-query.js +38 -0
  178. package/dist/hooks/use-media-query.js.map +1 -0
  179. package/dist/hooks/use-overlay-scrollbars.d.ts +31 -0
  180. package/dist/hooks/use-overlay-scrollbars.d.ts.map +1 -0
  181. package/dist/hooks/use-overlay-scrollbars.js +80 -0
  182. package/dist/hooks/use-overlay-scrollbars.js.map +1 -0
  183. package/dist/hooks/use-paged-append.d.ts +7 -0
  184. package/dist/hooks/use-paged-append.d.ts.map +1 -0
  185. package/dist/hooks/use-paged-append.js +53 -0
  186. package/dist/hooks/use-paged-append.js.map +1 -0
  187. package/dist/hooks/use-query-completion.d.ts +27 -0
  188. package/dist/hooks/use-query-completion.d.ts.map +1 -0
  189. package/dist/hooks/use-query-completion.js +100 -0
  190. package/dist/hooks/use-query-completion.js.map +1 -0
  191. package/dist/hooks/use-slash-shortcut.d.ts +9 -0
  192. package/dist/hooks/use-slash-shortcut.d.ts.map +1 -0
  193. package/dist/hooks/use-slash-shortcut.js +38 -0
  194. package/dist/hooks/use-slash-shortcut.js.map +1 -0
  195. package/dist/hooks/viewport-chrome.d.ts +50 -0
  196. package/dist/hooks/viewport-chrome.d.ts.map +1 -0
  197. package/dist/hooks/viewport-chrome.js +170 -0
  198. package/dist/hooks/viewport-chrome.js.map +1 -0
  199. package/dist/index.d.ts +2 -0
  200. package/dist/index.d.ts.map +1 -0
  201. package/dist/index.js +2 -0
  202. package/dist/index.js.map +1 -0
  203. package/dist/lib/cell-width-table.d.ts +3 -0
  204. package/dist/lib/cell-width-table.d.ts.map +1 -0
  205. package/dist/lib/cell-width-table.js +6 -0
  206. package/dist/lib/cell-width-table.js.map +1 -0
  207. package/dist/lib/cell-width.d.ts +7 -0
  208. package/dist/lib/cell-width.d.ts.map +1 -0
  209. package/dist/lib/cell-width.js +50 -0
  210. package/dist/lib/cell-width.js.map +1 -0
  211. package/dist/lib/clipboard.d.ts +8 -0
  212. package/dist/lib/clipboard.d.ts.map +1 -0
  213. package/dist/lib/clipboard.js +19 -0
  214. package/dist/lib/clipboard.js.map +1 -0
  215. package/dist/lib/color.d.ts +40 -0
  216. package/dist/lib/color.d.ts.map +1 -0
  217. package/dist/lib/color.js +177 -0
  218. package/dist/lib/color.js.map +1 -0
  219. package/dist/lib/scroll-map.d.ts +20 -0
  220. package/dist/lib/scroll-map.d.ts.map +1 -0
  221. package/dist/lib/scroll-map.js +46 -0
  222. package/dist/lib/scroll-map.js.map +1 -0
  223. package/dist/lib/text-metrics.d.ts +33 -0
  224. package/dist/lib/text-metrics.d.ts.map +1 -0
  225. package/dist/lib/text-metrics.js +89 -0
  226. package/dist/lib/text-metrics.js.map +1 -0
  227. package/dist/lib/theme-file.d.ts +51 -0
  228. package/dist/lib/theme-file.d.ts.map +1 -0
  229. package/dist/lib/theme-file.js +511 -0
  230. package/dist/lib/theme-file.js.map +1 -0
  231. package/dist/lib/unsaved-guard.d.ts +33 -0
  232. package/dist/lib/unsaved-guard.d.ts.map +1 -0
  233. package/dist/lib/unsaved-guard.js +37 -0
  234. package/dist/lib/unsaved-guard.js.map +1 -0
  235. package/dist/lib/utils.d.ts +3 -0
  236. package/dist/lib/utils.d.ts.map +1 -0
  237. package/dist/lib/utils.js +6 -0
  238. package/dist/lib/utils.js.map +1 -0
  239. package/dist/theme/custom.d.ts +8 -0
  240. package/dist/theme/custom.d.ts.map +1 -0
  241. package/dist/theme/custom.js +56 -0
  242. package/dist/theme/custom.js.map +1 -0
  243. package/dist/theme/index.d.ts +6 -0
  244. package/dist/theme/index.d.ts.map +1 -0
  245. package/dist/theme/index.js +5 -0
  246. package/dist/theme/index.js.map +1 -0
  247. package/dist/theme/paint-guard.d.ts +20 -0
  248. package/dist/theme/paint-guard.d.ts.map +1 -0
  249. package/dist/theme/paint-guard.js +120 -0
  250. package/dist/theme/paint-guard.js.map +1 -0
  251. package/dist/theme/picks.d.ts +22 -0
  252. package/dist/theme/picks.d.ts.map +1 -0
  253. package/dist/theme/picks.js +43 -0
  254. package/dist/theme/picks.js.map +1 -0
  255. package/dist/theme/pref.d.ts +39 -0
  256. package/dist/theme/pref.d.ts.map +1 -0
  257. package/dist/theme/pref.js +129 -0
  258. package/dist/theme/pref.js.map +1 -0
  259. package/dist/theme/provider.d.ts +41 -0
  260. package/dist/theme/provider.d.ts.map +1 -0
  261. package/dist/theme/provider.js +103 -0
  262. package/dist/theme/provider.js.map +1 -0
  263. package/dist/theme/store.d.ts +104 -0
  264. package/dist/theme/store.d.ts.map +1 -0
  265. package/dist/theme/store.js +509 -0
  266. package/dist/theme/store.js.map +1 -0
  267. package/package.json +94 -3
  268. package/styles.css +79 -0
  269. package/theme.css +186 -0
  270. package/vendor/shadcn-tailwind.css +664 -0
@@ -0,0 +1,19 @@
1
+ import { toast } from "sonner";
2
+ /**
3
+ * Copy and say so. The success message names what was copied — the caller
4
+ * knows that; the failure one does not, because every failure here is the
5
+ * same one: no clipboard to write to. Resolves to whether the copy worked,
6
+ * so a caller can offer a fallback such as selecting the text.
7
+ */
8
+ export async function copyToClipboard(text, ok) {
9
+ try {
10
+ await navigator.clipboard.writeText(text);
11
+ toast.success(ok);
12
+ return true;
13
+ }
14
+ catch {
15
+ toast.error("Clipboard is unavailable in this browser");
16
+ return false;
17
+ }
18
+ }
19
+ //# sourceMappingURL=clipboard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clipboard.js","sourceRoot":"","sources":["../../src/lib/clipboard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAE/B;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,IAAY,EACZ,EAAU;IAEV,IAAI,CAAC;QACH,MAAM,SAAS,CAAC,SAAS,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QAC1C,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAClB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,KAAK,CAAC,KAAK,CAAC,0CAA0C,CAAC,CAAC;QACxD,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
@@ -0,0 +1,40 @@
1
+ export type ColorFormat = "oklch" | "hex" | "rgb" | "hsl";
2
+ export type Gamut = "srgb" | "p3" | "out";
3
+ /** l 0–1, c ≥ 0, h in degrees [0, 360) — 0 when the colour has no hue — alpha 0–1. */
4
+ export interface Oklch {
5
+ l: number;
6
+ c: number;
7
+ h: number;
8
+ alpha: number;
9
+ }
10
+ export interface ParsedColor {
11
+ color: Oklch;
12
+ /** The syntax the text was written in, when it is one of the four; null otherwise. */
13
+ format: ColorFormat | null;
14
+ }
15
+ /** Any CSS colour culori parses; null for anything else, including the empty string. */
16
+ export declare function parseColor(text: string): ParsedColor | null;
17
+ export declare function gamutOf(color: Oklch): Gamut;
18
+ /** A display gamut a surface can be bounded by. */
19
+ export type DisplayGamut = Exclude<Gamut, "out">;
20
+ /**
21
+ * The largest chroma at lightness `l` and hue `h` (degrees) that is inside `gamut`, by bisection.
22
+ * It is 0 when `l <= 0` or `l >= 1`.
23
+ */
24
+ export declare function maxChroma(l: number, h: number, gamut: DisplayGamut): number;
25
+ /** CSS Color 4 gamut mapping into sRGB; lightness and hue kept, alpha kept. */
26
+ export declare function mapToSrgb(color: Oklch): Oklch;
27
+ /**
28
+ * The tokens' style: `oklch(0.577 0.245 27.325)`, `oklch(1 0 0 / 10%)`, `#18a46c`, `#18a46c80`,
29
+ * `rgb(34 197 94 / 50%)`, `hsl(330 81% 60%)`. hex, rgb and hsl are mapped into sRGB first.
30
+ * `alpha: false` omits alpha whatever the colour's alpha is. Default `alpha: true`.
31
+ */
32
+ export declare function formatColor(color: Oklch, format: ColorFormat, options?: {
33
+ alpha?: boolean;
34
+ }): string;
35
+ /**
36
+ * WCAG 2 ratio, 1–21. A translucent foreground is composited over the background first; the
37
+ * background's alpha is ignored.
38
+ */
39
+ export declare function contrastRatio(foreground: Oklch, background: Oklch): number;
40
+ //# sourceMappingURL=color.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"color.d.ts","sourceRoot":"","sources":["../../src/lib/color.ts"],"names":[],"mappings":"AA6BA,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,CAAC;AAC1D,MAAM,MAAM,KAAK,GAAG,MAAM,GAAG,IAAI,GAAG,KAAK,CAAC;AAE1C,sFAAsF;AACtF,MAAM,WAAW,KAAK;IACpB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,KAAK,CAAC;IACb,sFAAsF;IACtF,MAAM,EAAE,WAAW,GAAG,IAAI,CAAC;CAC5B;AAyCD,wFAAwF;AACxF,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAqB3D;AAUD,wBAAgB,OAAO,CAAC,KAAK,EAAE,KAAK,GAAG,KAAK,CAK3C;AAED,mDAAmD;AACnD,MAAM,MAAM,YAAY,GAAG,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;AAEjD;;;GAGG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,GAAG,MAAM,CAa3E;AAED,+EAA+E;AAC/E,wBAAgB,SAAS,CAAC,KAAK,EAAE,KAAK,GAAG,KAAK,CAQ7C;AAgBD;;;;GAIG;AACH,wBAAgB,WAAW,CACzB,KAAK,EAAE,KAAK,EACZ,MAAM,EAAE,WAAW,EACnB,OAAO,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,GAC5B,MAAM,CA8BR;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,GAAG,MAAM,CAU1E"}
@@ -0,0 +1,177 @@
1
+ import { converter, modeHsl, modeHsv, modeLrgb, modeOklab, modeOklch, modeP3, modeRgb, parse, useMode as registerMode, toGamut, wcagContrast, } from "culori/fn";
2
+ registerMode(modeRgb);
3
+ registerMode(modeLrgb);
4
+ registerMode(modeOklab);
5
+ registerMode(modeOklch);
6
+ registerMode(modeP3);
7
+ registerMode(modeHsl);
8
+ registerMode(modeHsv);
9
+ const toOklch = converter("oklch");
10
+ const toRgb = converter("rgb");
11
+ const toP3 = converter("p3");
12
+ const toHsl = converter("hsl");
13
+ const toSrgbGamut = toGamut("rgb", "oklch");
14
+ function formatOf(text) {
15
+ if (text.startsWith("#"))
16
+ return "hex";
17
+ if (text.startsWith("rgb(") || text.startsWith("rgba("))
18
+ return "rgb";
19
+ if (text.startsWith("hsl(") || text.startsWith("hsla("))
20
+ return "hsl";
21
+ if (text.startsWith("oklch("))
22
+ return "oklch";
23
+ return null;
24
+ }
25
+ const unit = (v) => Math.min(1, Math.max(0, v));
26
+ function wrapHue(h) {
27
+ const wrapped = h % 360;
28
+ return wrapped < 0 ? wrapped + 360 : wrapped;
29
+ }
30
+ function fromCulori(color) {
31
+ return {
32
+ l: unit(color.l ?? 0),
33
+ c: color.c ?? 0,
34
+ h: color.h === undefined || Number.isNaN(color.h) ? 0 : wrapHue(color.h),
35
+ alpha: color.alpha ?? 1,
36
+ };
37
+ }
38
+ function toCulori(color) {
39
+ return {
40
+ mode: "oklch",
41
+ l: color.l,
42
+ c: color.c,
43
+ h: color.h,
44
+ alpha: color.alpha,
45
+ };
46
+ }
47
+ /** Any CSS colour culori parses; null for anything else, including the empty string. */
48
+ export function parseColor(text) {
49
+ // CSS colour syntax is case-insensitive, culori's parser is not.
50
+ const trimmed = text.trim().toLowerCase();
51
+ if (trimmed === "")
52
+ return null;
53
+ const parsed = parse(trimmed);
54
+ if (parsed === undefined)
55
+ return null;
56
+ const format = formatOf(trimmed);
57
+ // CSS clamps rgb() channels and hsl() saturation and lightness when it parses them, culori
58
+ // does not: unclamped, rgb(300 0 0) is a colour outside sRGB that maps back as salmon, not red.
59
+ // color(srgb …) also parses to rgb, and CSS leaves that unclamped.
60
+ let source = parsed;
61
+ if (format === "rgb" && parsed.mode === "rgb")
62
+ source = {
63
+ ...parsed,
64
+ r: unit(parsed.r),
65
+ g: unit(parsed.g),
66
+ b: unit(parsed.b),
67
+ };
68
+ else if (format === "hsl" && parsed.mode === "hsl")
69
+ source = { ...parsed, s: unit(parsed.s), l: unit(parsed.l) };
70
+ return { color: fromCulori(toOklch(source)), format };
71
+ }
72
+ // Converting a colour sitting exactly on the gamut's edge — every hex value — out of oklch and back
73
+ // lands a hair outside [0, 1]; without the slack, #ffffff would read as outside sRGB.
74
+ const EDGE = 1e-6;
75
+ function inUnitCube(color) {
76
+ return [color.r, color.g, color.b].every((v) => v >= -EDGE && v <= 1 + EDGE);
77
+ }
78
+ export function gamutOf(color) {
79
+ const culori = toCulori(color);
80
+ if (inUnitCube(toRgb(culori)))
81
+ return "srgb";
82
+ if (inUnitCube(toP3(culori)))
83
+ return "p3";
84
+ return "out";
85
+ }
86
+ /**
87
+ * The largest chroma at lightness `l` and hue `h` (degrees) that is inside `gamut`, by bisection.
88
+ * It is 0 when `l <= 0` or `l >= 1`.
89
+ */
90
+ export function maxChroma(l, h, gamut) {
91
+ if (!(l > 0 && l < 1))
92
+ return 0;
93
+ const pixel = { mode: "oklch", l, c: 0, h };
94
+ const convert = gamut === "p3" ? () => toP3(pixel) : () => toRgb(pixel);
95
+ // The in-gamut part of a chroma ray is one interval [0, Cmax], so bisection finds its end.
96
+ let lo = 0;
97
+ let hi = 0.5;
98
+ for (let k = 0; k < 24; k++) {
99
+ pixel.c = (lo + hi) / 2;
100
+ if (inUnitCube(convert()))
101
+ lo = pixel.c;
102
+ else
103
+ hi = pixel.c;
104
+ }
105
+ return lo;
106
+ }
107
+ /** CSS Color 4 gamut mapping into sRGB; lightness and hue kept, alpha kept. */
108
+ export function mapToSrgb(color) {
109
+ if (gamutOf(color) === "srgb")
110
+ return color;
111
+ const mapped = fromCulori(toOklch(toSrgbGamut(toCulori(color))));
112
+ return {
113
+ ...mapped,
114
+ h: mapped.c < 1e-4 ? color.h : mapped.h,
115
+ alpha: color.alpha,
116
+ };
117
+ }
118
+ /** Rounds to `digits` decimals and prints without trailing zeros or a trailing `.`. */
119
+ function num(value, digits) {
120
+ const rounded = Number(value.toFixed(digits));
121
+ return String(Object.is(rounded, -0) ? 0 : rounded);
122
+ }
123
+ const byte = (v) => Math.round(unit(v) * 255);
124
+ const hex2 = (v) => v.toString(16).padStart(2, "0");
125
+ function srgbOf(color) {
126
+ const rgb = toRgb(toCulori(mapToSrgb(color)));
127
+ return { r: unit(rgb.r), g: unit(rgb.g), b: unit(rgb.b) };
128
+ }
129
+ /**
130
+ * The tokens' style: `oklch(0.577 0.245 27.325)`, `oklch(1 0 0 / 10%)`, `#18a46c`, `#18a46c80`,
131
+ * `rgb(34 197 94 / 50%)`, `hsl(330 81% 60%)`. hex, rgb and hsl are mapped into sRGB first.
132
+ * `alpha: false` omits alpha whatever the colour's alpha is. Default `alpha: true`.
133
+ */
134
+ export function formatColor(color, format, options) {
135
+ const withAlpha = options?.alpha ?? true;
136
+ const percent = num(unit(color.alpha) * 100, 1);
137
+ const alphaSuffix = withAlpha && percent !== "100" ? ` / ${percent}%` : "";
138
+ switch (format) {
139
+ case "oklch": {
140
+ const h = num(color.h, 3);
141
+ return `oklch(${num(color.l, 3)} ${num(color.c, 3)} ${h === "360" ? "0" : h}${alphaSuffix})`;
142
+ }
143
+ case "hex": {
144
+ const { r, g, b } = srgbOf(color);
145
+ const a = byte(color.alpha);
146
+ return `#${hex2(byte(r))}${hex2(byte(g))}${hex2(byte(b))}${withAlpha && a < 255 ? hex2(a) : ""}`;
147
+ }
148
+ case "rgb": {
149
+ const { r, g, b } = srgbOf(color);
150
+ return `rgb(${byte(r)} ${byte(g)} ${byte(b)}${alphaSuffix})`;
151
+ }
152
+ case "hsl": {
153
+ // Unquantised, a grey's channels differ in the last bits and hsl's saturation divides that
154
+ // noise by nearly zero: white came out as `hsl(300 Infinity% 100%)`.
155
+ const q = (v) => Math.round(v * 1e9) / 1e9;
156
+ const { r, g, b } = srgbOf(color);
157
+ const hsl = toHsl({ mode: "rgb", r: q(r), g: q(g), b: q(b) });
158
+ const grey = hsl.s === 0 || !Number.isFinite(hsl.s);
159
+ const h = num(grey || hsl.h === undefined ? 0 : hsl.h, 1);
160
+ const s = grey ? 0 : hsl.s;
161
+ return `hsl(${h === "360" ? "0" : h} ${num(s * 100, 1)}% ${num(hsl.l * 100, 1)}%${alphaSuffix})`;
162
+ }
163
+ }
164
+ }
165
+ /**
166
+ * WCAG 2 ratio, 1–21. A translucent foreground is composited over the background first; the
167
+ * background's alpha is ignored.
168
+ */
169
+ export function contrastRatio(foreground, background) {
170
+ const fg = srgbOf(foreground);
171
+ const bg = srgbOf(background);
172
+ const a = unit(foreground.alpha);
173
+ // Source-over in gamma-encoded sRGB, which is what a browser paints.
174
+ const mix = (f, b) => f * a + b * (1 - a);
175
+ return wcagContrast({ mode: "rgb", r: mix(fg.r, bg.r), g: mix(fg.g, bg.g), b: mix(fg.b, bg.b) }, { mode: "rgb", ...bg });
176
+ }
177
+ //# sourceMappingURL=color.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"color.js","sourceRoot":"","sources":["../../src/lib/color.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ export declare const MAX_SPACER_PX = 15000000;
2
+ export interface ScrollGeometry {
3
+ rowHeight: number;
4
+ viewportHeight: number;
5
+ /** Whole rows the viewport shows; a fractional remainder stays empty. */
6
+ viewportRows: number;
7
+ spacerHeight: number;
8
+ /** `scrollTop` maps linearly onto rows instead of one row per row height. */
9
+ scaled: boolean;
10
+ /** The largest top row: the last row then sits on the viewport's last whole row. */
11
+ maxTopRow: number;
12
+ }
13
+ /** Whole rows in `viewportHeight`, at least one. */
14
+ export declare function rowsInViewport(viewportHeight: number, rowHeight: number): number;
15
+ export declare function scrollGeometry(totalRows: number, rowHeight: number, viewportHeight: number): ScrollGeometry;
16
+ export declare function topRowFromScroll(scrollTop: number, g: ScrollGeometry): number;
17
+ export declare function scrollFromTopRow(topRow: number, g: ScrollGeometry): number;
18
+ /** The top row is always whole: the view steps by rows, never shows half of one. */
19
+ export declare function clampTopRow(topRow: number, g: ScrollGeometry): number;
20
+ //# sourceMappingURL=scroll-map.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scroll-map.d.ts","sourceRoot":"","sources":["../../src/lib/scroll-map.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,aAAa,WAAa,CAAC;AAExC,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,yEAAyE;IACzE,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,6EAA6E;IAC7E,MAAM,EAAE,OAAO,CAAC;IAChB,oFAAoF;IACpF,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,oDAAoD;AACpD,wBAAgB,cAAc,CAAC,cAAc,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,UAIvE;AAED,wBAAgB,cAAc,CAC5B,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,cAAc,EAAE,MAAM,GACrB,cAAc,CAehB;AAMD,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,CAAC,EAAE,cAAc,GAAG,MAAM,CAK7E;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,cAAc,GAAG,MAAM,CAI1E;AAED,oFAAoF;AACpF,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,cAAc,GAAG,MAAM,CAErE"}
@@ -0,0 +1,46 @@
1
+ // Firefox caps an element's height near 17.9M px, Chromium and WebKit near
2
+ // 33.5M; staying under both keeps the spacer exact wherever it fits.
3
+ export const MAX_SPACER_PX = 15_000_000;
4
+ /** Whole rows in `viewportHeight`, at least one. */
5
+ export function rowsInViewport(viewportHeight, rowHeight) {
6
+ // clientHeight is rounded to whole pixels, so a viewport sized to exactly
7
+ // n rows of a fractional row height can measure up to half a pixel short.
8
+ return Math.max(1, Math.floor((viewportHeight + 0.5) / rowHeight));
9
+ }
10
+ export function scrollGeometry(totalRows, rowHeight, viewportHeight) {
11
+ const viewportRows = rowsInViewport(viewportHeight, rowHeight);
12
+ const maxTopRow = Math.max(0, totalRows - viewportRows);
13
+ // Scrolling the full extent walks the top row from 0 to maxTopRow, one row
14
+ // height per row; the rows themselves stay pinned to the viewport.
15
+ const contentHeight = maxTopRow * rowHeight + viewportHeight;
16
+ const scaled = contentHeight > MAX_SPACER_PX;
17
+ return {
18
+ rowHeight,
19
+ viewportHeight,
20
+ viewportRows,
21
+ spacerHeight: scaled ? MAX_SPACER_PX : contentHeight,
22
+ scaled,
23
+ maxTopRow,
24
+ };
25
+ }
26
+ function maxScroll(g) {
27
+ return Math.max(0, g.spacerHeight - g.viewportHeight);
28
+ }
29
+ export function topRowFromScroll(scrollTop, g) {
30
+ const range = maxScroll(g);
31
+ const top = Math.min(Math.max(0, scrollTop), range);
32
+ if (!g.scaled)
33
+ return clampTopRow(top / g.rowHeight, g);
34
+ return range > 0 ? clampTopRow((top / range) * g.maxTopRow, g) : 0;
35
+ }
36
+ export function scrollFromTopRow(topRow, g) {
37
+ const row = clampTopRow(topRow, g);
38
+ if (!g.scaled)
39
+ return row * g.rowHeight;
40
+ return g.maxTopRow > 0 ? (row / g.maxTopRow) * maxScroll(g) : 0;
41
+ }
42
+ /** The top row is always whole: the view steps by rows, never shows half of one. */
43
+ export function clampTopRow(topRow, g) {
44
+ return Math.min(Math.max(0, Math.round(topRow)), g.maxTopRow);
45
+ }
46
+ //# sourceMappingURL=scroll-map.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scroll-map.js","sourceRoot":"","sources":["../../src/lib/scroll-map.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,qEAAqE;AACrE,MAAM,CAAC,MAAM,aAAa,GAAG,UAAU,CAAC;AAcxC,oDAAoD;AACpD,MAAM,UAAU,cAAc,CAAC,cAAsB,EAAE,SAAiB;IACtE,0EAA0E;IAC1E,0EAA0E;IAC1E,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,cAAc,GAAG,GAAG,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC;AACrE,CAAC;AAED,MAAM,UAAU,cAAc,CAC5B,SAAiB,EACjB,SAAiB,EACjB,cAAsB;IAEtB,MAAM,YAAY,GAAG,cAAc,CAAC,cAAc,EAAE,SAAS,CAAC,CAAC;IAC/D,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,SAAS,GAAG,YAAY,CAAC,CAAC;IACxD,2EAA2E;IAC3E,mEAAmE;IACnE,MAAM,aAAa,GAAG,SAAS,GAAG,SAAS,GAAG,cAAc,CAAC;IAC7D,MAAM,MAAM,GAAG,aAAa,GAAG,aAAa,CAAC;IAC7C,OAAO;QACL,SAAS;QACT,cAAc;QACd,YAAY;QACZ,YAAY,EAAE,MAAM,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,aAAa;QACpD,MAAM;QACN,SAAS;KACV,CAAC;AACJ,CAAC;AAED,SAAS,SAAS,CAAC,CAAiB;IAClC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,YAAY,GAAG,CAAC,CAAC,cAAc,CAAC,CAAC;AACxD,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,SAAiB,EAAE,CAAiB;IACnE,MAAM,KAAK,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC;IAC3B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,SAAS,CAAC,EAAE,KAAK,CAAC,CAAC;IACpD,IAAI,CAAC,CAAC,CAAC,MAAM;QAAE,OAAO,WAAW,CAAC,GAAG,GAAG,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;IACxD,OAAO,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACrE,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,MAAc,EAAE,CAAiB;IAChE,MAAM,GAAG,GAAG,WAAW,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IACnC,IAAI,CAAC,CAAC,CAAC,MAAM;QAAE,OAAO,GAAG,GAAG,CAAC,CAAC,SAAS,CAAC;IACxC,OAAO,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,SAAS,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAClE,CAAC;AAED,oFAAoF;AACpF,MAAM,UAAU,WAAW,CAAC,MAAc,EAAE,CAAiB;IAC3D,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC;AAChE,CAAC"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Text geometry for middle-eliding renderers. Every one of them reads the
3
+ * same numbers from here, because a second copy of the canvas set-up would let
4
+ * their 1/1 and 3/3 tiers drift apart without either one changing.
5
+ *
6
+ * Nothing here knows what the measured element is for: the budget an element
7
+ * is measured against is the caller's, and that is the part renderers
8
+ * genuinely disagree on.
9
+ */
10
+ export declare const pixels: (value: string) => number;
11
+ /** Measure the actual rendered text nodes, including their clipped characters. */
12
+ export declare function textWidth(element: Element, start?: number, end?: number): number;
13
+ export type SegmentMetrics = {
14
+ /** Width of the complete original segment, measured in CSS pixels. */
15
+ full: number;
16
+ /** First character + native ellipsis + last character (or full if shorter). */
17
+ minimum: number;
18
+ /** The one trailing character the 1/1 tier keeps outside the clipped head. */
19
+ tailWidth: number;
20
+ /** Three characters each side, or `Infinity` where that saves nothing. */
21
+ threeWidth: number;
22
+ /** The three trailing characters the 3/3 tier keeps. */
23
+ tailThreeWidth: number;
24
+ };
25
+ /**
26
+ * What a middle-elision allocator needs about one elidable run, measured in
27
+ * the styles it is actually rendered in.
28
+ *
29
+ * Counted in code points rather than UTF-16 units, so an astral character is
30
+ * one glyph to keep rather than half of a broken pair.
31
+ */
32
+ export declare function segmentMetrics(element: Element, text?: string): SegmentMetrics;
33
+ //# sourceMappingURL=text-metrics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text-metrics.d.ts","sourceRoot":"","sources":["../../src/lib/text-metrics.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,eAAO,MAAM,MAAM,UAAW,MAAM,WAAkC,CAAC;AAEvE,kFAAkF;AAClF,wBAAgB,SAAS,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,SAAI,EAAE,GAAG,SAAW,GAAG,MAAM,CAiB7E;AA8BD,MAAM,MAAM,cAAc,GAAG;IAC3B,sEAAsE;IACtE,IAAI,EAAE,MAAM,CAAC;IACb,+EAA+E;IAC/E,OAAO,EAAE,MAAM,CAAC;IAChB,8EAA8E;IAC9E,SAAS,EAAE,MAAM,CAAC;IAClB,0EAA0E;IAC1E,UAAU,EAAE,MAAM,CAAC;IACnB,wDAAwD;IACxD,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,OAAO,EAChB,IAAI,SAA4B,GAC/B,cAAc,CAyBhB"}
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Text geometry for middle-eliding renderers. Every one of them reads the
3
+ * same numbers from here, because a second copy of the canvas set-up would let
4
+ * their 1/1 and 3/3 tiers drift apart without either one changing.
5
+ *
6
+ * Nothing here knows what the measured element is for: the budget an element
7
+ * is measured against is the caller's, and that is the part renderers
8
+ * genuinely disagree on.
9
+ */
10
+ export const pixels = (value) => Number.parseFloat(value) || 0;
11
+ /** Measure the actual rendered text nodes, including their clipped characters. */
12
+ export function textWidth(element, start = 0, end = Infinity) {
13
+ const walker = document.createTreeWalker(element, NodeFilter.SHOW_TEXT);
14
+ const range = document.createRange();
15
+ let offset = 0;
16
+ let width = 0;
17
+ for (let node = walker.nextNode(); node; node = walker.nextNode()) {
18
+ const length = node.textContent?.length ?? 0;
19
+ const from = Math.max(0, start - offset);
20
+ const to = Math.min(length, end - offset);
21
+ if (from < to) {
22
+ range.setStart(node, from);
23
+ range.setEnd(node, to);
24
+ for (const rect of range.getClientRects())
25
+ width += rect.width;
26
+ }
27
+ offset += length;
28
+ }
29
+ return width;
30
+ }
31
+ function segmentMeasurer(element, text) {
32
+ const style = getComputedStyle(element);
33
+ // A clipped DOM Range can report both the full text and an extra visual
34
+ // ellipsis fragment. Measuring the original substrings on canvas avoids
35
+ // counting that decoration and keeps 1/3 tail changes independent of layout.
36
+ // Canvas creates neither a DOM text copy nor a second selectable identity.
37
+ const context = document.createElement("canvas").getContext("2d");
38
+ if (context) {
39
+ context.font = style.font;
40
+ context.fontKerning = style.fontKerning;
41
+ context.fontStretch = style.fontStretch;
42
+ context.fontVariantCaps = style.fontVariantCaps;
43
+ context.textRendering = style.textRendering;
44
+ context.letterSpacing =
45
+ style.letterSpacing === "normal" ? "0px" : style.letterSpacing;
46
+ context.wordSpacing =
47
+ style.wordSpacing === "normal" ? "0px" : style.wordSpacing;
48
+ }
49
+ return {
50
+ width(start = 0, end = text.length) {
51
+ return context
52
+ ? context.measureText(text.slice(start, end)).width
53
+ : textWidth(element, start, end);
54
+ },
55
+ ellipsis: context?.measureText("…").width ?? pixels(style.fontSize),
56
+ };
57
+ }
58
+ /**
59
+ * What a middle-elision allocator needs about one elidable run, measured in
60
+ * the styles it is actually rendered in.
61
+ *
62
+ * Counted in code points rather than UTF-16 units, so an astral character is
63
+ * one glyph to keep rather than half of a broken pair.
64
+ */
65
+ export function segmentMetrics(element, text = element.textContent ?? "") {
66
+ const chars = Array.from(text);
67
+ const first = chars[0]?.length ?? 0;
68
+ const last = chars.at(-1)?.length ?? 0;
69
+ const measured = segmentMeasurer(element, text);
70
+ const full = measured.width();
71
+ const ellipsis = measured.ellipsis;
72
+ const tailWidth = measured.width(text.length - last);
73
+ const headThree = chars.slice(0, 3).join("").length;
74
+ const tailThree = chars.slice(-3).join("").length;
75
+ return {
76
+ full,
77
+ minimum: chars.length < 3
78
+ ? full
79
+ : Math.min(full, measured.width(0, first) + ellipsis + tailWidth),
80
+ tailWidth,
81
+ threeWidth: chars.length > 6
82
+ ? measured.width(0, headThree) +
83
+ ellipsis +
84
+ measured.width(text.length - tailThree)
85
+ : Infinity,
86
+ tailThreeWidth: measured.width(text.length - tailThree),
87
+ };
88
+ }
89
+ //# sourceMappingURL=text-metrics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text-metrics.js","sourceRoot":"","sources":["../../src/lib/text-metrics.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,51 @@
1
+ import { type AnyCustomTheme, type CustomTerminalTheme, type CustomTheme, type TerminalTheme } from "@orz/themes";
2
+ export declare const THEME_FILE_FORMAT = "orz-themes";
3
+ export declare const THEME_FILE_VERSION = 1;
4
+ /** Text longer than this many characters is refused before parsing; the import panel also checks `File.size` against it before reading a file. */
5
+ export declare const THEME_FILE_MAX_LENGTH = 1000000;
6
+ /**
7
+ * The file for `customs` of both kinds, each in its kind's array, pretty-printed with two-space
8
+ * indent and a trailing newline. Ids and kinds are left out; every based side and based terminal
9
+ * carries `resolved`, computed against `themes` (UI and terminal-only lists together). A custom
10
+ * theme that composes to `null` is written without snapshots for the sides that cannot resolve.
11
+ */
12
+ export declare function serializeThemeFile(customs: readonly AnyCustomTheme[], themes: readonly TerminalTheme[]): string;
13
+ export type ImportStatus = "ok" | "renamed" | "adjusted" | "rejected";
14
+ export interface ImportEntry {
15
+ /** The label as written in the file. */
16
+ sourceLabel: string;
17
+ /** The kind `theme` has, after any conversion. */
18
+ kind: "ui" | "terminal";
19
+ status: ImportStatus;
20
+ /**
21
+ * Present unless rejected: the theme to add, label de-duplicated, no id, no `resolved`. A UI
22
+ * theme carries no `kind`; a terminal theme carries `kind: "terminal"`.
23
+ */
24
+ theme?: Omit<CustomTheme, "id" | "kind"> | Omit<CustomTerminalTheme, "id">;
25
+ /** One sentence each, shown on the row: renames, repairs, or the rejection reason. */
26
+ messages: string[];
27
+ }
28
+ export type ParseResult = {
29
+ ok: false;
30
+ error: string;
31
+ } | {
32
+ ok: true;
33
+ entries: ImportEntry[];
34
+ };
35
+ export interface ParseThemeFileOptions {
36
+ /** The store's slots. Default both true. */
37
+ slots?: {
38
+ ui: boolean;
39
+ terminal: boolean;
40
+ };
41
+ }
42
+ /**
43
+ * Parses and validates a theme file against this app's lists. `existingLabels` are the labels the
44
+ * new themes must not take (custom themes of both kinds, and the base themes of both slots);
45
+ * de-duplication also covers labels taken by earlier entries of the same file. Entries come out
46
+ * in file order, `themes` first, then `terminalThemes`. Never throws.
47
+ */
48
+ export declare function parseThemeFile(text: string, themes: readonly TerminalTheme[], existingLabels: readonly string[], { slots }?: ParseThemeFileOptions): ParseResult;
49
+ /** "X", or "X (n)" with the lowest n ≥ 2 that is free, compared case-insensitively. */
50
+ export declare function uniqueLabel(label: string, taken: readonly string[]): string;
51
+ //# sourceMappingURL=theme-file.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"theme-file.d.ts","sourceRoot":"","sources":["../../src/lib/theme-file.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,cAAc,EAInB,KAAK,mBAAmB,EACxB,KAAK,WAAW,EAOhB,KAAK,aAAa,EAMnB,MAAM,aAAa,CAAC;AAGrB,eAAO,MAAM,iBAAiB,eAAe,CAAC;AAC9C,eAAO,MAAM,kBAAkB,IAAI,CAAC;AACpC,kJAAkJ;AAClJ,eAAO,MAAM,qBAAqB,UAAY,CAAC;AAmD/C;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,SAAS,cAAc,EAAE,EAClC,MAAM,EAAE,SAAS,aAAa,EAAE,GAC/B,MAAM,CA0BR;AAoDD,MAAM,MAAM,YAAY,GAAG,IAAI,GAAG,SAAS,GAAG,UAAU,GAAG,UAAU,CAAC;AAEtE,MAAM,WAAW,WAAW;IAC1B,wCAAwC;IACxC,WAAW,EAAE,MAAM,CAAC;IACpB,kDAAkD;IAClD,IAAI,EAAE,IAAI,GAAG,UAAU,CAAC;IACxB,MAAM,EAAE,YAAY,CAAC;IACrB;;;OAGG;IACH,KAAK,CAAC,EAAE,IAAI,CAAC,WAAW,EAAE,IAAI,GAAG,MAAM,CAAC,GAAG,IAAI,CAAC,mBAAmB,EAAE,IAAI,CAAC,CAAC;IAC3E,sFAAsF;IACtF,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,MAAM,WAAW,GACnB;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC5B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,WAAW,EAAE,CAAA;CAAE,CAAC;AAqfzC,MAAM,WAAW,qBAAqB;IACpC,4CAA4C;IAC5C,KAAK,CAAC,EAAE;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAA;KAAE,CAAC;CAC5C;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,SAAS,aAAa,EAAE,EAChC,cAAc,EAAE,SAAS,MAAM,EAAE,EACjC,EAAE,KAAoC,EAAE,GAAE,qBAA0B,GACnE,WAAW,CAmCb;AAED,uFAAuF;AACvF,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAQ3E"}