@dooph-software/fluid-gradients 1.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/dist/index.js ADDED
@@ -0,0 +1,210 @@
1
+ import {
2
+ MAX_PALETTE_COLORS,
3
+ MIN_PALETTE_COLORS,
4
+ colorPalettes,
5
+ complementColor,
6
+ createSeededRandom,
7
+ findPalette,
8
+ hashStringToInt,
9
+ paletteAccent,
10
+ paletteComplement,
11
+ pickPalette,
12
+ validatePalette,
13
+ validatePalettes
14
+ } from "./chunk-KYCZHKFE.js";
15
+
16
+ // src/render.ts
17
+ import { createCanvas, ImageData } from "@napi-rs/canvas";
18
+ import sharp from "sharp";
19
+ import { createNoise2D } from "simplex-noise";
20
+ var lookDescriptions = {
21
+ baseNoiseFrequency: "Lower values give broader, softer color regions.",
22
+ domainWarpStrength: "How much the color field swirls.",
23
+ fractalOctaveCount: "More octaves add finer detail.",
24
+ colorTransitionContrast: "Sharpness of the edges between colors.",
25
+ grainOpacity: "Strength of the film-grain overlay."
26
+ };
27
+ var defaultLook = {
28
+ baseNoiseFrequency: 0.3,
29
+ domainWarpStrength: 1,
30
+ fractalOctaveCount: 2,
31
+ colorTransitionContrast: 1.75,
32
+ grainOpacity: 0.09
33
+ };
34
+ var DEFAULT_WIDTH = 1200;
35
+ var DEFAULT_HEIGHT = 1500;
36
+ var DEFAULT_WEBP_QUALITY = 82;
37
+ function hexToRgb(hex) {
38
+ const packed = parseInt(hex.slice(1), 16);
39
+ return [packed >> 16 & 255, packed >> 8 & 255, packed & 255];
40
+ }
41
+ function sampleColorRamp(rgbStops, position) {
42
+ const clamped = Math.max(0, Math.min(1, position));
43
+ const scaled = clamped * (rgbStops.length - 1);
44
+ const lowerIndex = Math.min(rgbStops.length - 2, Math.floor(scaled));
45
+ const blend = scaled - lowerIndex;
46
+ const [lowR, lowG, lowB] = rgbStops[lowerIndex];
47
+ const [highR, highG, highB] = rgbStops[lowerIndex + 1];
48
+ return [
49
+ lowR + (highR - lowR) * blend,
50
+ lowG + (highG - lowG) * blend,
51
+ lowB + (highB - lowB) * blend
52
+ ];
53
+ }
54
+ var smoothStep = (edge) => edge * edge * (3 - 2 * edge);
55
+ async function renderCanvas(options) {
56
+ const { seed, palette, width = DEFAULT_WIDTH, height = DEFAULT_HEIGHT } = options;
57
+ const look = { ...defaultLook, ...options.look };
58
+ const random = createSeededRandom(hashStringToInt(seed));
59
+ const rgbStops = palette.colors.map(hexToRgb);
60
+ const horizontalWarpNoise = createNoise2D(random);
61
+ const verticalWarpNoise = createNoise2D(random);
62
+ const colorFieldNoise = createNoise2D(random);
63
+ const sampleFractalNoise = (noise, sampleX, sampleY) => {
64
+ let total = 0;
65
+ let amplitude = 0.5;
66
+ let frequency = 1;
67
+ for (let octave = 0; octave < look.fractalOctaveCount; octave++) {
68
+ total += amplitude * noise(sampleX * frequency, sampleY * frequency);
69
+ amplitude *= 0.5;
70
+ frequency *= 2;
71
+ }
72
+ return total;
73
+ };
74
+ const canvas = createCanvas(width, height);
75
+ const context = canvas.getContext("2d");
76
+ const image = context.createImageData(width, height);
77
+ const pixels = image.data;
78
+ const aspectRatio = width / height;
79
+ for (let pixelY = 0; pixelY < height; pixelY++) {
80
+ for (let pixelX = 0; pixelX < width; pixelX++) {
81
+ const sampleX = pixelX / width * look.baseNoiseFrequency * aspectRatio;
82
+ const sampleY = pixelY / height * look.baseNoiseFrequency;
83
+ const horizontalWarpOffset = sampleFractalNoise(horizontalWarpNoise, sampleX, sampleY);
84
+ const verticalWarpOffset = sampleFractalNoise(verticalWarpNoise, sampleX + 5.2, sampleY + 1.3);
85
+ const rawFieldValue = sampleFractalNoise(
86
+ colorFieldNoise,
87
+ sampleX + look.domainWarpStrength * horizontalWarpOffset,
88
+ sampleY + look.domainWarpStrength * verticalWarpOffset
89
+ );
90
+ const normalizedValue = (rawFieldValue + 1) / 2;
91
+ const contrastedValue = smoothStep(
92
+ Math.max(0, Math.min(1, (normalizedValue - 0.5) * look.colorTransitionContrast + 0.5))
93
+ );
94
+ const [red, green, blue] = sampleColorRamp(rgbStops, contrastedValue);
95
+ const pixelIndex = (pixelY * width + pixelX) * 4;
96
+ pixels[pixelIndex] = red;
97
+ pixels[pixelIndex + 1] = green;
98
+ pixels[pixelIndex + 2] = blue;
99
+ pixels[pixelIndex + 3] = 255;
100
+ }
101
+ }
102
+ context.putImageData(image, 0, 0);
103
+ const grainPixels = new Uint8ClampedArray(width * height * 4);
104
+ for (let i = 0; i < width * height; i++) {
105
+ const brightness = Math.floor(random() * 256);
106
+ grainPixels[i * 4] = brightness;
107
+ grainPixels[i * 4 + 1] = brightness;
108
+ grainPixels[i * 4 + 2] = brightness;
109
+ grainPixels[i * 4 + 3] = 255;
110
+ }
111
+ const grainCanvas = createCanvas(width, height);
112
+ grainCanvas.getContext("2d").putImageData(new ImageData(grainPixels, width, height), 0, 0);
113
+ applyOverlay(context, grainCanvas, look.grainOpacity);
114
+ return { canvas, palette, accent: paletteAccent(palette), complement: paletteComplement(palette) };
115
+ }
116
+ function applyOverlay(context, overlay, opacity) {
117
+ context.save();
118
+ context.globalAlpha = opacity;
119
+ context.globalCompositeOperation = "overlay";
120
+ context.drawImage(overlay, 0, 0);
121
+ context.restore();
122
+ }
123
+ async function generateImage(options) {
124
+ const { format = "webp", quality = DEFAULT_WEBP_QUALITY } = options;
125
+ const { canvas, palette, accent, complement } = await renderCanvas(options);
126
+ const pngBuffer = canvas.encodeSync("png");
127
+ let buffer = pngBuffer;
128
+ if (format === "webp") {
129
+ buffer = await sharp(pngBuffer).webp({ quality }).toBuffer();
130
+ }
131
+ return {
132
+ buffer,
133
+ format,
134
+ width: canvas.width,
135
+ height: canvas.height,
136
+ seed: options.seed,
137
+ palette,
138
+ accent,
139
+ complement
140
+ };
141
+ }
142
+
143
+ // src/create.ts
144
+ function createMeshGradients(options) {
145
+ const { palettes, width = DEFAULT_WIDTH, height = DEFAULT_HEIGHT } = options;
146
+ validatePalettes(palettes);
147
+ validateDimension("width", width);
148
+ validateDimension("height", height);
149
+ const resolve = (seed, palette) => {
150
+ if (palette === void 0) return pickPalette(seed, palettes);
151
+ if (typeof palette === "string") return findPalette(palette, palettes);
152
+ validatePalette(palette);
153
+ return palette;
154
+ };
155
+ const resolveSize = (size) => {
156
+ const resolved = { width: size.width ?? width, height: size.height ?? height };
157
+ validateDimension("width", resolved.width);
158
+ validateDimension("height", resolved.height);
159
+ return resolved;
160
+ };
161
+ return {
162
+ palettes,
163
+ findPalette: (name) => findPalette(name, palettes),
164
+ pickPalette: (seed) => pickPalette(seed, palettes),
165
+ // async so an invalid palette rejects like every other render error.
166
+ render: async (renderOptions) => renderCanvas({
167
+ ...renderOptions,
168
+ ...resolveSize(renderOptions),
169
+ palette: resolve(renderOptions.seed, renderOptions.palette)
170
+ }),
171
+ generate: async (generateOptions) => generateImage({
172
+ ...generateOptions,
173
+ ...resolveSize(generateOptions),
174
+ palette: resolve(generateOptions.seed, generateOptions.palette)
175
+ })
176
+ };
177
+ }
178
+ function validateDimension(label, value) {
179
+ if (!Number.isInteger(value) || value <= 0) {
180
+ throw new Error(`${label} must be a positive integer. Got ${value}.`);
181
+ }
182
+ }
183
+
184
+ // src/index.ts
185
+ var builtIn = createMeshGradients({ palettes: colorPalettes });
186
+ var generateMeshGradient = builtIn.generate;
187
+ var renderMeshGradient = builtIn.render;
188
+ export {
189
+ DEFAULT_HEIGHT,
190
+ DEFAULT_WEBP_QUALITY,
191
+ DEFAULT_WIDTH,
192
+ MAX_PALETTE_COLORS,
193
+ MIN_PALETTE_COLORS,
194
+ colorPalettes,
195
+ complementColor,
196
+ createMeshGradients,
197
+ createSeededRandom,
198
+ defaultLook,
199
+ findPalette,
200
+ generateMeshGradient,
201
+ hashStringToInt,
202
+ lookDescriptions,
203
+ paletteAccent,
204
+ paletteComplement,
205
+ pickPalette,
206
+ renderMeshGradient,
207
+ validatePalette,
208
+ validatePalettes
209
+ };
210
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/render.ts","../src/create.ts","../src/index.ts"],"sourcesContent":["/*\n * render — the noise pipeline that turns a seed and a palette into pixels.\n *\n * ## behavior\n * - `renderCanvas` seeds one stream from the seed, builds three simplex noise\n * fields from it, domain-warps the third by the first two, and maps the result\n * along the palette ramp; the film grain then draws from the same stream.\n * - `generateImage` wraps that and encodes: PNG straight off the canvas, WebP\n * through sharp at `DEFAULT_WEBP_QUALITY`.\n * - `lookDescriptions` is the one copy of the per-knob prose; `src/flags.ts`\n * builds CLI help from it and `scripts/sync-docs.mjs` builds the README table.\n *\n * ## constraints\n * - The order values are drawn from `random` is fixed: three `createNoise2D`\n * calls, then one grain value per pixel. Inserting a draw, removing one, or\n * reordering them shifts every pixel of every image — the grain loop is not\n * incidental noise that can be swapped for `Math.random`; it consumes the\n * tail of the same stream. `test/fluid-gradients.test.mjs` compares seed `aspect` /\n * `jewel-peacock` byte-for-byte against artwork already shipped in Aspect.\n * - `defaultLook` and the bare constants in the pixel loop — the `5.2` / `1.3`\n * warp offsets, `amplitude` starting at 0.5 and halving, `frequency`\n * doubling, the `smoothStep` around the contrast term — are the prototype's\n * values, not tuning suggestions. Any of them changes every image rendered\n * without an explicit `look`, which is almost all of them.\n * - Noise is sampled in normalized coordinates (`pixelX / width`), with the\n * aspect ratio applied to X only. This is what makes a 700×800 render the same\n * composition as the 1200×1500 default, which `scripts/generate-examples.mjs`\n * relies on to ship small previews of the real output. Sampling in absolute\n * pixels would make every size a different picture.\n * - `DEFAULT_WEBP_QUALITY` and the plain `sharp(...).webp({ quality })` call are\n * part of the shipped bytes. Adding encoder options (effort, lossless,\n * chroma subsampling) changes the fixture comparison even though nothing about\n * the rendered pixels moved.\n */\nimport { createCanvas, ImageData, type Canvas, type SKRSContext2D } from '@napi-rs/canvas';\nimport sharp from 'sharp';\nimport { createNoise2D } from 'simplex-noise';\nimport { paletteAccent, paletteComplement, type Palette, type PaletteName } from './palettes';\nimport { createSeededRandom, hashStringToInt } from './random';\n\n/**\n * Knobs that define the gradient's look. The defaults are the exact values from\n * the original prototype; changing them changes every image for a given seed.\n */\nexport type GradientLook = {\n /** Zoom of the noise field. Lower = broader, softer color regions. */\n baseNoiseFrequency: number;\n /** How far the warp noise displaces the color field. */\n domainWarpStrength: number;\n /** Fractal noise octaves. More = finer detail. */\n fractalOctaveCount: number;\n /** Sharpness of transitions between palette stops. */\n colorTransitionContrast: number;\n /** Opacity of the film-grain overlay, 0–1. */\n grainOpacity: number;\n};\n\n/** One-line description per knob. Shared by the CLI help and the generated README table. */\nexport const lookDescriptions: Readonly<Record<keyof GradientLook, string>> = {\n baseNoiseFrequency: 'Lower values give broader, softer color regions.',\n domainWarpStrength: 'How much the color field swirls.',\n fractalOctaveCount: 'More octaves add finer detail.',\n colorTransitionContrast: 'Sharpness of the edges between colors.',\n grainOpacity: 'Strength of the film-grain overlay.',\n};\n\nexport const defaultLook: Readonly<GradientLook> = {\n baseNoiseFrequency: 0.3,\n domainWarpStrength: 1.0,\n fractalOctaveCount: 2,\n colorTransitionContrast: 1.75,\n grainOpacity: 0.09,\n};\n\nexport type MeshGradientOptions<Name extends string = PaletteName> = {\n /** Any string. The same seed + palette + size + look always renders the same image. */\n seed: string;\n /** Palette object, palette name, or omitted to pick deterministically from the seed. */\n palette?: Palette | Name;\n /** Pixels. Defaults to 1200. */\n width?: number;\n /** Pixels. Defaults to 1500. */\n height?: number;\n look?: Partial<GradientLook>;\n};\n\nexport type MeshGradientFormatOptions = {\n /** Defaults to 'webp'. */\n format?: 'webp' | 'png';\n /** WebP quality, 1–100. Defaults to 82. */\n quality?: number;\n};\n\n/** Options after the palette has been resolved to an object. */\nexport type ResolvedMeshGradientOptions = Omit<MeshGradientOptions, 'palette'> & {\n palette: Palette;\n};\n\nexport type MeshGradientCanvas = {\n canvas: Canvas;\n palette: Palette;\n /** Mid-tone hex from the palette, handy as a UI accent next to the image. */\n accent: string;\n /** Opposite-hue color built to stand out on the image, for chips and geometry. */\n complement: string;\n};\n\nexport type MeshGradientImage = {\n buffer: Buffer;\n format: 'webp' | 'png';\n width: number;\n height: number;\n seed: string;\n palette: Palette;\n accent: string;\n complement: string;\n};\n\nexport const DEFAULT_WIDTH = 1200;\nexport const DEFAULT_HEIGHT = 1500;\nexport const DEFAULT_WEBP_QUALITY = 82;\n\nfunction hexToRgb(hex: string): [number, number, number] {\n const packed = parseInt(hex.slice(1), 16);\n return [(packed >> 16) & 255, (packed >> 8) & 255, packed & 255];\n}\n\nfunction sampleColorRamp(\n rgbStops: [number, number, number][],\n position: number,\n): [number, number, number] {\n const clamped = Math.max(0, Math.min(1, position));\n const scaled = clamped * (rgbStops.length - 1);\n const lowerIndex = Math.min(rgbStops.length - 2, Math.floor(scaled));\n const blend = scaled - lowerIndex;\n const [lowR, lowG, lowB] = rgbStops[lowerIndex];\n const [highR, highG, highB] = rgbStops[lowerIndex + 1];\n return [\n lowR + (highR - lowR) * blend,\n lowG + (highG - lowG) * blend,\n lowB + (highB - lowB) * blend,\n ];\n}\n\nconst smoothStep = (edge: number) => edge * edge * (3 - 2 * edge);\n\n/**\n * Render a domain-warped noise gradient onto a canvas. The pixel pipeline and\n * the order random values are drawn in are verbatim from the prototype — both\n * must stay fixed for seeds to keep producing the same image.\n */\nexport async function renderCanvas(\n options: ResolvedMeshGradientOptions,\n): Promise<MeshGradientCanvas> {\n const { seed, palette, width = DEFAULT_WIDTH, height = DEFAULT_HEIGHT } = options;\n const look = { ...defaultLook, ...options.look };\n\n const random = createSeededRandom(hashStringToInt(seed));\n const rgbStops = palette.colors.map(hexToRgb);\n\n const horizontalWarpNoise = createNoise2D(random);\n const verticalWarpNoise = createNoise2D(random);\n const colorFieldNoise = createNoise2D(random);\n\n const sampleFractalNoise = (\n noise: (x: number, y: number) => number,\n sampleX: number,\n sampleY: number,\n ) => {\n let total = 0;\n let amplitude = 0.5;\n let frequency = 1;\n for (let octave = 0; octave < look.fractalOctaveCount; octave++) {\n total += amplitude * noise(sampleX * frequency, sampleY * frequency);\n amplitude *= 0.5;\n frequency *= 2;\n }\n return total;\n };\n\n const canvas = createCanvas(width, height);\n const context = canvas.getContext('2d');\n const image = context.createImageData(width, height);\n const pixels = image.data;\n const aspectRatio = width / height;\n\n for (let pixelY = 0; pixelY < height; pixelY++) {\n for (let pixelX = 0; pixelX < width; pixelX++) {\n const sampleX = (pixelX / width) * look.baseNoiseFrequency * aspectRatio;\n const sampleY = (pixelY / height) * look.baseNoiseFrequency;\n\n const horizontalWarpOffset = sampleFractalNoise(horizontalWarpNoise, sampleX, sampleY);\n const verticalWarpOffset = sampleFractalNoise(verticalWarpNoise, sampleX + 5.2, sampleY + 1.3);\n\n const rawFieldValue = sampleFractalNoise(\n colorFieldNoise,\n sampleX + look.domainWarpStrength * horizontalWarpOffset,\n sampleY + look.domainWarpStrength * verticalWarpOffset,\n );\n const normalizedValue = (rawFieldValue + 1) / 2;\n\n const contrastedValue = smoothStep(\n Math.max(0, Math.min(1, (normalizedValue - 0.5) * look.colorTransitionContrast + 0.5)),\n );\n\n const [red, green, blue] = sampleColorRamp(rgbStops, contrastedValue);\n const pixelIndex = (pixelY * width + pixelX) * 4;\n pixels[pixelIndex] = red;\n pixels[pixelIndex + 1] = green;\n pixels[pixelIndex + 2] = blue;\n pixels[pixelIndex + 3] = 255;\n }\n }\n\n context.putImageData(image, 0, 0);\n\n // Film-grain overlay. Draws from the same random stream as the noise above.\n const grainPixels = new Uint8ClampedArray(width * height * 4);\n for (let i = 0; i < width * height; i++) {\n const brightness = Math.floor(random() * 256);\n grainPixels[i * 4] = brightness;\n grainPixels[i * 4 + 1] = brightness;\n grainPixels[i * 4 + 2] = brightness;\n grainPixels[i * 4 + 3] = 255;\n }\n const grainCanvas = createCanvas(width, height);\n grainCanvas.getContext('2d').putImageData(new ImageData(grainPixels, width, height), 0, 0);\n applyOverlay(context, grainCanvas, look.grainOpacity);\n\n return { canvas, palette, accent: paletteAccent(palette), complement: paletteComplement(palette) };\n}\n\nfunction applyOverlay(context: SKRSContext2D, overlay: Canvas, opacity: number) {\n context.save();\n context.globalAlpha = opacity;\n context.globalCompositeOperation = 'overlay';\n context.drawImage(overlay, 0, 0);\n context.restore();\n}\n\n/**\n * Render a gradient and encode it. WebP (the default) is encoded with sharp;\n * PNG comes straight from the canvas.\n */\nexport async function generateImage(\n options: ResolvedMeshGradientOptions & MeshGradientFormatOptions,\n): Promise<MeshGradientImage> {\n const { format = 'webp', quality = DEFAULT_WEBP_QUALITY } = options;\n const { canvas, palette, accent, complement } = await renderCanvas(options);\n const pngBuffer = canvas.encodeSync('png');\n\n let buffer: Buffer = pngBuffer;\n if (format === 'webp') {\n buffer = await sharp(pngBuffer).webp({ quality }).toBuffer();\n }\n\n return {\n buffer,\n format,\n width: canvas.width,\n height: canvas.height,\n seed: options.seed,\n palette,\n accent,\n complement,\n };\n}\n","import {\n findPalette,\n pickPalette,\n validatePalette,\n validatePalettes,\n type Palette,\n} from './palettes';\nimport {\n DEFAULT_HEIGHT,\n DEFAULT_WIDTH,\n generateImage,\n renderCanvas,\n type MeshGradientCanvas,\n type MeshGradientFormatOptions,\n type MeshGradientImage,\n type MeshGradientOptions,\n} from './render';\n\nexport type CreateMeshGradientsOptions<P extends readonly Palette[]> = {\n palettes: P;\n /** Default output width in pixels for this generator. Per-call `width` wins. Defaults to 1200. */\n width?: number;\n /** Default output height in pixels for this generator. Per-call `height` wins. Defaults to 1500. */\n height?: number;\n};\n\nexport type MeshGradients<Name extends string> = {\n palettes: readonly Palette[];\n /** Look up a palette in this set by name. Accepts any string and throws on a miss. */\n findPalette: (name: Name | (string & {})) => Palette;\n /** Deterministically pick a palette in this set from a seed. */\n pickPalette: (seed: string) => Palette;\n /** Render to an `@napi-rs/canvas` canvas, e.g. to draw on top before encoding. */\n render: (options: MeshGradientOptions<Name>) => Promise<MeshGradientCanvas>;\n /** Render and encode to WebP (default) or PNG. */\n generate: (\n options: MeshGradientOptions<Name> & MeshGradientFormatOptions,\n ) => Promise<MeshGradientImage>;\n};\n\n/**\n * Create a generator bound to a palette set and default output size. Palettes\n * and size are validated here, so a bad config fails when the module loads\n * instead of on first render.\n *\n * @example\n * export const gradients = createMeshGradients({\n * palettes: [...colorPalettes, { name: 'brand', colors: ['#f4f4f5', '#8fa579', '#1f2c44'] }],\n * width: 1920,\n * height: 1080,\n * });\n */\nexport function createMeshGradients<const P extends readonly Palette[]>(\n options: CreateMeshGradientsOptions<P>,\n): MeshGradients<P[number]['name']> {\n const { palettes, width = DEFAULT_WIDTH, height = DEFAULT_HEIGHT } = options;\n validatePalettes(palettes);\n validateDimension('width', width);\n validateDimension('height', height);\n\n const resolve = (seed: string, palette: Palette | string | undefined): Palette => {\n if (palette === undefined) return pickPalette(seed, palettes);\n if (typeof palette === 'string') return findPalette(palette, palettes);\n validatePalette(palette);\n return palette;\n };\n\n const resolveSize = (size: { width?: number; height?: number }) => {\n const resolved = { width: size.width ?? width, height: size.height ?? height };\n validateDimension('width', resolved.width);\n validateDimension('height', resolved.height);\n return resolved;\n };\n\n return {\n palettes,\n findPalette: (name) => findPalette(name, palettes),\n pickPalette: (seed) => pickPalette(seed, palettes),\n // async so an invalid palette rejects like every other render error.\n render: async (renderOptions) =>\n renderCanvas({\n ...renderOptions,\n ...resolveSize(renderOptions),\n palette: resolve(renderOptions.seed, renderOptions.palette),\n }),\n generate: async (generateOptions) =>\n generateImage({\n ...generateOptions,\n ...resolveSize(generateOptions),\n palette: resolve(generateOptions.seed, generateOptions.palette),\n }),\n };\n}\n\nfunction validateDimension(label: 'width' | 'height', value: number): void {\n if (!Number.isInteger(value) || value <= 0) {\n throw new Error(`${label} must be a positive integer. Got ${value}.`);\n }\n}\n","import { createMeshGradients } from './create';\nimport { colorPalettes } from './palettes';\n\nexport {\n createMeshGradients,\n type CreateMeshGradientsOptions,\n type MeshGradients,\n} from './create';\nexport {\n colorPalettes,\n complementColor,\n findPalette,\n MAX_PALETTE_COLORS,\n MIN_PALETTE_COLORS,\n paletteAccent,\n paletteComplement,\n pickPalette,\n validatePalette,\n validatePalettes,\n type HexColor,\n type Palette,\n type PaletteColors,\n type PaletteName,\n} from './palettes';\nexport {\n DEFAULT_HEIGHT,\n DEFAULT_WEBP_QUALITY,\n DEFAULT_WIDTH,\n defaultLook,\n lookDescriptions,\n type GradientLook,\n type MeshGradientCanvas,\n type MeshGradientFormatOptions,\n type MeshGradientImage,\n type MeshGradientOptions,\n} from './render';\nexport { createSeededRandom, hashStringToInt } from './random';\n\n/** Generator bound to the built-in palettes. */\nconst builtIn = createMeshGradients({ palettes: colorPalettes });\n\n/** Render and encode a gradient using the built-in palettes. */\nexport const generateMeshGradient = builtIn.generate;\n/** Render a gradient to a canvas using the built-in palettes. */\nexport const renderMeshGradient = builtIn.render;\n"],"mappings":"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","names":[]}
@@ -0,0 +1,25 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true});
2
+
3
+
4
+
5
+
6
+
7
+
8
+
9
+
10
+
11
+
12
+ var _chunkCRRF2Q64cjs = require('./chunk-CRRF2Q64.cjs');
13
+
14
+
15
+
16
+
17
+
18
+
19
+
20
+
21
+
22
+
23
+
24
+ exports.MAX_PALETTE_COLORS = _chunkCRRF2Q64cjs.MAX_PALETTE_COLORS; exports.MIN_PALETTE_COLORS = _chunkCRRF2Q64cjs.MIN_PALETTE_COLORS; exports.colorPalettes = _chunkCRRF2Q64cjs.colorPalettes; exports.complementColor = _chunkCRRF2Q64cjs.complementColor; exports.findPalette = _chunkCRRF2Q64cjs.findPalette; exports.paletteAccent = _chunkCRRF2Q64cjs.paletteAccent; exports.paletteComplement = _chunkCRRF2Q64cjs.paletteComplement; exports.pickPalette = _chunkCRRF2Q64cjs.pickPalette; exports.validatePalette = _chunkCRRF2Q64cjs.validatePalette; exports.validatePalettes = _chunkCRRF2Q64cjs.validatePalettes;
25
+ //# sourceMappingURL=palettes.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["c:\\Users\\stick\\Github\\dooph\\mesh-gradients\\dist\\palettes.cjs"],"names":[],"mappings":"AAAA;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF,wDAA6B;AAC7B;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF,2lBAAC","file":"C:\\Users\\stick\\Github\\dooph\\mesh-gradients\\dist\\palettes.cjs"}
@@ -0,0 +1,261 @@
1
+ /**
2
+ * The complement of a `#rrggbb` color, built to stand out: the opposite OKLCH
3
+ * hue at its most saturated readable lightness. Grays get the opposite-lightness gray.
4
+ */
5
+ declare function complementColor(hex: string): string;
6
+
7
+ declare const MIN_PALETTE_COLORS = 3;
8
+ declare const MAX_PALETTE_COLORS = 5;
9
+ /** `#rrggbb`. The type only checks the `#`; the 6 hex digits are checked at runtime. */
10
+ type HexColor = `#${string}`;
11
+ /** A color ramp of 3–5 stops, light → dark. */
12
+ type PaletteColors = readonly [HexColor, HexColor, HexColor] | readonly [HexColor, HexColor, HexColor, HexColor] | readonly [HexColor, HexColor, HexColor, HexColor, HexColor];
13
+ type Palette = {
14
+ /** Unique within a palette set. Seed-based picks are keyed on it, so renaming reshuffles. */
15
+ name: string;
16
+ colors: PaletteColors;
17
+ };
18
+ declare const colorPalettes: readonly [{
19
+ readonly name: "amber-latte";
20
+ readonly colors: readonly ["#faf4ea", "#f2d29b", "#e0a45c", "#b9722f"];
21
+ }, {
22
+ readonly name: "terracotta-sand";
23
+ readonly colors: readonly ["#f6ede1", "#e8c9a0", "#cf8f63", "#9c4e33"];
24
+ }, {
25
+ readonly name: "peach-blush";
26
+ readonly colors: readonly ["#fdf1ec", "#f8cbb6", "#ef9a8a", "#d76e73"];
27
+ }, {
28
+ readonly name: "coral-cream";
29
+ readonly colors: readonly ["#fbe9df", "#f5b79a", "#e8735a", "#c33f36"];
30
+ }, {
31
+ readonly name: "honey-espresso";
32
+ readonly colors: readonly ["#f7ecd9", "#e5b877", "#b9793f", "#5f3418"];
33
+ }, {
34
+ readonly name: "apricot-plum";
35
+ readonly colors: readonly ["#fdf0e6", "#f6c79a", "#e58b6f", "#7d4a6b"];
36
+ }, {
37
+ readonly name: "golden-hour";
38
+ readonly colors: readonly ["#fdf3e1", "#f7cf8a", "#e89a5a", "#9a4a3a"];
39
+ }, {
40
+ readonly name: "vermilion-blush";
41
+ readonly colors: readonly ["#fbe8e2", "#f7c6a8", "#e4562f", "#a82a1c"];
42
+ }, {
43
+ readonly name: "ivory-gilt";
44
+ readonly colors: readonly ["#fffdf7", "#f7ecd0", "#e0c47e", "#b08c3a"];
45
+ }, {
46
+ readonly name: "rose-gold";
47
+ readonly colors: readonly ["#fceee8", "#f2cdbd", "#dba18c", "#b3705c"];
48
+ }, {
49
+ readonly name: "peony-glow";
50
+ readonly colors: readonly ["#fdeef3", "#f9b3c8", "#f78a5c", "#f2a93f"];
51
+ }, {
52
+ readonly name: "lavender-rose";
53
+ readonly colors: readonly ["#f4eefb", "#d9c2ef", "#c98fb8", "#8a5fd0"];
54
+ }, {
55
+ readonly name: "violet-mist";
56
+ readonly colors: readonly ["#f1eefb", "#c9b8ef", "#8f7fe0", "#4a3d9c"];
57
+ }, {
58
+ readonly name: "mauve-plum";
59
+ readonly colors: readonly ["#f6f0f2", "#d8b8c6", "#a5738c", "#5f3a55"];
60
+ }, {
61
+ readonly name: "periwinkle-blush";
62
+ readonly colors: readonly ["#f0f0fb", "#c3c6f0", "#cf9fc6", "#6a5fd0"];
63
+ }, {
64
+ readonly name: "orchid-frost";
65
+ readonly colors: readonly ["#f6eff6", "#e0c2e0", "#b98fc4", "#7a5aa6", "#3f2f5c"];
66
+ }, {
67
+ readonly name: "quartz-rose";
68
+ readonly colors: readonly ["#faf1f1", "#ecc9cc", "#c99aa3", "#8a5f6a"];
69
+ }, {
70
+ readonly name: "wisteria-sky";
71
+ readonly colors: readonly ["#f2f0fb", "#cfc6ef", "#9fb2e6", "#4f6aa6"];
72
+ }, {
73
+ readonly name: "zephyr-dawn";
74
+ readonly colors: readonly ["#fdf3f5", "#f2d6e2", "#c6d6f2", "#7f9fd8"];
75
+ }, {
76
+ readonly name: "candy-sky";
77
+ readonly colors: readonly ["#f6e8ff", "#f7a3e8", "#9aa8f5", "#3fa0f5"];
78
+ }, {
79
+ readonly name: "sky-slate";
80
+ readonly colors: readonly ["#eef4fb", "#bcd2ef", "#6f8fc4", "#2f3f66"];
81
+ }, {
82
+ readonly name: "teal-fog";
83
+ readonly colors: readonly ["#eef7f6", "#b6dcd8", "#5fa39c", "#234a49"];
84
+ }, {
85
+ readonly name: "arctic-ink";
86
+ readonly colors: readonly ["#f2f6f8", "#cddbe2", "#7f96a3", "#1c2630"];
87
+ }, {
88
+ readonly name: "cloud-navy";
89
+ readonly colors: readonly ["#f0f3f7", "#c6d1de", "#7488a3", "#1f2c44"];
90
+ }, {
91
+ readonly name: "denim-clay";
92
+ readonly colors: readonly ["#eef2f6", "#aebfd0", "#5f7794", "#b06a4e"];
93
+ }, {
94
+ readonly name: "cobalt-ice";
95
+ readonly colors: readonly ["#e8eeff", "#b9cdfb", "#2f4fd8", "#1836b8"];
96
+ }, {
97
+ readonly name: "sage-mist";
98
+ readonly colors: readonly ["#f2f5ee", "#cdd8bd", "#8fa579", "#4a5c3a"];
99
+ }, {
100
+ readonly name: "mint-sea";
101
+ readonly colors: readonly ["#eef8f4", "#b6e2d2", "#5fb39c", "#1f5c4d"];
102
+ }, {
103
+ readonly name: "olive-cream";
104
+ readonly colors: readonly ["#f6f3e6", "#dbd09b", "#a39a5c", "#5f5a2f"];
105
+ }, {
106
+ readonly name: "eucalyptus-fog";
107
+ readonly colors: readonly ["#eff4f1", "#cbdcd2", "#94b3a6", "#4f6b60"];
108
+ }, {
109
+ readonly name: "kelp-forest";
110
+ readonly colors: readonly ["#eef3ea", "#a9c49a", "#4f7a5a", "#1f3a2f"];
111
+ }, {
112
+ readonly name: "yuzu-mint";
113
+ readonly colors: readonly ["#fbfbe6", "#eef29a", "#b3d98a", "#3f8f7a"];
114
+ }, {
115
+ readonly name: "lime-lagoon";
116
+ readonly colors: readonly ["#f8ffe8", "#c8f56a", "#6fe3d8", "#2fc6d8"];
117
+ }, {
118
+ readonly name: "bliss-meadow";
119
+ readonly colors: readonly ["#f4fbff", "#8ed0f2", "#6cb03a", "#2d6b22"];
120
+ }, {
121
+ readonly name: "periwinkle-gold";
122
+ readonly colors: readonly ["#dfe6ff", "#a9b6f0", "#5b6bd6", "#e6c25a"];
123
+ }, {
124
+ readonly name: "lilac-lemon";
125
+ readonly colors: readonly ["#f3eefb", "#d3c2ef", "#9f8fd0", "#e6d25a"];
126
+ }, {
127
+ readonly name: "blush-teal";
128
+ readonly colors: readonly ["#fbeee9", "#f2c2b0", "#d78a8f", "#3f9c94"];
129
+ }, {
130
+ readonly name: "sage-coral";
131
+ readonly colors: readonly ["#f2f5ee", "#cdd8bd", "#8fa579", "#e0755a"];
132
+ }, {
133
+ readonly name: "slate-amber";
134
+ readonly colors: readonly ["#eef2f6", "#bcc9d8", "#5f7794", "#e0a24e"];
135
+ }, {
136
+ readonly name: "ultramarine-sand";
137
+ readonly colors: readonly ["#dfe8ff", "#e6c27a", "#4757dd", "#232a9c"];
138
+ }, {
139
+ readonly name: "amber-tide";
140
+ readonly colors: readonly ["#eef4f8", "#a9c8ef", "#f2b96b", "#df8a33"];
141
+ }, {
142
+ readonly name: "sunset-rose";
143
+ readonly colors: readonly ["#fce9d6", "#f6a95c", "#e8617a", "#8a3d7a"];
144
+ }, {
145
+ readonly name: "berry-bloom";
146
+ readonly colors: readonly ["#fbe9f2", "#e88fb8", "#c94f8f", "#7a2f6a"];
147
+ }, {
148
+ readonly name: "jewel-peacock";
149
+ readonly colors: readonly ["#e6f5f0", "#4fb3a6", "#1f7a99", "#243f8a"];
150
+ }, {
151
+ readonly name: "iris-meadow";
152
+ readonly colors: readonly ["#eaf5ec", "#7cc48f", "#3f8fb3", "#5f4fb0"];
153
+ }, {
154
+ readonly name: "autumn-spice";
155
+ readonly colors: readonly ["#f7ecd6", "#dba24f", "#b5522f", "#6e2f4a"];
156
+ }, {
157
+ readonly name: "ultraviolet-sea";
158
+ readonly colors: readonly ["#e9f0fb", "#6f9fe0", "#4a5fc4", "#6a3da6"];
159
+ }, {
160
+ readonly name: "purple-rain";
161
+ readonly colors: readonly ["#f1ebf7", "#b69ad6", "#6f3fa8", "#2a1740"];
162
+ }, {
163
+ readonly name: "nectarine-fizz";
164
+ readonly colors: readonly ["#fff1e6", "#ffc59a", "#ff8a5c", "#d9485f"];
165
+ }, {
166
+ readonly name: "raspberry-cream";
167
+ readonly colors: readonly ["#fdeef1", "#f4a9bd", "#d9537a", "#8a1f45"];
168
+ }, {
169
+ readonly name: "pastel-confetti";
170
+ readonly colors: readonly ["#ffffff", "#f9f3cf", "#cbbcf2", "#e8798f"];
171
+ }, {
172
+ readonly name: "burgundy-velvet";
173
+ readonly colors: readonly ["#f2dfe0", "#c98a91", "#8a2740", "#4a0f22"];
174
+ }, {
175
+ readonly name: "indigo-beam";
176
+ readonly colors: readonly ["#ffffff", "#a9b6f7", "#4a3fe0", "#2a1fb0"];
177
+ }, {
178
+ readonly name: "crimson-ice";
179
+ readonly colors: readonly ["#f2f7fc", "#f8d7e0", "#e0517f", "#a81050"];
180
+ }, {
181
+ readonly name: "fog-plum";
182
+ readonly colors: readonly ["#f2f0f3", "#cfc6d2", "#9a8a9c", "#5f4a5c"];
183
+ }, {
184
+ readonly name: "sand-sky";
185
+ readonly colors: readonly ["#f4efe4", "#e0d3b8", "#a9bcc4", "#5f7f8c"];
186
+ }, {
187
+ readonly name: "xanadu-sage";
188
+ readonly colors: readonly ["#f1f4f1", "#c8d3c8", "#738678", "#36443a"];
189
+ }, {
190
+ readonly name: "walnut-grain";
191
+ readonly colors: readonly ["#e8d5b7", "#c49a6c", "#8a5a34", "#4a2c17"];
192
+ }, {
193
+ readonly name: "onyx-gold";
194
+ readonly colors: readonly ["#e8c877", "#b8862f", "#3a2f14", "#0b0b0d"];
195
+ }, {
196
+ readonly name: "onyx-silver";
197
+ readonly colors: readonly ["#d7dade", "#8b9199", "#3a3f45", "#0c0d0f"];
198
+ }, {
199
+ readonly name: "graphite-royal";
200
+ readonly colors: readonly ["#9fb0e8", "#3f56c4", "#2a2e38", "#101218"];
201
+ }, {
202
+ readonly name: "onyx-ultraviolet";
203
+ readonly colors: readonly ["#c9a6ff", "#7b3ff2", "#2a1450", "#08070d"];
204
+ }, {
205
+ readonly name: "onyx-snow";
206
+ readonly colors: readonly ["#ffffff", "#b0b0b0", "#4a4a4a", "#000000"];
207
+ }, {
208
+ readonly name: "midnight-ember";
209
+ readonly colors: readonly ["#f0a468", "#c23f1f", "#2a1410", "#0a0a0c"];
210
+ }, {
211
+ readonly name: "slate-harbor";
212
+ readonly colors: readonly ["#aeb7bd", "#6f7b84", "#3c454c", "#1a1f24"];
213
+ }, {
214
+ readonly name: "ivory-ink";
215
+ readonly colors: readonly ["#f7f4ef", "#d8d2c8", "#8a8378", "#20201d"];
216
+ }, {
217
+ readonly name: "paper-graphite";
218
+ readonly colors: readonly ["#f4f4f5", "#cfcfd4", "#83838f", "#1c1c22"];
219
+ }, {
220
+ readonly name: "bone-coffee";
221
+ readonly colors: readonly ["#f5efe6", "#d9c9b3", "#9c8468", "#3a2c1e"];
222
+ }, {
223
+ readonly name: "porcelain-greige";
224
+ readonly colors: readonly ["#f6f3ef", "#ddd4c9", "#b0a596", "#6a5f52"];
225
+ }, {
226
+ readonly name: "battleship-steel";
227
+ readonly colors: readonly ["#e6e9e8", "#b9c0be", "#7d8683", "#3f4745"];
228
+ }, {
229
+ readonly name: "gunmetal-fog";
230
+ readonly colors: readonly ["#eceef1", "#c2c7cd", "#7b838d", "#343a42"];
231
+ }];
232
+ /** Union of every built-in palette name, e.g. `"mint-sea"`. */
233
+ type PaletteName = (typeof colorPalettes)[number]['name'];
234
+ /** Primary accent for UI chips: the palette ramp's mid-tone. */
235
+ declare function paletteAccent(palette: Palette): string;
236
+ /**
237
+ * A color built to stand out on the palette's images: the hue opposite the
238
+ * accent (or the whole ramp, when the accent is near-gray) at its most saturated
239
+ * readable lightness. For chips, geometry and highlights.
240
+ */
241
+ declare function paletteComplement(palette: Palette): string;
242
+ /**
243
+ * Runtime check for palettes that didn't come through the type system
244
+ * (JSON, CLI input, `as` casts). Throws with the offending palette's name.
245
+ */
246
+ declare function validatePalette(palette: Palette): void;
247
+ /** Validates every palette and that names are unique. */
248
+ declare function validatePalettes(palettes: readonly Palette[]): void;
249
+ /** Look up a palette by name. Throws if not found. */
250
+ declare function findPalette(name: PaletteName | (string & {}), palettes?: readonly Palette[]): Palette;
251
+ /**
252
+ * Deterministically pick a palette from a seed.
253
+ *
254
+ * Uses rendezvous (highest-random-weight) hashing: every palette gets a
255
+ * pseudo-random score from `seed + name`, and the highest score wins. The list's
256
+ * order doesn't matter, adding a palette only moves the seeds the new palette
257
+ * wins (~1 in N), and removing one only moves the seeds that had picked it.
258
+ */
259
+ declare function pickPalette(seed: string, palettes?: readonly Palette[]): Palette;
260
+
261
+ export { type HexColor, MAX_PALETTE_COLORS, MIN_PALETTE_COLORS, type Palette, type PaletteColors, type PaletteName, colorPalettes, complementColor, findPalette, paletteAccent, paletteComplement, pickPalette, validatePalette, validatePalettes };