@arraypress/covers 1.0.0 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (246) hide show
  1. package/CHANGELOG.md +118 -0
  2. package/README.md +18 -524
  3. package/package.json +34 -5
  4. package/recipes/brand.json +1143 -0
  5. package/recipes/flyers/afterglow-motion.json +540 -0
  6. package/recipes/flyers/afterglow.json +70 -0
  7. package/recipes/flyers/arcfield-mirror-motion.json +397 -0
  8. package/recipes/flyers/arcfield-mirror.json +364 -0
  9. package/recipes/flyers/bauhaus-oktav.json +319 -0
  10. package/recipes/flyers/bauhaus-ritmo.json +735 -0
  11. package/recipes/flyers/bauhaus-volta.json +394 -0
  12. package/recipes/flyers/bloomfall.json +99 -0
  13. package/recipes/flyers/drag-solenne-motion.json +444 -0
  14. package/recipes/flyers/drag-solenne.json +426 -0
  15. package/recipes/flyers/fennick-patches.json +874 -0
  16. package/recipes/flyers/ferro-pass.json +398 -0
  17. package/recipes/flyers/ferrova.json +812 -0
  18. package/recipes/flyers/grid-rest-rhythm.json +125 -0
  19. package/recipes/flyers/groovy-honeycomb.json +816 -0
  20. package/recipes/flyers/groovy-marigold.json +479 -0
  21. package/recipes/flyers/groovy-peachfuzz.json +974 -0
  22. package/recipes/flyers/halden-orbit-motion.json +381 -0
  23. package/recipes/flyers/halden-orbit.json +356 -0
  24. package/recipes/flyers/halvard.json +120 -0
  25. package/recipes/flyers/kairo-weekender.json +696 -0
  26. package/recipes/flyers/linger-motion.json +518 -0
  27. package/recipes/flyers/linger.json +67 -0
  28. package/recipes/flyers/little-orchard.json +105 -0
  29. package/recipes/flyers/lofi-hexmoth.json +912 -0
  30. package/recipes/flyers/lofi-lowbit-motion.json +888 -0
  31. package/recipes/flyers/lofi-lowbit.json +880 -0
  32. package/recipes/flyers/lofi-nullpoint.json +3013 -0
  33. package/recipes/flyers/lumio-stack-motion.json +648 -0
  34. package/recipes/flyers/lumio-stack.json +438 -0
  35. package/recipes/flyers/magazine-cover.json +168 -0
  36. package/recipes/flyers/magazine-extended.json +269 -0
  37. package/recipes/flyers/magazine-knockout.json +40 -0
  38. package/recipes/flyers/northfold-varsity.json +618 -0
  39. package/recipes/flyers/open-studio.json +69 -0
  40. package/recipes/flyers/orrin-merch.json +715 -0
  41. package/recipes/flyers/oxyde.json +113 -0
  42. package/recipes/flyers/quiet-fields.json +610 -0
  43. package/recipes/flyers/scrapbook-halcyon.json +948 -0
  44. package/recipes/flyers/solenne-ticket.json +730 -0
  45. package/recipes/flyers/solvane-motion.json +576 -0
  46. package/recipes/flyers/solvane.json +98 -0
  47. package/recipes/flyers/subduct.json +99 -0
  48. package/recipes/flyers/sunvale-lineup.json +584 -0
  49. package/recipes/flyers/tidewell.json +91 -0
  50. package/recipes/flyers/tour-kairo.json +535 -0
  51. package/recipes/flyers/velora-stickers.json +885 -0
  52. package/recipes/flyers/wheatpaste-black.json +313 -0
  53. package/recipes/flyers/wheatpaste-mark.json +198 -0
  54. package/recipes/flyers/wheatpaste-torn.json +114 -0
  55. package/recipes/flyers/wheatpaste-wall.json +984 -0
  56. package/recipes/flyers/wheatpaste-words.json +233 -0
  57. package/recipes/release/aura.json +137 -0
  58. package/recipes/release/chrome.json +433 -0
  59. package/recipes/release/collage.json +372 -0
  60. package/recipes/release/duotone.json +113 -0
  61. package/recipes/release/film-still.json +102 -0
  62. package/recipes/release/headline.json +70 -0
  63. package/recipes/release/instant.json +170 -0
  64. package/recipes/release/label.json +364 -0
  65. package/recipes/release/liquid-glass.json +301 -0
  66. package/recipes/release/odd-tile-dark.json +688 -0
  67. package/recipes/release/odd-tile-dense.json +1056 -0
  68. package/recipes/release/odd-tile-shift.json +541 -0
  69. package/recipes/release/odd-tile-split.json +571 -0
  70. package/recipes/release/odd-tile.json +633 -0
  71. package/recipes/release/off-edge.json +96 -0
  72. package/recipes/release/plinth.json +794 -0
  73. package/recipes/release/riso.json +108 -0
  74. package/recipes/release/series/fluid-label.json +561 -0
  75. package/recipes/release/series/fluid-pixel.json +334 -0
  76. package/recipes/release/series/hairline.json +305 -0
  77. package/recipes/release/series/halo.json +282 -0
  78. package/recipes/release/series/mark-crop.json +135 -0
  79. package/recipes/release/series/mark-window.json +150 -0
  80. package/recipes/release/series/photo-label.json +475 -0
  81. package/recipes/release/series/smear-field.json +345 -0
  82. package/recipes/release/series/smear.json +202 -0
  83. package/recipes/release/series/spin.json +172 -0
  84. package/recipes/release/series/split-ghost.json +230 -0
  85. package/recipes/release/series/zoom.json +168 -0
  86. package/recipes/release/small-print.json +114 -0
  87. package/recipes/release/stack.json +156 -0
  88. package/recipes/release/swiss.json +117 -0
  89. package/schema/recipe.schema.json +7624 -827
  90. package/src/animate/index.d.ts +9 -0
  91. package/src/animate/index.js +11 -0
  92. package/src/animate/keyframes.d.ts +21 -0
  93. package/src/animate/keyframes.js +119 -0
  94. package/src/animate/marquee.d.ts +30 -0
  95. package/src/animate/marquee.js +93 -0
  96. package/src/animate/morph.d.ts +8 -0
  97. package/src/animate/morph.js +99 -0
  98. package/src/animate/motion.d.ts +59 -0
  99. package/src/animate/motion.js +63 -0
  100. package/src/animate/sheet.d.ts +19 -0
  101. package/src/animate/sheet.js +61 -0
  102. package/src/animate/timing.d.ts +28 -0
  103. package/src/animate/timing.js +146 -0
  104. package/src/animate/tracks.d.ts +18 -0
  105. package/src/animate/tracks.js +139 -0
  106. package/src/builtins.js +4 -0
  107. package/src/diagram.js +4 -3
  108. package/src/extract.js +6 -5
  109. package/src/fonts.d.ts +55 -0
  110. package/src/fonts.js +245 -53
  111. package/src/formats/cut.d.ts +10 -0
  112. package/src/formats/cut.js +77 -0
  113. package/src/formats/index.d.ts +6 -0
  114. package/src/formats/index.js +9 -0
  115. package/src/formats/presets.d.ts +41 -0
  116. package/src/formats/presets.js +89 -0
  117. package/src/formats/print.d.ts +25 -0
  118. package/src/formats/print.js +117 -0
  119. package/src/formats/zones.d.ts +10 -0
  120. package/src/formats/zones.js +64 -0
  121. package/src/geometry.js +5 -35
  122. package/src/grid.js +1 -1
  123. package/src/handmade.js +9 -7
  124. package/src/icons.js +42 -9
  125. package/src/index.d.ts +232 -55
  126. package/src/index.js +14 -6
  127. package/src/inter-data.js +27 -0
  128. package/src/json-recipe.js +201 -27
  129. package/src/kit/index.d.ts +20 -0
  130. package/src/kit/index.js +58 -0
  131. package/src/layer-options.js +43 -4
  132. package/src/layers/barcode.d.ts +19 -0
  133. package/src/layers/barcode.js +63 -0
  134. package/src/layers/code128.d.ts +4 -0
  135. package/src/layers/code128.js +28 -0
  136. package/src/layers/expr.d.ts +6 -0
  137. package/src/layers/expr.js +121 -0
  138. package/src/layers/geometry.d.ts +6 -0
  139. package/src/layers/geometry.js +238 -0
  140. package/src/layers/index.d.ts +18 -0
  141. package/src/layers/index.js +15 -0
  142. package/src/layers/perforation.d.ts +14 -0
  143. package/src/layers/perforation.js +47 -0
  144. package/src/layers/repeat.d.ts +29 -0
  145. package/src/layers/repeat.js +90 -0
  146. package/src/layers/shape.d.ts +46 -0
  147. package/src/layers/shape.js +115 -0
  148. package/src/layers/soft.d.ts +5 -0
  149. package/src/layers/soft.js +43 -0
  150. package/src/layers/svg.d.ts +23 -0
  151. package/src/layers/svg.js +108 -0
  152. package/src/layers/table.d.ts +25 -0
  153. package/src/layers/table.js +80 -0
  154. package/src/layers/tear.d.ts +21 -0
  155. package/src/layers/tear.js +74 -0
  156. package/src/layers/texture.d.ts +17 -0
  157. package/src/layers/texture.js +106 -0
  158. package/src/layers/util.d.ts +8 -0
  159. package/src/layers/util.js +61 -0
  160. package/src/layers.js +129 -61
  161. package/src/lint/image-size.d.ts +4 -0
  162. package/src/lint/image-size.js +34 -0
  163. package/src/lint/index.d.ts +16 -0
  164. package/src/lint/index.js +127 -0
  165. package/src/lint/rules.d.ts +5 -0
  166. package/src/lint/rules.js +19 -0
  167. package/src/node.d.ts +2 -0
  168. package/src/node.js +20 -0
  169. package/src/paint/index.d.ts +49 -0
  170. package/src/paint/index.js +66 -0
  171. package/src/paint/presets.d.ts +10 -0
  172. package/src/paint/presets.js +47 -0
  173. package/src/paint/tile.d.ts +7 -0
  174. package/src/paint/tile.js +55 -0
  175. package/src/palette.js +1 -1
  176. package/src/platform/files.d.ts +6 -0
  177. package/src/platform/files.js +41 -0
  178. package/src/platform/harfbuzz.d.ts +9 -0
  179. package/src/platform/harfbuzz.js +80 -0
  180. package/src/platform/index.d.ts +8 -0
  181. package/src/platform/index.js +15 -0
  182. package/src/platform/load.d.ts +6 -0
  183. package/src/platform/load.js +71 -0
  184. package/src/platform/no-harfbuzz.d.ts +2 -0
  185. package/src/platform/no-harfbuzz.js +11 -0
  186. package/src/platform/settings.d.ts +28 -0
  187. package/src/platform/settings.js +51 -0
  188. package/src/print.js +16 -13
  189. package/src/recipes.js +5 -4
  190. package/src/render/backdrop.d.ts +6 -0
  191. package/src/render/backdrop.js +55 -0
  192. package/src/render/context.d.ts +82 -0
  193. package/src/render/grain.d.ts +7 -0
  194. package/src/render/grain.js +113 -0
  195. package/src/render/group.d.ts +23 -0
  196. package/src/render/group.js +67 -0
  197. package/src/render/key.d.ts +5 -0
  198. package/src/render/key.js +90 -0
  199. package/src/render/report.d.ts +5 -0
  200. package/src/render/report.js +19 -0
  201. package/src/render/transform.d.ts +36 -0
  202. package/src/render/transform.js +139 -0
  203. package/src/render/use.d.ts +27 -0
  204. package/src/render/use.js +73 -0
  205. package/src/render/wrap.d.ts +14 -0
  206. package/src/render/wrap.js +144 -0
  207. package/src/render.js +343 -215
  208. package/src/text.d.ts +29 -0
  209. package/src/text.js +23 -16
  210. package/src/type/auto-color.d.ts +9 -0
  211. package/src/type/auto-color.js +44 -0
  212. package/src/type/fallback.d.ts +11 -0
  213. package/src/type/fallback.js +92 -0
  214. package/src/type/glyphs.d.ts +24 -0
  215. package/src/type/glyphs.js +87 -0
  216. package/src/type/index.d.ts +5 -0
  217. package/src/type/index.js +10 -0
  218. package/src/type/kern.d.ts +4 -0
  219. package/src/type/kern.js +144 -0
  220. package/src/type/layout-ref.d.ts +10 -0
  221. package/src/type/layout-ref.js +74 -0
  222. package/src/type/line.d.ts +11 -0
  223. package/src/type/line.js +105 -0
  224. package/src/type/live.d.ts +6 -0
  225. package/src/type/live.js +39 -0
  226. package/src/type/name.d.ts +4 -0
  227. package/src/type/name.js +32 -0
  228. package/src/type/path.d.ts +18 -0
  229. package/src/type/path.js +89 -0
  230. package/src/type/pixel.d.ts +7 -0
  231. package/src/type/pixel.js +85 -0
  232. package/src/type/sfnt.d.ts +20 -0
  233. package/src/type/sfnt.js +258 -0
  234. package/src/type/spans.d.ts +33 -0
  235. package/src/type/spans.js +138 -0
  236. package/src/type/symbols.d.ts +7 -0
  237. package/src/type/symbols.js +89 -0
  238. package/src/type/typeset.d.ts +10 -0
  239. package/src/type/typeset.js +363 -0
  240. package/src/type/woff2.d.ts +6 -0
  241. package/src/type/woff2.js +301 -0
  242. package/src/version.js +4 -0
  243. package/src/color.js +0 -246
  244. package/src/png.js +0 -77
  245. package/src/rng.js +0 -95
  246. package/src/svg.js +0 -69
package/src/index.d.ts CHANGED
@@ -4,11 +4,44 @@
4
4
  * WebP / PNG / AVIF / JPEG.
5
5
  */
6
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`;
7
+ import type { Effect as AnyEffect, EffectKind, EffectMask as AnyEffectMask, Color, Length, Rng } from '@logomarks/image-effects';
8
+ import type { LayerTransform } from './render/transform.js';
9
+ import type { Paint } from './paint/index.js';
10
+ import type { TextPath } from './type/path.js';
11
+ import type { GroupLayer } from './render/group.js';
12
+ import type { DefsLayer, UseLayer } from './render/use.js';
13
+ import type { RepeatLayer } from './layers/repeat.js';
14
+ import type { TextSpan } from './type/spans.js';
15
+ import type { GlyphControl } from './type/glyphs.js';
16
+ import type { Cut } from './formats/cut.js';
17
+ import type { ShapeLayer, SvgLayer, TableLayer, BarcodeLayer, PerforationLayer, TearLayer, TextureLayer } from './layers/index.js';
18
+ import type { Zones, PrintInfo, FormatName } from './formats/index.js';
19
+ import type { Motion, MotionPreset, Easing } from './animate/index.js';
20
+
21
+ export type { RepeatLayer, DefsLayer, UseLayer, TextSpan, GlyphControl, Cut, Motion, MotionPreset, Easing };
22
+ export type { Color, Length, Rng, LayerTransform, EffectKind, Paint, TextPath, GroupLayer, ShapeLayer, SvgLayer, TableLayer, BarcodeLayer, PerforationLayer, TearLayer, TextureLayer, Zones, PrintInfo, FormatName };
23
+ export type { ShapeSpec, TableCell } from './layers/index.js';
24
+ export type { Format } from './formats/index.js';
25
+ export type { LintIssue } from './lint/index.js';
26
+ export type { Settings } from './platform/index.js';
27
+
28
+ export { safeBox } from './formats/index.js';
29
+ export { rasterize } from '@logomarks/image-effects/raster';
30
+ import type { RasterizeOptions } from '@logomarks/image-effects/raster';
31
+ export type { RasterizeOptions };
32
+ export { lint } from './lint/index.js';
33
+ export { kit, KIT_FORMATS } from './kit/index.js';
34
+ export { FORMATS } from './formats/index.js';
35
+ export { configure } from './platform/index.js';
36
+ export { EFFECTS, EFFECT_OPTIONS } from '@logomarks/image-effects';
37
+ export { valueAt, MOTION_PRESETS } from './animate/index.js';
38
+ export { toHex, toOklch, fromOklch, mixOklab, mixOklch, onBackground, gradientAt } from '@logomarks/image-effects/color';
39
+ export { createRng, hashString } from '@logomarks/image-effects/rng';
40
+
41
+ /** An effect on a layer (image-effects' effects, with masks and maps given as layers). */
42
+ export type Effect = AnyEffect<Layer>;
43
+ /** Where an effect applies: a gradient, or layers. */
44
+ export type EffectMask = AnyEffectMask<Layer>;
12
45
 
13
46
  /** Where a box sits inside the safe area. */
14
47
  export type Anchor =
@@ -67,15 +100,22 @@ export type PaletteInput =
67
100
  onAccent?: Color;
68
101
  };
69
102
 
70
- /** A font the text layers can use. TrueType, OpenType or WOFF (not WOFF2). */
103
+ /** A font the text layers can use. TrueType, OpenType or WOFF (WOFF2 only for live text). */
71
104
  export interface FontSource {
72
105
  /** Family name, as text layers ask for it (`font: 'Inter'`). */
73
106
  name: string;
74
- data: Uint8Array | ArrayBuffer;
75
- /** Default 400. */
76
- weight?: number;
107
+ /** The font's bytes; or give `src`. */
108
+ data?: Uint8Array | ArrayBuffer;
109
+ /** Where to load it from: a path (Node), a URL, or anything a `configure({ load })` hook reads. */
110
+ src?: string | URL;
111
+ /** Default 400. A variable font's range (`[100, 900]`) matches any weight in it and sets `wght`. */
112
+ weight?: number | [number, number];
77
113
  /** Default `'normal'`. */
78
114
  style?: 'normal' | 'italic';
115
+ /** Variable-font axes applied whenever this face is used. */
116
+ axes?: Record<string, number>;
117
+ /** Use this face for characters other fonts lack (glyph fallback). */
118
+ fallback?: boolean;
79
119
  }
80
120
 
81
121
  /** Options every layer takes. */
@@ -87,6 +127,20 @@ export interface BaseLayer {
87
127
  /** 0–1. */
88
128
  opacity?: number;
89
129
  blend?: BlendMode;
130
+ /** Move, turn, scale, skew or mirror the layer. (On a text layer, a case keyword is still its case.) */
131
+ transform?: LayerTransform | string;
132
+ /** Effects (filters, pixel effects, copies), applied in order; a kind alone takes its defaults. */
133
+ effects?: Effect | EffectKind | (Effect | EffectKind)[];
134
+ /** Layers whose luminance masks this one (white shows). */
135
+ mask?: Layer[];
136
+ /** Layers cut out of this one (knockout type and marks, punched holes). */
137
+ knockout?: Layer[];
138
+ /** Layers this one shows only inside. */
139
+ clip?: Layer[];
140
+ /** Draw the layer in a box: the print `'trim'` or `'safe'` area, or fractions of the canvas. */
141
+ area?: 'bleed' | 'trim' | 'safe' | Region;
142
+ /** Move in the SVG itself (CSS keyframes, no script): a motion, a preset name, or a list. */
143
+ motion?: Motion | MotionPreset | (Motion | MotionPreset)[];
90
144
  }
91
145
 
92
146
  export interface SolidLayer extends BaseLayer {
@@ -196,10 +250,15 @@ export interface StripesLayer extends BaseLayer {
196
250
 
197
251
  export interface ImageLayer extends BaseLayer {
198
252
  type: 'image';
199
- /** A local file path, a `data:` URI, or bytes. Never fetched from the network. */
200
- src: string | Uint8Array;
253
+ /** A local file path, a `data:` URI, bytes, or a URL (fetched in browsers and Workers; in Node only with `configure({ network: true })` or a loader). */
254
+ src: string | URL | Uint8Array | ArrayBuffer;
201
255
  /** Default `'cover'`. */
202
- fit?: 'cover' | 'contain';
256
+ fit?: 'cover' | 'contain' | 'stretch';
257
+ /** An explicit box in px or `'%'` (instead of `region`). */
258
+ x?: Length | `${number}%`;
259
+ y?: Length | `${number}%`;
260
+ width?: Length | `${number}%`;
261
+ height?: Length | `${number}%`;
203
262
  /** Crop focus: an anchor, or (with sharp) `'attention'` / `'entropy'`. Default `'center'`. */
204
263
  focus?: Anchor | 'attention' | 'entropy';
205
264
  /** Default the whole canvas. */
@@ -311,12 +370,19 @@ export interface IconLayer extends BaseLayer {
311
370
  svg?: string;
312
371
  /** A bare path `d` (filled with the colour). */
313
372
  path?: string;
314
- /** For `path`. Default `[0, 0, 24, 24]`. */
373
+ /** For `path`. Default `[0, 0, 24, 24]` (`[0, 0, 512, 512]` for `markup`). */
315
374
  viewBox?: [number, number, number, number];
375
+ /** Inner SVG markup (a mark's `<path>`s on a 512 grid), filled with `color`. */
376
+ markup?: string;
377
+ /** Top-left corner in px (a mark at `translate(x y) scale(size / 512)`), instead of anchoring. */
378
+ x?: Length | `${number}%`;
379
+ y?: Length | `${number}%`;
316
380
  /** Default `'24vmin'` (`'15vmin'` on a tile). */
317
381
  size?: Length;
318
382
  /** Default the accent on a tile, else `palette.text`. */
319
383
  color?: Color;
384
+ /** A paint instead of `color`: a gradient, tile or preset laid over the icon's grid. */
385
+ fill?: Paint;
320
386
  /** Override stroke icons' `stroke-width` (in icon units). */
321
387
  strokeWidth?: number;
322
388
  /** Default `'center'`. */
@@ -347,10 +413,60 @@ export interface TextLabel {
347
413
  gap?: Length;
348
414
  }
349
415
 
350
- export interface TextLayer extends BaseLayer {
416
+ export interface TextLayer extends Omit<BaseLayer, 'transform'> {
351
417
  type: 'text';
352
- content: string;
353
- /** Family. Default `'Inter'` (bundled). */
418
+ /** The text; an array is one line per item. (Optional with `spans`.) */
419
+ content?: string | string[];
420
+ /** First baseline x (px or `'%'`): places the text like SVG `<text x y>`, `align` acting as the anchor. */
421
+ x?: Length | `${number}%`;
422
+ y?: Length | `${number}%`;
423
+ /** Justify every line to this width (`textLength`). */
424
+ width?: Length;
425
+ /** Justify every line to `maxWidth`. */
426
+ justify?: boolean;
427
+ /** Default `'spacingAndGlyphs'` (scale the glyphs); `'spacing'` opens the letter-spacing. */
428
+ lengthAdjust?: 'spacingAndGlyphs' | 'spacing';
429
+ /** `'width'`: the block fills the width; `'lines'`: each line does; `'words'`: a word a line, the widest filling the width; `'wrap'`: wrap at the width, then fill it; `'shrink'`: shrink before wrapping. */
430
+ fit?: 'width' | 'lines' | 'words' | 'wrap' | 'shrink';
431
+ /** `'words'`: a word a line. */
432
+ split?: 'words';
433
+ /** Shrink the block to this height. */
434
+ maxHeight?: Length;
435
+ /** Mixed runs on the same lines, each with its own font, weight, size, colour (instead of `content`). */
436
+ spans?: (TextSpan | string)[];
437
+ /** Per-glyph scale, shift, turn and colour (arrays cycle). */
438
+ glyphs?: GlyphControl;
439
+ /** A family (or list) for characters the font lacks; then fonts registered with `fallback: true`, then built-in symbols. */
440
+ fallback?: string | string[];
441
+ /** Dashes on the stroke (`stroke`): `[on, off]` lengths, `'dashed'` or `'dotted'`. */
442
+ dash?: 'dashed' | 'dotted' | Length[];
443
+ /** Block extrusion: a depth, or its options. */
444
+ extrude?: Length | { depth?: Length; angle?: number; offset?: [Length, Length]; steps?: number; color?: Color; to?: Color };
445
+ /** `color: 'auto'` takes the dark colour above this backdrop luma (0–1). Default 0.72. */
446
+ autoThreshold?: number;
447
+ /** Stretch about the first baseline: `[sx, sy]`, or `sy`. */
448
+ stretch?: number | [number, number];
449
+ /** Variable-font axes: `{ wdth: 50, SOFT: 100, WONK: 1, opsz: 144 }`. */
450
+ axes?: Record<string, number>;
451
+ /** Outline colour (with `fill: 'none'` for outline type). */
452
+ stroke?: Color;
453
+ strokeWidth?: Length;
454
+ /** Keylines under the fill, widest first (a preset paint brings its own). */
455
+ strokes?: { color: Color; width: Length; fill?: Color; dash?: 'dashed' | 'dotted' | Length[] }[];
456
+ fill?: Paint;
457
+ /** Set the text along a path. */
458
+ path?: TextPath;
459
+ /** Along the path: 0–1, or px. Default 0.5 (centred). */
460
+ startOffset?: number | Length;
461
+ /** Space the text to fill the path (a ring); a length leaves that gap. */
462
+ fitPath?: boolean | Length;
463
+ /** Shift off the path. */
464
+ dy?: Length;
465
+ /** `'live'`: `<text>` with the font embedded by `@font-face` (or linked by URL). Default `'outline'`. */
466
+ render?: 'outline' | 'live';
467
+ /** For `color: 'auto'`. Default `['#ffffff', '#0b0b0b']`. */
468
+ autoColors?: [Color, Color];
469
+ /** Family. Default `'Inter'` (bundled). `'pixel'`: the built-in 5×7 face. */
354
470
  font?: string;
355
471
  /** Default 600. */
356
472
  weight?: number;
@@ -363,8 +479,8 @@ export interface TextLayer extends BaseLayer {
363
479
  lineHeight?: number;
364
480
  /** Em. Default -0.02. */
365
481
  letterSpacing?: number;
366
- /** Default `palette.text`. */
367
- color?: Color;
482
+ /** Default `palette.text`. `'auto'`: white or near-black, whichever reads better on what's drawn underneath. */
483
+ color?: Color | 'auto';
368
484
  /** Default from the anchor. */
369
485
  align?: 'left' | 'center' | 'right';
370
486
  /** Default `'bottom-left'`. */
@@ -381,7 +497,8 @@ export interface TextLayer extends BaseLayer {
381
497
  ellipsis?: string;
382
498
  /** Even out line lengths. Default true. */
383
499
  balance?: boolean;
384
- transform?: 'none' | 'uppercase' | 'lowercase';
500
+ /** The case (`'uppercase'`, `'lowercase'`), or a geometric transform. */
501
+ transform?: 'none' | 'uppercase' | 'lowercase' | LayerTransform;
385
502
  /** A small line above (a string, or its options). */
386
503
  label?: string | TextLabel;
387
504
  /** A pill or box behind the block. */
@@ -601,13 +718,15 @@ export interface RuleLayer extends BaseLayer {
601
718
  }
602
719
 
603
720
  export type Layer =
721
+ | GroupLayer | ShapeLayer | SvgLayer | TableLayer | BarcodeLayer | PerforationLayer | TearLayer | TextureLayer
604
722
  | InkLayer | DitherLayer | ContourLayer | WaveformLayer | TilesLayer | RuleLayer
605
723
  | DiagramLayer | PaperLayer | ScribbleLayer
606
724
  | SolidLayer | LinearLayer | RadialLayer | MeshLayer | StripesLayer | ImageLayer
607
- | GrainLayer | PatternLayer | ShapesLayer | IconLayer | TextLayer | FrameLayer;
725
+ | GrainLayer | PatternLayer | ShapesLayer | IconLayer | TextLayer | FrameLayer
726
+ | RepeatLayer | DefsLayer | UseLayer;
608
727
 
609
728
  /** Built-in recipe names. */
610
- export type RecipeName = 'aurora' | 'gridTitle' | 'iconTile' | 'bigType' | 'stripes' | 'duotone' | 'mark' | 'diagram' | 'cutout' | 'riso' | 'dither' | 'contour' | 'waveform' | 'bauhaus' | 'issue' | 'photo';
729
+ export type RecipeName = 'aurora' | 'gridTitle' | 'iconTile' | 'bigType' | 'stripes' | 'duotone' | 'mark' | 'diagram' | 'cutout' | 'riso' | 'dither' | 'contour' | 'waveform' | 'bauhaus' | 'issue' | 'photo' | 'brand';
611
730
 
612
731
  /** What a recipe function receives besides the options. */
613
732
  export interface RecipeContext {
@@ -619,6 +738,12 @@ export interface RecipeContext {
619
738
  u: number;
620
739
  /** The cover's seed. */
621
740
  seed?: string;
741
+ /** The safe box (a format's safe zone, a print piece's trim less its safe margin, else the canvas). */
742
+ safeX?: number; safeY?: number; safeWidth?: number; safeHeight?: number;
743
+ /** Print: bleed and safe margin in px, the trim size, px per mm, dpi. */
744
+ bleed?: number; safe?: number; trimWidth?: number; trimHeight?: number; mm?: number; dpi?: number;
745
+ /** The format's name, when one was given. */
746
+ format?: string;
622
747
  }
623
748
 
624
749
  /** A custom recipe. JSON recipes defined with {@link defineRecipe} also carry `recipeName` and `json`. */
@@ -645,6 +770,9 @@ export type RecipeValue =
645
770
  | { ramp: RecipeValue[] }
646
771
  | { taper: [RecipeValue, RecipeValue] }
647
772
  | { reverse: RecipeValue }
773
+ | { join: [RecipeValue] | [RecipeValue, RecipeValue] } | { upper: RecipeValue } | { lower: RecipeValue }
774
+ | { slice: [RecipeValue, RecipeValue] | [RecipeValue, RecipeValue, RecipeValue] }
775
+ | { keyframes: [number, RecipeValue][]; ease?: 'linear' | 'in' | 'out' | 'inOut' | 'step'; loop?: boolean | 'pingpong' }
648
776
  | { [key: string]: RecipeValue };
649
777
 
650
778
  /** A condition: an option name (set?), `{ name: value }` (equals; `mode` is the palette's), `not`, `all`, `any`. */
@@ -677,6 +805,10 @@ export interface RecipeJson {
677
805
  output?: { lossless?: boolean };
678
806
  /** Named values, resolved in order; referenced as `"$name"`. */
679
807
  vars?: Record<string, RecipeValue>;
808
+ /** The pages of a two-sided piece; layers pick theirs with `when: { page: 'back' }`. */
809
+ pages?: string[];
810
+ /** Fonts the recipe uses: a path beside the recipe file, or a URL. */
811
+ fonts?: { name: string; src: string; weight?: number | [number, number]; style?: 'normal' | 'italic'; axes?: Record<string, number>; fallback?: boolean }[];
680
812
  layers: RecipeJsonLayer[];
681
813
  }
682
814
 
@@ -742,6 +874,23 @@ export interface RecipeOptions {
742
874
  show?: 'blur';
743
875
  /** `mark` icon colour. `'auto'` (default): white whenever it clears 3:1 against everything under the icon, else near-black. */
744
876
  iconColor?: 'auto' | 'white' | 'black' | Color;
877
+ /** Brand and event data, for recipes that read them (`{name}`, `{lineup.0}`). */
878
+ name?: string;
879
+ tagline?: string;
880
+ /** A mark: `<path>` markup on a 512 grid. */
881
+ mark?: string;
882
+ photo?: string | Uint8Array | URL;
883
+ /** The photo's subject cut out (transparent, in the same frame). */
884
+ cutout?: string | Uint8Array | URL;
885
+ lines?: string[];
886
+ lineup?: string[];
887
+ dates?: string[];
888
+ tracklist?: (string | string[] | Record<string, string>)[];
889
+ venue?: string;
890
+ address?: string;
891
+ time?: string;
892
+ price?: string;
893
+ meta?: unknown;
745
894
  /** Any recipe layer by id: `mesh`, `grid`, `grain`, `bg`, `glow`, `rings`, `wash`, `stripes`, `spot`, `scrim`… */
746
895
  [layerId: string]: unknown;
747
896
  }
@@ -768,6 +917,32 @@ export interface CoverOptions extends RecipeOptions {
768
917
  radius?: Length;
769
918
  /** Preferred encoding, overriding the recipe's. */
770
919
  output?: { lossless?: boolean };
920
+ /** A named format (`'release'`, `'instagram-story'`, `'business-card'`, …; see `FORMATS`) or a custom size with a unit. */
921
+ format?: FormatName | { width: number; height: number; unit?: 'px' | 'mm' | 'in'; bleed?: number; safe?: number; zones?: Zones };
922
+ /** Print: dots per inch. Default 300. */
923
+ dpi?: number;
924
+ /** Print: bleed in mm (default the format's, usually 3), or `false`. */
925
+ bleed?: number | false;
926
+ /** Print: safe margin inside the trim, mm. */
927
+ safe?: number;
928
+ /** Print: crop marks in a slug round the bleed. */
929
+ cropMarks?: boolean;
930
+ /** Print: draw the die line (`CutContour`). */
931
+ cutLine?: boolean;
932
+ /** Print: the die-cut shape. */
933
+ cut?: Cut;
934
+ /** Draw the format's safe zones over the art (for previews). */
935
+ overlay?: boolean;
936
+ /** Which page of a two-sided piece to draw. Default `'front'`. */
937
+ page?: string;
938
+ /** `'live'`: every text layer as `<text>` with `@font-face`. */
939
+ textRender?: 'outline' | 'live';
940
+ /** Seconds: the length of an animated cover's loop (keyframes with `loop` wrap at it). */
941
+ loop?: number;
942
+ /** `false`: `renderSvg` draws a still, no CSS (layers' `motion` at the `poster` time, else at rest). */
943
+ animate?: boolean;
944
+ /** Seconds: the frame stills show (`renderImage`, `renderBlob`, `animate: false`, and an animated SVG under reduced motion). Default: the layers at rest. */
945
+ poster?: number;
771
946
  }
772
947
 
773
948
  /** A resolved cover (from {@link cover}). */
@@ -782,12 +957,26 @@ export interface Cover {
782
957
  radius?: Length;
783
958
  /** Preferred encoding (from the recipe, or set here). */
784
959
  output?: { lossless?: boolean };
960
+ /** Print set-up, when the format is a print size. */
961
+ print?: PrintInfo;
962
+ /** The format's name. */
963
+ format?: string;
964
+ /** The format's safe zones, px. */
965
+ zones?: Zones;
966
+ page?: string;
967
+ pages?: string[];
968
+ overlay?: boolean;
969
+ loop?: number;
970
+ /** `false` when the cover is drawn as a still. */
971
+ animate?: false;
972
+ poster?: number;
973
+ textRender?: 'outline' | 'live';
785
974
  }
786
975
 
787
976
  export interface RenderImageOptions {
788
977
  /** Default `'webp'`. */
789
978
  format?: 'webp' | 'png' | 'avif' | 'jpeg';
790
- /** Default 90 for WebP, 72 for AVIF, 88 for JPEG (high enough to keep the grain; see README). */
979
+ /** Default 90 for WebP, 72 for AVIF, 88 for JPEG (high enough to keep the grain; see docs/performance.md). */
791
980
  quality?: number;
792
981
  /** Lossless WebP / AVIF (default: the recipe's preference, else lossy). */
793
982
  lossless?: boolean;
@@ -797,16 +986,6 @@ export interface RenderImageOptions {
797
986
  cache?: boolean | string;
798
987
  }
799
988
 
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
989
  /** An Iconify JSON set (`@iconify-json/<prefix>/icons.json`). */
811
990
  export interface IconSet {
812
991
  prefix: string;
@@ -821,10 +1000,29 @@ export interface IconSet {
821
1000
  /** Resolve a cover's size and palette and expand its recipe. Pure and synchronous. */
822
1001
  export function cover(options: CoverOptions | Cover): Cover;
823
1002
 
1003
+ /** How `renderSvg` draws. */
1004
+ export interface RenderSvgOptions {
1005
+ /** A still at this time, in seconds, for keyframed layers and `motion` (instead of playing it). */
1006
+ t?: number;
1007
+ /** `'webp'`: embedded photos and pixel effects re-encoded as WebP where smaller (needs sharp). For SVGs shown in browsers; librsvg (sharp) can't draw WebP. */
1008
+ images?: 'webp';
1009
+ /** WebP quality for photos (JPEG sources), 1–100. Default 82. PNG sources go lossless. */
1010
+ quality?: number;
1011
+ }
1012
+
824
1013
  /** 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>;
1014
+ export function renderSvg(input: CoverOptions | Cover, options?: RenderSvgOptions): Promise<string>;
1015
+
1016
+ /** Render each page of a two-sided piece: `{ front, back }`. */
1017
+ export function renderPages(input: CoverOptions, options?: { pages?: string[] }): Promise<Record<string, string>>;
1018
+
1019
+ /** Render to an image in the browser (a canvas; no sharp): a still, the `poster` frame or the layers at rest. */
1020
+ export function renderBlob(input: CoverOptions | Cover, options?: RasterizeOptions): Promise<Blob>;
826
1021
 
827
- /** Render to image bytes with sharp (a peer dependency). */
1022
+ /** Register a font for every cover. */
1023
+ export function registerFont(font: FontSource): void;
1024
+
1025
+ /** Render to image bytes with sharp (a peer dependency; Node). */
828
1026
  export function renderImage(input: CoverOptions | Cover, options?: RenderImageOptions): Promise<Buffer>;
829
1027
 
830
1028
  /** The content-hash cache key for a cover and output options (40 hex chars). */
@@ -857,21 +1055,6 @@ export function resolvePalette(input?: PaletteInput | Palette): Palette;
857
1055
  /** Derive a palette from a theme's tokens (or just an accent). */
858
1056
  export function derivePalette(tokens: { accent: Color; bg?: Color; text?: Color; colors?: Color[]; mode?: 'light' | 'dark' }): Palette;
859
1057
 
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
1058
  /** Register an Iconify icon set (no `@iconify-json/*` install needed). */
876
1059
  export function registerIcons(set: IconSet): void;
877
1060
 
@@ -881,17 +1064,11 @@ export function resolveSize(options?: { width?: number; height?: number; aspect?
881
1064
  /** Named aspect ratios (width / height). */
882
1065
  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
1066
 
884
- /** A seeded random stream (for custom recipes). */
885
- export function createRng(seed: string | number): Rng;
886
-
887
1067
  /** Dominant colours of a photo, most common first (needs sharp). */
888
1068
  export function extractPalette(src: string | Uint8Array, options?: { count?: number }): Promise<string[]>;
889
1069
 
890
1070
  /** Classic risograph inks, by name (approximations). */
891
1071
  export const RISO_INKS: Readonly<Record<'pink' | 'blue' | 'yellow' | 'teal' | 'red' | 'green' | 'orange' | 'purple' | 'navy' | 'black', string>>;
892
1072
 
893
- /** Hash a string to a 32-bit unsigned integer. */
894
- export function hashString(str: string, salt?: number): number;
895
-
896
1073
  /** The layer renderers, by type (for introspection). */
897
1074
  export const layers: Readonly<Record<Layer['type'], (layer: any, context: any) => unknown>>;
package/src/index.js CHANGED
@@ -1,19 +1,27 @@
1
1
  /**
2
- * @arraypress/covers — build-time cover images for posts, products and
3
- * pages: soft mesh gradients, grain, patterns, stripes, shapes, icons and
4
- * type, from one seed, as SVG or WebP/PNG/AVIF/JPEG.
2
+ * @arraypress/covers — covers, flyers, posters, release art, social
3
+ * formats and print stationery from one seed and a stack of layers, as SVG
4
+ * or WebP/PNG/AVIF/JPEG. Runs in Node, browsers and Cloudflare Workers.
5
5
  *
6
6
  * @module @arraypress/covers
7
7
  */
8
8
 
9
- export { cover, renderSvg, renderImage, coverKey, MIME, DEFAULT_QUALITY, VERSION } from './render.js';
9
+ export { cover, renderSvg, renderImage, renderPages, renderBlob, coverKey, safeBox, MIME, DEFAULT_QUALITY, VERSION } from './render.js';
10
10
  export { recipes } from './recipes.js';
11
11
  export { defineRecipe, loadRecipe } from './json-recipe.js';
12
12
  export { palettes, resolvePalette, derivePalette } from './palette.js';
13
- export { toHex, toOklch, fromOklch, mixOklab, mixOklch, onBackground, gradientAt } from './color.js';
13
+ export { toHex, toOklch, fromOklch, mixOklab, mixOklch, onBackground, gradientAt } from '@logomarks/image-effects/color';
14
14
  export { registerIcons } from './icons.js';
15
+ export { registerFont } from './fonts.js';
16
+ export { configure } from './platform/index.js';
15
17
  export { resolveSize, ASPECTS } from './geometry.js';
16
- export { createRng, hashString } from './rng.js';
18
+ export { FORMATS } from './formats/index.js';
19
+ export { createRng, hashString } from '@logomarks/image-effects/rng';
17
20
  export { LAYERS as layers } from './layers.js';
18
21
  export { extractPalette } from './extract.js';
19
22
  export { RISO_INKS } from './print.js';
23
+ export { EFFECTS, EFFECT_OPTIONS } from '@logomarks/image-effects';
24
+ export { rasterize } from '@logomarks/image-effects/raster';
25
+ export { lint } from './lint/index.js';
26
+ export { kit, KIT_FORMATS } from './kit/index.js';
27
+ export { valueAt, MOTION_PRESETS } from './animate/index.js';