@arraypress/covers 1.1.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 (244) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/README.md +18 -809
  3. package/package.json +2 -1
  4. package/recipes/flyers/afterglow-motion.json +540 -0
  5. package/recipes/flyers/afterglow.json +70 -0
  6. package/recipes/flyers/arcfield-mirror-motion.json +397 -0
  7. package/recipes/flyers/arcfield-mirror.json +364 -0
  8. package/recipes/flyers/bauhaus-oktav.json +319 -0
  9. package/recipes/flyers/bauhaus-ritmo.json +735 -0
  10. package/recipes/flyers/bauhaus-volta.json +394 -0
  11. package/recipes/flyers/bloomfall.json +99 -0
  12. package/recipes/flyers/drag-solenne-motion.json +444 -0
  13. package/recipes/flyers/drag-solenne.json +426 -0
  14. package/recipes/flyers/fennick-patches.json +874 -0
  15. package/recipes/flyers/ferro-pass.json +398 -0
  16. package/recipes/flyers/ferrova.json +812 -0
  17. package/recipes/flyers/grid-rest-rhythm.json +125 -0
  18. package/recipes/flyers/groovy-honeycomb.json +816 -0
  19. package/recipes/flyers/groovy-marigold.json +479 -0
  20. package/recipes/flyers/groovy-peachfuzz.json +974 -0
  21. package/recipes/flyers/halden-orbit-motion.json +381 -0
  22. package/recipes/flyers/halden-orbit.json +356 -0
  23. package/recipes/flyers/halvard.json +120 -0
  24. package/recipes/flyers/kairo-weekender.json +696 -0
  25. package/recipes/flyers/linger-motion.json +518 -0
  26. package/recipes/flyers/linger.json +67 -0
  27. package/recipes/flyers/little-orchard.json +105 -0
  28. package/recipes/flyers/lofi-hexmoth.json +912 -0
  29. package/recipes/flyers/lofi-lowbit-motion.json +888 -0
  30. package/recipes/flyers/lofi-lowbit.json +880 -0
  31. package/recipes/flyers/lofi-nullpoint.json +3013 -0
  32. package/recipes/flyers/lumio-stack-motion.json +648 -0
  33. package/recipes/flyers/lumio-stack.json +438 -0
  34. package/recipes/flyers/magazine-cover.json +168 -0
  35. package/recipes/flyers/magazine-extended.json +269 -0
  36. package/recipes/flyers/magazine-knockout.json +40 -0
  37. package/recipes/flyers/northfold-varsity.json +618 -0
  38. package/recipes/flyers/open-studio.json +69 -0
  39. package/recipes/flyers/orrin-merch.json +715 -0
  40. package/recipes/flyers/oxyde.json +113 -0
  41. package/recipes/flyers/quiet-fields.json +610 -0
  42. package/recipes/flyers/scrapbook-halcyon.json +948 -0
  43. package/recipes/flyers/solenne-ticket.json +730 -0
  44. package/recipes/flyers/solvane-motion.json +576 -0
  45. package/recipes/flyers/solvane.json +98 -0
  46. package/recipes/flyers/subduct.json +99 -0
  47. package/recipes/flyers/sunvale-lineup.json +584 -0
  48. package/recipes/flyers/tidewell.json +91 -0
  49. package/recipes/flyers/tour-kairo.json +535 -0
  50. package/recipes/flyers/velora-stickers.json +885 -0
  51. package/recipes/flyers/wheatpaste-black.json +313 -0
  52. package/recipes/flyers/wheatpaste-mark.json +198 -0
  53. package/recipes/flyers/wheatpaste-torn.json +114 -0
  54. package/recipes/flyers/wheatpaste-wall.json +984 -0
  55. package/recipes/flyers/wheatpaste-words.json +233 -0
  56. package/recipes/release/aura.json +137 -0
  57. package/recipes/release/chrome.json +433 -0
  58. package/recipes/release/collage.json +372 -0
  59. package/recipes/release/duotone.json +113 -0
  60. package/recipes/release/film-still.json +102 -0
  61. package/recipes/release/headline.json +70 -0
  62. package/recipes/release/instant.json +170 -0
  63. package/recipes/release/label.json +364 -0
  64. package/recipes/release/liquid-glass.json +301 -0
  65. package/recipes/release/odd-tile-dark.json +688 -0
  66. package/recipes/release/odd-tile-dense.json +1056 -0
  67. package/recipes/release/odd-tile-shift.json +541 -0
  68. package/recipes/release/odd-tile-split.json +571 -0
  69. package/recipes/release/odd-tile.json +633 -0
  70. package/recipes/release/off-edge.json +96 -0
  71. package/recipes/release/plinth.json +794 -0
  72. package/recipes/release/riso.json +108 -0
  73. package/recipes/release/series/fluid-label.json +561 -0
  74. package/recipes/release/series/fluid-pixel.json +334 -0
  75. package/recipes/release/series/hairline.json +305 -0
  76. package/recipes/release/series/halo.json +282 -0
  77. package/recipes/release/series/mark-crop.json +135 -0
  78. package/recipes/release/series/mark-window.json +150 -0
  79. package/recipes/release/series/photo-label.json +475 -0
  80. package/recipes/release/series/smear-field.json +345 -0
  81. package/recipes/release/series/smear.json +202 -0
  82. package/recipes/release/series/spin.json +172 -0
  83. package/recipes/release/series/split-ghost.json +230 -0
  84. package/recipes/release/series/zoom.json +168 -0
  85. package/recipes/release/small-print.json +114 -0
  86. package/recipes/release/stack.json +156 -0
  87. package/recipes/release/swiss.json +117 -0
  88. package/schema/recipe.schema.json +332 -65
  89. package/src/animate/index.d.ts +9 -19
  90. package/src/animate/index.js +6 -111
  91. package/src/animate/keyframes.d.ts +21 -0
  92. package/src/animate/keyframes.js +119 -0
  93. package/src/animate/marquee.d.ts +30 -0
  94. package/src/animate/marquee.js +93 -0
  95. package/src/animate/morph.d.ts +8 -0
  96. package/src/animate/morph.js +99 -0
  97. package/src/animate/motion.d.ts +59 -0
  98. package/src/animate/motion.js +63 -0
  99. package/src/animate/sheet.d.ts +19 -0
  100. package/src/animate/sheet.js +61 -0
  101. package/src/animate/timing.d.ts +28 -0
  102. package/src/animate/timing.js +146 -0
  103. package/src/animate/tracks.d.ts +18 -0
  104. package/src/animate/tracks.js +139 -0
  105. package/src/diagram.js +4 -3
  106. package/src/extract.js +2 -2
  107. package/src/fonts.js +1 -2
  108. package/src/geometry.js +4 -34
  109. package/src/grid.js +1 -1
  110. package/src/handmade.js +9 -7
  111. package/src/icons.js +1 -2
  112. package/src/index.d.ts +39 -48
  113. package/src/index.js +5 -5
  114. package/src/inter-data.js +1 -1
  115. package/src/json-recipe.js +23 -7
  116. package/src/layer-options.js +21 -1
  117. package/src/layers/barcode.js +3 -3
  118. package/src/layers/geometry.js +5 -5
  119. package/src/layers/perforation.js +3 -3
  120. package/src/layers/repeat.js +2 -2
  121. package/src/layers/shape.d.ts +2 -2
  122. package/src/layers/shape.js +5 -5
  123. package/src/layers/soft.js +2 -2
  124. package/src/layers/svg.js +2 -3
  125. package/src/layers/table.js +3 -3
  126. package/src/layers/tear.d.ts +1 -1
  127. package/src/layers/tear.js +4 -4
  128. package/src/layers/texture.js +3 -3
  129. package/src/layers/util.js +15 -2
  130. package/src/layers.js +10 -26
  131. package/src/{effects/paint.js → paint/index.js} +5 -5
  132. package/src/{effects/paint-presets.d.ts → paint/presets.d.ts} +1 -1
  133. package/src/{effects/paint-presets.js → paint/presets.js} +1 -1
  134. package/src/{effects → paint}/tile.d.ts +1 -1
  135. package/src/{effects → paint}/tile.js +4 -4
  136. package/src/palette.js +1 -1
  137. package/src/platform/files.d.ts +6 -0
  138. package/src/platform/files.js +41 -0
  139. package/src/platform/harfbuzz.js +1 -1
  140. package/src/platform/index.d.ts +3 -3
  141. package/src/platform/index.js +6 -4
  142. package/src/platform/load.js +1 -2
  143. package/src/print.js +9 -7
  144. package/src/recipes.js +1 -1
  145. package/src/render/backdrop.js +2 -2
  146. package/src/render/grain.js +1 -1
  147. package/src/render/group.js +3 -3
  148. package/src/render/key.js +1 -2
  149. package/src/{effects → render}/transform.d.ts +2 -1
  150. package/src/{effects → render}/transform.js +17 -5
  151. package/src/render/use.d.ts +1 -1
  152. package/src/render/use.js +2 -2
  153. package/src/render/wrap.js +5 -60
  154. package/src/render.js +55 -18
  155. package/src/text.js +1 -1
  156. package/src/type/auto-color.js +1 -1
  157. package/src/type/glyphs.js +2 -2
  158. package/src/type/line.js +1 -1
  159. package/src/type/live.js +1 -1
  160. package/src/type/path.js +2 -2
  161. package/src/type/pixel.d.ts +0 -2
  162. package/src/type/pixel.js +3 -33
  163. package/src/type/spans.js +2 -2
  164. package/src/type/typeset.js +19 -8
  165. package/src/version.js +1 -1
  166. package/src/color.js +0 -246
  167. package/src/effects/ascii.d.ts +0 -7
  168. package/src/effects/ascii.js +0 -93
  169. package/src/effects/buffer.d.ts +0 -60
  170. package/src/effects/buffer.js +0 -472
  171. package/src/effects/detail.d.ts +0 -12
  172. package/src/effects/detail.js +0 -124
  173. package/src/effects/digital.d.ts +0 -14
  174. package/src/effects/digital.js +0 -216
  175. package/src/effects/distort.d.ts +0 -20
  176. package/src/effects/distort.js +0 -314
  177. package/src/effects/dither.d.ts +0 -8
  178. package/src/effects/dither.js +0 -147
  179. package/src/effects/drop-shadow.d.ts +0 -4
  180. package/src/effects/drop-shadow.js +0 -45
  181. package/src/effects/extrude.d.ts +0 -5
  182. package/src/effects/extrude.js +0 -55
  183. package/src/effects/film.d.ts +0 -11
  184. package/src/effects/film.js +0 -204
  185. package/src/effects/glow.d.ts +0 -8
  186. package/src/effects/glow.js +0 -56
  187. package/src/effects/grade.d.ts +0 -30
  188. package/src/effects/grade.js +0 -274
  189. package/src/effects/halftone.d.ts +0 -8
  190. package/src/effects/halftone.js +0 -95
  191. package/src/effects/index.d.ts +0 -125
  192. package/src/effects/index.js +0 -277
  193. package/src/effects/ink.d.ts +0 -12
  194. package/src/effects/ink.js +0 -70
  195. package/src/effects/lens.d.ts +0 -14
  196. package/src/effects/lens.js +0 -277
  197. package/src/effects/light.d.ts +0 -10
  198. package/src/effects/light.js +0 -59
  199. package/src/effects/looks.d.ts +0 -9
  200. package/src/effects/looks.js +0 -74
  201. package/src/effects/mask.d.ts +0 -52
  202. package/src/effects/mask.js +0 -314
  203. package/src/effects/motion.d.ts +0 -6
  204. package/src/effects/motion.js +0 -60
  205. package/src/effects/newsprint.d.ts +0 -6
  206. package/src/effects/newsprint.js +0 -89
  207. package/src/effects/optics.d.ts +0 -10
  208. package/src/effects/optics.js +0 -213
  209. package/src/effects/options.d.ts +0 -6
  210. package/src/effects/options.js +0 -106
  211. package/src/effects/pixels.d.ts +0 -24
  212. package/src/effects/pixels.js +0 -138
  213. package/src/effects/polar.d.ts +0 -10
  214. package/src/effects/polar.js +0 -265
  215. package/src/effects/region.d.ts +0 -8
  216. package/src/effects/region.js +0 -136
  217. package/src/effects/round-border.d.ts +0 -5
  218. package/src/effects/round-border.js +0 -62
  219. package/src/effects/screen.d.ts +0 -14
  220. package/src/effects/screen.js +0 -212
  221. package/src/effects/sort.d.ts +0 -8
  222. package/src/effects/sort.js +0 -125
  223. package/src/effects/symmetry.d.ts +0 -6
  224. package/src/effects/symmetry.js +0 -74
  225. package/src/effects/tone.d.ts +0 -16
  226. package/src/effects/tone.js +0 -117
  227. package/src/effects/util.d.ts +0 -16
  228. package/src/effects/util.js +0 -85
  229. package/src/platform/bytes.d.ts +0 -22
  230. package/src/platform/bytes.js +0 -103
  231. package/src/platform/compress.d.ts +0 -12
  232. package/src/platform/compress.js +0 -81
  233. package/src/platform/node.d.ts +0 -26
  234. package/src/platform/node.js +0 -86
  235. package/src/png.js +0 -126
  236. package/src/raster/index.d.ts +0 -24
  237. package/src/raster/index.js +0 -171
  238. package/src/render/content.d.ts +0 -5
  239. package/src/render/content.js +0 -58
  240. package/src/render/tiles.d.ts +0 -6
  241. package/src/render/tiles.js +0 -77
  242. package/src/rng.js +0 -95
  243. package/src/svg.js +0 -69
  244. /package/src/{effects/paint.d.ts → paint/index.d.ts} +0 -0
package/src/index.d.ts CHANGED
@@ -4,9 +4,9 @@
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
- import type { LayerTransform, Effect, EffectKind, EffectMask } from './effects/index.js';
9
- import type { Paint } from './effects/paint.js';
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
10
  import type { TextPath } from './type/path.js';
11
11
  import type { GroupLayer } from './render/group.js';
12
12
  import type { DefsLayer, UseLayer } from './render/use.js';
@@ -16,29 +16,32 @@ import type { GlyphControl } from './type/glyphs.js';
16
16
  import type { Cut } from './formats/cut.js';
17
17
  import type { ShapeLayer, SvgLayer, TableLayer, BarcodeLayer, PerforationLayer, TearLayer, TextureLayer } from './layers/index.js';
18
18
  import type { Zones, PrintInfo, FormatName } from './formats/index.js';
19
+ import type { Motion, MotionPreset, Easing } from './animate/index.js';
19
20
 
20
- export type { RepeatLayer, DefsLayer, UseLayer, TextSpan, GlyphControl, Cut };
21
- export type { LayerTransform, Effect, EffectKind, EffectMask, Paint, TextPath, GroupLayer, ShapeLayer, SvgLayer, TableLayer, BarcodeLayer, PerforationLayer, TearLayer, TextureLayer, Zones, PrintInfo, FormatName };
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 };
22
23
  export type { ShapeSpec, TableCell } from './layers/index.js';
23
24
  export type { Format } from './formats/index.js';
24
25
  export type { LintIssue } from './lint/index.js';
25
26
  export type { Settings } from './platform/index.js';
26
27
 
27
28
  export { safeBox } from './formats/index.js';
28
- export { rasterize } from './raster/index.js';
29
- import type { RasterizeOptions } from './raster/index.js';
29
+ export { rasterize } from '@logomarks/image-effects/raster';
30
+ import type { RasterizeOptions } from '@logomarks/image-effects/raster';
30
31
  export type { RasterizeOptions };
31
32
  export { lint } from './lint/index.js';
32
33
  export { kit, KIT_FORMATS } from './kit/index.js';
33
34
  export { FORMATS } from './formats/index.js';
34
35
  export { configure } from './platform/index.js';
35
- export { EFFECTS, EFFECT_OPTIONS } from './effects/index.js';
36
- export { valueAt } from './animate/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';
37
40
 
38
- export type Color = string;
39
-
40
- /** Pixels, or a viewport unit of the canvas: `'6vmin'`, `'4vw'`, `'4vh'`, `'5vmax'`. */
41
- export type Length = number | `${number}vmin` | `${number}vmax` | `${number}vw` | `${number}vh` | `${number}px`;
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>;
42
45
 
43
46
  /** Where a box sits inside the safe area. */
44
47
  export type Anchor =
@@ -136,6 +139,8 @@ export interface BaseLayer {
136
139
  clip?: Layer[];
137
140
  /** Draw the layer in a box: the print `'trim'` or `'safe'` area, or fractions of the canvas. */
138
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)[];
139
144
  }
140
145
 
141
146
  export interface SolidLayer extends BaseLayer {
@@ -934,6 +939,10 @@ export interface CoverOptions extends RecipeOptions {
934
939
  textRender?: 'outline' | 'live';
935
940
  /** Seconds: the length of an animated cover's loop (keyframes with `loop` wrap at it). */
936
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;
937
946
  }
938
947
 
939
948
  /** A resolved cover (from {@link cover}). */
@@ -958,13 +967,16 @@ export interface Cover {
958
967
  pages?: string[];
959
968
  overlay?: boolean;
960
969
  loop?: number;
970
+ /** `false` when the cover is drawn as a still. */
971
+ animate?: false;
972
+ poster?: number;
961
973
  textRender?: 'outline' | 'live';
962
974
  }
963
975
 
964
976
  export interface RenderImageOptions {
965
977
  /** Default `'webp'`. */
966
978
  format?: 'webp' | 'png' | 'avif' | 'jpeg';
967
- /** 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). */
968
980
  quality?: number;
969
981
  /** Lossless WebP / AVIF (default: the recipe's preference, else lossy). */
970
982
  lossless?: boolean;
@@ -974,16 +986,6 @@ export interface RenderImageOptions {
974
986
  cache?: boolean | string;
975
987
  }
976
988
 
977
- /** Seeded random stream. */
978
- export interface Rng {
979
- next(): number;
980
- range(min: number, max: number): number;
981
- int(min: number, max: number): number;
982
- pick<T>(items: readonly T[]): T;
983
- shuffle<T>(items: readonly T[]): T[];
984
- chance(p?: number): boolean;
985
- }
986
-
987
989
  /** An Iconify JSON set (`@iconify-json/<prefix>/icons.json`). */
988
990
  export interface IconSet {
989
991
  prefix: string;
@@ -998,13 +1000,23 @@ export interface IconSet {
998
1000
  /** Resolve a cover's size and palette and expand its recipe. Pure and synchronous. */
999
1001
  export function cover(options: CoverOptions | Cover): Cover;
1000
1002
 
1001
- /** Render to an SVG string (text and icons as outlines; byte-identical for the same input). `t`: the time to draw, in seconds, for keyframed layers. */
1002
- export function renderSvg(input: CoverOptions | Cover, options?: { t?: number }): Promise<string>;
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
+
1013
+ /** Render to an SVG string (text and icons as outlines; byte-identical for the same input). */
1014
+ export function renderSvg(input: CoverOptions | Cover, options?: RenderSvgOptions): Promise<string>;
1003
1015
 
1004
1016
  /** Render each page of a two-sided piece: `{ front, back }`. */
1005
1017
  export function renderPages(input: CoverOptions, options?: { pages?: string[] }): Promise<Record<string, string>>;
1006
1018
 
1007
- /** Render to an image in the browser (a canvas; no sharp). */
1019
+ /** Render to an image in the browser (a canvas; no sharp): a still, the `poster` frame or the layers at rest. */
1008
1020
  export function renderBlob(input: CoverOptions | Cover, options?: RasterizeOptions): Promise<Blob>;
1009
1021
 
1010
1022
  /** Register a font for every cover. */
@@ -1043,21 +1055,6 @@ export function resolvePalette(input?: PaletteInput | Palette): Palette;
1043
1055
  /** Derive a palette from a theme's tokens (or just an accent). */
1044
1056
  export function derivePalette(tokens: { accent: Color; bg?: Color; text?: Color; colors?: Color[]; mode?: 'light' | 'dark' }): Palette;
1045
1057
 
1046
- /** Parse a CSS colour to `#rrggbb`. */
1047
- export function toHex(color: Color): string;
1048
- /** A colour in OKLCH (lightness 0–1, chroma, hue in degrees). */
1049
- export function toOklch(color: Color): { l: number; c: number; h: number };
1050
- /** OKLCH to `#rrggbb` (chroma reduced to fit sRGB). */
1051
- export function fromOklch(lch: { l: number; c: number; h: number }): string;
1052
- /** Mix two colours in OKLab. */
1053
- export function mixOklab(a: Color, b: Color, t: number): string;
1054
- /** `prefer` if it clears `ratio` on every background, else the better of white and near-black (3:1 suits icons, WCAG 1.4.11). */
1055
- export function onBackground(backgrounds: Color | Color[], ratio?: number, prefer?: 'white' | 'black' | Color): string;
1056
- /** The colour of an eased multi-stop gradient at t (0–1), as the gradient layers draw it. */
1057
- export function gradientAt(colors: Color[], t: number, ease?: boolean): string;
1058
- /** Mix two colours in OKLCH (hue the short way; stays vivid). */
1059
- export function mixOklch(a: Color, b: Color, t: number): string;
1060
-
1061
1058
  /** Register an Iconify icon set (no `@iconify-json/*` install needed). */
1062
1059
  export function registerIcons(set: IconSet): void;
1063
1060
 
@@ -1067,17 +1064,11 @@ export function resolveSize(options?: { width?: number; height?: number; aspect?
1067
1064
  /** Named aspect ratios (width / height). */
1068
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 }>;
1069
1066
 
1070
- /** A seeded random stream (for custom recipes). */
1071
- export function createRng(seed: string | number): Rng;
1072
-
1073
1067
  /** Dominant colours of a photo, most common first (needs sharp). */
1074
1068
  export function extractPalette(src: string | Uint8Array, options?: { count?: number }): Promise<string[]>;
1075
1069
 
1076
1070
  /** Classic risograph inks, by name (approximations). */
1077
1071
  export const RISO_INKS: Readonly<Record<'pink' | 'blue' | 'yellow' | 'teal' | 'red' | 'green' | 'orange' | 'purple' | 'navy' | 'black', string>>;
1078
1072
 
1079
- /** Hash a string to a 32-bit unsigned integer. */
1080
- export function hashString(str: string, salt?: number): number;
1081
-
1082
1073
  /** The layer renderers, by type (for introspection). */
1083
1074
  export const layers: Readonly<Record<Layer['type'], (layer: any, context: any) => unknown>>;
package/src/index.js CHANGED
@@ -10,18 +10,18 @@ export { cover, renderSvg, renderImage, renderPages, renderBlob, coverKey, safeB
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
15
  export { registerFont } from './fonts.js';
16
16
  export { configure } from './platform/index.js';
17
17
  export { resolveSize, ASPECTS } from './geometry.js';
18
18
  export { FORMATS } from './formats/index.js';
19
- export { createRng, hashString } from './rng.js';
19
+ export { createRng, hashString } from '@logomarks/image-effects/rng';
20
20
  export { LAYERS as layers } from './layers.js';
21
21
  export { extractPalette } from './extract.js';
22
22
  export { RISO_INKS } from './print.js';
23
- export { EFFECTS, EFFECT_OPTIONS } from './effects/index.js';
24
- export { rasterize } from './raster/index.js';
23
+ export { EFFECTS, EFFECT_OPTIONS } from '@logomarks/image-effects';
24
+ export { rasterize } from '@logomarks/image-effects/raster';
25
25
  export { lint } from './lint/index.js';
26
26
  export { kit, KIT_FORMATS } from './kit/index.js';
27
- export { valueAt } from './animate/index.js';
27
+ export { valueAt, MOTION_PRESETS } from './animate/index.js';
package/src/inter-data.js CHANGED
@@ -1,7 +1,7 @@
1
1
  // Generated by scripts/generate.mjs from fonts/*.woff. Do not edit.
2
2
  // Inter is © The Inter Project Authors, SIL Open Font License 1.1 (fonts/OFL.txt).
3
3
 
4
- import { fromBase64 } from './platform/bytes.js';
4
+ import { fromBase64 } from './platform/index.js';
5
5
 
6
6
  /** @type {Record<number, string>} */
7
7
  const DATA = {
@@ -55,15 +55,16 @@
55
55
 
56
56
  import { readTextSync, node } from './platform/index.js';
57
57
  import { readableText } from '@arraypress/color-utils';
58
- import { createRng } from './rng.js';
59
- import { len, ANCHORS } from './geometry.js';
60
- import { toHex, mixOklab, mixOklch, onBackground, gradientAt } from './color.js';
58
+ import { createRng } from '@logomarks/image-effects/rng';
59
+ import { len } from '@logomarks/image-effects/length';
60
+ import { ANCHORS } from './geometry.js';
61
+ import { toHex, mixOklab, mixOklch, onBackground, gradientAt } from '@logomarks/image-effects/color';
61
62
  import { LAYERS, toneColor } from './layers.js';
62
- import { LAYER_OPTIONS, COMMON } from './layer-options.js';
63
- import { EFFECT_OPTIONS, EFFECT_COMMON } from './effects/options.js';
64
- import { MASK_GRADIENTS } from './effects/mask.js';
63
+ import { LAYER_OPTIONS, COMMON, MOTION_OPTIONS } from './layer-options.js';
64
+ import { EFFECT_OPTIONS, EFFECT_COMMON, MASK_GRADIENTS } from '@logomarks/image-effects';
65
65
  import { shapesOnly } from './layers/svg.js';
66
66
  import { LAYOUT_FIELDS } from './type/layout-ref.js';
67
+ import { MOTION_PRESETS } from './animate/timing.js';
67
68
 
68
69
  /** Options every recipe can read without declaring them. */
69
70
  const CONTENT = ['title', 'label', 'icon', 'image', 'name', 'mark', 'photo', 'cutout', 'date', 'dates', 'time', 'venue', 'address', 'lines', 'lineup', 'tracklist', 'price', 'meta', 'page'];
@@ -181,7 +182,7 @@ export function loadRecipe(path) {
181
182
  * @property {Record<string, unknown>} params
182
183
  * @property {Record<string, unknown>} vars
183
184
  * @property {any} c - The recipe context.
184
- * @property {() => import('./rng.js').Rng} rng - The current stream.
185
+ * @property {() => import('@logomarks/image-effects/rng').Rng} rng - The current stream.
185
186
  */
186
187
 
187
188
  /**
@@ -546,6 +547,8 @@ export function validate(json) {
546
547
  if (!Object.hasOwn(known, k)) fail(`${path}.${k}`, `"${layer.type}" layers have no option "${k}" (${Object.keys(known).join(', ')})`);
547
548
  if (k === 'effects') {
548
549
  checkEffects(`${path}.effects`, v);
550
+ } else if (k === 'motion') {
551
+ checkMotion(`${path}.motion`, v);
549
552
  } else if (NESTED.includes(k) && Array.isArray(v) && v.every((x) => isPlain(x) && typeof x.type === 'string')) {
550
553
  v.forEach((x, j) => checkLayer(`${path}.${k}[${j}]${x.id ? ` (${x.id})` : ''}`, x));
551
554
  } else if (k === 'layer' && isPlain(v) && typeof v.type === 'string') {
@@ -583,6 +586,19 @@ export function validate(json) {
583
586
  }
584
587
  });
585
588
  };
589
+ // Motion: known presets and options (values are checked as values).
590
+ const checkMotion = (/** @type {string} */ path, /** @type {any} */ v) => {
591
+ (Array.isArray(v) ? v : [v]).forEach((m, i) => {
592
+ const at = Array.isArray(v) ? `${path}[${i}]` : path;
593
+ const preset = typeof m === 'string' ? m : m?.preset;
594
+ if (typeof preset === 'string' && !/[{$@]/.test(preset) && !Object.hasOwn(MOTION_PRESETS, preset)) fail(at, `unknown motion preset "${preset}" (${Object.keys(MOTION_PRESETS).join(', ')})`);
595
+ if (!isPlain(m)) return checkValue(at, m);
596
+ for (const [k, x] of Object.entries(m)) {
597
+ if (!Object.hasOwn(MOTION_OPTIONS, k)) fail(`${at}.${k}`, `motions have no option "${k}" (${Object.keys(MOTION_OPTIONS).join(', ')})`);
598
+ checkValue(`${at}.${k}`, x);
599
+ }
600
+ });
601
+ };
586
602
  const checkEffectMask = (/** @type {string} */ path, /** @type {any} */ m) => {
587
603
  const keys = ['gradient', 'from', 'to', 'center', 'angle', 'aspect', 'invert', 'ease', 'layers'];
588
604
  for (const k of Object.keys(m)) if (!keys.includes(k)) fail(`${path}.${k}`, `unknown mask option (${keys.join(', ')})`);
@@ -49,8 +49,28 @@ export const LAYER_OPTIONS = {
49
49
  export const COMMON = {
50
50
  opacity: '0–1.', blend: 'A CSS mix-blend-mode.', seed: "This layer's own random stream.",
51
51
  transform: '{ translate, rotate, scale, skewX, skewY, mirror: x | y, origin } (on text, a case keyword is still the case).',
52
- effects: '[{ kind, mask, colorSpace, … }]: filters, pixel effects and copies, in order (see the README\'s Effects table; kinds in EFFECTS).',
52
+ effects: '[{ kind, mask, colorSpace, … }]: filters, pixel effects and copies, in order (see docs/effects.md; kinds in EFFECTS).',
53
53
  mask: 'Layers whose luminance masks this one (white shows).', knockout: 'Layers cut out of this one.', clip: 'Layers this one shows only inside.',
54
54
  area: 'trim | safe | bleed, or { x, y, width, height } fractions: draw the layer in that box.',
55
+ motion: 'A motion, a preset name (drift | spin | breathe | blink | marquee), or a list: plays in the SVG as CSS keyframes (see MOTION_OPTIONS).',
56
+ };
57
+
58
+ /** @type {Record<string, string>} A motion's options. */
59
+ export const MOTION_OPTIONS = {
60
+ preset: 'drift | spin | breathe | blink | marquee: its defaults, under whatever else is set.',
61
+ translate: '[x, y] to move by (px in the layer\'s grid, "%" or lengths), or a list of points [[0, 0], [x, y], …].',
62
+ path: 'Travel a path from where the layer is: path data, { d }, or a text-path kind ({ kind: circle | ellipse | arc | wave, … }).',
63
+ rotate: 'Degrees to turn to, or a list of angles.',
64
+ scale: 'A factor to grow to, or a list.', scaleX: 'Horizontal factor, or a list.', scaleY: 'Vertical factor, or a list.',
65
+ opacity: '0–1 to fade to (times the layer\'s own), or a list.',
66
+ origin: '[x, y] the turn and scale are about (px, "%" or lengths). Default the centre.',
67
+ vary: 'Layer options as lists of stops ({ axes: { wdth: [62, 150] } }): drawn frame by frame, played as outline morphs.',
68
+ frames: 'vary: frames drawn per stop. Default 12.',
69
+ marquee: 'true: text on a path runs along it, one repeat of its words per duration (they must repeat).',
70
+ duration: 'Seconds per iteration. Default 2 (or the preset\'s).',
71
+ delay: 'Seconds before it starts; negative starts it part-way.',
72
+ easing: 'linear | ease | in | out | inOut | step, or [x1, y1, x2, y2]: between each pair of stops.',
73
+ direction: 'normal | reverse | alternate | alternate-reverse',
74
+ iterations: 'A number, or "infinite" (the default).',
55
75
  };
56
76
 
@@ -4,9 +4,9 @@
4
4
  * @module layers/barcode
5
5
  */
6
6
 
7
- import { attrs, num } from '../svg.js';
8
- import { len } from '../geometry.js';
9
- import { toHex } from '../color.js';
7
+ import { attrs, num } from '@logomarks/image-effects/svg';
8
+ import { len } from '@logomarks/image-effects/length';
9
+ import { toHex } from '@logomarks/image-effects/color';
10
10
  import { faceFor, setLine } from '../type/line.js';
11
11
  import { pos, wrap, contentOf } from './util.js';
12
12
  import { code128 } from './code128.js';
@@ -6,11 +6,11 @@
6
6
  * @module layers/geometry
7
7
  */
8
8
 
9
- import { attrs, num } from '../svg.js';
10
- import { len } from '../geometry.js';
9
+ import { attrs, num } from '@logomarks/image-effects/svg';
10
+ import { len } from '@logomarks/image-effects/length';
11
11
  import { pos } from './util.js';
12
12
  import { cutPath } from '../formats/cut.js';
13
- import { invert, apply } from '../effects/transform.js';
13
+ import { invert, apply } from '../render/transform.js';
14
14
 
15
15
 
16
16
  /**
@@ -18,7 +18,7 @@ import { invert, apply } from '../effects/transform.js';
18
18
  *
19
19
  * @param {[number, number]} a @param {[number, number]} b
20
20
  * @param {{ kind: string, size: number, step: number }} e
21
- * @param {import('../rng.js').Rng} rng
21
+ * @param {import('@logomarks/image-effects/rng').Rng} rng
22
22
  * @returns {[number, number][]} Points from a (exclusive) to b (inclusive).
23
23
  */
24
24
  function edgePoints(a, b, e, rng) {
@@ -211,7 +211,7 @@ export function geometry(s, ctx) {
211
211
  *
212
212
  * @param {{ x: number, y: number, width: number, height: number, cut?: any, mm?: number } | undefined} cut - Trim box and cut, canvas px.
213
213
  * @param {number} outset - Grow (or, negative, shrink) the outline, in the layer's units.
214
- * @param {import('../effects/transform.js').Affine | undefined} matrix - Layer units to canvas px.
214
+ * @param {import('../render/transform.js').Affine | undefined} matrix - Layer units to canvas px.
215
215
  * @param {{ width: number, height: number }} canvas
216
216
  * @returns {{ d: string, box: { x: number, y: number, width: number, height: number } }}
217
217
  */
@@ -4,9 +4,9 @@
4
4
  * @module layers/perforation
5
5
  */
6
6
 
7
- import { attrs, num } from '../svg.js';
8
- import { len } from '../geometry.js';
9
- import { toHex } from '../color.js';
7
+ import { attrs, num } from '@logomarks/image-effects/svg';
8
+ import { len } from '@logomarks/image-effects/length';
9
+ import { toHex } from '@logomarks/image-effects/color';
10
10
  import { pos, wrap } from './util.js';
11
11
 
12
12
 
@@ -19,8 +19,8 @@
19
19
  * @module layers/repeat
20
20
  */
21
21
 
22
- import { num } from '../svg.js';
23
- import { len } from '../geometry.js';
22
+ import { num } from '@logomarks/image-effects/svg';
23
+ import { len } from '@logomarks/image-effects/length';
24
24
  import { pos } from './util.js';
25
25
  import { fill, evaluate } from './expr.js';
26
26
 
@@ -1,9 +1,9 @@
1
1
  // The `shape` layer.
2
2
  import type { Color, Length, BaseLayer, Anchor } from '../index.js';
3
3
  import type { RenderContext, Markup } from '../render/context.js';
4
- import type { Paint } from '../effects/paint.js';
4
+ import type { Paint } from '../paint/index.js';
5
5
 
6
- /** One exact shape (see the README's `shape` table). */
6
+ /** One exact shape (see the `shape` row in docs/layers.md). */
7
7
  export interface ShapeSpec {
8
8
  kind?: 'rect' | 'circle' | 'ellipse' | 'line' | 'polygon' | 'polyline' | 'star' | 'burst' | 'scallop' | 'rays' | 'ring' | 'sector' | 'arch' | 'wave' | 'path' | 'cut';
9
9
  x?: Length | `${number}%`; y?: Length | `${number}%`; width?: Length | `${number}%`; height?: Length | `${number}%`;
@@ -4,11 +4,11 @@
4
4
  * @module layers/shape
5
5
  */
6
6
 
7
- import { attrs, num } from '../svg.js';
8
- import { len } from '../geometry.js';
9
- import { toHex } from '../color.js';
10
- import { paint } from '../effects/paint.js';
11
- import { presetStrokes } from '../effects/paint-presets.js';
7
+ import { attrs, num } from '@logomarks/image-effects/svg';
8
+ import { len } from '@logomarks/image-effects/length';
9
+ import { toHex } from '@logomarks/image-effects/color';
10
+ import { paint } from '../paint/index.js';
11
+ import { presetStrokes } from '../paint/presets.js';
12
12
  import { wrap, pos } from './util.js';
13
13
  import { geometry } from './geometry.js';
14
14
  import { soften } from './soft.js';
@@ -6,8 +6,8 @@
6
6
  * @module layers/soft
7
7
  */
8
8
 
9
- import { attrs, num } from '../svg.js';
10
- import { toBase64 } from '../platform/bytes.js';
9
+ import { attrs, num } from '@logomarks/image-effects/svg';
10
+ import { toBase64 } from '../platform/index.js';
11
11
 
12
12
  /**
13
13
  * Blur markup by `sigma` (px in the layer's units). In `renderImage`, the markup
package/src/layers/svg.js CHANGED
@@ -4,11 +4,10 @@
4
4
  * @module layers/svg
5
5
  */
6
6
 
7
- import { attrs, esc, num } from '../svg.js';
7
+ import { attrs, esc, num } from '@logomarks/image-effects/svg';
8
8
  import { unit } from '../geometry.js';
9
9
  import { pos, wrap } from './util.js';
10
- import { expandDropShadows } from '../effects/drop-shadow.js';
11
- import { filterReach } from '../effects/region.js';
10
+ import { expandDropShadows, filterReach } from '@logomarks/image-effects';
12
11
 
13
12
 
14
13
  /**
@@ -4,9 +4,9 @@
4
4
  * @module layers/table
5
5
  */
6
6
 
7
- import { attrs, num } from '../svg.js';
8
- import { len } from '../geometry.js';
9
- import { toHex } from '../color.js';
7
+ import { attrs, num } from '@logomarks/image-effects/svg';
8
+ import { len } from '@logomarks/image-effects/length';
9
+ import { toHex } from '@logomarks/image-effects/color';
10
10
  import { faceFor, setLine } from '../type/line.js';
11
11
  import { pos, wrap, contentOf } from './util.js';
12
12
 
@@ -1,7 +1,7 @@
1
1
  // The `tear` layer.
2
2
  import type { Color, Length, BaseLayer, Anchor } from '../index.js';
3
3
  import type { RenderContext, Markup } from '../render/context.js';
4
- import type { Paint } from '../effects/paint.js';
4
+ import type { Paint } from '../paint/index.js';
5
5
 
6
6
  export interface TearLayer extends BaseLayer {
7
7
  type: 'tear';
@@ -4,10 +4,10 @@
4
4
  * @module layers/tear
5
5
  */
6
6
 
7
- import { attrs, num } from '../svg.js';
8
- import { len } from '../geometry.js';
9
- import { toHex } from '../color.js';
10
- import { paint } from '../effects/paint.js';
7
+ import { attrs, num } from '@logomarks/image-effects/svg';
8
+ import { len } from '@logomarks/image-effects/length';
9
+ import { toHex } from '@logomarks/image-effects/color';
10
+ import { paint } from '../paint/index.js';
11
11
  import { pos, wrap } from './util.js';
12
12
 
13
13
 
@@ -4,9 +4,9 @@
4
4
  * @module layers/texture
5
5
  */
6
6
 
7
- import { attrs, num } from '../svg.js';
8
- import { len } from '../geometry.js';
9
- import { toHex } from '../color.js';
7
+ import { attrs, num } from '@logomarks/image-effects/svg';
8
+ import { len } from '@logomarks/image-effects/length';
9
+ import { toHex } from '@logomarks/image-effects/color';
10
10
  import { pos, wrap } from './util.js';
11
11
 
12
12
 
@@ -5,8 +5,8 @@
5
5
  * @module layers/util
6
6
  */
7
7
 
8
- import { attrs, esc } from '../svg.js';
9
- import { len } from '../geometry.js';
8
+ import { attrs, esc } from '@logomarks/image-effects/svg';
9
+ import { len } from '@logomarks/image-effects/length';
10
10
 
11
11
  /**
12
12
  * A coordinate: px as a number, `'50%'` of the canvas along `axis`, or a
@@ -22,6 +22,19 @@ export function pos(v, axis, canvas) {
22
22
  return len(/** @type {any} */ (v), canvas, 0);
23
23
  }
24
24
 
25
+ /**
26
+ * Opacity and blend-mode attributes shared by every layer.
27
+ *
28
+ * @param {{ opacity?: number, blend?: string }} layer - The layer.
29
+ * @returns {Record<string, string | number | undefined>} Attributes for the layer's group.
30
+ */
31
+ export function layerAttrs(layer) {
32
+ return {
33
+ opacity: layer.opacity != null && layer.opacity < 1 ? Math.max(0, layer.opacity) : undefined,
34
+ style: layer.blend && layer.blend !== 'normal' ? `mix-blend-mode:${layer.blend}` : undefined,
35
+ };
36
+ }
37
+
25
38
  /**
26
39
  * Wrap markup in a group with the layer's `opacity` and `blend` (only when
27
40
  * it has them).
package/src/layers.js CHANGED
@@ -14,11 +14,12 @@
14
14
  * @module layers
15
15
  */
16
16
 
17
- import { loadBytes } from './platform/index.js';
18
- import { toBase64, latin1, utf8 } from './platform/bytes.js';
19
- import { attrs, esc, layerAttrs, num } from './svg.js';
20
- import { len, place, anchorAlign, unit, regionPx } from './geometry.js';
21
- import { toHex, mixOklab, toOklch, fromOklch } from './color.js';
17
+ import { loadBytes, toBase64 } from './platform/index.js';
18
+ import { attrs, esc, num } from '@logomarks/image-effects/svg';
19
+ import { imageMime } from '@logomarks/image-effects';
20
+ import { len } from '@logomarks/image-effects/length';
21
+ import { place, anchorAlign, unit, regionPx } from './geometry.js';
22
+ import { toHex, mixOklab, toOklch, fromOklch } from '@logomarks/image-effects/color';
22
23
  import { harfbuzz, loadFont, pickFont } from './fonts.js';
23
24
  import { layout, runPath, caseText } from './text.js';
24
25
  import { resolveIcon, parseSvgIcon } from './icons.js';
@@ -29,8 +30,8 @@ import { ink, dither, contour, waveform, tiles, rule } from './print.js';
29
30
  import { typeset, wantsType } from './type/index.js';
30
31
  import { recordLayout } from './type/layout-ref.js';
31
32
  import { shape, svg, table, barcode, perforation, tear, texture } from './layers/index.js';
32
- import { pos } from './layers/util.js';
33
- import { paint } from './effects/paint.js';
33
+ import { pos, layerAttrs } from './layers/util.js';
34
+ import { paint } from './paint/index.js';
34
35
  import { imageSize } from './lint/image-size.js';
35
36
 
36
37
  /**
@@ -41,7 +42,7 @@ import { imageSize } from './lint/image-size.js';
41
42
  * @property {number} height - Canvas height in px.
42
43
  * @property {{ width: number, height: number }} canvas - The same, as an object.
43
44
  * @property {import('./index.js').Palette} palette - The resolved palette.
44
- * @property {import('./rng.js').Rng} rng - This layer's own random stream.
45
+ * @property {import('@logomarks/image-effects/rng').Rng} rng - This layer's own random stream.
45
46
  * @property {string} id - A unique id prefix for this layer's defs.
46
47
  * @property {import('./index.js').FontSource[]} fonts - Available fonts (Inter last).
47
48
  * @property {any} [sharp] - The `sharp` module, when available.
@@ -421,7 +422,7 @@ export async function image(layer, ctx) {
421
422
  const fit = layer.fit ?? 'cover';
422
423
  const focus = layer.focus ?? 'center';
423
424
  let bytes = typeof layer.src === 'string' || layer.src instanceof URL ? await readSource(layer.src) : await loadBytes(layer.src, 'image');
424
- let mime = sniff(bytes);
425
+ let mime = imageMime(bytes);
425
426
  let preserve = fit === 'stretch' ? 'none' : `${alignFor(focus)} ${fit === 'contain' ? 'meet' : 'slice'}`;
426
427
  let sharpBlurred = false;
427
428
 
@@ -548,23 +549,6 @@ async function readSource(src) {
548
549
  return loadBytes(src, 'image');
549
550
  }
550
551
 
551
- /**
552
- * Sniff an image's MIME type from its first bytes.
553
- *
554
- * @param {Uint8Array} b - Image bytes.
555
- * @returns {string | undefined} The MIME type.
556
- */
557
- export function sniff(b) {
558
- if (b[0] === 0x89 && b[1] === 0x50) return 'image/png';
559
- if (b[0] === 0xff && b[1] === 0xd8) return 'image/jpeg';
560
- if (b[0] === 0x47 && b[1] === 0x49) return 'image/gif';
561
- const head = latin1(b.subarray(0, 32));
562
- if (head.startsWith('RIFF') && head.slice(8, 12) === 'WEBP') return 'image/webp';
563
- if (head.slice(4, 12) === 'ftypavif' || head.slice(4, 12) === 'ftypavis') return 'image/avif';
564
- if (/^\s*(<\?xml|<svg)/i.test(utf8(b.subarray(0, 256)))) return 'image/svg+xml';
565
- return undefined;
566
- }
567
-
568
552
  // ---------------------------------------------------------------------------
569
553
  // Texture
570
554
  // ---------------------------------------------------------------------------
@@ -4,13 +4,13 @@
4
4
  * radial fields with `rx`/`ry`), a `tile` pattern (checker, stripes, dots,
5
5
  * grid or a custom tile), or a named preset (`'chrome'`).
6
6
  *
7
- * @module effects/paint
7
+ * @module paint
8
8
  */
9
9
 
10
- import { attrs, num } from '../svg.js';
11
- import { len } from '../geometry.js';
12
- import { toHex } from '../color.js';
13
- import { PRESETS } from './paint-presets.js';
10
+ import { attrs, num } from '@logomarks/image-effects/svg';
11
+ import { len } from '@logomarks/image-effects/length';
12
+ import { toHex } from '@logomarks/image-effects/color';
13
+ import { PRESETS } from './presets.js';
14
14
  import { tile } from './tile.js';
15
15
 
16
16
  /**
@@ -1,5 +1,5 @@
1
1
  // Named paints: chrome, steel and gold, painted as a reflected horizon.
2
- import type { GradientPaint } from './paint.js';
2
+ import type { GradientPaint } from './index.js';
3
3
  import type { Color, Length } from '../index.js';
4
4
 
5
5
  /** Paint presets, by name. */
@@ -4,7 +4,7 @@
4
4
  * horizon a little past the middle, warm ground below it, and a light
5
5
  * bounce at the very bottom. Pair it with a light keyline (`strokes`).
6
6
  *
7
- * @module effects/paint-presets
7
+ * @module paint/presets
8
8
  */
9
9
 
10
10
  /** Paint presets, by name. A paint `{ preset, …fields }` overrides any of them. */