@arraypress/covers 0.0.0-stage → 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/CHANGELOG.md +50 -0
- package/LICENSE +29 -0
- package/README.md +561 -2
- package/fonts/OFL.txt +93 -0
- package/fonts/inter-latin-400.woff +0 -0
- package/fonts/inter-latin-500.woff +0 -0
- package/fonts/inter-latin-600.woff +0 -0
- package/fonts/inter-latin-700.woff +0 -0
- package/package.json +96 -4
- package/recipes/aurora.json +132 -0
- package/recipes/bauhaus.json +113 -0
- package/recipes/bigType.json +103 -0
- package/recipes/contour.json +109 -0
- package/recipes/cutout.json +191 -0
- package/recipes/diagram.json +92 -0
- package/recipes/dither.json +170 -0
- package/recipes/duotone.json +75 -0
- package/recipes/gridTitle.json +136 -0
- package/recipes/iconTile.json +214 -0
- package/recipes/issue.json +221 -0
- package/recipes/mark.json +170 -0
- package/recipes/photo.json +156 -0
- package/recipes/riso.json +387 -0
- package/recipes/stripes.json +206 -0
- package/recipes/waveform.json +154 -0
- package/schema/recipe.schema.json +2292 -0
- package/src/astro.d.ts +31 -0
- package/src/astro.js +86 -0
- package/src/color.js +246 -0
- package/src/diagram.js +248 -0
- package/src/extract.js +90 -0
- package/src/flatten.js +234 -0
- package/src/fonts.js +200 -0
- package/src/geometry.js +187 -0
- package/src/grid.js +41 -0
- package/src/handmade.js +253 -0
- package/src/icons.js +157 -0
- package/src/index.d.ts +897 -0
- package/src/index.js +19 -0
- package/src/json-recipe.js +458 -0
- package/src/layer-options.js +37 -0
- package/src/layers.js +972 -0
- package/src/palette.js +185 -0
- package/src/png.js +77 -0
- package/src/print.js +658 -0
- package/src/recipes.js +78 -0
- package/src/render.js +462 -0
- package/src/rng.js +95 -0
- package/src/svg.js +69 -0
- package/src/text.js +230 -0
package/src/index.d.ts
ADDED
|
@@ -0,0 +1,897 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @arraypress/covers — build-time cover images from a seed: mesh
|
|
3
|
+
* gradients, grain, patterns, stripes, shapes, icons and type, as SVG or
|
|
4
|
+
* WebP / PNG / AVIF / JPEG.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/** A CSS colour: hex (3/4/6/8 digits), `rgb()`/`rgba()` or `hsl()`/`hsla()`. Alpha is ignored. */
|
|
8
|
+
export type Color = string;
|
|
9
|
+
|
|
10
|
+
/** Pixels, or a viewport unit of the canvas: `'6vmin'`, `'4vw'`, `'4vh'`, `'5vmax'`. */
|
|
11
|
+
export type Length = number | `${number}vmin` | `${number}vmax` | `${number}vw` | `${number}vh` | `${number}px`;
|
|
12
|
+
|
|
13
|
+
/** Where a box sits inside the safe area. */
|
|
14
|
+
export type Anchor =
|
|
15
|
+
| 'top-left' | 'top' | 'top-right'
|
|
16
|
+
| 'left' | 'center' | 'right'
|
|
17
|
+
| 'bottom-left' | 'bottom' | 'bottom-right';
|
|
18
|
+
|
|
19
|
+
/** A CSS `mix-blend-mode`. */
|
|
20
|
+
export type BlendMode =
|
|
21
|
+
| 'normal' | 'multiply' | 'screen' | 'overlay' | 'darken' | 'lighten' | 'color-dodge' | 'color-burn'
|
|
22
|
+
| 'hard-light' | 'soft-light' | 'difference' | 'exclusion' | 'hue' | 'saturation' | 'color' | 'luminosity';
|
|
23
|
+
|
|
24
|
+
/** A point as fractions (0–1) of the canvas. */
|
|
25
|
+
export interface Point { x?: number; y?: number }
|
|
26
|
+
|
|
27
|
+
/** A rectangle as fractions (0–1) of the canvas. `width`/`height` default to the rest of the canvas. */
|
|
28
|
+
export interface Region { x?: number; y?: number; width?: number; height?: number }
|
|
29
|
+
|
|
30
|
+
/** A resolved palette: what every layer draws its default colours from. */
|
|
31
|
+
export interface Palette {
|
|
32
|
+
readonly mode: 'light' | 'dark';
|
|
33
|
+
/** Page colour under everything. */
|
|
34
|
+
readonly bg: string;
|
|
35
|
+
/** Gradient, blob, stripe and shape colours. */
|
|
36
|
+
readonly colors: readonly string[];
|
|
37
|
+
/** Type, readable on `bg` (7:1 when derived). */
|
|
38
|
+
readonly text: string;
|
|
39
|
+
/** The one strong colour. */
|
|
40
|
+
readonly accent: string;
|
|
41
|
+
/** White, or near-black when the accent is light: marks drawn on the accent. */
|
|
42
|
+
readonly onAccent: string;
|
|
43
|
+
/** Secondary type. */
|
|
44
|
+
readonly muted: string;
|
|
45
|
+
/** Raised things on `bg` (tiles, pills). */
|
|
46
|
+
readonly surface: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* A palette as written: a preset name, one colour (an accent to derive
|
|
51
|
+
* from), `[bg, ...colors]`, or a partial object.
|
|
52
|
+
*/
|
|
53
|
+
/** Built-in palette presets. */
|
|
54
|
+
export type PresetName = 'paper' | 'ink' | 'dawn' | 'dusk' | 'ocean' | 'ember' | 'moss' | 'carbon' | 'mono';
|
|
55
|
+
|
|
56
|
+
export type PaletteInput =
|
|
57
|
+
| PresetName
|
|
58
|
+
| (string & {})
|
|
59
|
+
| Color[]
|
|
60
|
+
| {
|
|
61
|
+
bg?: Color;
|
|
62
|
+
colors?: Color[];
|
|
63
|
+
text?: Color;
|
|
64
|
+
accent?: Color;
|
|
65
|
+
muted?: Color;
|
|
66
|
+
surface?: Color;
|
|
67
|
+
onAccent?: Color;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
/** A font the text layers can use. TrueType, OpenType or WOFF (not WOFF2). */
|
|
71
|
+
export interface FontSource {
|
|
72
|
+
/** Family name, as text layers ask for it (`font: 'Inter'`). */
|
|
73
|
+
name: string;
|
|
74
|
+
data: Uint8Array | ArrayBuffer;
|
|
75
|
+
/** Default 400. */
|
|
76
|
+
weight?: number;
|
|
77
|
+
/** Default `'normal'`. */
|
|
78
|
+
style?: 'normal' | 'italic';
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** Options every layer takes. */
|
|
82
|
+
export interface BaseLayer {
|
|
83
|
+
/** Names the layer, for recipe overrides (and seeds its random stream). */
|
|
84
|
+
id?: string;
|
|
85
|
+
/** Seeds this layer's random stream independently of the cover's (default: `id`, else type + index). */
|
|
86
|
+
seed?: string;
|
|
87
|
+
/** 0–1. */
|
|
88
|
+
opacity?: number;
|
|
89
|
+
blend?: BlendMode;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export interface SolidLayer extends BaseLayer {
|
|
93
|
+
type: 'solid';
|
|
94
|
+
/** Default `palette.bg`. */
|
|
95
|
+
color?: Color;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export interface LinearLayer extends BaseLayer {
|
|
99
|
+
type: 'linear';
|
|
100
|
+
/** Default the first two palette colours. */
|
|
101
|
+
colors?: Color[];
|
|
102
|
+
/** Per-colour stop opacity. */
|
|
103
|
+
opacities?: number[];
|
|
104
|
+
/** CSS angle: 0 points up, 90 right. Default seeded. */
|
|
105
|
+
angle?: number;
|
|
106
|
+
/** Ease between stops (smoothstep). Default true. */
|
|
107
|
+
ease?: boolean;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export interface RadialLayer extends BaseLayer {
|
|
111
|
+
type: 'radial';
|
|
112
|
+
/** Centre to edge. Default `[colors[0], bg]`. */
|
|
113
|
+
colors?: Color[];
|
|
114
|
+
/** Centre, fractions of the canvas. Default seeded. */
|
|
115
|
+
x?: number;
|
|
116
|
+
y?: number;
|
|
117
|
+
/** Default `'80vmax'`. */
|
|
118
|
+
radius?: Length;
|
|
119
|
+
/** Default true. */
|
|
120
|
+
ease?: boolean;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export interface MeshBlob {
|
|
124
|
+
/** Centre, fractions of the canvas. */
|
|
125
|
+
x?: number;
|
|
126
|
+
y?: number;
|
|
127
|
+
/** Radius as a fraction of the longer side. */
|
|
128
|
+
r?: number;
|
|
129
|
+
/** Height-to-width ratio. */
|
|
130
|
+
squash?: number;
|
|
131
|
+
/** Degrees. */
|
|
132
|
+
rotate?: number;
|
|
133
|
+
color?: Color;
|
|
134
|
+
opacity?: number;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export interface MeshLayer extends BaseLayer {
|
|
138
|
+
type: 'mesh';
|
|
139
|
+
/** Default the palette's colours. */
|
|
140
|
+
colors?: Color[];
|
|
141
|
+
/** How many distinct colours to use (default 3 from the palette; all of `colors` when given). */
|
|
142
|
+
hues?: number;
|
|
143
|
+
/** Neighbouring hues only: keep the colours within this many degrees (OKLCH), making neighbours when the palette has too few. */
|
|
144
|
+
hueSpan?: number;
|
|
145
|
+
/** Number of fields. Default 4. */
|
|
146
|
+
count?: number;
|
|
147
|
+
/** `'sweep'` (default): long fields along one direction, centres free to leave the frame. `'ring'`: rounder blobs round the centre. */
|
|
148
|
+
arrangement?: 'sweep' | 'ring';
|
|
149
|
+
/** Sweep direction, degrees. Default seeded. */
|
|
150
|
+
angle?: number;
|
|
151
|
+
/** How much more a sweep blurs along its direction than across. Default 1.5. */
|
|
152
|
+
streak?: number;
|
|
153
|
+
/** Blur std deviation. Default `'11vmax'`. */
|
|
154
|
+
blur?: Length;
|
|
155
|
+
/** How far fields wander, 0–1. Default 1 (sweep), 0.7 (ring). */
|
|
156
|
+
spread?: number;
|
|
157
|
+
/** Radius range, fractions of the longer side. Default `[0.42, 0.72]` (sweep), `[0.3, 0.52]` (ring). */
|
|
158
|
+
size?: [number, number];
|
|
159
|
+
/** Pull every colour into an OKLCH lightness / chroma band, keeping its hue. */
|
|
160
|
+
tone?: { l?: [number, number]; c?: [number, number] };
|
|
161
|
+
/** A second field meeting the first along a soft curve, with a sharper edge and a faint sheen (silk). */
|
|
162
|
+
fold?: boolean | { color?: Color; to?: Color; position?: number; bend?: number; softness?: number; sheen?: number };
|
|
163
|
+
/** Thin light motion streaks along the sweep. Default 0. */
|
|
164
|
+
streaks?: number;
|
|
165
|
+
/** Take the colours from a photo (path or bytes; needs sharp). */
|
|
166
|
+
from?: string | Uint8Array;
|
|
167
|
+
/** Under the fields: `'auto'` (default, a gradient across the sweep between the first two colours), `'bg'`, a colour, or `null`. */
|
|
168
|
+
base?: 'auto' | 'bg' | Color | null | false;
|
|
169
|
+
/** Place fields explicitly (overrides `count`). */
|
|
170
|
+
blobs?: MeshBlob[];
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export interface StripesLayer extends BaseLayer {
|
|
174
|
+
type: 'stripes';
|
|
175
|
+
/** Default `'vertical'`. */
|
|
176
|
+
direction?: 'vertical' | 'horizontal' | 'diagonal';
|
|
177
|
+
/** For `diagonal`. Default 45. */
|
|
178
|
+
angle?: number;
|
|
179
|
+
/** Default 8 (or the length of `widths`). */
|
|
180
|
+
count?: number;
|
|
181
|
+
/** `'even'` (default), `'random'`, `'taper'` (each `taper`× the last) or relative weights. */
|
|
182
|
+
widths?: 'even' | 'random' | 'taper' | number[];
|
|
183
|
+
/** Ratio for `widths: 'taper'`. Default 0.78. */
|
|
184
|
+
taper?: number;
|
|
185
|
+
/** Cycled. Default the palette's colours. */
|
|
186
|
+
colors?: Color[];
|
|
187
|
+
/** Space between stripes (the background shows). Default 0. */
|
|
188
|
+
gap?: Length;
|
|
189
|
+
/** Stripe corner radius. */
|
|
190
|
+
radius?: Length;
|
|
191
|
+
/** Default the whole canvas. */
|
|
192
|
+
region?: Region;
|
|
193
|
+
/** Corner radius of the region. */
|
|
194
|
+
regionRadius?: Length;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export interface ImageLayer extends BaseLayer {
|
|
198
|
+
type: 'image';
|
|
199
|
+
/** A local file path, a `data:` URI, or bytes. Never fetched from the network. */
|
|
200
|
+
src: string | Uint8Array;
|
|
201
|
+
/** Default `'cover'`. */
|
|
202
|
+
fit?: 'cover' | 'contain';
|
|
203
|
+
/** Crop focus: an anchor, or (with sharp) `'attention'` / `'entropy'`. Default `'center'`. */
|
|
204
|
+
focus?: Anchor | 'attention' | 'entropy';
|
|
205
|
+
/** Default the whole canvas. */
|
|
206
|
+
region?: Region;
|
|
207
|
+
/** Map tones to two colours (shadows, highlights); `true` derives them from the palette. */
|
|
208
|
+
duotone?: boolean | [Color, Color];
|
|
209
|
+
grayscale?: boolean;
|
|
210
|
+
/** Stretch tones before mapping (needs sharp). Default true with `duotone`/`grayscale`. */
|
|
211
|
+
normalize?: boolean;
|
|
212
|
+
/** A colour wash over the image. */
|
|
213
|
+
tint?: Color | { color?: Color; amount?: number; blend?: BlendMode };
|
|
214
|
+
blur?: Length;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export interface GrainLayer extends BaseLayer {
|
|
218
|
+
type: 'grain';
|
|
219
|
+
/** Strength, 0–1. Default 0.08. */
|
|
220
|
+
amount?: number;
|
|
221
|
+
/** Grain size in px. Default 1. */
|
|
222
|
+
size?: number;
|
|
223
|
+
/** Default true. */
|
|
224
|
+
monochrome?: boolean;
|
|
225
|
+
/** `'edges'`: heavier toward the edges and corners (spray paint). */
|
|
226
|
+
vary?: 'edges' | null;
|
|
227
|
+
/** Default `'overlay'`. In raster output: normal, multiply, screen, overlay, soft-light. */
|
|
228
|
+
blend?: 'normal' | 'multiply' | 'screen' | 'overlay' | 'soft-light';
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export interface PatternLayer extends BaseLayer {
|
|
232
|
+
type: 'pattern';
|
|
233
|
+
/** Default `'grid'`. */
|
|
234
|
+
kind?: 'grid' | 'dots' | 'lines' | 'rings' | 'waves';
|
|
235
|
+
/** Cell / spacing. Default `'8.33vmin'` (`'3.2vmin'` dots, `'7vmin'` rings). */
|
|
236
|
+
size?: Length;
|
|
237
|
+
/** Line width (dot radius for dots). */
|
|
238
|
+
stroke?: Length;
|
|
239
|
+
/** Default `palette.text`. */
|
|
240
|
+
color?: Color;
|
|
241
|
+
/** Default 0.14. */
|
|
242
|
+
opacity?: number;
|
|
243
|
+
/** For `lines`, degrees. */
|
|
244
|
+
angle?: number;
|
|
245
|
+
/** Centre of rings / grid alignment, fractions. Default the middle. */
|
|
246
|
+
center?: Point;
|
|
247
|
+
/** For `waves`. */
|
|
248
|
+
wavelength?: Length;
|
|
249
|
+
amplitude?: Length;
|
|
250
|
+
/** Fade out with a mask. Default `'none'`. */
|
|
251
|
+
fade?: 'none' | 'radial' | 'edges' | 'top' | 'bottom' | 'left' | 'right';
|
|
252
|
+
/** Dashed or dotted lines (or `[on, off]` lengths). */
|
|
253
|
+
dash?: 'dashed' | 'dotted' | true | [Length, Length];
|
|
254
|
+
/** Grid / lines: N equal cells (inner lines only) instead of `size`. */
|
|
255
|
+
divisions?: number;
|
|
256
|
+
/** Regions (fractions) the lines break around. */
|
|
257
|
+
gaps?: Region[];
|
|
258
|
+
region?: Region;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
export interface ShapeItem {
|
|
262
|
+
kind?: 'circle' | 'square' | 'pill';
|
|
263
|
+
/** Centre, fractions of the region. */
|
|
264
|
+
x?: number;
|
|
265
|
+
y?: number;
|
|
266
|
+
size?: Length;
|
|
267
|
+
color?: Color;
|
|
268
|
+
rotate?: number;
|
|
269
|
+
opacity?: number;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
export interface ShapesLayer extends BaseLayer {
|
|
273
|
+
type: 'shapes';
|
|
274
|
+
/** Default `'circle'`. */
|
|
275
|
+
kind?: 'circle' | 'square' | 'pill' | 'mixed';
|
|
276
|
+
/** Default 6. */
|
|
277
|
+
count?: number;
|
|
278
|
+
/** `[min, max]`. Default `['7vmin', '20vmin']`. */
|
|
279
|
+
size?: [Length, Length];
|
|
280
|
+
colors?: Color[];
|
|
281
|
+
/** Default `'scatter'` (spread out, avoiding overlaps). */
|
|
282
|
+
placement?: 'scatter' | 'row' | 'column';
|
|
283
|
+
/** Place shapes one by one (overrides `count`/`placement`). */
|
|
284
|
+
items?: ShapeItem[];
|
|
285
|
+
region?: Region;
|
|
286
|
+
/** Random tilt for squares and pills. Default true. */
|
|
287
|
+
rotate?: boolean;
|
|
288
|
+
/** Square corner radius as a fraction of size. Default 0.22. */
|
|
289
|
+
radius?: number;
|
|
290
|
+
/** Draw outlines of this width instead of fills. */
|
|
291
|
+
outline?: boolean | Length;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
export interface IconTile {
|
|
295
|
+
/** Default `size / 0.5`. */
|
|
296
|
+
size?: Length;
|
|
297
|
+
radius?: Length;
|
|
298
|
+
/** Default `palette.surface`. */
|
|
299
|
+
color?: Color;
|
|
300
|
+
/** Default `'soft'`. */
|
|
301
|
+
shadow?: 'soft' | 'none';
|
|
302
|
+
/** Hairline border colour; `false` for none. */
|
|
303
|
+
border?: Color | false;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
export interface IconLayer extends BaseLayer {
|
|
307
|
+
type: 'icon';
|
|
308
|
+
/** An Iconify name: `'lucide:feather'`, `'simple-icons:github'`. */
|
|
309
|
+
name?: string;
|
|
310
|
+
/** A raw SVG string. */
|
|
311
|
+
svg?: string;
|
|
312
|
+
/** A bare path `d` (filled with the colour). */
|
|
313
|
+
path?: string;
|
|
314
|
+
/** For `path`. Default `[0, 0, 24, 24]`. */
|
|
315
|
+
viewBox?: [number, number, number, number];
|
|
316
|
+
/** Default `'24vmin'` (`'15vmin'` on a tile). */
|
|
317
|
+
size?: Length;
|
|
318
|
+
/** Default the accent on a tile, else `palette.text`. */
|
|
319
|
+
color?: Color;
|
|
320
|
+
/** Override stroke icons' `stroke-width` (in icon units). */
|
|
321
|
+
strokeWidth?: number;
|
|
322
|
+
/** Default `'center'`. */
|
|
323
|
+
anchor?: Anchor;
|
|
324
|
+
/** Explicit centre-of-anchor placement, fractions. */
|
|
325
|
+
position?: Point;
|
|
326
|
+
offset?: { x?: Length; y?: Length };
|
|
327
|
+
margin?: Length;
|
|
328
|
+
tile?: boolean | IconTile;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
export interface TextLabel {
|
|
332
|
+
content: string;
|
|
333
|
+
font?: string;
|
|
334
|
+
/** Default 500. */
|
|
335
|
+
weight?: number;
|
|
336
|
+
style?: 'normal' | 'italic';
|
|
337
|
+
size?: Length;
|
|
338
|
+
/** Default `palette.muted`. */
|
|
339
|
+
color?: Color;
|
|
340
|
+
/** Em. Default 0.08. */
|
|
341
|
+
letterSpacing?: number;
|
|
342
|
+
/** Default `'uppercase'`. */
|
|
343
|
+
transform?: 'none' | 'uppercase' | 'lowercase';
|
|
344
|
+
lineHeight?: number;
|
|
345
|
+
maxLines?: number;
|
|
346
|
+
/** Space between the label and the text. */
|
|
347
|
+
gap?: Length;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
export interface TextLayer extends BaseLayer {
|
|
351
|
+
type: 'text';
|
|
352
|
+
content: string;
|
|
353
|
+
/** Family. Default `'Inter'` (bundled). */
|
|
354
|
+
font?: string;
|
|
355
|
+
/** Default 600. */
|
|
356
|
+
weight?: number;
|
|
357
|
+
style?: 'normal' | 'italic';
|
|
358
|
+
/** Default 6% of the design unit (`sqrt(width × height)`). */
|
|
359
|
+
size?: Length;
|
|
360
|
+
/** Shrink toward this before truncating. */
|
|
361
|
+
minSize?: Length;
|
|
362
|
+
/** Default 1.12. */
|
|
363
|
+
lineHeight?: number;
|
|
364
|
+
/** Em. Default -0.02. */
|
|
365
|
+
letterSpacing?: number;
|
|
366
|
+
/** Default `palette.text`. */
|
|
367
|
+
color?: Color;
|
|
368
|
+
/** Default from the anchor. */
|
|
369
|
+
align?: 'left' | 'center' | 'right';
|
|
370
|
+
/** Default `'bottom-left'`. */
|
|
371
|
+
anchor?: Anchor;
|
|
372
|
+
position?: Point;
|
|
373
|
+
offset?: { x?: Length; y?: Length };
|
|
374
|
+
/** Safe margin. Default 7% of the design unit. */
|
|
375
|
+
margin?: Length;
|
|
376
|
+
/** Default the canvas width less the margins. */
|
|
377
|
+
maxWidth?: Length;
|
|
378
|
+
/** Default 3. */
|
|
379
|
+
maxLines?: number;
|
|
380
|
+
/** Default `'…'`. */
|
|
381
|
+
ellipsis?: string;
|
|
382
|
+
/** Even out line lengths. Default true. */
|
|
383
|
+
balance?: boolean;
|
|
384
|
+
transform?: 'none' | 'uppercase' | 'lowercase';
|
|
385
|
+
/** A small line above (a string, or its options). */
|
|
386
|
+
label?: string | TextLabel;
|
|
387
|
+
/** A pill or box behind the block. */
|
|
388
|
+
background?: Color | { color?: Color; radius?: Length; padding?: Length | [Length, Length]; opacity?: number };
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
export interface FrameLayer extends BaseLayer {
|
|
392
|
+
type: 'frame';
|
|
393
|
+
/** Default `'3.5vmin'`. */
|
|
394
|
+
inset?: Length;
|
|
395
|
+
/** Default `'2vmin'`. */
|
|
396
|
+
radius?: Length;
|
|
397
|
+
/** Default `'0.15vmin'`; 0 for none. */
|
|
398
|
+
stroke?: Length;
|
|
399
|
+
/** Default `palette.text` (at opacity 0.22). */
|
|
400
|
+
color?: Color;
|
|
401
|
+
/** Fill the inset rectangle (a card). */
|
|
402
|
+
fill?: Color;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
export interface DiagramNode {
|
|
406
|
+
/** Iconify name or raw SVG; the spark when left out. */
|
|
407
|
+
icon?: string;
|
|
408
|
+
/** Default `'square'` (`'circle'` in the square layout, `'none'` in the hub). */
|
|
409
|
+
shape?: 'square' | 'circle' | 'none';
|
|
410
|
+
size?: Length;
|
|
411
|
+
/** Corner brackets around the node. */
|
|
412
|
+
brackets?: boolean;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
export interface DiagramLayer extends BaseLayer {
|
|
416
|
+
type: 'diagram';
|
|
417
|
+
/** One icon, drawn large as line art (shortcut for one node). */
|
|
418
|
+
icon?: string;
|
|
419
|
+
/** For a single icon. Default `'none'`. */
|
|
420
|
+
shape?: 'square' | 'circle' | 'none';
|
|
421
|
+
nodes?: DiagramNode[];
|
|
422
|
+
/** Default from the node count. */
|
|
423
|
+
layout?: 'single' | 'row' | 'triangle' | 'square' | 'stack' | 'hub' | null;
|
|
424
|
+
/** `true`: straight arrows; `'loop'`: also an elbow back (row); `'both'`: there and back (triangle); `'cycle'` (square). */
|
|
425
|
+
arrows?: boolean | 'loop' | 'both' | 'cycle' | 'merge' | null;
|
|
426
|
+
size?: Length;
|
|
427
|
+
gap?: Length;
|
|
428
|
+
/** Default `'0.3vmin'`. */
|
|
429
|
+
stroke?: Length;
|
|
430
|
+
/** Default white. */
|
|
431
|
+
color?: Color;
|
|
432
|
+
radius?: Length;
|
|
433
|
+
/** Icon size inside a tile, 0–1. Default 0.44. */
|
|
434
|
+
iconScale?: number;
|
|
435
|
+
position?: Point;
|
|
436
|
+
/** A dotted grid (4×4 cells) that breaks around the drawing. */
|
|
437
|
+
grid?: boolean | { divisions?: number; opacity?: number; stroke?: Length; color?: Color; pad?: Length };
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
export interface PaperItem {
|
|
441
|
+
kind?: 'rect' | 'square' | 'circle' | 'blob' | 'arch' | 'pill';
|
|
442
|
+
/** Centre, fractions of the region. */
|
|
443
|
+
x?: number;
|
|
444
|
+
y?: number;
|
|
445
|
+
width?: Length;
|
|
446
|
+
height?: Length;
|
|
447
|
+
size?: Length;
|
|
448
|
+
rotate?: number;
|
|
449
|
+
color?: Color;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
export interface PaperLayer extends BaseLayer {
|
|
453
|
+
type: 'paper';
|
|
454
|
+
/** Shapes in the collage. Default 4. */
|
|
455
|
+
count?: number;
|
|
456
|
+
/** Default `'mixed'`. */
|
|
457
|
+
kind?: 'mixed' | 'rect' | 'square' | 'circle' | 'blob' | 'arch' | 'pill';
|
|
458
|
+
/** Default `#faf9f5`. */
|
|
459
|
+
color?: Color;
|
|
460
|
+
/** Default the middle 60%. */
|
|
461
|
+
region?: Region;
|
|
462
|
+
items?: PaperItem[];
|
|
463
|
+
/** How rough the cut edges are, 0–1. Default 0.5. */
|
|
464
|
+
wobble?: number;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
export interface ScribbleLayer extends BaseLayer {
|
|
468
|
+
type: 'scribble';
|
|
469
|
+
/** An icon to re-draw by hand (Iconify name or raw SVG); the spark when left out. */
|
|
470
|
+
icon?: string;
|
|
471
|
+
/** A hand-drawn node graph instead: `true` or `{ nodes, dot }`. */
|
|
472
|
+
graph?: boolean | { nodes?: number; dot?: number };
|
|
473
|
+
/** Default `#faf9f5`. */
|
|
474
|
+
color?: Color;
|
|
475
|
+
/** Default `'1.3vmin'`. */
|
|
476
|
+
stroke?: Length;
|
|
477
|
+
/** Default `'46vmin'`. */
|
|
478
|
+
size?: Length;
|
|
479
|
+
position?: Point;
|
|
480
|
+
/** 0–1. Default 0.5. */
|
|
481
|
+
wobble?: number;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
export interface InkShape {
|
|
485
|
+
kind: 'circle' | 'ring' | 'half' | 'arch' | 'band' | 'rect' | 'text';
|
|
486
|
+
/** Centre, fractions of the canvas (bands use `y` only). */
|
|
487
|
+
x?: number;
|
|
488
|
+
y?: number;
|
|
489
|
+
/** Radius (circle, ring, half). Default `'30vmin'`. */
|
|
490
|
+
r?: Length;
|
|
491
|
+
size?: Length;
|
|
492
|
+
width?: Length;
|
|
493
|
+
height?: Length;
|
|
494
|
+
/** Ring thickness. */
|
|
495
|
+
thickness?: Length;
|
|
496
|
+
rotate?: number;
|
|
497
|
+
/** For `kind: 'text'`: the text, and any text-layer options. */
|
|
498
|
+
content?: string;
|
|
499
|
+
[option: string]: unknown;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
export interface InkLayer extends BaseLayer {
|
|
503
|
+
type: 'ink';
|
|
504
|
+
/** A colour, or a riso ink: `'riso:pink'`, `'riso:blue'`, … (see `RISO_INKS`). */
|
|
505
|
+
color?: Color;
|
|
506
|
+
shapes?: InkShape[];
|
|
507
|
+
/** Misregistration. Default seeded, 0.3–1% of the short side. */
|
|
508
|
+
offset?: { x?: Length; y?: Length };
|
|
509
|
+
/** Default `'grain'` (speckle). */
|
|
510
|
+
texture?: 'grain' | 'halftone' | 'none';
|
|
511
|
+
/** Grain: share of paper showing through. Default 0.09. */
|
|
512
|
+
speckle?: number;
|
|
513
|
+
/** Halftone cell, dot radius (fraction of the cell) and screen angle (0 or 45). */
|
|
514
|
+
cell?: Length;
|
|
515
|
+
dot?: number;
|
|
516
|
+
angle?: 0 | 45;
|
|
517
|
+
/** `[min, max]`: ink density across the sheet. Default `[0.78, 1]`. */
|
|
518
|
+
density?: [number, number];
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
export interface DitherLayer extends BaseLayer {
|
|
522
|
+
type: 'dither';
|
|
523
|
+
/** `[ground, ink]`, or three colours for two dithered bands. */
|
|
524
|
+
colors?: Color[];
|
|
525
|
+
/** Default `'field'` (smooth noise). */
|
|
526
|
+
source?: 'field' | 'linear' | 'radial';
|
|
527
|
+
shape?: 'none' | 'circle' | 'sphere' | 'arch';
|
|
528
|
+
size?: Length;
|
|
529
|
+
center?: Point;
|
|
530
|
+
/** Default `'bayer4'`. */
|
|
531
|
+
matrix?: 'bayer4' | 'bayer8' | 'noise';
|
|
532
|
+
/** Pixel size. Default `'0.9vmin'`. */
|
|
533
|
+
cell?: Length;
|
|
534
|
+
angle?: number;
|
|
535
|
+
scale?: number;
|
|
536
|
+
gamma?: number;
|
|
537
|
+
range?: [number, number];
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
export interface ContourLayer extends BaseLayer {
|
|
541
|
+
type: 'contour';
|
|
542
|
+
/** Default 20. */
|
|
543
|
+
levels?: number;
|
|
544
|
+
color?: Color;
|
|
545
|
+
stroke?: Length;
|
|
546
|
+
/** The level drawn in the accent (default seeded), or `false`. */
|
|
547
|
+
highlight?: number | false;
|
|
548
|
+
highlightColor?: Color;
|
|
549
|
+
/** Every Nth line thicker. Default 5; 0 for none. */
|
|
550
|
+
index?: number;
|
|
551
|
+
indexWeight?: number;
|
|
552
|
+
scale?: number;
|
|
553
|
+
octaves?: number;
|
|
554
|
+
resolution?: number;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
export interface WaveformLayer extends BaseLayer {
|
|
558
|
+
type: 'waveform';
|
|
559
|
+
/** Peak values (any scale). */
|
|
560
|
+
peaks?: number[] | null;
|
|
561
|
+
/** An audio file decoded at build (needs `@arraypress/waveform-gen`). */
|
|
562
|
+
audio?: string | null;
|
|
563
|
+
/** Default 88. */
|
|
564
|
+
bars?: number;
|
|
565
|
+
/** Default `'mirrored'`. */
|
|
566
|
+
style?: 'mirrored' | 'bars' | 'line' | 'dots';
|
|
567
|
+
color?: Color;
|
|
568
|
+
region?: Region;
|
|
569
|
+
gap?: number;
|
|
570
|
+
round?: boolean;
|
|
571
|
+
stroke?: Length;
|
|
572
|
+
/** `true`: the duration (from `audio` or `duration`); or any text. */
|
|
573
|
+
caption?: boolean | string | null;
|
|
574
|
+
/** Seconds, or text such as `'48:12'`. */
|
|
575
|
+
duration?: number | string | null;
|
|
576
|
+
captionSize?: Length;
|
|
577
|
+
captionColor?: Color;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
export interface TilesLayer extends BaseLayer {
|
|
581
|
+
type: 'tiles';
|
|
582
|
+
cols?: number;
|
|
583
|
+
rows?: number;
|
|
584
|
+
/** Seeds the grid's rhythm, shared across a series. Default: the cover's own. */
|
|
585
|
+
series?: string | null;
|
|
586
|
+
colors?: Color[];
|
|
587
|
+
ground?: Color;
|
|
588
|
+
kinds?: ('quarter' | 'half' | 'circle' | 'square' | 'triangle')[];
|
|
589
|
+
merge?: boolean;
|
|
590
|
+
region?: Region;
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
export interface RuleLayer extends BaseLayer {
|
|
594
|
+
type: 'rule';
|
|
595
|
+
y?: number;
|
|
596
|
+
from?: Point;
|
|
597
|
+
to?: Point;
|
|
598
|
+
inset?: Length;
|
|
599
|
+
color?: Color;
|
|
600
|
+
stroke?: Length;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
export type Layer =
|
|
604
|
+
| InkLayer | DitherLayer | ContourLayer | WaveformLayer | TilesLayer | RuleLayer
|
|
605
|
+
| DiagramLayer | PaperLayer | ScribbleLayer
|
|
606
|
+
| SolidLayer | LinearLayer | RadialLayer | MeshLayer | StripesLayer | ImageLayer
|
|
607
|
+
| GrainLayer | PatternLayer | ShapesLayer | IconLayer | TextLayer | FrameLayer;
|
|
608
|
+
|
|
609
|
+
/** Built-in recipe names. */
|
|
610
|
+
export type RecipeName = 'aurora' | 'gridTitle' | 'iconTile' | 'bigType' | 'stripes' | 'duotone' | 'mark' | 'diagram' | 'cutout' | 'riso' | 'dither' | 'contour' | 'waveform' | 'bauhaus' | 'issue' | 'photo';
|
|
611
|
+
|
|
612
|
+
/** What a recipe function receives besides the options. */
|
|
613
|
+
export interface RecipeContext {
|
|
614
|
+
width: number;
|
|
615
|
+
height: number;
|
|
616
|
+
palette: Palette;
|
|
617
|
+
rng: Rng;
|
|
618
|
+
/** `sqrt(width × height)`. */
|
|
619
|
+
u: number;
|
|
620
|
+
/** The cover's seed. */
|
|
621
|
+
seed?: string;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
/** A custom recipe. JSON recipes defined with {@link defineRecipe} also carry `recipeName` and `json`. */
|
|
625
|
+
export type Recipe = ((options: RecipeOptions, context: RecipeContext) => Layer[]) & { recipeName?: string; json?: RecipeJson };
|
|
626
|
+
|
|
627
|
+
/**
|
|
628
|
+
* A recipe value in JSON: a literal, a token (`"@accent"`, `"$var"`,
|
|
629
|
+
* `"{title}"`), or an operator object (`{ "mix": [a, b, t] }`, `{ "if": … }`).
|
|
630
|
+
*/
|
|
631
|
+
export type RecipeValue =
|
|
632
|
+
| string | number | boolean | null
|
|
633
|
+
| RecipeValue[]
|
|
634
|
+
| { if: RecipeCondition; then: RecipeValue; else?: RecipeValue }
|
|
635
|
+
| { mix: [RecipeValue, RecipeValue, RecipeValue] | [RecipeValue, RecipeValue, RecipeValue, 'oklab' | 'oklch'] }
|
|
636
|
+
| { readable: RecipeValue }
|
|
637
|
+
| { gradientUnder: [RecipeValue, RecipeValue, RecipeValue] }
|
|
638
|
+
| { tone: [RecipeValue, RecipeValue] }
|
|
639
|
+
| { length: RecipeValue }
|
|
640
|
+
| { pick: RecipeValue[] }
|
|
641
|
+
| { range: [RecipeValue, RecipeValue] }
|
|
642
|
+
| { int: [RecipeValue, RecipeValue] }
|
|
643
|
+
| { add: RecipeValue[] } | { sub: RecipeValue[] } | { mul: RecipeValue[] } | { div: [RecipeValue, RecipeValue] }
|
|
644
|
+
| { min: RecipeValue[] } | { max: RecipeValue[] }
|
|
645
|
+
| { ramp: RecipeValue[] }
|
|
646
|
+
| { taper: [RecipeValue, RecipeValue] }
|
|
647
|
+
| { reverse: RecipeValue }
|
|
648
|
+
| { [key: string]: RecipeValue };
|
|
649
|
+
|
|
650
|
+
/** A condition: an option name (set?), `{ name: value }` (equals; `mode` is the palette's), `not`, `all`, `any`. */
|
|
651
|
+
export type RecipeCondition =
|
|
652
|
+
| string
|
|
653
|
+
| boolean
|
|
654
|
+
| { not: RecipeCondition }
|
|
655
|
+
| { all: RecipeCondition[] }
|
|
656
|
+
| { any: RecipeCondition[] }
|
|
657
|
+
| { [option: string]: string | number | boolean | (string | number | boolean)[] };
|
|
658
|
+
|
|
659
|
+
/** One layer of a JSON recipe: a layer type, its options as recipe values, and an optional `when`. */
|
|
660
|
+
export interface RecipeJsonLayer {
|
|
661
|
+
type: Layer['type'];
|
|
662
|
+
id?: string;
|
|
663
|
+
when?: RecipeCondition;
|
|
664
|
+
[option: string]: RecipeValue | RecipeCondition | undefined;
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
/** A recipe as data (see `schema/recipe.schema.json`). */
|
|
668
|
+
export interface RecipeJson {
|
|
669
|
+
$schema?: string;
|
|
670
|
+
name?: string;
|
|
671
|
+
description?: string;
|
|
672
|
+
/** Options this recipe reads, with defaults; cover options of the same name override them. */
|
|
673
|
+
params?: Record<string, RecipeValue>;
|
|
674
|
+
/** Options that must be set. */
|
|
675
|
+
requires?: string[];
|
|
676
|
+
/** The encoding this style prefers (renderImage options win). */
|
|
677
|
+
output?: { lossless?: boolean };
|
|
678
|
+
/** Named values, resolved in order; referenced as `"$name"`. */
|
|
679
|
+
vars?: Record<string, RecipeValue>;
|
|
680
|
+
layers: RecipeJsonLayer[];
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
/** Per-layer customisation: a string is the layer's content, an object is merged in, `false` removes it. */
|
|
684
|
+
export type LayerOverride = string | false | null | Partial<Layer> | Record<string, unknown>;
|
|
685
|
+
|
|
686
|
+
/** Options recipes read, plus overrides by layer id. */
|
|
687
|
+
export interface RecipeOptions {
|
|
688
|
+
/** Headline (or the title layer's options). */
|
|
689
|
+
title?: string | Partial<TextLayer>;
|
|
690
|
+
/** Small line above the title. */
|
|
691
|
+
label?: string | TextLabel;
|
|
692
|
+
/** Iconify name for `iconTile` / `stripes`, or the icon layer's options. */
|
|
693
|
+
icon?: string | Partial<IconLayer>;
|
|
694
|
+
/** Image for `duotone`: a path or bytes, or the image layer's options. */
|
|
695
|
+
image?: string | Uint8Array | Partial<ImageLayer>;
|
|
696
|
+
/** `stripes`: which side the band runs down. */
|
|
697
|
+
side?: 'left' | 'right';
|
|
698
|
+
/** `mark`: the accent (`'solid'`, default) or an accent gradient. */
|
|
699
|
+
background?: 'solid' | 'gradient';
|
|
700
|
+
/** `mark` gradient angle (CSS degrees). Default 135. */
|
|
701
|
+
angle?: number;
|
|
702
|
+
/** `mark` gradient colours. Default the accent → a mix toward the palette's second colour. */
|
|
703
|
+
colors?: Color[];
|
|
704
|
+
/** `diagram` nodes, layout and arrows (see {@link DiagramLayer}). */
|
|
705
|
+
nodes?: DiagramNode[];
|
|
706
|
+
layout?: DiagramLayer['layout'];
|
|
707
|
+
arrows?: DiagramLayer['arrows'];
|
|
708
|
+
/** `cutout`: a hand-cut panel on the page colour instead of a full field. */
|
|
709
|
+
panel?: boolean;
|
|
710
|
+
/** `cutout`: paper shapes (default 3, none on a panel). */
|
|
711
|
+
shapes?: number;
|
|
712
|
+
/** `cutout`: a hand-drawn node graph instead of an icon. */
|
|
713
|
+
graph?: boolean;
|
|
714
|
+
/** `cutout`: line colour, light (paper) or dark (ink). Default from the composition. */
|
|
715
|
+
stroke?: 'light' | 'dark';
|
|
716
|
+
/** `riso`: inks (colours or `'riso:<name>'`), texture, and a third ink. */
|
|
717
|
+
inks?: Color[];
|
|
718
|
+
texture?: 'grain' | 'halftone' | 'none';
|
|
719
|
+
third?: boolean;
|
|
720
|
+
/** `dither`: matrix, cell, source, shape, three colours. */
|
|
721
|
+
matrix?: 'bayer4' | 'bayer8' | 'noise';
|
|
722
|
+
cell?: Length;
|
|
723
|
+
source?: 'field' | 'linear' | 'radial';
|
|
724
|
+
shape?: 'none' | 'circle' | 'sphere' | 'arch';
|
|
725
|
+
three?: boolean;
|
|
726
|
+
/** `contour`: how many isolines. */
|
|
727
|
+
levels?: number;
|
|
728
|
+
/** `waveform`: peaks, or an audio file; duration; style; field. */
|
|
729
|
+
peaks?: number[];
|
|
730
|
+
audio?: string;
|
|
731
|
+
duration?: number | string;
|
|
732
|
+
style?: 'mirrored' | 'bars' | 'line' | 'dots';
|
|
733
|
+
field?: 'flat' | 'toned';
|
|
734
|
+
/** `bauhaus`: a series seed (shared grid rhythm). */
|
|
735
|
+
series?: string;
|
|
736
|
+
/** `issue`: number, masthead, date, and the "No." prefix. */
|
|
737
|
+
number?: number | string;
|
|
738
|
+
masthead?: string;
|
|
739
|
+
date?: string;
|
|
740
|
+
prefix?: boolean;
|
|
741
|
+
/** `photo`: `'blur'` shows the photo blurred instead of a field of its colours. */
|
|
742
|
+
show?: 'blur';
|
|
743
|
+
/** `mark` icon colour. `'auto'` (default): white whenever it clears 3:1 against everything under the icon, else near-black. */
|
|
744
|
+
iconColor?: 'auto' | 'white' | 'black' | Color;
|
|
745
|
+
/** Any recipe layer by id: `mesh`, `grid`, `grain`, `bg`, `glow`, `rings`, `wash`, `stripes`, `spot`, `scrim`… */
|
|
746
|
+
[layerId: string]: unknown;
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
/** A cover as written. */
|
|
750
|
+
export interface CoverOptions extends RecipeOptions {
|
|
751
|
+
/** Default 1200 (or from `height` and `aspect`). */
|
|
752
|
+
width?: number;
|
|
753
|
+
height?: number;
|
|
754
|
+
/** `'1:1'` (default), `'16:9'`, `'4:5'`, `'3:2'`, `'4:3'`, `'2:1'`, `'9:16'`, `'og'` (1200×630), any `'w:h'`, or a number. */
|
|
755
|
+
aspect?: keyof typeof ASPECTS | `${number}:${number}` | number;
|
|
756
|
+
/** Drives every random choice. Use the post's slug. Default the title. */
|
|
757
|
+
seed?: string | number;
|
|
758
|
+
palette?: PaletteInput | Palette;
|
|
759
|
+
/** A built-in recipe, your own function, or a JSON recipe document. */
|
|
760
|
+
recipe?: RecipeName | Recipe | RecipeJson;
|
|
761
|
+
/** Without a recipe: the whole stack. With one: extra layers painted on top. */
|
|
762
|
+
layers?: Layer[];
|
|
763
|
+
/** Extra fonts. Inter 400/500/600/700 is always available. */
|
|
764
|
+
fonts?: FontSource[];
|
|
765
|
+
/** Accessible name in the SVG `<title>`. Default the title. */
|
|
766
|
+
alt?: string;
|
|
767
|
+
/** Round the cover's corners (transparent outside; use PNG/WebP/AVIF). */
|
|
768
|
+
radius?: Length;
|
|
769
|
+
/** Preferred encoding, overriding the recipe's. */
|
|
770
|
+
output?: { lossless?: boolean };
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
/** A resolved cover (from {@link cover}). */
|
|
774
|
+
export interface Cover {
|
|
775
|
+
width: number;
|
|
776
|
+
height: number;
|
|
777
|
+
seed: string;
|
|
778
|
+
palette: Palette;
|
|
779
|
+
layers: Layer[];
|
|
780
|
+
fonts: FontSource[];
|
|
781
|
+
alt?: string;
|
|
782
|
+
radius?: Length;
|
|
783
|
+
/** Preferred encoding (from the recipe, or set here). */
|
|
784
|
+
output?: { lossless?: boolean };
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
export interface RenderImageOptions {
|
|
788
|
+
/** Default `'webp'`. */
|
|
789
|
+
format?: 'webp' | 'png' | 'avif' | 'jpeg';
|
|
790
|
+
/** Default 90 for WebP, 72 for AVIF, 88 for JPEG (high enough to keep the grain; see README). */
|
|
791
|
+
quality?: number;
|
|
792
|
+
/** Lossless WebP / AVIF (default: the recipe's preference, else lossy). */
|
|
793
|
+
lossless?: boolean;
|
|
794
|
+
/** Encoder effort (WebP/AVIF 0–9, PNG compression 0–9). */
|
|
795
|
+
effort?: number;
|
|
796
|
+
/** Cache renders by content hash: `true` for `node_modules/.cache/covers`, or a directory. */
|
|
797
|
+
cache?: boolean | string;
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
/** Seeded random stream. */
|
|
801
|
+
export interface Rng {
|
|
802
|
+
next(): number;
|
|
803
|
+
range(min: number, max: number): number;
|
|
804
|
+
int(min: number, max: number): number;
|
|
805
|
+
pick<T>(items: readonly T[]): T;
|
|
806
|
+
shuffle<T>(items: readonly T[]): T[];
|
|
807
|
+
chance(p?: number): boolean;
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
/** An Iconify JSON set (`@iconify-json/<prefix>/icons.json`). */
|
|
811
|
+
export interface IconSet {
|
|
812
|
+
prefix: string;
|
|
813
|
+
icons: Record<string, { body: string; width?: number; height?: number; left?: number; top?: number }>;
|
|
814
|
+
aliases?: Record<string, { parent: string; width?: number; height?: number }>;
|
|
815
|
+
width?: number;
|
|
816
|
+
height?: number;
|
|
817
|
+
left?: number;
|
|
818
|
+
top?: number;
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
/** Resolve a cover's size and palette and expand its recipe. Pure and synchronous. */
|
|
822
|
+
export function cover(options: CoverOptions | Cover): Cover;
|
|
823
|
+
|
|
824
|
+
/** Render to an SVG string (text and icons as outlines; byte-identical for the same input). */
|
|
825
|
+
export function renderSvg(input: CoverOptions | Cover): Promise<string>;
|
|
826
|
+
|
|
827
|
+
/** Render to image bytes with sharp (a peer dependency). */
|
|
828
|
+
export function renderImage(input: CoverOptions | Cover, options?: RenderImageOptions): Promise<Buffer>;
|
|
829
|
+
|
|
830
|
+
/** The content-hash cache key for a cover and output options (40 hex chars). */
|
|
831
|
+
export function coverKey(input: CoverOptions | Cover, options?: RenderImageOptions): Promise<string>;
|
|
832
|
+
|
|
833
|
+
/** Output MIME types. */
|
|
834
|
+
export const MIME: Readonly<{ webp: 'image/webp'; png: 'image/png'; avif: 'image/avif'; jpeg: 'image/jpeg' }>;
|
|
835
|
+
|
|
836
|
+
/** This package's version. */
|
|
837
|
+
export const VERSION: string;
|
|
838
|
+
|
|
839
|
+
/** The built-in recipes. */
|
|
840
|
+
export const recipes: Readonly<Record<RecipeName, Recipe>>;
|
|
841
|
+
|
|
842
|
+
/** Turn a JSON recipe into a recipe function (validated; throws with the JSON path on a problem). */
|
|
843
|
+
export function defineRecipe(json: RecipeJson): Recipe;
|
|
844
|
+
|
|
845
|
+
/** Read and define a JSON recipe file (synchronous). */
|
|
846
|
+
export function loadRecipe(path: string | URL): Recipe;
|
|
847
|
+
|
|
848
|
+
/** Default encoder quality, by format. */
|
|
849
|
+
export const DEFAULT_QUALITY: Readonly<{ webp: number; avif: number; jpeg: number; png: number }>;
|
|
850
|
+
|
|
851
|
+
/** Curated palette presets. */
|
|
852
|
+
export const palettes: Readonly<Record<PresetName, Readonly<{ bg: string; colors: readonly string[]; text: string; accent: string }>>>;
|
|
853
|
+
|
|
854
|
+
/** Resolve any palette input. */
|
|
855
|
+
export function resolvePalette(input?: PaletteInput | Palette): Palette;
|
|
856
|
+
|
|
857
|
+
/** Derive a palette from a theme's tokens (or just an accent). */
|
|
858
|
+
export function derivePalette(tokens: { accent: Color; bg?: Color; text?: Color; colors?: Color[]; mode?: 'light' | 'dark' }): Palette;
|
|
859
|
+
|
|
860
|
+
/** Parse a CSS colour to `#rrggbb`. */
|
|
861
|
+
export function toHex(color: Color): string;
|
|
862
|
+
/** A colour in OKLCH (lightness 0–1, chroma, hue in degrees). */
|
|
863
|
+
export function toOklch(color: Color): { l: number; c: number; h: number };
|
|
864
|
+
/** OKLCH to `#rrggbb` (chroma reduced to fit sRGB). */
|
|
865
|
+
export function fromOklch(lch: { l: number; c: number; h: number }): string;
|
|
866
|
+
/** Mix two colours in OKLab. */
|
|
867
|
+
export function mixOklab(a: Color, b: Color, t: number): string;
|
|
868
|
+
/** `prefer` if it clears `ratio` on every background, else the better of white and near-black (3:1 suits icons, WCAG 1.4.11). */
|
|
869
|
+
export function onBackground(backgrounds: Color | Color[], ratio?: number, prefer?: 'white' | 'black' | Color): string;
|
|
870
|
+
/** The colour of an eased multi-stop gradient at t (0–1), as the gradient layers draw it. */
|
|
871
|
+
export function gradientAt(colors: Color[], t: number, ease?: boolean): string;
|
|
872
|
+
/** Mix two colours in OKLCH (hue the short way; stays vivid). */
|
|
873
|
+
export function mixOklch(a: Color, b: Color, t: number): string;
|
|
874
|
+
|
|
875
|
+
/** Register an Iconify icon set (no `@iconify-json/*` install needed). */
|
|
876
|
+
export function registerIcons(set: IconSet): void;
|
|
877
|
+
|
|
878
|
+
/** Pixel size from `width` / `height` / `aspect`. */
|
|
879
|
+
export function resolveSize(options?: { width?: number; height?: number; aspect?: string | number }): { width: number; height: number };
|
|
880
|
+
|
|
881
|
+
/** Named aspect ratios (width / height). */
|
|
882
|
+
export const ASPECTS: Readonly<{ '1:1': number; '16:9': number; '4:5': number; '3:2': number; '4:3': number; '2:1': number; '9:16': number; og: number }>;
|
|
883
|
+
|
|
884
|
+
/** A seeded random stream (for custom recipes). */
|
|
885
|
+
export function createRng(seed: string | number): Rng;
|
|
886
|
+
|
|
887
|
+
/** Dominant colours of a photo, most common first (needs sharp). */
|
|
888
|
+
export function extractPalette(src: string | Uint8Array, options?: { count?: number }): Promise<string[]>;
|
|
889
|
+
|
|
890
|
+
/** Classic risograph inks, by name (approximations). */
|
|
891
|
+
export const RISO_INKS: Readonly<Record<'pink' | 'blue' | 'yellow' | 'teal' | 'red' | 'green' | 'orange' | 'purple' | 'navy' | 'black', string>>;
|
|
892
|
+
|
|
893
|
+
/** Hash a string to a 32-bit unsigned integer. */
|
|
894
|
+
export function hashString(str: string, salt?: number): number;
|
|
895
|
+
|
|
896
|
+
/** The layer renderers, by type (for introspection). */
|
|
897
|
+
export const layers: Readonly<Record<Layer['type'], (layer: any, context: any) => unknown>>;
|