@dooph-software/fluid-gradients 1.0.0 → 3.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.
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
  <img src="./fluid-gradients-cover-art.png" width="400" alt="fluid-gradients-cover-art">
5
5
  </p>
6
6
 
7
- Deterministic, seeded fluid-gradient images plus a curated set of <!-- docs:begin:palette-count -->71<!-- docs:end:palette-count --> color palettes. The same seed, palette, size and look always render the same image.
7
+ Deterministic, seeded fluid-gradient images plus a curated set of <!-- docs:begin:palette-count -->95<!-- docs:end:palette-count --> color palettes. The same seed, palette, size and look always render the same image.
8
8
 
9
9
  ```bash
10
10
  npm install @dooph-software/fluid-gradients
@@ -127,16 +127,28 @@ Use `renderMeshGradient(options)` to get the raw `@napi-rs/canvas` canvas if you
127
127
 
128
128
  `look` overrides the rendering knobs. The defaults are exported as `defaultLook`:
129
129
 
130
- <!-- docs:begin:look-knobs -->
130
+ Two knobs add a second pass instead of tuning the fluid. **`sweep`** carries the color through a slow, drifting current like dye in water and softens it like a long exposure, so it pulls into curving wisps and silk-like folds. **`ground`** gives part of the image over to a pale ground in the palette's lightest color, so the color reads as floating over a background with room for text. Both are 0–1 and default to 0, where the image is byte-for-byte the plain fluid. They come from their own seeded stream, so raising one reshapes the same composition rather than starting a new one. They cost about a second at full size (1200×1500), and unlike the fluid, a wider render isn't exactly the left part of a narrower one.
131
131
 
132
- | Knob | Default | Effect |
133
- | ------------------------- | ------- | ------------------------------------------------ |
134
- | `baseNoiseFrequency` | `0.3` | Lower values give broader, softer color regions. |
135
- | `domainWarpStrength` | `1` | How much the color field swirls. |
136
- | `fractalOctaveCount` | `2` | More octaves add finer detail. |
137
- | `colorTransitionContrast` | `1.75` | Sharpness of the edges between colors. |
138
- | `grainOpacity` | `0.09` | Strength of the film-grain overlay. |
132
+ ```ts
133
+ import { generateMeshGradient } from "@dooph-software/fluid-gradients";
139
134
 
135
+ const { buffer } = await generateMeshGradient({
136
+ seed: "post-42",
137
+ palette: "tulip-blur",
138
+ look: { sweep: 0.7, ground: 0.4 },
139
+ });
140
+ ```
141
+
142
+ <!-- docs:begin:look-knobs -->
143
+ | Knob | Default | Effect |
144
+ | --- | --- | --- |
145
+ | `baseNoiseFrequency` | `0.3` | Lower values give broader, softer color regions. |
146
+ | `domainWarpStrength` | `1` | How much the color field swirls. |
147
+ | `fractalOctaveCount` | `2` | More octaves add finer detail. |
148
+ | `colorTransitionContrast` | `1.75` | Sharpness of the edges between colors. |
149
+ | `grainOpacity` | `0.09` | Strength of the film-grain overlay. |
150
+ | `sweep` | `0` | Drifts the color into soft, curving wisps, like dye in water or folded silk, 0–1. 0 is the plain fluid. |
151
+ | `ground` | `0` | Roughly the share of the image, 0–1, left as a pale ground in the palette's lightest color. |
140
152
  <!-- docs:end:look-knobs -->
141
153
 
142
154
  ## CLI
@@ -144,7 +156,7 @@ Use `renderMeshGradient(options)` to get the raw `@napi-rs/canvas` canvas if you
144
156
  Every flag is optional. With none, it writes one WebP to `output/` with a random seed and the palette that seed picks. Both are printed with the result, so passing them back reproduces the image exactly.
145
157
 
146
158
  ```bash
147
- npm run gradient # in this repo (builds first, then runs the CLI)
159
+ npm run gradient # in this repo (runs src/ directly, no build)
148
160
  npx fluid-gradient # anywhere the package is installed
149
161
  npm run gradient -- --seed hero --palette purple-rain --width 1920 --height 1080
150
162
  npx fluid-gradient --seed autocad-0.3.0 --out assets/0.3.0.webp
@@ -152,7 +164,6 @@ npx fluid-gradient --list-palettes
152
164
  ```
153
165
 
154
166
  <!-- docs:begin:cli-help -->
155
-
156
167
  ```text
157
168
  Usage: fluid-gradient [options]
158
169
 
@@ -170,14 +181,17 @@ Options:
170
181
  --fractal-octave-count <n> More octaves add finer detail. Default 2.
171
182
  --color-transition-contrast <n> Sharpness of the edges between colors. Default 1.75.
172
183
  --grain-opacity <0-1> Strength of the film-grain overlay. Default 0.09.
184
+ --sweep <0-1> Drifts the color into soft, curving wisps, like dye in water or folded silk, 0–1. 0 is the plain fluid. Default 0.
185
+ --ground <0-1> Roughly the share of the image, 0–1, left as a pale ground in the palette's lightest color. Default 0.
173
186
 
174
187
  Output & info:
175
188
  --out <path> Output file, .webp or .png. Default: output/<palette>-<seed>.webp
189
+ --random-palette Pick the palette at random instead of deriving it from --seed. The name is printed.
190
+ --chaos Ignore the palettes: use 3–5 random colors (the same --seed gives the same colors).
176
191
  --json Print result metadata as JSON.
177
192
  --list-palettes Print built-in palette names and exit.
178
193
  --help Show this help (-h).
179
194
  ```
180
-
181
195
  <!-- docs:end:cli-help -->
182
196
 
183
197
  Flags are defined once in [src/flags.ts](src/flags.ts) and the CLI builds its parser, help text and options from that table. The table is typed as "one entry per render option", so adding an option to the API fails the typecheck until it has a flag — the CLI and the TS API can't drift. `--out`, `--json`, `--list-palettes` and `--help` are CLI-only, since they're file output and terminal display.
@@ -180,13 +180,43 @@ var colorPalettes = [
180
180
  { name: "onyx-snow", colors: ["#ffffff", "#b0b0b0", "#4a4a4a", "#000000"] },
181
181
  { name: "midnight-ember", colors: ["#f0a468", "#c23f1f", "#2a1410", "#0a0a0c"] },
182
182
  { name: "slate-harbor", colors: ["#aeb7bd", "#6f7b84", "#3c454c", "#1a1f24"] },
183
+ { name: "ember-mono", colors: ["#ff9a6b", "#cf5604", "#632400", "#180501"] },
184
+ // ── Monochromatic: one hue, lightness stepping down (OKLCH-spaced) ──
185
+ { name: "tangerine-mono", colors: ["#ffeee4", "#feb079", "#dc7100", "#824103"] },
186
+ { name: "electric-blue-mono", colors: ["#ebf2fe", "#a4c6fc", "#5d91e7", "#2a5397"] },
187
+ { name: "magenta-mono", colors: ["#feecf5", "#edaed1", "#cd6ca6", "#823564"] },
188
+ { name: "emerald-mono", colors: ["#e2f8ea", "#95d5af", "#2cab70", "#03663e"] },
189
+ { name: "violet-mono", colors: ["#f2f0fe", "#c8b7ff", "#9e71fd", "#5f38a7"] },
190
+ { name: "crimson-mono", colors: ["#ffedec", "#feaaa9", "#f44f5d", "#9e122b"] },
191
+ { name: "aqua-mono", colors: ["#dff7f8", "#8dd2d6", "#39a4a9", "#026266"] },
192
+ { name: "burgundy-mono", colors: ["#fbe0e4", "#dc9ea8", "#a7415a", "#560c24"] },
193
+ { name: "indigo-mono", colors: ["#eaeefe", "#a7b2f7", "#5a5ce2", "#261b81"] },
194
+ { name: "lime-mono", colors: ["#edfcd7", "#c2e87d", "#9fc93e", "#638119"] },
195
+ // ── Monochromatic earth tones: same idea, chroma held up so they stay
196
+ // clay / ochre / moss rather than collapsing to greige ──
197
+ { name: "terracotta-mono", colors: ["#fdeae4", "#f6b19b", "#c5694c", "#73311b"] },
198
+ { name: "ochre-mono", colors: ["#f9edd5", "#dec082", "#a87f09", "#5b4404"] },
199
+ { name: "olive-mono", colors: ["#edf2d7", "#c2cb91", "#848e38", "#474d04"] },
200
+ { name: "fern-mono", colors: ["#e3f5df", "#a9d1a0", "#5f9752", "#2a5320"] },
201
+ { name: "verdigris-mono", colors: ["#d6f7f1", "#96d2c9", "#38988d", "#07534c"] },
202
+ { name: "umber-mono", colors: ["#feeada", "#e1bc9d", "#ab7a4f", "#623f1e"] },
203
+ { name: "adobe-mono", colors: ["#fee9e8", "#eeb3b0", "#bb6d6b", "#6c3434"] },
183
204
  // ── Neutral / mono ──
184
205
  { name: "ivory-ink", colors: ["#f7f4ef", "#d8d2c8", "#8a8378", "#20201d"] },
185
206
  { name: "paper-graphite", colors: ["#f4f4f5", "#cfcfd4", "#83838f", "#1c1c22"] },
186
207
  { name: "bone-coffee", colors: ["#f5efe6", "#d9c9b3", "#9c8468", "#3a2c1e"] },
187
208
  { name: "porcelain-greige", colors: ["#f6f3ef", "#ddd4c9", "#b0a596", "#6a5f52"] },
188
209
  { name: "battleship-steel", colors: ["#e6e9e8", "#b9c0be", "#7d8683", "#3f4745"] },
189
- { name: "gunmetal-fog", colors: ["#eceef1", "#c2c7cd", "#7b838d", "#343a42"] }
210
+ { name: "gunmetal-fog", colors: ["#eceef1", "#c2c7cd", "#7b838d", "#343a42"] },
211
+ { name: "silver-steel", colors: ["#e2e5e8", "#c4c9cf", "#9aa2ac", "#646f7d"] },
212
+ // ── Airy: a near-white base with soft color splashes, in the style of
213
+ // blurred editorial gradients (light stops first, then the splash hues) ──
214
+ { name: "petal-bloom", colors: ["#e4eeff", "#bcd2ff", "#dfa6e9", "#fa787b", "#fba96a"] },
215
+ { name: "tulip-blur", colors: ["#e6f1f6", "#a8d4e8", "#f1c4d0", "#ee9a6e", "#f4b81c"] },
216
+ { name: "silk-rose", colors: ["#fcedf4", "#f2cfdd", "#f0c1b8", "#d68ba5", "#b07a9f"] },
217
+ // ── Flags: sampled from heraldic flag art ──
218
+ { name: "sultans-eagle", colors: ["#f2da38", "#e3c42a", "#c9a21d", "#a1353a", "#6a1c26"] },
219
+ { name: "zhuge-nu", colors: ["#f0f3ee", "#dfc652", "#4fa79b", "#1f8283", "#0f5252"] }
190
220
  ];
191
221
  function paletteAccent(palette) {
192
222
  const colors = palette.colors;
@@ -257,4 +287,4 @@ export {
257
287
  findPalette,
258
288
  pickPalette
259
289
  };
260
- //# sourceMappingURL=chunk-KYCZHKFE.js.map
290
+ //# sourceMappingURL=chunk-OGG3CQZ4.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/color.ts","../src/random.ts","../src/palettes.ts"],"sourcesContent":["/*\n * color — perceptual color math for colors derived from a palette.\n *\n * ## behavior\n * - The complement is the design-sense complement: the hue opposite the source\n * (OKLCH hue + 180°), at the most saturated color sRGB can show for that hue\n * within a readable lightness band (VIVID_L_MIN–VIVID_L_MAX), chroma capped at\n * VIVID_MAX_CHROMA. It is built to stand out on the gradient as a chip or\n * shape, so it deliberately does NOT keep the source's lightness or chroma —\n * keeping them made muted palettes produce muted, invisible complements.\n * - `paletteComplementColor` takes the hue from the accent, unless the accent is\n * too gray to have a reliable hue (chroma < HUE_TRUST_CHROMA); then it uses the\n * chroma-weighted hue of the whole ramp, whose faint tint is still real.\n * - A source with no usable hue at all (pure grays) has no opposite hue; it gets\n * a gray at the opposite lightness instead.\n *\n * ## constraints\n * - Output is part of what consumers store. Complements are rendered as UI\n * chips and geometry next to images generated earlier; changing the color\n * space, the lightness band, the chroma cap, the search steps or the rounding\n * shifts every complement. Treat it like the render pipeline: a deliberate,\n * announced change, not a refactor.\n * - Zero dependencies. This module is reachable from the `/palettes` entry,\n * which promises to be safe for browser and edge bundles.\n */\n\ntype Rgb = [number, number, number];\n\n/** Below this OKLCH chroma a color has no meaningful hue. */\nconst ACHROMATIC_CHROMA = 0.01;\n/** Below this, an accent's hue is too faint to trust; use the whole ramp's instead. */\nconst HUE_TRUST_CHROMA = 0.03;\n/** Lightness band searched for the most saturated opposite color (OKLab L, in hundredths). */\nconst VIVID_L_MIN = 50;\nconst VIVID_L_MAX = 80;\n/** Cap so the most saturated hues (e.g. blue, magenta) stay vivid rather than neon. */\nconst VIVID_MAX_CHROMA = 0.2;\n/** Upper bound of the chroma search; no sRGB color exceeds it. */\nconst CHROMA_SEARCH_MAX = 0.4;\n/** Bisection steps; 2^-24 of the search range is far below 1/255 per channel. */\nconst GAMUT_STEPS = 24;\n\nconst toLinear = (c: number) => (c <= 0.04045 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4);\nconst toGamma = (c: number) => (c <= 0.0031308 ? 12.92 * c : 1.055 * c ** (1 / 2.4) - 0.055);\n\nfunction hexToRgb(hex: string): Rgb {\n return [1, 3, 5].map((i) => parseInt(hex.slice(i, i + 2), 16) / 255) as Rgb;\n}\n\nfunction rgbToHex(rgb: Rgb): string {\n return (\n '#' +\n rgb\n .map((c) => Math.round(Math.min(1, Math.max(0, c)) * 255).toString(16).padStart(2, '0'))\n .join('')\n );\n}\n\nfunction hexToOklab(hex: string): Rgb {\n const [r, g, b] = hexToRgb(hex).map(toLinear);\n const l = Math.cbrt(0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b);\n const m = Math.cbrt(0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b);\n const s = Math.cbrt(0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b);\n return [\n 0.2104542553 * l + 0.793617785 * m - 0.0040720468 * s,\n 1.9779984951 * l - 2.428592205 * m + 0.4505937099 * s,\n 0.0259040371 * l + 0.7827717662 * m - 0.808675766 * s,\n ];\n}\n\n/** Returns sRGB in gamma space, possibly outside [0, 1] when out of gamut. */\nfunction oklabToSrgb([L, a, b]: Rgb): Rgb {\n const l = (L + 0.3963377774 * a + 0.2158037573 * b) ** 3;\n const m = (L - 0.1055613458 * a - 0.0638541728 * b) ** 3;\n const s = (L - 0.0894841775 * a - 1.291485548 * b) ** 3;\n return [\n 4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s,\n -1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s,\n -0.0041960863 * l - 0.7034186147 * m + 1.707614701 * s,\n ].map(toGamma) as Rgb;\n}\n\nconst inGamut = (rgb: Rgb) => rgb.every((c) => c >= 0 && c <= 1);\nconst atLch = (L: number, C: number, hue: number) =>\n oklabToSrgb([L, C * Math.cos(hue), C * Math.sin(hue)]);\n\n/** Largest chroma sRGB can show at this lightness and hue. */\nfunction maxChroma(L: number, hue: number): number {\n let low = 0;\n let high = CHROMA_SEARCH_MAX;\n for (let step = 0; step < GAMUT_STEPS; step++) {\n const mid = (low + high) / 2;\n if (inGamut(atLch(L, mid, hue))) low = mid;\n else high = mid;\n }\n return low;\n}\n\n/** The most saturated in-band color at the hue opposite (a, b). */\nfunction vividOpposite(a: number, b: number): string {\n const hue = Math.atan2(b, a) + Math.PI;\n let bestL = VIVID_L_MIN / 100;\n let bestC = -1;\n for (let l = VIVID_L_MIN; l <= VIVID_L_MAX; l++) {\n const chroma = maxChroma(l / 100, hue);\n if (chroma > bestC) {\n bestC = chroma;\n bestL = l / 100;\n }\n }\n return rgbToHex(atLch(bestL, Math.min(bestC, VIVID_MAX_CHROMA), hue));\n}\n\n/** A hueless color's only opposite: the same gray on the other side of mid lightness. */\nfunction oppositeGray(L: number): string {\n return rgbToHex(atLch(L > 0.5 ? 0.25 : 0.85, 0, 0));\n}\n\n/**\n * The complement of a `#rrggbb` color, built to stand out: the opposite OKLCH\n * hue at its most saturated readable lightness. Grays get the opposite-lightness gray.\n */\nexport function complementColor(hex: string): string {\n const [L, a, b] = hexToOklab(hex);\n return Math.hypot(a, b) < ACHROMATIC_CHROMA ? oppositeGray(L) : vividOpposite(a, b);\n}\n\n/**\n * Complement for a palette: opposite the accent's hue, or — when the accent is\n * near-gray — opposite the chroma-weighted hue of the whole ramp.\n */\nexport function paletteComplementColor(accent: string, ramp: readonly string[]): string {\n const [L, a, b] = hexToOklab(accent);\n if (Math.hypot(a, b) >= HUE_TRUST_CHROMA) return vividOpposite(a, b);\n\n // Summing OKLab a/b weights each stop's hue by its chroma.\n let sumA = 0;\n let sumB = 0;\n for (const color of ramp) {\n const [, ca, cb] = hexToOklab(color);\n sumA += ca;\n sumB += cb;\n }\n const meanA = sumA / ramp.length;\n const meanB = sumB / ramp.length;\n return Math.hypot(meanA, meanB) < ACHROMATIC_CHROMA / 2 ? oppositeGray(L) : vividOpposite(meanA, meanB);\n}\n","/*\n * random — the deterministic RNG behind every seeded image and every palette pick.\n *\n * ## behavior\n * - `hashStringToInt` is FNV-1a/32; `createSeededRandom` is Mulberry32 returning\n * floats in [0, 1). Both are re-exported from the package root, so their\n * signatures are public API.\n * - Two consumers: `render.ts` seeds one stream per image and draws from it for\n * the whole pipeline, and `pickPalette` in `palettes.ts` scores each palette\n * with a fresh one-shot stream.\n *\n * ## constraints\n * - Both algorithms are frozen. These are the exact functions Aspect's released\n * 0.3.0 artwork was rendered with, and `test/fluid-gradients.test.mjs` compares\n * a render byte-for-byte against that shipped image. Swapping in a \"better\"\n * hash or a stronger PRNG — or reordering the mixing steps — changes every\n * image every seed has ever produced and reshuffles which palette each seed\n * picks. There is no migration for artwork already published.\n * - `hashStringToInt` hashes the raw string, code unit by code unit. Do not\n * normalize the input here (trim, lowercase, NFC): seeds are arbitrary user\n * strings, and folding two of them together remaps the images they render.\n * A caller that wants normalized seeds normalizes before calling.\n */\n\n/** FNV-1a hash of a string to an unsigned 32-bit integer. */\nexport function hashStringToInt(seed: string): number {\n let hash = 2166136261 >>> 0;\n for (let i = 0; i < seed.length; i++) {\n hash ^= seed.charCodeAt(i);\n hash = Math.imul(hash, 16777619);\n }\n return hash >>> 0;\n}\n\n/** Mulberry32 PRNG. Returns floats in [0, 1). */\nexport function createSeededRandom(seedInt: number): () => number {\n let state = seedInt;\n return function nextRandom() {\n state |= 0;\n state = (state + 0x6d2b79f5) | 0;\n let mixed = Math.imul(state ^ (state >>> 15), 1 | state);\n mixed = (mixed + Math.imul(mixed ^ (mixed >>> 7), 61 | mixed)) ^ mixed;\n return ((mixed ^ (mixed >>> 14)) >>> 0) / 4294967296;\n };\n}\n","/*\n * palettes — the curated palette set, its runtime validation, and seed → palette\n * selection.\n *\n * Design intent for new entries: analogous families anchored by neutrals, muted\n * and earthy options included. Ramps run light → dark, the order the renderer\n * maps noise values along; the \"dark-focused\" group starts mid-tone on purpose\n * so those images read dark with the light stop as a glow.\n *\n * ## behavior\n * - `colorPalettes` is the built-in set. `PaletteName` is derived from it, so\n * the literal names are part of the package's public types.\n * - `pickPalette` uses rendezvous (highest-random-weight) hashing: each palette\n * scores `seed::palette::<name>` through the RNG and the highest wins, with\n * the name as the tiebreak.\n * - `validatePalette` / `validatePalettes` are the runtime gate for palettes\n * that bypassed the type system (JSON, CLI input, casts).\n * - `paletteAccent` is the ramp's mid stop by position; `paletteComplement` is\n * a vivid opposite-hue color for chips, computed by `./color`.\n *\n * ## constraints\n * - A palette's name is its identity, not a label. It is the rendezvous hash\n * key, so renaming one re-picks every seed that had landed on it; it is also\n * the filename in `examples/` and the key in `examples/featured.json`.\n * Renaming a palette is a breaking change to callers' existing artwork, not a\n * copy edit.\n * - Selection stays rendezvous-hashed. An index- or modulo-based pick over the\n * array is smaller code and re-picks nearly every seed the moment a palette is\n * added or removed; `test/fluid-gradients.test.mjs` asserts that adding one\n * moves only the seeds that land on the new palette. The `::palette::`\n * separator and the run through `createSeededRandom` are part of the key —\n * hashing `seed + name` directly mixes shared prefixes poorly and is a\n * different assignment for every seed.\n * - `colorPalettes` keeps `as const satisfies readonly Palette[]`. Dropping the\n * `as const` widens `PaletteName` to `string`, which silently removes name\n * checking for every consumer; the `@ts-expect-error` lines in\n * `test/types.check.ts` then stop erroring and `npm run lint` fails.\n * - Every ramp is 3–5 stops and the runtime check enforces it. The width of\n * `PaletteColors` and `MIN_PALETTE_COLORS`/`MAX_PALETTE_COLORS` must move\n * together — a 6-stop tuple added to the type alone would pass typecheck and\n * then throw at render time for the caller.\n */\nimport { complementColor, paletteComplementColor } from './color';\nimport { createSeededRandom, hashStringToInt } from './random';\n\nexport { complementColor };\n\nexport const MIN_PALETTE_COLORS = 3;\nexport const MAX_PALETTE_COLORS = 5;\n\n/** `#rrggbb`. The type only checks the `#`; the 6 hex digits are checked at runtime. */\nexport type HexColor = `#${string}`;\n\n/** A color ramp of 3–5 stops, light → dark. */\nexport type PaletteColors =\n | readonly [HexColor, HexColor, HexColor]\n | readonly [HexColor, HexColor, HexColor, HexColor]\n | readonly [HexColor, HexColor, HexColor, HexColor, HexColor];\n\nexport type Palette = {\n /** Unique within a palette set. Seed-based picks are keyed on it, so renaming reshuffles. */\n name: string;\n colors: PaletteColors;\n};\n\nexport const colorPalettes = [\n // ── Warm: amber / cream / clay / terracotta (analogous) ──\n { name: 'amber-latte', colors: ['#faf4ea', '#f2d29b', '#e0a45c', '#b9722f'] },\n { name: 'terracotta-sand', colors: ['#f6ede1', '#e8c9a0', '#cf8f63', '#9c4e33'] },\n { name: 'peach-blush', colors: ['#fdf1ec', '#f8cbb6', '#ef9a8a', '#d76e73'] },\n { name: 'coral-cream', colors: ['#fbe9df', '#f5b79a', '#e8735a', '#c33f36'] },\n { name: 'honey-espresso', colors: ['#f7ecd9', '#e5b877', '#b9793f', '#5f3418'] },\n { name: 'apricot-plum', colors: ['#fdf0e6', '#f6c79a', '#e58b6f', '#7d4a6b'] },\n { name: 'golden-hour', colors: ['#fdf3e1', '#f7cf8a', '#e89a5a', '#9a4a3a'] },\n { name: 'vermilion-blush', colors: ['#fbe8e2', '#f7c6a8', '#e4562f', '#a82a1c'] },\n { name: 'ivory-gilt', colors: ['#fffdf7', '#f7ecd0', '#e0c47e', '#b08c3a'] },\n { name: 'rose-gold', colors: ['#fceee8', '#f2cdbd', '#dba18c', '#b3705c'] },\n { name: 'peony-glow', colors: ['#fdeef3', '#f9b3c8', '#f78a5c', '#f2a93f'] },\n\n // ── Pink / violet / mauve ──\n { name: 'lavender-rose', colors: ['#f4eefb', '#d9c2ef', '#c98fb8', '#8a5fd0'] },\n { name: 'violet-mist', colors: ['#f1eefb', '#c9b8ef', '#8f7fe0', '#4a3d9c'] },\n { name: 'mauve-plum', colors: ['#f6f0f2', '#d8b8c6', '#a5738c', '#5f3a55'] },\n { name: 'periwinkle-blush', colors: ['#f0f0fb', '#c3c6f0', '#cf9fc6', '#6a5fd0'] },\n { name: 'orchid-frost', colors: ['#f6eff6', '#e0c2e0', '#b98fc4', '#7a5aa6', '#3f2f5c'] },\n { name: 'quartz-rose', colors: ['#faf1f1', '#ecc9cc', '#c99aa3', '#8a5f6a'] },\n { name: 'wisteria-sky', colors: ['#f2f0fb', '#cfc6ef', '#9fb2e6', '#4f6aa6'] },\n { name: 'zephyr-dawn', colors: ['#fdf3f5', '#f2d6e2', '#c6d6f2', '#7f9fd8'] },\n { name: 'candy-sky', colors: ['#f6e8ff', '#f7a3e8', '#9aa8f5', '#3fa0f5'] },\n\n // ── Cool: blue / slate / sky (analogous) ──\n { name: 'sky-slate', colors: ['#eef4fb', '#bcd2ef', '#6f8fc4', '#2f3f66'] },\n { name: 'teal-fog', colors: ['#eef7f6', '#b6dcd8', '#5fa39c', '#234a49'] },\n { name: 'arctic-ink', colors: ['#f2f6f8', '#cddbe2', '#7f96a3', '#1c2630'] },\n { name: 'cloud-navy', colors: ['#f0f3f7', '#c6d1de', '#7488a3', '#1f2c44'] },\n { name: 'denim-clay', colors: ['#eef2f6', '#aebfd0', '#5f7794', '#b06a4e'] },\n { name: 'cobalt-ice', colors: ['#e8eeff', '#b9cdfb', '#2f4fd8', '#1836b8'] },\n\n // ── Green: sage / moss / mint / olive (analogous) ──\n { name: 'sage-mist', colors: ['#f2f5ee', '#cdd8bd', '#8fa579', '#4a5c3a'] },\n { name: 'mint-sea', colors: ['#eef8f4', '#b6e2d2', '#5fb39c', '#1f5c4d'] },\n { name: 'olive-cream', colors: ['#f6f3e6', '#dbd09b', '#a39a5c', '#5f5a2f'] },\n { name: 'eucalyptus-fog', colors: ['#eff4f1', '#cbdcd2', '#94b3a6', '#4f6b60'] },\n { name: 'kelp-forest', colors: ['#eef3ea', '#a9c49a', '#4f7a5a', '#1f3a2f'] },\n { name: 'yuzu-mint', colors: ['#fbfbe6', '#eef29a', '#b3d98a', '#3f8f7a'] },\n { name: 'lime-lagoon', colors: ['#f8ffe8', '#c8f56a', '#6fe3d8', '#2fc6d8'] },\n { name: 'bliss-meadow', colors: ['#f4fbff', '#8ed0f2', '#6cb03a', '#2d6b22'] },\n\n // ── Analogous + a single contrasting splash ──\n { name: 'periwinkle-gold', colors: ['#dfe6ff', '#a9b6f0', '#5b6bd6', '#e6c25a'] },\n { name: 'lilac-lemon', colors: ['#f3eefb', '#d3c2ef', '#9f8fd0', '#e6d25a'] },\n { name: 'blush-teal', colors: ['#fbeee9', '#f2c2b0', '#d78a8f', '#3f9c94'] },\n { name: 'sage-coral', colors: ['#f2f5ee', '#cdd8bd', '#8fa579', '#e0755a'] },\n { name: 'slate-amber', colors: ['#eef2f6', '#bcc9d8', '#5f7794', '#e0a24e'] },\n { name: 'ultramarine-sand', colors: ['#dfe8ff', '#e6c27a', '#4757dd', '#232a9c'] },\n { name: 'amber-tide', colors: ['#eef4f8', '#a9c8ef', '#f2b96b', '#df8a33'] },\n\n // ── Colorful / multi-hue (rich & saturated, jewel tones) ──\n { name: 'sunset-rose', colors: ['#fce9d6', '#f6a95c', '#e8617a', '#8a3d7a'] },\n { name: 'berry-bloom', colors: ['#fbe9f2', '#e88fb8', '#c94f8f', '#7a2f6a'] },\n { name: 'jewel-peacock', colors: ['#e6f5f0', '#4fb3a6', '#1f7a99', '#243f8a'] },\n { name: 'iris-meadow', colors: ['#eaf5ec', '#7cc48f', '#3f8fb3', '#5f4fb0'] },\n { name: 'autumn-spice', colors: ['#f7ecd6', '#dba24f', '#b5522f', '#6e2f4a'] },\n { name: 'ultraviolet-sea', colors: ['#e9f0fb', '#6f9fe0', '#4a5fc4', '#6a3da6'] },\n { name: 'purple-rain', colors: ['#f1ebf7', '#b69ad6', '#6f3fa8', '#2a1740'] },\n { name: 'nectarine-fizz', colors: ['#fff1e6', '#ffc59a', '#ff8a5c', '#d9485f'] },\n { name: 'raspberry-cream', colors: ['#fdeef1', '#f4a9bd', '#d9537a', '#8a1f45'] },\n { name: 'pastel-confetti', colors: ['#ffffff', '#f9f3cf', '#cbbcf2', '#e8798f'] },\n { name: 'burgundy-velvet', colors: ['#f2dfe0', '#c98a91', '#8a2740', '#4a0f22'] },\n { name: 'indigo-beam', colors: ['#ffffff', '#a9b6f7', '#4a3fe0', '#2a1fb0'] },\n { name: 'crimson-ice', colors: ['#f2f7fc', '#f8d7e0', '#e0517f', '#a81050'] },\n\n // ── Muted / dusty / earthy ──\n { name: 'fog-plum', colors: ['#f2f0f3', '#cfc6d2', '#9a8a9c', '#5f4a5c'] },\n { name: 'sand-sky', colors: ['#f4efe4', '#e0d3b8', '#a9bcc4', '#5f7f8c'] },\n { name: 'xanadu-sage', colors: ['#f1f4f1', '#c8d3c8', '#738678', '#36443a'] },\n { name: 'walnut-grain', colors: ['#e8d5b7', '#c49a6c', '#8a5a34', '#4a2c17'] },\n\n // ── Dark-focused: ramps that start mid-tone instead of near-white, so the\n // image reads dark overall with the lighter stop as a glow ──\n { name: 'onyx-gold', colors: ['#e8c877', '#b8862f', '#3a2f14', '#0b0b0d'] },\n { name: 'onyx-silver', colors: ['#d7dade', '#8b9199', '#3a3f45', '#0c0d0f'] },\n { name: 'graphite-royal', colors: ['#9fb0e8', '#3f56c4', '#2a2e38', '#101218'] },\n { name: 'onyx-ultraviolet', colors: ['#c9a6ff', '#7b3ff2', '#2a1450', '#08070d'] },\n { name: 'onyx-snow', colors: ['#ffffff', '#b0b0b0', '#4a4a4a', '#000000'] },\n { name: 'midnight-ember', colors: ['#f0a468', '#c23f1f', '#2a1410', '#0a0a0c'] },\n { name: 'slate-harbor', colors: ['#aeb7bd', '#6f7b84', '#3c454c', '#1a1f24'] },\n\n // ── Neutral / mono ──\n { name: 'ivory-ink', colors: ['#f7f4ef', '#d8d2c8', '#8a8378', '#20201d'] },\n { name: 'paper-graphite', colors: ['#f4f4f5', '#cfcfd4', '#83838f', '#1c1c22'] },\n { name: 'bone-coffee', colors: ['#f5efe6', '#d9c9b3', '#9c8468', '#3a2c1e'] },\n { name: 'porcelain-greige', colors: ['#f6f3ef', '#ddd4c9', '#b0a596', '#6a5f52'] },\n { name: 'battleship-steel', colors: ['#e6e9e8', '#b9c0be', '#7d8683', '#3f4745'] },\n { name: 'gunmetal-fog', colors: ['#eceef1', '#c2c7cd', '#7b838d', '#343a42'] },\n] as const satisfies readonly Palette[];\n\n/** Union of every built-in palette name, e.g. `\"mint-sea\"`. */\nexport type PaletteName = (typeof colorPalettes)[number]['name'];\n\n/** Primary accent for UI chips: the palette ramp's mid-tone. */\nexport function paletteAccent(palette: Palette): string {\n const colors = palette.colors;\n return colors[Math.floor(colors.length / 2)];\n}\n\n/**\n * A color built to stand out on the palette's images: the hue opposite the\n * accent (or the whole ramp, when the accent is near-gray) at its most saturated\n * readable lightness. For chips, geometry and highlights.\n */\nexport function paletteComplement(palette: Palette): string {\n return paletteComplementColor(paletteAccent(palette), palette.colors);\n}\n\nconst HEX_COLOR = /^#[0-9a-fA-F]{6}$/;\n\n/**\n * Runtime check for palettes that didn't come through the type system\n * (JSON, CLI input, `as` casts). Throws with the offending palette's name.\n */\nexport function validatePalette(palette: Palette): void {\n const label = `Palette \"${palette?.name}\"`;\n if (typeof palette?.name !== 'string' || palette.name.length === 0) {\n throw new Error('Palette needs a non-empty name.');\n }\n const { colors } = palette;\n if (\n !Array.isArray(colors) ||\n colors.length < MIN_PALETTE_COLORS ||\n colors.length > MAX_PALETTE_COLORS\n ) {\n throw new Error(\n `${label} needs ${MIN_PALETTE_COLORS}–${MAX_PALETTE_COLORS} colors. Got ${Array.isArray(colors) ? colors.length : typeof colors}.`,\n );\n }\n for (const color of colors) {\n if (typeof color !== 'string' || !HEX_COLOR.test(color)) {\n throw new Error(`${label} has an invalid color \"${color}\". Use #rrggbb.`);\n }\n }\n}\n\n/** Validates every palette and that names are unique. */\nexport function validatePalettes(palettes: readonly Palette[]): void {\n if (palettes.length === 0) throw new Error('At least one palette is required.');\n const seen = new Set<string>();\n for (const palette of palettes) {\n validatePalette(palette);\n if (seen.has(palette.name)) throw new Error(`Duplicate palette name \"${palette.name}\".`);\n seen.add(palette.name);\n }\n}\n\n/** Look up a palette by name. Throws if not found. */\nexport function findPalette(\n name: PaletteName | (string & {}),\n palettes: readonly Palette[] = colorPalettes,\n): Palette {\n const palette = palettes.find((p) => p.name === name);\n if (!palette) {\n throw new Error(`Unknown palette \"${name}\". Valid: ${palettes.map((p) => p.name).join(', ')}`);\n }\n return palette;\n}\n\n/**\n * Deterministically pick a palette from a seed.\n *\n * Uses rendezvous (highest-random-weight) hashing: every palette gets a\n * pseudo-random score from `seed + name`, and the highest score wins. The list's\n * order doesn't matter, adding a palette only moves the seeds the new palette\n * wins (~1 in N), and removing one only moves the seeds that had picked it.\n */\nexport function pickPalette(seed: string, palettes: readonly Palette[] = colorPalettes): Palette {\n let best: Palette | undefined;\n let bestScore = -1;\n for (const palette of palettes) {\n // Run the hash through the PRNG once: FNV-1a alone mixes trailing\n // characters poorly, and names often share prefixes.\n const score = createSeededRandom(hashStringToInt(`${seed}::palette::${palette.name}`))();\n if (score > bestScore || (score === bestScore && palette.name < best!.name)) {\n best = palette;\n bestScore = score;\n }\n }\n if (!best) throw new Error('At least one palette is required.');\n return best;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/color.ts","../src/random.ts","../src/palettes.ts"],"sourcesContent":["/*\n * color — perceptual color math for colors derived from a palette.\n *\n * ## behavior\n * - The complement is the design-sense complement: the hue opposite the source\n * (OKLCH hue + 180°), at the most saturated color sRGB can show for that hue\n * within a readable lightness band (VIVID_L_MIN–VIVID_L_MAX), chroma capped at\n * VIVID_MAX_CHROMA. It is built to stand out on the gradient as a chip or\n * shape, so it deliberately does NOT keep the source's lightness or chroma —\n * keeping them made muted palettes produce muted, invisible complements.\n * - `paletteComplementColor` takes the hue from the accent, unless the accent is\n * too gray to have a reliable hue (chroma < HUE_TRUST_CHROMA); then it uses the\n * chroma-weighted hue of the whole ramp, whose faint tint is still real.\n * - A source with no usable hue at all (pure grays) has no opposite hue; it gets\n * a gray at the opposite lightness instead.\n *\n * ## constraints\n * - Output is part of what consumers store. Complements are rendered as UI\n * chips and geometry next to images generated earlier; changing the color\n * space, the lightness band, the chroma cap, the search steps or the rounding\n * shifts every complement. Treat it like the render pipeline: a deliberate,\n * announced change, not a refactor.\n * - Zero dependencies. This module is reachable from the `/palettes` entry,\n * which promises to be safe for browser and edge bundles.\n */\n\ntype Rgb = [number, number, number];\n\n/** Below this OKLCH chroma a color has no meaningful hue. */\nconst ACHROMATIC_CHROMA = 0.01;\n/** Below this, an accent's hue is too faint to trust; use the whole ramp's instead. */\nconst HUE_TRUST_CHROMA = 0.03;\n/** Lightness band searched for the most saturated opposite color (OKLab L, in hundredths). */\nconst VIVID_L_MIN = 50;\nconst VIVID_L_MAX = 80;\n/** Cap so the most saturated hues (e.g. blue, magenta) stay vivid rather than neon. */\nconst VIVID_MAX_CHROMA = 0.2;\n/** Upper bound of the chroma search; no sRGB color exceeds it. */\nconst CHROMA_SEARCH_MAX = 0.4;\n/** Bisection steps; 2^-24 of the search range is far below 1/255 per channel. */\nconst GAMUT_STEPS = 24;\n\nconst toLinear = (c: number) => (c <= 0.04045 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4);\nconst toGamma = (c: number) => (c <= 0.0031308 ? 12.92 * c : 1.055 * c ** (1 / 2.4) - 0.055);\n\nfunction hexToRgb(hex: string): Rgb {\n return [1, 3, 5].map((i) => parseInt(hex.slice(i, i + 2), 16) / 255) as Rgb;\n}\n\nfunction rgbToHex(rgb: Rgb): string {\n return (\n '#' +\n rgb\n .map((c) => Math.round(Math.min(1, Math.max(0, c)) * 255).toString(16).padStart(2, '0'))\n .join('')\n );\n}\n\nfunction hexToOklab(hex: string): Rgb {\n const [r, g, b] = hexToRgb(hex).map(toLinear);\n const l = Math.cbrt(0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b);\n const m = Math.cbrt(0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b);\n const s = Math.cbrt(0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b);\n return [\n 0.2104542553 * l + 0.793617785 * m - 0.0040720468 * s,\n 1.9779984951 * l - 2.428592205 * m + 0.4505937099 * s,\n 0.0259040371 * l + 0.7827717662 * m - 0.808675766 * s,\n ];\n}\n\n/** Returns sRGB in gamma space, possibly outside [0, 1] when out of gamut. */\nfunction oklabToSrgb([L, a, b]: Rgb): Rgb {\n const l = (L + 0.3963377774 * a + 0.2158037573 * b) ** 3;\n const m = (L - 0.1055613458 * a - 0.0638541728 * b) ** 3;\n const s = (L - 0.0894841775 * a - 1.291485548 * b) ** 3;\n return [\n 4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s,\n -1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s,\n -0.0041960863 * l - 0.7034186147 * m + 1.707614701 * s,\n ].map(toGamma) as Rgb;\n}\n\nconst inGamut = (rgb: Rgb) => rgb.every((c) => c >= 0 && c <= 1);\nconst atLch = (L: number, C: number, hue: number) =>\n oklabToSrgb([L, C * Math.cos(hue), C * Math.sin(hue)]);\n\n/** Largest chroma sRGB can show at this lightness and hue. */\nfunction maxChroma(L: number, hue: number): number {\n let low = 0;\n let high = CHROMA_SEARCH_MAX;\n for (let step = 0; step < GAMUT_STEPS; step++) {\n const mid = (low + high) / 2;\n if (inGamut(atLch(L, mid, hue))) low = mid;\n else high = mid;\n }\n return low;\n}\n\n/** The most saturated in-band color at the hue opposite (a, b). */\nfunction vividOpposite(a: number, b: number): string {\n const hue = Math.atan2(b, a) + Math.PI;\n let bestL = VIVID_L_MIN / 100;\n let bestC = -1;\n for (let l = VIVID_L_MIN; l <= VIVID_L_MAX; l++) {\n const chroma = maxChroma(l / 100, hue);\n if (chroma > bestC) {\n bestC = chroma;\n bestL = l / 100;\n }\n }\n return rgbToHex(atLch(bestL, Math.min(bestC, VIVID_MAX_CHROMA), hue));\n}\n\n/** A hueless color's only opposite: the same gray on the other side of mid lightness. */\nfunction oppositeGray(L: number): string {\n return rgbToHex(atLch(L > 0.5 ? 0.25 : 0.85, 0, 0));\n}\n\n/**\n * The complement of a `#rrggbb` color, built to stand out: the opposite OKLCH\n * hue at its most saturated readable lightness. Grays get the opposite-lightness gray.\n */\nexport function complementColor(hex: string): string {\n const [L, a, b] = hexToOklab(hex);\n return Math.hypot(a, b) < ACHROMATIC_CHROMA ? oppositeGray(L) : vividOpposite(a, b);\n}\n\n/**\n * Complement for a palette: opposite the accent's hue, or — when the accent is\n * near-gray — opposite the chroma-weighted hue of the whole ramp.\n */\nexport function paletteComplementColor(accent: string, ramp: readonly string[]): string {\n const [L, a, b] = hexToOklab(accent);\n if (Math.hypot(a, b) >= HUE_TRUST_CHROMA) return vividOpposite(a, b);\n\n // Summing OKLab a/b weights each stop's hue by its chroma.\n let sumA = 0;\n let sumB = 0;\n for (const color of ramp) {\n const [, ca, cb] = hexToOklab(color);\n sumA += ca;\n sumB += cb;\n }\n const meanA = sumA / ramp.length;\n const meanB = sumB / ramp.length;\n return Math.hypot(meanA, meanB) < ACHROMATIC_CHROMA / 2 ? oppositeGray(L) : vividOpposite(meanA, meanB);\n}\n","/*\n * random — the deterministic RNG behind every seeded image and every palette pick.\n *\n * ## behavior\n * - `hashStringToInt` is FNV-1a/32; `createSeededRandom` is Mulberry32 returning\n * floats in [0, 1). Both are re-exported from the package root, so their\n * signatures are public API.\n * - Two consumers: `render.ts` seeds one stream per image and draws from it for\n * the whole pipeline, and `pickPalette` in `palettes.ts` scores each palette\n * with a fresh one-shot stream.\n *\n * ## constraints\n * - Both algorithms are frozen. These are the exact functions Aspect's released\n * 0.3.0 artwork was rendered with, and `test/fluid-gradients.test.mjs` compares\n * a render byte-for-byte against that shipped image. Swapping in a \"better\"\n * hash or a stronger PRNG — or reordering the mixing steps — changes every\n * image every seed has ever produced and reshuffles which palette each seed\n * picks. There is no migration for artwork already published.\n * - `hashStringToInt` hashes the raw string, code unit by code unit. Do not\n * normalize the input here (trim, lowercase, NFC): seeds are arbitrary user\n * strings, and folding two of them together remaps the images they render.\n * A caller that wants normalized seeds normalizes before calling.\n */\n\n/** FNV-1a hash of a string to an unsigned 32-bit integer. */\nexport function hashStringToInt(seed: string): number {\n let hash = 2166136261 >>> 0;\n for (let i = 0; i < seed.length; i++) {\n hash ^= seed.charCodeAt(i);\n hash = Math.imul(hash, 16777619);\n }\n return hash >>> 0;\n}\n\n/** Mulberry32 PRNG. Returns floats in [0, 1). */\nexport function createSeededRandom(seedInt: number): () => number {\n let state = seedInt;\n return function nextRandom() {\n state |= 0;\n state = (state + 0x6d2b79f5) | 0;\n let mixed = Math.imul(state ^ (state >>> 15), 1 | state);\n mixed = (mixed + Math.imul(mixed ^ (mixed >>> 7), 61 | mixed)) ^ mixed;\n return ((mixed ^ (mixed >>> 14)) >>> 0) / 4294967296;\n };\n}\n","/*\n * palettes — the curated palette set, its runtime validation, and seed → palette\n * selection.\n *\n * Design intent for new entries: analogous families anchored by neutrals, muted\n * and earthy options included. Ramps run light → dark, the order the renderer\n * maps noise values along; the \"dark-focused\" group starts mid-tone on purpose\n * so those images read dark with the light stop as a glow.\n *\n * ## behavior\n * - `colorPalettes` is the built-in set. `PaletteName` is derived from it, so\n * the literal names are part of the package's public types.\n * - `pickPalette` uses rendezvous (highest-random-weight) hashing: each palette\n * scores `seed::palette::<name>` through the RNG and the highest wins, with\n * the name as the tiebreak.\n * - `validatePalette` / `validatePalettes` are the runtime gate for palettes\n * that bypassed the type system (JSON, CLI input, casts).\n * - `paletteAccent` is the ramp's mid stop by position; `paletteComplement` is\n * a vivid opposite-hue color for chips, computed by `./color`.\n *\n * ## constraints\n * - A palette's name is its identity, not a label. It is the rendezvous hash\n * key, so renaming one re-picks every seed that had landed on it; it is also\n * the filename in `examples/` and the key in `examples/featured.json`.\n * Renaming a palette is a breaking change to callers' existing artwork, not a\n * copy edit.\n * - Selection stays rendezvous-hashed. An index- or modulo-based pick over the\n * array is smaller code and re-picks nearly every seed the moment a palette is\n * added or removed; `test/fluid-gradients.test.mjs` asserts that adding one\n * moves only the seeds that land on the new palette. The `::palette::`\n * separator and the run through `createSeededRandom` are part of the key —\n * hashing `seed + name` directly mixes shared prefixes poorly and is a\n * different assignment for every seed.\n * - `colorPalettes` keeps `as const satisfies readonly Palette[]`. Dropping the\n * `as const` widens `PaletteName` to `string`, which silently removes name\n * checking for every consumer; the `@ts-expect-error` lines in\n * `test/types.check.ts` then stop erroring and `npm run lint` fails.\n * - Every ramp is 3–5 stops and the runtime check enforces it. The width of\n * `PaletteColors` and `MIN_PALETTE_COLORS`/`MAX_PALETTE_COLORS` must move\n * together — a 6-stop tuple added to the type alone would pass typecheck and\n * then throw at render time for the caller.\n */\nimport { complementColor, paletteComplementColor } from './color';\nimport { createSeededRandom, hashStringToInt } from './random';\n\nexport { complementColor };\n\nexport const MIN_PALETTE_COLORS = 3;\nexport const MAX_PALETTE_COLORS = 5;\n\n/** `#rrggbb`. The type only checks the `#`; the 6 hex digits are checked at runtime. */\nexport type HexColor = `#${string}`;\n\n/** A color ramp of 3–5 stops, light → dark. */\nexport type PaletteColors =\n | readonly [HexColor, HexColor, HexColor]\n | readonly [HexColor, HexColor, HexColor, HexColor]\n | readonly [HexColor, HexColor, HexColor, HexColor, HexColor];\n\nexport type Palette = {\n /** Unique within a palette set. Seed-based picks are keyed on it, so renaming reshuffles. */\n name: string;\n colors: PaletteColors;\n};\n\nexport const colorPalettes = [\n // ── Warm: amber / cream / clay / terracotta (analogous) ──\n { name: 'amber-latte', colors: ['#faf4ea', '#f2d29b', '#e0a45c', '#b9722f'] },\n { name: 'terracotta-sand', colors: ['#f6ede1', '#e8c9a0', '#cf8f63', '#9c4e33'] },\n { name: 'peach-blush', colors: ['#fdf1ec', '#f8cbb6', '#ef9a8a', '#d76e73'] },\n { name: 'coral-cream', colors: ['#fbe9df', '#f5b79a', '#e8735a', '#c33f36'] },\n { name: 'honey-espresso', colors: ['#f7ecd9', '#e5b877', '#b9793f', '#5f3418'] },\n { name: 'apricot-plum', colors: ['#fdf0e6', '#f6c79a', '#e58b6f', '#7d4a6b'] },\n { name: 'golden-hour', colors: ['#fdf3e1', '#f7cf8a', '#e89a5a', '#9a4a3a'] },\n { name: 'vermilion-blush', colors: ['#fbe8e2', '#f7c6a8', '#e4562f', '#a82a1c'] },\n { name: 'ivory-gilt', colors: ['#fffdf7', '#f7ecd0', '#e0c47e', '#b08c3a'] },\n { name: 'rose-gold', colors: ['#fceee8', '#f2cdbd', '#dba18c', '#b3705c'] },\n { name: 'peony-glow', colors: ['#fdeef3', '#f9b3c8', '#f78a5c', '#f2a93f'] },\n\n // ── Pink / violet / mauve ──\n { name: 'lavender-rose', colors: ['#f4eefb', '#d9c2ef', '#c98fb8', '#8a5fd0'] },\n { name: 'violet-mist', colors: ['#f1eefb', '#c9b8ef', '#8f7fe0', '#4a3d9c'] },\n { name: 'mauve-plum', colors: ['#f6f0f2', '#d8b8c6', '#a5738c', '#5f3a55'] },\n { name: 'periwinkle-blush', colors: ['#f0f0fb', '#c3c6f0', '#cf9fc6', '#6a5fd0'] },\n { name: 'orchid-frost', colors: ['#f6eff6', '#e0c2e0', '#b98fc4', '#7a5aa6', '#3f2f5c'] },\n { name: 'quartz-rose', colors: ['#faf1f1', '#ecc9cc', '#c99aa3', '#8a5f6a'] },\n { name: 'wisteria-sky', colors: ['#f2f0fb', '#cfc6ef', '#9fb2e6', '#4f6aa6'] },\n { name: 'zephyr-dawn', colors: ['#fdf3f5', '#f2d6e2', '#c6d6f2', '#7f9fd8'] },\n { name: 'candy-sky', colors: ['#f6e8ff', '#f7a3e8', '#9aa8f5', '#3fa0f5'] },\n\n // ── Cool: blue / slate / sky (analogous) ──\n { name: 'sky-slate', colors: ['#eef4fb', '#bcd2ef', '#6f8fc4', '#2f3f66'] },\n { name: 'teal-fog', colors: ['#eef7f6', '#b6dcd8', '#5fa39c', '#234a49'] },\n { name: 'arctic-ink', colors: ['#f2f6f8', '#cddbe2', '#7f96a3', '#1c2630'] },\n { name: 'cloud-navy', colors: ['#f0f3f7', '#c6d1de', '#7488a3', '#1f2c44'] },\n { name: 'denim-clay', colors: ['#eef2f6', '#aebfd0', '#5f7794', '#b06a4e'] },\n { name: 'cobalt-ice', colors: ['#e8eeff', '#b9cdfb', '#2f4fd8', '#1836b8'] },\n\n // ── Green: sage / moss / mint / olive (analogous) ──\n { name: 'sage-mist', colors: ['#f2f5ee', '#cdd8bd', '#8fa579', '#4a5c3a'] },\n { name: 'mint-sea', colors: ['#eef8f4', '#b6e2d2', '#5fb39c', '#1f5c4d'] },\n { name: 'olive-cream', colors: ['#f6f3e6', '#dbd09b', '#a39a5c', '#5f5a2f'] },\n { name: 'eucalyptus-fog', colors: ['#eff4f1', '#cbdcd2', '#94b3a6', '#4f6b60'] },\n { name: 'kelp-forest', colors: ['#eef3ea', '#a9c49a', '#4f7a5a', '#1f3a2f'] },\n { name: 'yuzu-mint', colors: ['#fbfbe6', '#eef29a', '#b3d98a', '#3f8f7a'] },\n { name: 'lime-lagoon', colors: ['#f8ffe8', '#c8f56a', '#6fe3d8', '#2fc6d8'] },\n { name: 'bliss-meadow', colors: ['#f4fbff', '#8ed0f2', '#6cb03a', '#2d6b22'] },\n\n // ── Analogous + a single contrasting splash ──\n { name: 'periwinkle-gold', colors: ['#dfe6ff', '#a9b6f0', '#5b6bd6', '#e6c25a'] },\n { name: 'lilac-lemon', colors: ['#f3eefb', '#d3c2ef', '#9f8fd0', '#e6d25a'] },\n { name: 'blush-teal', colors: ['#fbeee9', '#f2c2b0', '#d78a8f', '#3f9c94'] },\n { name: 'sage-coral', colors: ['#f2f5ee', '#cdd8bd', '#8fa579', '#e0755a'] },\n { name: 'slate-amber', colors: ['#eef2f6', '#bcc9d8', '#5f7794', '#e0a24e'] },\n { name: 'ultramarine-sand', colors: ['#dfe8ff', '#e6c27a', '#4757dd', '#232a9c'] },\n { name: 'amber-tide', colors: ['#eef4f8', '#a9c8ef', '#f2b96b', '#df8a33'] },\n\n // ── Colorful / multi-hue (rich & saturated, jewel tones) ──\n { name: 'sunset-rose', colors: ['#fce9d6', '#f6a95c', '#e8617a', '#8a3d7a'] },\n { name: 'berry-bloom', colors: ['#fbe9f2', '#e88fb8', '#c94f8f', '#7a2f6a'] },\n { name: 'jewel-peacock', colors: ['#e6f5f0', '#4fb3a6', '#1f7a99', '#243f8a'] },\n { name: 'iris-meadow', colors: ['#eaf5ec', '#7cc48f', '#3f8fb3', '#5f4fb0'] },\n { name: 'autumn-spice', colors: ['#f7ecd6', '#dba24f', '#b5522f', '#6e2f4a'] },\n { name: 'ultraviolet-sea', colors: ['#e9f0fb', '#6f9fe0', '#4a5fc4', '#6a3da6'] },\n { name: 'purple-rain', colors: ['#f1ebf7', '#b69ad6', '#6f3fa8', '#2a1740'] },\n { name: 'nectarine-fizz', colors: ['#fff1e6', '#ffc59a', '#ff8a5c', '#d9485f'] },\n { name: 'raspberry-cream', colors: ['#fdeef1', '#f4a9bd', '#d9537a', '#8a1f45'] },\n { name: 'pastel-confetti', colors: ['#ffffff', '#f9f3cf', '#cbbcf2', '#e8798f'] },\n { name: 'burgundy-velvet', colors: ['#f2dfe0', '#c98a91', '#8a2740', '#4a0f22'] },\n { name: 'indigo-beam', colors: ['#ffffff', '#a9b6f7', '#4a3fe0', '#2a1fb0'] },\n { name: 'crimson-ice', colors: ['#f2f7fc', '#f8d7e0', '#e0517f', '#a81050'] },\n\n // ── Muted / dusty / earthy ──\n { name: 'fog-plum', colors: ['#f2f0f3', '#cfc6d2', '#9a8a9c', '#5f4a5c'] },\n { name: 'sand-sky', colors: ['#f4efe4', '#e0d3b8', '#a9bcc4', '#5f7f8c'] },\n { name: 'xanadu-sage', colors: ['#f1f4f1', '#c8d3c8', '#738678', '#36443a'] },\n { name: 'walnut-grain', colors: ['#e8d5b7', '#c49a6c', '#8a5a34', '#4a2c17'] },\n\n // ── Dark-focused: ramps that start mid-tone instead of near-white, so the\n // image reads dark overall with the lighter stop as a glow ──\n { name: 'onyx-gold', colors: ['#e8c877', '#b8862f', '#3a2f14', '#0b0b0d'] },\n { name: 'onyx-silver', colors: ['#d7dade', '#8b9199', '#3a3f45', '#0c0d0f'] },\n { name: 'graphite-royal', colors: ['#9fb0e8', '#3f56c4', '#2a2e38', '#101218'] },\n { name: 'onyx-ultraviolet', colors: ['#c9a6ff', '#7b3ff2', '#2a1450', '#08070d'] },\n { name: 'onyx-snow', colors: ['#ffffff', '#b0b0b0', '#4a4a4a', '#000000'] },\n { name: 'midnight-ember', colors: ['#f0a468', '#c23f1f', '#2a1410', '#0a0a0c'] },\n { name: 'slate-harbor', colors: ['#aeb7bd', '#6f7b84', '#3c454c', '#1a1f24'] },\n { name: 'ember-mono', colors: ['#ff9a6b', '#cf5604', '#632400', '#180501'] },\n\n // ── Monochromatic: one hue, lightness stepping down (OKLCH-spaced) ──\n { name: 'tangerine-mono', colors: ['#ffeee4', '#feb079', '#dc7100', '#824103'] },\n { name: 'electric-blue-mono', colors: ['#ebf2fe', '#a4c6fc', '#5d91e7', '#2a5397'] },\n { name: 'magenta-mono', colors: ['#feecf5', '#edaed1', '#cd6ca6', '#823564'] },\n { name: 'emerald-mono', colors: ['#e2f8ea', '#95d5af', '#2cab70', '#03663e'] },\n { name: 'violet-mono', colors: ['#f2f0fe', '#c8b7ff', '#9e71fd', '#5f38a7'] },\n { name: 'crimson-mono', colors: ['#ffedec', '#feaaa9', '#f44f5d', '#9e122b'] },\n { name: 'aqua-mono', colors: ['#dff7f8', '#8dd2d6', '#39a4a9', '#026266'] },\n\n { name: 'burgundy-mono', colors: ['#fbe0e4', '#dc9ea8', '#a7415a', '#560c24'] },\n { name: 'indigo-mono', colors: ['#eaeefe', '#a7b2f7', '#5a5ce2', '#261b81'] },\n { name: 'lime-mono', colors: ['#edfcd7', '#c2e87d', '#9fc93e', '#638119'] },\n\n // ── Monochromatic earth tones: same idea, chroma held up so they stay\n // clay / ochre / moss rather than collapsing to greige ──\n { name: 'terracotta-mono', colors: ['#fdeae4', '#f6b19b', '#c5694c', '#73311b'] },\n { name: 'ochre-mono', colors: ['#f9edd5', '#dec082', '#a87f09', '#5b4404'] },\n { name: 'olive-mono', colors: ['#edf2d7', '#c2cb91', '#848e38', '#474d04'] },\n { name: 'fern-mono', colors: ['#e3f5df', '#a9d1a0', '#5f9752', '#2a5320'] },\n { name: 'verdigris-mono', colors: ['#d6f7f1', '#96d2c9', '#38988d', '#07534c'] },\n { name: 'umber-mono', colors: ['#feeada', '#e1bc9d', '#ab7a4f', '#623f1e'] },\n { name: 'adobe-mono', colors: ['#fee9e8', '#eeb3b0', '#bb6d6b', '#6c3434'] },\n\n // ── Neutral / mono ──\n { name: 'ivory-ink', colors: ['#f7f4ef', '#d8d2c8', '#8a8378', '#20201d'] },\n { name: 'paper-graphite', colors: ['#f4f4f5', '#cfcfd4', '#83838f', '#1c1c22'] },\n { name: 'bone-coffee', colors: ['#f5efe6', '#d9c9b3', '#9c8468', '#3a2c1e'] },\n { name: 'porcelain-greige', colors: ['#f6f3ef', '#ddd4c9', '#b0a596', '#6a5f52'] },\n { name: 'battleship-steel', colors: ['#e6e9e8', '#b9c0be', '#7d8683', '#3f4745'] },\n { name: 'gunmetal-fog', colors: ['#eceef1', '#c2c7cd', '#7b838d', '#343a42'] },\n { name: 'silver-steel', colors: ['#e2e5e8', '#c4c9cf', '#9aa2ac', '#646f7d'] },\n // ── Airy: a near-white base with soft color splashes, in the style of\n // blurred editorial gradients (light stops first, then the splash hues) ──\n { name: 'petal-bloom', colors: ['#e4eeff', '#bcd2ff', '#dfa6e9', '#fa787b', '#fba96a'] },\n { name: 'tulip-blur', colors: ['#e6f1f6', '#a8d4e8', '#f1c4d0', '#ee9a6e', '#f4b81c'] },\n { name: 'silk-rose', colors: ['#fcedf4', '#f2cfdd', '#f0c1b8', '#d68ba5', '#b07a9f'] },\n\n // ── Flags: sampled from heraldic flag art ──\n { name: 'sultans-eagle', colors: ['#f2da38', '#e3c42a', '#c9a21d', '#a1353a', '#6a1c26'] },\n { name: 'zhuge-nu', colors: ['#f0f3ee', '#dfc652', '#4fa79b', '#1f8283', '#0f5252'] },\n] as const satisfies readonly Palette[];\n\n/** Union of every built-in palette name, e.g. `\"mint-sea\"`. */\nexport type PaletteName = (typeof colorPalettes)[number]['name'];\n\n/** Primary accent for UI chips: the palette ramp's mid-tone. */\nexport function paletteAccent(palette: Palette): string {\n const colors = palette.colors;\n return colors[Math.floor(colors.length / 2)];\n}\n\n/**\n * A color built to stand out on the palette's images: the hue opposite the\n * accent (or the whole ramp, when the accent is near-gray) at its most saturated\n * readable lightness. For chips, geometry and highlights.\n */\nexport function paletteComplement(palette: Palette): string {\n return paletteComplementColor(paletteAccent(palette), palette.colors);\n}\n\nconst HEX_COLOR = /^#[0-9a-fA-F]{6}$/;\n\n/**\n * Runtime check for palettes that didn't come through the type system\n * (JSON, CLI input, `as` casts). Throws with the offending palette's name.\n */\nexport function validatePalette(palette: Palette): void {\n const label = `Palette \"${palette?.name}\"`;\n if (typeof palette?.name !== 'string' || palette.name.length === 0) {\n throw new Error('Palette needs a non-empty name.');\n }\n const { colors } = palette;\n if (\n !Array.isArray(colors) ||\n colors.length < MIN_PALETTE_COLORS ||\n colors.length > MAX_PALETTE_COLORS\n ) {\n throw new Error(\n `${label} needs ${MIN_PALETTE_COLORS}–${MAX_PALETTE_COLORS} colors. Got ${Array.isArray(colors) ? colors.length : typeof colors}.`,\n );\n }\n for (const color of colors) {\n if (typeof color !== 'string' || !HEX_COLOR.test(color)) {\n throw new Error(`${label} has an invalid color \"${color}\". Use #rrggbb.`);\n }\n }\n}\n\n/** Validates every palette and that names are unique. */\nexport function validatePalettes(palettes: readonly Palette[]): void {\n if (palettes.length === 0) throw new Error('At least one palette is required.');\n const seen = new Set<string>();\n for (const palette of palettes) {\n validatePalette(palette);\n if (seen.has(palette.name)) throw new Error(`Duplicate palette name \"${palette.name}\".`);\n seen.add(palette.name);\n }\n}\n\n/** Look up a palette by name. Throws if not found. */\nexport function findPalette(\n name: PaletteName | (string & {}),\n palettes: readonly Palette[] = colorPalettes,\n): Palette {\n const palette = palettes.find((p) => p.name === name);\n if (!palette) {\n throw new Error(`Unknown palette \"${name}\". Valid: ${palettes.map((p) => p.name).join(', ')}`);\n }\n return palette;\n}\n\n/**\n * Deterministically pick a palette from a seed.\n *\n * Uses rendezvous (highest-random-weight) hashing: every palette gets a\n * pseudo-random score from `seed + name`, and the highest score wins. The list's\n * order doesn't matter, adding a palette only moves the seeds the new palette\n * wins (~1 in N), and removing one only moves the seeds that had picked it.\n */\nexport function pickPalette(seed: string, palettes: readonly Palette[] = colorPalettes): Palette {\n let best: Palette | undefined;\n let bestScore = -1;\n for (const palette of palettes) {\n // Run the hash through the PRNG once: FNV-1a alone mixes trailing\n // characters poorly, and names often share prefixes.\n const score = createSeededRandom(hashStringToInt(`${seed}::palette::${palette.name}`))();\n if (score > bestScore || (score === bestScore && palette.name < best!.name)) {\n best = palette;\n bestScore = score;\n }\n }\n if (!best) throw new Error('At least one palette is required.');\n return best;\n}\n"],"mappings":"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","names":[]}
@@ -180,13 +180,43 @@ var colorPalettes = [
180
180
  { name: "onyx-snow", colors: ["#ffffff", "#b0b0b0", "#4a4a4a", "#000000"] },
181
181
  { name: "midnight-ember", colors: ["#f0a468", "#c23f1f", "#2a1410", "#0a0a0c"] },
182
182
  { name: "slate-harbor", colors: ["#aeb7bd", "#6f7b84", "#3c454c", "#1a1f24"] },
183
+ { name: "ember-mono", colors: ["#ff9a6b", "#cf5604", "#632400", "#180501"] },
184
+ // ── Monochromatic: one hue, lightness stepping down (OKLCH-spaced) ──
185
+ { name: "tangerine-mono", colors: ["#ffeee4", "#feb079", "#dc7100", "#824103"] },
186
+ { name: "electric-blue-mono", colors: ["#ebf2fe", "#a4c6fc", "#5d91e7", "#2a5397"] },
187
+ { name: "magenta-mono", colors: ["#feecf5", "#edaed1", "#cd6ca6", "#823564"] },
188
+ { name: "emerald-mono", colors: ["#e2f8ea", "#95d5af", "#2cab70", "#03663e"] },
189
+ { name: "violet-mono", colors: ["#f2f0fe", "#c8b7ff", "#9e71fd", "#5f38a7"] },
190
+ { name: "crimson-mono", colors: ["#ffedec", "#feaaa9", "#f44f5d", "#9e122b"] },
191
+ { name: "aqua-mono", colors: ["#dff7f8", "#8dd2d6", "#39a4a9", "#026266"] },
192
+ { name: "burgundy-mono", colors: ["#fbe0e4", "#dc9ea8", "#a7415a", "#560c24"] },
193
+ { name: "indigo-mono", colors: ["#eaeefe", "#a7b2f7", "#5a5ce2", "#261b81"] },
194
+ { name: "lime-mono", colors: ["#edfcd7", "#c2e87d", "#9fc93e", "#638119"] },
195
+ // ── Monochromatic earth tones: same idea, chroma held up so they stay
196
+ // clay / ochre / moss rather than collapsing to greige ──
197
+ { name: "terracotta-mono", colors: ["#fdeae4", "#f6b19b", "#c5694c", "#73311b"] },
198
+ { name: "ochre-mono", colors: ["#f9edd5", "#dec082", "#a87f09", "#5b4404"] },
199
+ { name: "olive-mono", colors: ["#edf2d7", "#c2cb91", "#848e38", "#474d04"] },
200
+ { name: "fern-mono", colors: ["#e3f5df", "#a9d1a0", "#5f9752", "#2a5320"] },
201
+ { name: "verdigris-mono", colors: ["#d6f7f1", "#96d2c9", "#38988d", "#07534c"] },
202
+ { name: "umber-mono", colors: ["#feeada", "#e1bc9d", "#ab7a4f", "#623f1e"] },
203
+ { name: "adobe-mono", colors: ["#fee9e8", "#eeb3b0", "#bb6d6b", "#6c3434"] },
183
204
  // ── Neutral / mono ──
184
205
  { name: "ivory-ink", colors: ["#f7f4ef", "#d8d2c8", "#8a8378", "#20201d"] },
185
206
  { name: "paper-graphite", colors: ["#f4f4f5", "#cfcfd4", "#83838f", "#1c1c22"] },
186
207
  { name: "bone-coffee", colors: ["#f5efe6", "#d9c9b3", "#9c8468", "#3a2c1e"] },
187
208
  { name: "porcelain-greige", colors: ["#f6f3ef", "#ddd4c9", "#b0a596", "#6a5f52"] },
188
209
  { name: "battleship-steel", colors: ["#e6e9e8", "#b9c0be", "#7d8683", "#3f4745"] },
189
- { name: "gunmetal-fog", colors: ["#eceef1", "#c2c7cd", "#7b838d", "#343a42"] }
210
+ { name: "gunmetal-fog", colors: ["#eceef1", "#c2c7cd", "#7b838d", "#343a42"] },
211
+ { name: "silver-steel", colors: ["#e2e5e8", "#c4c9cf", "#9aa2ac", "#646f7d"] },
212
+ // ── Airy: a near-white base with soft color splashes, in the style of
213
+ // blurred editorial gradients (light stops first, then the splash hues) ──
214
+ { name: "petal-bloom", colors: ["#e4eeff", "#bcd2ff", "#dfa6e9", "#fa787b", "#fba96a"] },
215
+ { name: "tulip-blur", colors: ["#e6f1f6", "#a8d4e8", "#f1c4d0", "#ee9a6e", "#f4b81c"] },
216
+ { name: "silk-rose", colors: ["#fcedf4", "#f2cfdd", "#f0c1b8", "#d68ba5", "#b07a9f"] },
217
+ // ── Flags: sampled from heraldic flag art ──
218
+ { name: "sultans-eagle", colors: ["#f2da38", "#e3c42a", "#c9a21d", "#a1353a", "#6a1c26"] },
219
+ { name: "zhuge-nu", colors: ["#f0f3ee", "#dfc652", "#4fa79b", "#1f8283", "#0f5252"] }
190
220
  ];
191
221
  function paletteAccent(palette) {
192
222
  const colors = palette.colors;
@@ -257,4 +287,4 @@ function pickPalette(seed, palettes = colorPalettes) {
257
287
 
258
288
 
259
289
  exports.complementColor = complementColor; exports.hashStringToInt = hashStringToInt; exports.createSeededRandom = createSeededRandom; exports.MIN_PALETTE_COLORS = MIN_PALETTE_COLORS; exports.MAX_PALETTE_COLORS = MAX_PALETTE_COLORS; exports.colorPalettes = colorPalettes; exports.paletteAccent = paletteAccent; exports.paletteComplement = paletteComplement; exports.validatePalette = validatePalette; exports.validatePalettes = validatePalettes; exports.findPalette = findPalette; exports.pickPalette = pickPalette;
260
- //# sourceMappingURL=chunk-CRRF2Q64.cjs.map
290
+ //# sourceMappingURL=chunk-VF6TCELO.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/home/runner/work/fluid-gradients/fluid-gradients/dist/chunk-VF6TCELO.cjs","../src/color.ts","../src/random.ts","../src/palettes.ts"],"names":[],"mappings":"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","file":"/home/runner/work/fluid-gradients/fluid-gradients/dist/chunk-VF6TCELO.cjs","sourcesContent":[null,"/*\n * color — perceptual color math for colors derived from a palette.\n *\n * ## behavior\n * - The complement is the design-sense complement: the hue opposite the source\n * (OKLCH hue + 180°), at the most saturated color sRGB can show for that hue\n * within a readable lightness band (VIVID_L_MIN–VIVID_L_MAX), chroma capped at\n * VIVID_MAX_CHROMA. It is built to stand out on the gradient as a chip or\n * shape, so it deliberately does NOT keep the source's lightness or chroma —\n * keeping them made muted palettes produce muted, invisible complements.\n * - `paletteComplementColor` takes the hue from the accent, unless the accent is\n * too gray to have a reliable hue (chroma < HUE_TRUST_CHROMA); then it uses the\n * chroma-weighted hue of the whole ramp, whose faint tint is still real.\n * - A source with no usable hue at all (pure grays) has no opposite hue; it gets\n * a gray at the opposite lightness instead.\n *\n * ## constraints\n * - Output is part of what consumers store. Complements are rendered as UI\n * chips and geometry next to images generated earlier; changing the color\n * space, the lightness band, the chroma cap, the search steps or the rounding\n * shifts every complement. Treat it like the render pipeline: a deliberate,\n * announced change, not a refactor.\n * - Zero dependencies. This module is reachable from the `/palettes` entry,\n * which promises to be safe for browser and edge bundles.\n */\n\ntype Rgb = [number, number, number];\n\n/** Below this OKLCH chroma a color has no meaningful hue. */\nconst ACHROMATIC_CHROMA = 0.01;\n/** Below this, an accent's hue is too faint to trust; use the whole ramp's instead. */\nconst HUE_TRUST_CHROMA = 0.03;\n/** Lightness band searched for the most saturated opposite color (OKLab L, in hundredths). */\nconst VIVID_L_MIN = 50;\nconst VIVID_L_MAX = 80;\n/** Cap so the most saturated hues (e.g. blue, magenta) stay vivid rather than neon. */\nconst VIVID_MAX_CHROMA = 0.2;\n/** Upper bound of the chroma search; no sRGB color exceeds it. */\nconst CHROMA_SEARCH_MAX = 0.4;\n/** Bisection steps; 2^-24 of the search range is far below 1/255 per channel. */\nconst GAMUT_STEPS = 24;\n\nconst toLinear = (c: number) => (c <= 0.04045 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4);\nconst toGamma = (c: number) => (c <= 0.0031308 ? 12.92 * c : 1.055 * c ** (1 / 2.4) - 0.055);\n\nfunction hexToRgb(hex: string): Rgb {\n return [1, 3, 5].map((i) => parseInt(hex.slice(i, i + 2), 16) / 255) as Rgb;\n}\n\nfunction rgbToHex(rgb: Rgb): string {\n return (\n '#' +\n rgb\n .map((c) => Math.round(Math.min(1, Math.max(0, c)) * 255).toString(16).padStart(2, '0'))\n .join('')\n );\n}\n\nfunction hexToOklab(hex: string): Rgb {\n const [r, g, b] = hexToRgb(hex).map(toLinear);\n const l = Math.cbrt(0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b);\n const m = Math.cbrt(0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b);\n const s = Math.cbrt(0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b);\n return [\n 0.2104542553 * l + 0.793617785 * m - 0.0040720468 * s,\n 1.9779984951 * l - 2.428592205 * m + 0.4505937099 * s,\n 0.0259040371 * l + 0.7827717662 * m - 0.808675766 * s,\n ];\n}\n\n/** Returns sRGB in gamma space, possibly outside [0, 1] when out of gamut. */\nfunction oklabToSrgb([L, a, b]: Rgb): Rgb {\n const l = (L + 0.3963377774 * a + 0.2158037573 * b) ** 3;\n const m = (L - 0.1055613458 * a - 0.0638541728 * b) ** 3;\n const s = (L - 0.0894841775 * a - 1.291485548 * b) ** 3;\n return [\n 4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s,\n -1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s,\n -0.0041960863 * l - 0.7034186147 * m + 1.707614701 * s,\n ].map(toGamma) as Rgb;\n}\n\nconst inGamut = (rgb: Rgb) => rgb.every((c) => c >= 0 && c <= 1);\nconst atLch = (L: number, C: number, hue: number) =>\n oklabToSrgb([L, C * Math.cos(hue), C * Math.sin(hue)]);\n\n/** Largest chroma sRGB can show at this lightness and hue. */\nfunction maxChroma(L: number, hue: number): number {\n let low = 0;\n let high = CHROMA_SEARCH_MAX;\n for (let step = 0; step < GAMUT_STEPS; step++) {\n const mid = (low + high) / 2;\n if (inGamut(atLch(L, mid, hue))) low = mid;\n else high = mid;\n }\n return low;\n}\n\n/** The most saturated in-band color at the hue opposite (a, b). */\nfunction vividOpposite(a: number, b: number): string {\n const hue = Math.atan2(b, a) + Math.PI;\n let bestL = VIVID_L_MIN / 100;\n let bestC = -1;\n for (let l = VIVID_L_MIN; l <= VIVID_L_MAX; l++) {\n const chroma = maxChroma(l / 100, hue);\n if (chroma > bestC) {\n bestC = chroma;\n bestL = l / 100;\n }\n }\n return rgbToHex(atLch(bestL, Math.min(bestC, VIVID_MAX_CHROMA), hue));\n}\n\n/** A hueless color's only opposite: the same gray on the other side of mid lightness. */\nfunction oppositeGray(L: number): string {\n return rgbToHex(atLch(L > 0.5 ? 0.25 : 0.85, 0, 0));\n}\n\n/**\n * The complement of a `#rrggbb` color, built to stand out: the opposite OKLCH\n * hue at its most saturated readable lightness. Grays get the opposite-lightness gray.\n */\nexport function complementColor(hex: string): string {\n const [L, a, b] = hexToOklab(hex);\n return Math.hypot(a, b) < ACHROMATIC_CHROMA ? oppositeGray(L) : vividOpposite(a, b);\n}\n\n/**\n * Complement for a palette: opposite the accent's hue, or — when the accent is\n * near-gray — opposite the chroma-weighted hue of the whole ramp.\n */\nexport function paletteComplementColor(accent: string, ramp: readonly string[]): string {\n const [L, a, b] = hexToOklab(accent);\n if (Math.hypot(a, b) >= HUE_TRUST_CHROMA) return vividOpposite(a, b);\n\n // Summing OKLab a/b weights each stop's hue by its chroma.\n let sumA = 0;\n let sumB = 0;\n for (const color of ramp) {\n const [, ca, cb] = hexToOklab(color);\n sumA += ca;\n sumB += cb;\n }\n const meanA = sumA / ramp.length;\n const meanB = sumB / ramp.length;\n return Math.hypot(meanA, meanB) < ACHROMATIC_CHROMA / 2 ? oppositeGray(L) : vividOpposite(meanA, meanB);\n}\n","/*\n * random — the deterministic RNG behind every seeded image and every palette pick.\n *\n * ## behavior\n * - `hashStringToInt` is FNV-1a/32; `createSeededRandom` is Mulberry32 returning\n * floats in [0, 1). Both are re-exported from the package root, so their\n * signatures are public API.\n * - Two consumers: `render.ts` seeds one stream per image and draws from it for\n * the whole pipeline, and `pickPalette` in `palettes.ts` scores each palette\n * with a fresh one-shot stream.\n *\n * ## constraints\n * - Both algorithms are frozen. These are the exact functions Aspect's released\n * 0.3.0 artwork was rendered with, and `test/fluid-gradients.test.mjs` compares\n * a render byte-for-byte against that shipped image. Swapping in a \"better\"\n * hash or a stronger PRNG — or reordering the mixing steps — changes every\n * image every seed has ever produced and reshuffles which palette each seed\n * picks. There is no migration for artwork already published.\n * - `hashStringToInt` hashes the raw string, code unit by code unit. Do not\n * normalize the input here (trim, lowercase, NFC): seeds are arbitrary user\n * strings, and folding two of them together remaps the images they render.\n * A caller that wants normalized seeds normalizes before calling.\n */\n\n/** FNV-1a hash of a string to an unsigned 32-bit integer. */\nexport function hashStringToInt(seed: string): number {\n let hash = 2166136261 >>> 0;\n for (let i = 0; i < seed.length; i++) {\n hash ^= seed.charCodeAt(i);\n hash = Math.imul(hash, 16777619);\n }\n return hash >>> 0;\n}\n\n/** Mulberry32 PRNG. Returns floats in [0, 1). */\nexport function createSeededRandom(seedInt: number): () => number {\n let state = seedInt;\n return function nextRandom() {\n state |= 0;\n state = (state + 0x6d2b79f5) | 0;\n let mixed = Math.imul(state ^ (state >>> 15), 1 | state);\n mixed = (mixed + Math.imul(mixed ^ (mixed >>> 7), 61 | mixed)) ^ mixed;\n return ((mixed ^ (mixed >>> 14)) >>> 0) / 4294967296;\n };\n}\n","/*\n * palettes — the curated palette set, its runtime validation, and seed → palette\n * selection.\n *\n * Design intent for new entries: analogous families anchored by neutrals, muted\n * and earthy options included. Ramps run light → dark, the order the renderer\n * maps noise values along; the \"dark-focused\" group starts mid-tone on purpose\n * so those images read dark with the light stop as a glow.\n *\n * ## behavior\n * - `colorPalettes` is the built-in set. `PaletteName` is derived from it, so\n * the literal names are part of the package's public types.\n * - `pickPalette` uses rendezvous (highest-random-weight) hashing: each palette\n * scores `seed::palette::<name>` through the RNG and the highest wins, with\n * the name as the tiebreak.\n * - `validatePalette` / `validatePalettes` are the runtime gate for palettes\n * that bypassed the type system (JSON, CLI input, casts).\n * - `paletteAccent` is the ramp's mid stop by position; `paletteComplement` is\n * a vivid opposite-hue color for chips, computed by `./color`.\n *\n * ## constraints\n * - A palette's name is its identity, not a label. It is the rendezvous hash\n * key, so renaming one re-picks every seed that had landed on it; it is also\n * the filename in `examples/` and the key in `examples/featured.json`.\n * Renaming a palette is a breaking change to callers' existing artwork, not a\n * copy edit.\n * - Selection stays rendezvous-hashed. An index- or modulo-based pick over the\n * array is smaller code and re-picks nearly every seed the moment a palette is\n * added or removed; `test/fluid-gradients.test.mjs` asserts that adding one\n * moves only the seeds that land on the new palette. The `::palette::`\n * separator and the run through `createSeededRandom` are part of the key —\n * hashing `seed + name` directly mixes shared prefixes poorly and is a\n * different assignment for every seed.\n * - `colorPalettes` keeps `as const satisfies readonly Palette[]`. Dropping the\n * `as const` widens `PaletteName` to `string`, which silently removes name\n * checking for every consumer; the `@ts-expect-error` lines in\n * `test/types.check.ts` then stop erroring and `npm run lint` fails.\n * - Every ramp is 3–5 stops and the runtime check enforces it. The width of\n * `PaletteColors` and `MIN_PALETTE_COLORS`/`MAX_PALETTE_COLORS` must move\n * together — a 6-stop tuple added to the type alone would pass typecheck and\n * then throw at render time for the caller.\n */\nimport { complementColor, paletteComplementColor } from './color';\nimport { createSeededRandom, hashStringToInt } from './random';\n\nexport { complementColor };\n\nexport const MIN_PALETTE_COLORS = 3;\nexport const MAX_PALETTE_COLORS = 5;\n\n/** `#rrggbb`. The type only checks the `#`; the 6 hex digits are checked at runtime. */\nexport type HexColor = `#${string}`;\n\n/** A color ramp of 3–5 stops, light → dark. */\nexport type PaletteColors =\n | readonly [HexColor, HexColor, HexColor]\n | readonly [HexColor, HexColor, HexColor, HexColor]\n | readonly [HexColor, HexColor, HexColor, HexColor, HexColor];\n\nexport type Palette = {\n /** Unique within a palette set. Seed-based picks are keyed on it, so renaming reshuffles. */\n name: string;\n colors: PaletteColors;\n};\n\nexport const colorPalettes = [\n // ── Warm: amber / cream / clay / terracotta (analogous) ──\n { name: 'amber-latte', colors: ['#faf4ea', '#f2d29b', '#e0a45c', '#b9722f'] },\n { name: 'terracotta-sand', colors: ['#f6ede1', '#e8c9a0', '#cf8f63', '#9c4e33'] },\n { name: 'peach-blush', colors: ['#fdf1ec', '#f8cbb6', '#ef9a8a', '#d76e73'] },\n { name: 'coral-cream', colors: ['#fbe9df', '#f5b79a', '#e8735a', '#c33f36'] },\n { name: 'honey-espresso', colors: ['#f7ecd9', '#e5b877', '#b9793f', '#5f3418'] },\n { name: 'apricot-plum', colors: ['#fdf0e6', '#f6c79a', '#e58b6f', '#7d4a6b'] },\n { name: 'golden-hour', colors: ['#fdf3e1', '#f7cf8a', '#e89a5a', '#9a4a3a'] },\n { name: 'vermilion-blush', colors: ['#fbe8e2', '#f7c6a8', '#e4562f', '#a82a1c'] },\n { name: 'ivory-gilt', colors: ['#fffdf7', '#f7ecd0', '#e0c47e', '#b08c3a'] },\n { name: 'rose-gold', colors: ['#fceee8', '#f2cdbd', '#dba18c', '#b3705c'] },\n { name: 'peony-glow', colors: ['#fdeef3', '#f9b3c8', '#f78a5c', '#f2a93f'] },\n\n // ── Pink / violet / mauve ──\n { name: 'lavender-rose', colors: ['#f4eefb', '#d9c2ef', '#c98fb8', '#8a5fd0'] },\n { name: 'violet-mist', colors: ['#f1eefb', '#c9b8ef', '#8f7fe0', '#4a3d9c'] },\n { name: 'mauve-plum', colors: ['#f6f0f2', '#d8b8c6', '#a5738c', '#5f3a55'] },\n { name: 'periwinkle-blush', colors: ['#f0f0fb', '#c3c6f0', '#cf9fc6', '#6a5fd0'] },\n { name: 'orchid-frost', colors: ['#f6eff6', '#e0c2e0', '#b98fc4', '#7a5aa6', '#3f2f5c'] },\n { name: 'quartz-rose', colors: ['#faf1f1', '#ecc9cc', '#c99aa3', '#8a5f6a'] },\n { name: 'wisteria-sky', colors: ['#f2f0fb', '#cfc6ef', '#9fb2e6', '#4f6aa6'] },\n { name: 'zephyr-dawn', colors: ['#fdf3f5', '#f2d6e2', '#c6d6f2', '#7f9fd8'] },\n { name: 'candy-sky', colors: ['#f6e8ff', '#f7a3e8', '#9aa8f5', '#3fa0f5'] },\n\n // ── Cool: blue / slate / sky (analogous) ──\n { name: 'sky-slate', colors: ['#eef4fb', '#bcd2ef', '#6f8fc4', '#2f3f66'] },\n { name: 'teal-fog', colors: ['#eef7f6', '#b6dcd8', '#5fa39c', '#234a49'] },\n { name: 'arctic-ink', colors: ['#f2f6f8', '#cddbe2', '#7f96a3', '#1c2630'] },\n { name: 'cloud-navy', colors: ['#f0f3f7', '#c6d1de', '#7488a3', '#1f2c44'] },\n { name: 'denim-clay', colors: ['#eef2f6', '#aebfd0', '#5f7794', '#b06a4e'] },\n { name: 'cobalt-ice', colors: ['#e8eeff', '#b9cdfb', '#2f4fd8', '#1836b8'] },\n\n // ── Green: sage / moss / mint / olive (analogous) ──\n { name: 'sage-mist', colors: ['#f2f5ee', '#cdd8bd', '#8fa579', '#4a5c3a'] },\n { name: 'mint-sea', colors: ['#eef8f4', '#b6e2d2', '#5fb39c', '#1f5c4d'] },\n { name: 'olive-cream', colors: ['#f6f3e6', '#dbd09b', '#a39a5c', '#5f5a2f'] },\n { name: 'eucalyptus-fog', colors: ['#eff4f1', '#cbdcd2', '#94b3a6', '#4f6b60'] },\n { name: 'kelp-forest', colors: ['#eef3ea', '#a9c49a', '#4f7a5a', '#1f3a2f'] },\n { name: 'yuzu-mint', colors: ['#fbfbe6', '#eef29a', '#b3d98a', '#3f8f7a'] },\n { name: 'lime-lagoon', colors: ['#f8ffe8', '#c8f56a', '#6fe3d8', '#2fc6d8'] },\n { name: 'bliss-meadow', colors: ['#f4fbff', '#8ed0f2', '#6cb03a', '#2d6b22'] },\n\n // ── Analogous + a single contrasting splash ──\n { name: 'periwinkle-gold', colors: ['#dfe6ff', '#a9b6f0', '#5b6bd6', '#e6c25a'] },\n { name: 'lilac-lemon', colors: ['#f3eefb', '#d3c2ef', '#9f8fd0', '#e6d25a'] },\n { name: 'blush-teal', colors: ['#fbeee9', '#f2c2b0', '#d78a8f', '#3f9c94'] },\n { name: 'sage-coral', colors: ['#f2f5ee', '#cdd8bd', '#8fa579', '#e0755a'] },\n { name: 'slate-amber', colors: ['#eef2f6', '#bcc9d8', '#5f7794', '#e0a24e'] },\n { name: 'ultramarine-sand', colors: ['#dfe8ff', '#e6c27a', '#4757dd', '#232a9c'] },\n { name: 'amber-tide', colors: ['#eef4f8', '#a9c8ef', '#f2b96b', '#df8a33'] },\n\n // ── Colorful / multi-hue (rich & saturated, jewel tones) ──\n { name: 'sunset-rose', colors: ['#fce9d6', '#f6a95c', '#e8617a', '#8a3d7a'] },\n { name: 'berry-bloom', colors: ['#fbe9f2', '#e88fb8', '#c94f8f', '#7a2f6a'] },\n { name: 'jewel-peacock', colors: ['#e6f5f0', '#4fb3a6', '#1f7a99', '#243f8a'] },\n { name: 'iris-meadow', colors: ['#eaf5ec', '#7cc48f', '#3f8fb3', '#5f4fb0'] },\n { name: 'autumn-spice', colors: ['#f7ecd6', '#dba24f', '#b5522f', '#6e2f4a'] },\n { name: 'ultraviolet-sea', colors: ['#e9f0fb', '#6f9fe0', '#4a5fc4', '#6a3da6'] },\n { name: 'purple-rain', colors: ['#f1ebf7', '#b69ad6', '#6f3fa8', '#2a1740'] },\n { name: 'nectarine-fizz', colors: ['#fff1e6', '#ffc59a', '#ff8a5c', '#d9485f'] },\n { name: 'raspberry-cream', colors: ['#fdeef1', '#f4a9bd', '#d9537a', '#8a1f45'] },\n { name: 'pastel-confetti', colors: ['#ffffff', '#f9f3cf', '#cbbcf2', '#e8798f'] },\n { name: 'burgundy-velvet', colors: ['#f2dfe0', '#c98a91', '#8a2740', '#4a0f22'] },\n { name: 'indigo-beam', colors: ['#ffffff', '#a9b6f7', '#4a3fe0', '#2a1fb0'] },\n { name: 'crimson-ice', colors: ['#f2f7fc', '#f8d7e0', '#e0517f', '#a81050'] },\n\n // ── Muted / dusty / earthy ──\n { name: 'fog-plum', colors: ['#f2f0f3', '#cfc6d2', '#9a8a9c', '#5f4a5c'] },\n { name: 'sand-sky', colors: ['#f4efe4', '#e0d3b8', '#a9bcc4', '#5f7f8c'] },\n { name: 'xanadu-sage', colors: ['#f1f4f1', '#c8d3c8', '#738678', '#36443a'] },\n { name: 'walnut-grain', colors: ['#e8d5b7', '#c49a6c', '#8a5a34', '#4a2c17'] },\n\n // ── Dark-focused: ramps that start mid-tone instead of near-white, so the\n // image reads dark overall with the lighter stop as a glow ──\n { name: 'onyx-gold', colors: ['#e8c877', '#b8862f', '#3a2f14', '#0b0b0d'] },\n { name: 'onyx-silver', colors: ['#d7dade', '#8b9199', '#3a3f45', '#0c0d0f'] },\n { name: 'graphite-royal', colors: ['#9fb0e8', '#3f56c4', '#2a2e38', '#101218'] },\n { name: 'onyx-ultraviolet', colors: ['#c9a6ff', '#7b3ff2', '#2a1450', '#08070d'] },\n { name: 'onyx-snow', colors: ['#ffffff', '#b0b0b0', '#4a4a4a', '#000000'] },\n { name: 'midnight-ember', colors: ['#f0a468', '#c23f1f', '#2a1410', '#0a0a0c'] },\n { name: 'slate-harbor', colors: ['#aeb7bd', '#6f7b84', '#3c454c', '#1a1f24'] },\n { name: 'ember-mono', colors: ['#ff9a6b', '#cf5604', '#632400', '#180501'] },\n\n // ── Monochromatic: one hue, lightness stepping down (OKLCH-spaced) ──\n { name: 'tangerine-mono', colors: ['#ffeee4', '#feb079', '#dc7100', '#824103'] },\n { name: 'electric-blue-mono', colors: ['#ebf2fe', '#a4c6fc', '#5d91e7', '#2a5397'] },\n { name: 'magenta-mono', colors: ['#feecf5', '#edaed1', '#cd6ca6', '#823564'] },\n { name: 'emerald-mono', colors: ['#e2f8ea', '#95d5af', '#2cab70', '#03663e'] },\n { name: 'violet-mono', colors: ['#f2f0fe', '#c8b7ff', '#9e71fd', '#5f38a7'] },\n { name: 'crimson-mono', colors: ['#ffedec', '#feaaa9', '#f44f5d', '#9e122b'] },\n { name: 'aqua-mono', colors: ['#dff7f8', '#8dd2d6', '#39a4a9', '#026266'] },\n\n { name: 'burgundy-mono', colors: ['#fbe0e4', '#dc9ea8', '#a7415a', '#560c24'] },\n { name: 'indigo-mono', colors: ['#eaeefe', '#a7b2f7', '#5a5ce2', '#261b81'] },\n { name: 'lime-mono', colors: ['#edfcd7', '#c2e87d', '#9fc93e', '#638119'] },\n\n // ── Monochromatic earth tones: same idea, chroma held up so they stay\n // clay / ochre / moss rather than collapsing to greige ──\n { name: 'terracotta-mono', colors: ['#fdeae4', '#f6b19b', '#c5694c', '#73311b'] },\n { name: 'ochre-mono', colors: ['#f9edd5', '#dec082', '#a87f09', '#5b4404'] },\n { name: 'olive-mono', colors: ['#edf2d7', '#c2cb91', '#848e38', '#474d04'] },\n { name: 'fern-mono', colors: ['#e3f5df', '#a9d1a0', '#5f9752', '#2a5320'] },\n { name: 'verdigris-mono', colors: ['#d6f7f1', '#96d2c9', '#38988d', '#07534c'] },\n { name: 'umber-mono', colors: ['#feeada', '#e1bc9d', '#ab7a4f', '#623f1e'] },\n { name: 'adobe-mono', colors: ['#fee9e8', '#eeb3b0', '#bb6d6b', '#6c3434'] },\n\n // ── Neutral / mono ──\n { name: 'ivory-ink', colors: ['#f7f4ef', '#d8d2c8', '#8a8378', '#20201d'] },\n { name: 'paper-graphite', colors: ['#f4f4f5', '#cfcfd4', '#83838f', '#1c1c22'] },\n { name: 'bone-coffee', colors: ['#f5efe6', '#d9c9b3', '#9c8468', '#3a2c1e'] },\n { name: 'porcelain-greige', colors: ['#f6f3ef', '#ddd4c9', '#b0a596', '#6a5f52'] },\n { name: 'battleship-steel', colors: ['#e6e9e8', '#b9c0be', '#7d8683', '#3f4745'] },\n { name: 'gunmetal-fog', colors: ['#eceef1', '#c2c7cd', '#7b838d', '#343a42'] },\n { name: 'silver-steel', colors: ['#e2e5e8', '#c4c9cf', '#9aa2ac', '#646f7d'] },\n // ── Airy: a near-white base with soft color splashes, in the style of\n // blurred editorial gradients (light stops first, then the splash hues) ──\n { name: 'petal-bloom', colors: ['#e4eeff', '#bcd2ff', '#dfa6e9', '#fa787b', '#fba96a'] },\n { name: 'tulip-blur', colors: ['#e6f1f6', '#a8d4e8', '#f1c4d0', '#ee9a6e', '#f4b81c'] },\n { name: 'silk-rose', colors: ['#fcedf4', '#f2cfdd', '#f0c1b8', '#d68ba5', '#b07a9f'] },\n\n // ── Flags: sampled from heraldic flag art ──\n { name: 'sultans-eagle', colors: ['#f2da38', '#e3c42a', '#c9a21d', '#a1353a', '#6a1c26'] },\n { name: 'zhuge-nu', colors: ['#f0f3ee', '#dfc652', '#4fa79b', '#1f8283', '#0f5252'] },\n] as const satisfies readonly Palette[];\n\n/** Union of every built-in palette name, e.g. `\"mint-sea\"`. */\nexport type PaletteName = (typeof colorPalettes)[number]['name'];\n\n/** Primary accent for UI chips: the palette ramp's mid-tone. */\nexport function paletteAccent(palette: Palette): string {\n const colors = palette.colors;\n return colors[Math.floor(colors.length / 2)];\n}\n\n/**\n * A color built to stand out on the palette's images: the hue opposite the\n * accent (or the whole ramp, when the accent is near-gray) at its most saturated\n * readable lightness. For chips, geometry and highlights.\n */\nexport function paletteComplement(palette: Palette): string {\n return paletteComplementColor(paletteAccent(palette), palette.colors);\n}\n\nconst HEX_COLOR = /^#[0-9a-fA-F]{6}$/;\n\n/**\n * Runtime check for palettes that didn't come through the type system\n * (JSON, CLI input, `as` casts). Throws with the offending palette's name.\n */\nexport function validatePalette(palette: Palette): void {\n const label = `Palette \"${palette?.name}\"`;\n if (typeof palette?.name !== 'string' || palette.name.length === 0) {\n throw new Error('Palette needs a non-empty name.');\n }\n const { colors } = palette;\n if (\n !Array.isArray(colors) ||\n colors.length < MIN_PALETTE_COLORS ||\n colors.length > MAX_PALETTE_COLORS\n ) {\n throw new Error(\n `${label} needs ${MIN_PALETTE_COLORS}–${MAX_PALETTE_COLORS} colors. Got ${Array.isArray(colors) ? colors.length : typeof colors}.`,\n );\n }\n for (const color of colors) {\n if (typeof color !== 'string' || !HEX_COLOR.test(color)) {\n throw new Error(`${label} has an invalid color \"${color}\". Use #rrggbb.`);\n }\n }\n}\n\n/** Validates every palette and that names are unique. */\nexport function validatePalettes(palettes: readonly Palette[]): void {\n if (palettes.length === 0) throw new Error('At least one palette is required.');\n const seen = new Set<string>();\n for (const palette of palettes) {\n validatePalette(palette);\n if (seen.has(palette.name)) throw new Error(`Duplicate palette name \"${palette.name}\".`);\n seen.add(palette.name);\n }\n}\n\n/** Look up a palette by name. Throws if not found. */\nexport function findPalette(\n name: PaletteName | (string & {}),\n palettes: readonly Palette[] = colorPalettes,\n): Palette {\n const palette = palettes.find((p) => p.name === name);\n if (!palette) {\n throw new Error(`Unknown palette \"${name}\". Valid: ${palettes.map((p) => p.name).join(', ')}`);\n }\n return palette;\n}\n\n/**\n * Deterministically pick a palette from a seed.\n *\n * Uses rendezvous (highest-random-weight) hashing: every palette gets a\n * pseudo-random score from `seed + name`, and the highest score wins. The list's\n * order doesn't matter, adding a palette only moves the seeds the new palette\n * wins (~1 in N), and removing one only moves the seeds that had picked it.\n */\nexport function pickPalette(seed: string, palettes: readonly Palette[] = colorPalettes): Palette {\n let best: Palette | undefined;\n let bestScore = -1;\n for (const palette of palettes) {\n // Run the hash through the PRNG once: FNV-1a alone mixes trailing\n // characters poorly, and names often share prefixes.\n const score = createSeededRandom(hashStringToInt(`${seed}::palette::${palette.name}`))();\n if (score > bestScore || (score === bestScore && palette.name < best!.name)) {\n best = palette;\n bestScore = score;\n }\n }\n if (!best) throw new Error('At least one palette is required.');\n return best;\n}\n"]}