create-cogenta 0.5.3 → 0.7.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.
Files changed (178) hide show
  1. package/dist/blueprint-defaults.js +1 -1
  2. package/dist/blueprint-defaults.js.map +1 -1
  3. package/dist/blueprints/registry.d.ts +13 -1
  4. package/dist/blueprints/registry.d.ts.map +1 -1
  5. package/dist/blueprints/registry.js +23 -5
  6. package/dist/blueprints/registry.js.map +1 -1
  7. package/dist/index.d.ts +3 -3
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +2 -2
  10. package/dist/index.js.map +1 -1
  11. package/dist/playground-reset.js +2 -2
  12. package/dist/playground-reset.js.map +1 -1
  13. package/dist/recap.js +1 -1
  14. package/dist/recap.js.map +1 -1
  15. package/dist/scaffold.d.ts +5 -3
  16. package/dist/scaffold.d.ts.map +1 -1
  17. package/dist/scaffold.js +20 -13
  18. package/dist/scaffold.js.map +1 -1
  19. package/dist/types.d.ts.map +1 -1
  20. package/dist/types.js +2 -1
  21. package/dist/types.js.map +1 -1
  22. package/dist/wizard.js +1 -1
  23. package/dist/wizard.js.map +1 -1
  24. package/package.json +14 -13
  25. package/dist/blueprints/assets/photos/association/community-cleanup.jpg +0 -0
  26. package/dist/blueprints/assets/photos/association/fundraising-dinner.jpg +0 -0
  27. package/dist/blueprints/assets/photos/association/garden-planting.jpg +0 -0
  28. package/dist/blueprints/assets/photos/association/harvest-food-drive.jpg +0 -0
  29. package/dist/blueprints/assets/photos/association/hero.jpg +0 -0
  30. package/dist/blueprints/assets/photos/association/volunteer-orientation.jpg +0 -0
  31. package/dist/blueprints/assets/photos/association/winter-coat-collection.jpg +0 -0
  32. package/dist/blueprints/assets/photos/blog/blog-runs-itself.jpg +0 -0
  33. package/dist/blueprints/assets/photos/blog/desk-setup.jpg +0 -0
  34. package/dist/blueprints/assets/photos/blog/editing.jpg +0 -0
  35. package/dist/blueprints/assets/photos/blog/hero.jpg +0 -0
  36. package/dist/blueprints/assets/photos/blog/notebooks.jpg +0 -0
  37. package/dist/blueprints/assets/photos/blog/plain-text-editor.jpg +0 -0
  38. package/dist/blueprints/assets/photos/blog/reading-nonfiction.jpg +0 -0
  39. package/dist/blueprints/assets/photos/blog/small-tool.jpg +0 -0
  40. package/dist/blueprints/assets/photos/blog/writing-schedule.jpg +0 -0
  41. package/dist/blueprints/assets/photos/magazine/business-2.jpg +0 -0
  42. package/dist/blueprints/assets/photos/magazine/business-3.jpg +0 -0
  43. package/dist/blueprints/assets/photos/magazine/business.jpg +0 -0
  44. package/dist/blueprints/assets/photos/magazine/culture-2.jpg +0 -0
  45. package/dist/blueprints/assets/photos/magazine/culture-3.jpg +0 -0
  46. package/dist/blueprints/assets/photos/magazine/culture.jpg +0 -0
  47. package/dist/blueprints/assets/photos/magazine/news-2.jpg +0 -0
  48. package/dist/blueprints/assets/photos/magazine/news-3.jpg +0 -0
  49. package/dist/blueprints/assets/photos/magazine/news.jpg +0 -0
  50. package/dist/blueprints/assets/photos/magazine/opinion-2.jpg +0 -0
  51. package/dist/blueprints/assets/photos/magazine/opinion-3.jpg +0 -0
  52. package/dist/blueprints/assets/photos/magazine/opinion.jpg +0 -0
  53. package/dist/blueprints/assets/photos/portfolio/adatum-publishing.jpg +0 -0
  54. package/dist/blueprints/assets/photos/portfolio/contoso-mobile-app.jpg +0 -0
  55. package/dist/blueprints/assets/photos/portfolio/fabrikam-annual-report.jpg +0 -0
  56. package/dist/blueprints/assets/photos/portfolio/hero.jpg +0 -0
  57. package/dist/blueprints/assets/photos/portfolio/litware-signage.jpg +0 -0
  58. package/dist/blueprints/assets/photos/portfolio/northwind-rebrand.jpg +0 -0
  59. package/dist/blueprints/assets/photos/portfolio/proseware-launch.jpg +0 -0
  60. package/dist/blueprints/assets/photos/portfolio/tailspin-streaming.jpg +0 -0
  61. package/dist/blueprints/assets/photos/portfolio/wingtip-wayfinding.jpg +0 -0
  62. package/dist/blueprints/assets/photos/restaurant/charred-octopus.jpg +0 -0
  63. package/dist/blueprints/assets/photos/restaurant/chocolate-tart.jpg +0 -0
  64. package/dist/blueprints/assets/photos/restaurant/creme-brulee.jpg +0 -0
  65. package/dist/blueprints/assets/photos/restaurant/hero.jpg +0 -0
  66. package/dist/blueprints/assets/photos/restaurant/house-red.jpg +0 -0
  67. package/dist/blueprints/assets/photos/restaurant/house-white.jpg +0 -0
  68. package/dist/blueprints/assets/photos/restaurant/pan-seared-trout.jpg +0 -0
  69. package/dist/blueprints/assets/photos/restaurant/poached-pear.jpg +0 -0
  70. package/dist/blueprints/assets/photos/restaurant/roasted-beet-salad.jpg +0 -0
  71. package/dist/blueprints/assets/photos/restaurant/slow-roast-duck-leg.jpg +0 -0
  72. package/dist/blueprints/assets/photos/restaurant/soup-of-the-day.jpg +0 -0
  73. package/dist/blueprints/assets/photos/restaurant/sparkling-water.jpg +0 -0
  74. package/dist/blueprints/assets/photos/restaurant/wild-mushroom-risotto.jpg +0 -0
  75. package/dist/blueprints/assets/photos/saas/avatar-1.jpg +0 -0
  76. package/dist/blueprints/assets/photos/saas/hero.jpg +0 -0
  77. package/dist/blueprints/assets/photos/store/camp-blanket.jpg +0 -0
  78. package/dist/blueprints/assets/photos/store/canvas-tote.jpg +0 -0
  79. package/dist/blueprints/assets/photos/store/cast-iron-skillet.jpg +0 -0
  80. package/dist/blueprints/assets/photos/store/ceramic-pour-over-set.jpg +0 -0
  81. package/dist/blueprints/assets/photos/store/enamel-mug.jpg +0 -0
  82. package/dist/blueprints/assets/photos/store/everyday-tee.jpg +0 -0
  83. package/dist/blueprints/assets/photos/store/field-jacket.jpg +0 -0
  84. package/dist/blueprints/assets/photos/store/hero.jpg +0 -0
  85. package/dist/blueprints/assets/photos/store/leather-card-holder.jpg +0 -0
  86. package/dist/blueprints/assets/photos/store/linen-table-runner.jpg +0 -0
  87. package/dist/blueprints/assets/photos/store/trail-tote.jpg +0 -0
  88. package/dist/blueprints/assets/photos/store/wool-beanie.jpg +0 -0
  89. package/dist/blueprints/assets/photos/store/wool-overshirt.jpg +0 -0
  90. package/dist/blueprints/assets/photos/vitrine/avatar-1.jpg +0 -0
  91. package/dist/blueprints/assets/photos/vitrine/avatar-2.jpg +0 -0
  92. package/dist/blueprints/assets/photos/vitrine/avatar-3.jpg +0 -0
  93. package/dist/blueprints/assets/photos/vitrine/dashboard.jpg +0 -0
  94. package/dist/blueprints/assets/photos/vitrine/hero.jpg +0 -0
  95. package/dist/blueprints/association.d.ts +0 -151
  96. package/dist/blueprints/association.d.ts.map +0 -1
  97. package/dist/blueprints/association.js +0 -732
  98. package/dist/blueprints/association.js.map +0 -1
  99. package/dist/blueprints/blog.d.ts +0 -198
  100. package/dist/blueprints/blog.d.ts.map +0 -1
  101. package/dist/blueprints/blog.js +0 -665
  102. package/dist/blueprints/blog.js.map +0 -1
  103. package/dist/blueprints/content-pack.d.ts +0 -143
  104. package/dist/blueprints/content-pack.d.ts.map +0 -1
  105. package/dist/blueprints/content-pack.js +0 -111
  106. package/dist/blueprints/content-pack.js.map +0 -1
  107. package/dist/blueprints/content-packs.d.ts +0 -11
  108. package/dist/blueprints/content-packs.d.ts.map +0 -1
  109. package/dist/blueprints/content-packs.js +0 -29
  110. package/dist/blueprints/content-packs.js.map +0 -1
  111. package/dist/blueprints/demo-media.d.ts +0 -39
  112. package/dist/blueprints/demo-media.d.ts.map +0 -1
  113. package/dist/blueprints/demo-media.js +0 -30
  114. package/dist/blueprints/demo-media.js.map +0 -1
  115. package/dist/blueprints/documentation.d.ts +0 -134
  116. package/dist/blueprints/documentation.d.ts.map +0 -1
  117. package/dist/blueprints/documentation.js +0 -678
  118. package/dist/blueprints/documentation.js.map +0 -1
  119. package/dist/blueprints/magazine.d.ts +0 -145
  120. package/dist/blueprints/magazine.d.ts.map +0 -1
  121. package/dist/blueprints/magazine.js +0 -543
  122. package/dist/blueprints/magazine.js.map +0 -1
  123. package/dist/blueprints/menus.d.ts +0 -25
  124. package/dist/blueprints/menus.d.ts.map +0 -1
  125. package/dist/blueprints/menus.js +0 -33
  126. package/dist/blueprints/menus.js.map +0 -1
  127. package/dist/blueprints/photo-assets.d.ts +0 -3
  128. package/dist/blueprints/photo-assets.d.ts.map +0 -1
  129. package/dist/blueprints/photo-assets.js +0 -33
  130. package/dist/blueprints/photo-assets.js.map +0 -1
  131. package/dist/blueprints/portfolio.d.ts +0 -141
  132. package/dist/blueprints/portfolio.d.ts.map +0 -1
  133. package/dist/blueprints/portfolio.js +0 -700
  134. package/dist/blueprints/portfolio.js.map +0 -1
  135. package/dist/blueprints/restaurant.d.ts +0 -124
  136. package/dist/blueprints/restaurant.d.ts.map +0 -1
  137. package/dist/blueprints/restaurant.js +0 -457
  138. package/dist/blueprints/restaurant.js.map +0 -1
  139. package/dist/blueprints/saas.d.ts +0 -133
  140. package/dist/blueprints/saas.d.ts.map +0 -1
  141. package/dist/blueprints/saas.js +0 -551
  142. package/dist/blueprints/saas.js.map +0 -1
  143. package/dist/blueprints/site-settings-seed.d.ts +0 -17
  144. package/dist/blueprints/site-settings-seed.d.ts.map +0 -1
  145. package/dist/blueprints/site-settings-seed.js +0 -45
  146. package/dist/blueprints/site-settings-seed.js.map +0 -1
  147. package/dist/blueprints/starting-skins.d.ts +0 -19
  148. package/dist/blueprints/starting-skins.d.ts.map +0 -1
  149. package/dist/blueprints/starting-skins.js +0 -301
  150. package/dist/blueprints/starting-skins.js.map +0 -1
  151. package/dist/blueprints/store.d.ts +0 -173
  152. package/dist/blueprints/store.d.ts.map +0 -1
  153. package/dist/blueprints/store.js +0 -643
  154. package/dist/blueprints/store.js.map +0 -1
  155. package/dist/blueprints/vitrine.d.ts +0 -178
  156. package/dist/blueprints/vitrine.d.ts.map +0 -1
  157. package/dist/blueprints/vitrine.js +0 -529
  158. package/dist/blueprints/vitrine.js.map +0 -1
  159. package/dist/demo-art/compositions.d.ts +0 -48
  160. package/dist/demo-art/compositions.d.ts.map +0 -1
  161. package/dist/demo-art/compositions.js +0 -690
  162. package/dist/demo-art/compositions.js.map +0 -1
  163. package/dist/demo-art/index.d.ts +0 -8
  164. package/dist/demo-art/index.d.ts.map +0 -1
  165. package/dist/demo-art/index.js +0 -5
  166. package/dist/demo-art/index.js.map +0 -1
  167. package/dist/demo-art/oklch.d.ts +0 -39
  168. package/dist/demo-art/oklch.d.ts.map +0 -1
  169. package/dist/demo-art/oklch.js +0 -79
  170. package/dist/demo-art/oklch.js.map +0 -1
  171. package/dist/demo-art/png.d.ts +0 -24
  172. package/dist/demo-art/png.d.ts.map +0 -1
  173. package/dist/demo-art/png.js +0 -110
  174. package/dist/demo-art/png.js.map +0 -1
  175. package/dist/demo-art/render.d.ts +0 -228
  176. package/dist/demo-art/render.d.ts.map +0 -1
  177. package/dist/demo-art/render.js +0 -478
  178. package/dist/demo-art/render.js.map +0 -1
@@ -1,690 +0,0 @@
1
- import { companionTones, oklchToRgb, rgbToOklch, withChroma, withLightness } from './oklch.js';
2
- import { mulberry32 } from './render.js';
3
- // ---------------------------------------------------------------- colour helpers
4
- function hexToRgb(hex) {
5
- const clean = hex.replace('#', '');
6
- const r = Number.parseInt(clean.slice(0, 2), 16) / 255;
7
- const g = Number.parseInt(clean.slice(2, 4), 16) / 255;
8
- const b = Number.parseInt(clean.slice(4, 6), 16) / 255;
9
- return { r, g, b };
10
- }
11
- function mix(a, b, t) {
12
- return { r: a.r + (b.r - a.r) * t, g: a.g + (b.g - a.g) * t, b: a.b + (b.b - a.b) * t };
13
- }
14
- const WHITE = { r: 1, g: 1, b: 1 };
15
- const BLACK = { r: 0, g: 0, b: 0 };
16
- function lighten(color, amount) {
17
- return mix(color, WHITE, amount);
18
- }
19
- function darken(color, amount) {
20
- return mix(color, BLACK, amount);
21
- }
22
- function toRgb(palette) {
23
- return {
24
- bg: hexToRgb(palette.bg),
25
- fg: hexToRgb(palette.fg),
26
- accent: hexToRgb(palette.accent),
27
- accentFg: hexToRgb(palette.accentFg),
28
- muted: hexToRgb(palette.muted),
29
- mutedFg: hexToRgb(palette.mutedFg),
30
- border: hexToRgb(palette.border),
31
- };
32
- }
33
- /**
34
- * Pulls a hue (degrees) into the amber/terracotta arc used for "warm"
35
- * compositions, leaving it alone if it's already there. A small rotation of
36
- * the accent's *own* hue (as `rotateHue` alone would do) keeps a cool accent
37
- * — a store's teal, say — cool; the "arch & sun"/"warm" family needs its
38
- * backdrop to read as warm regardless of the palette it was built from, so
39
- * this clamps to the nearer edge of the warm arc instead of rotating by a
40
- * fixed offset.
41
- */
42
- function clampHueToWarmArc(hueDegrees) {
43
- const warmLow = 25;
44
- const warmHigh = 70;
45
- if (hueDegrees >= warmLow && hueDegrees <= warmHigh)
46
- return hueDegrees;
47
- const circularDistance = (a, b) => Math.min(Math.abs(a - b), 360 - Math.abs(a - b));
48
- return circularDistance(hueDegrees, warmLow) <= circularDistance(hueDegrees, warmHigh)
49
- ? warmLow
50
- : warmHigh;
51
- }
52
- function buildTones(c) {
53
- const spread = companionTones(c.accent, 2);
54
- const hue1 = spread[0] ?? c.accent;
55
- const hue2 = spread[1] ?? c.accent;
56
- const accentOklch = rgbToOklch(c.accent);
57
- const warmHue = clampHueToWarmArc(accentOklch.h);
58
- return {
59
- hue1,
60
- hue2,
61
- deep: darken(c.accent, 0.22),
62
- pale: lighten(c.muted, 0.06),
63
- warm: oklchToRgb({ l: 0.86, c: Math.max(accentOklch.c, 0.08), h: warmHue }),
64
- ink: withLightness(withChroma(c.accent, 0.4), 0.15),
65
- };
66
- }
67
- /**
68
- * A straight, hairline-thin flat segment between two fractional points —
69
- * used for grid connectors, editorial rules, and floor lines. `render.ts`
70
- * only offers a length+rotation `LineLayer`, so this does the point-to-point
71
- * trigonometry once, in one place, for every composition below.
72
- */
73
- function segment(from, to, canvasWidth, canvasHeight, thickness, color, alpha = 1) {
74
- const shorter = Math.min(canvasWidth, canvasHeight);
75
- const x1 = from[0] * canvasWidth;
76
- const y1 = from[1] * canvasHeight;
77
- const x2 = to[0] * canvasWidth;
78
- const y2 = to[1] * canvasHeight;
79
- const dx = x2 - x1;
80
- const dy = y2 - y1;
81
- const lengthPx = Math.max(Math.sqrt(dx * dx + dy * dy), 1);
82
- const rotation = (Math.atan2(dy, dx) * 180) / Math.PI;
83
- const center = [(x1 + x2) / 2 / canvasWidth, (y1 + y2) / 2 / canvasHeight];
84
- return {
85
- kind: 'line',
86
- center,
87
- length: lengthPx / shorter,
88
- thickness,
89
- rotation,
90
- color,
91
- alpha,
92
- };
93
- }
94
- const HERO_FAMILY = {
95
- mesh: 'grid',
96
- grid: 'grid',
97
- geometric: 'blocks',
98
- blocks: 'blocks',
99
- diagonal: 'bands',
100
- bands: 'bands',
101
- radial: 'rings',
102
- rings: 'rings',
103
- dark: 'ink',
104
- ink: 'ink',
105
- warm: 'sun',
106
- sun: 'sun',
107
- };
108
- const HERO_WIDTH = 1600;
109
- const HERO_HEIGHT = 1000;
110
- /**
111
- * A 1600×1000 hero visual. Every theme frames the hero's media beside the
112
- * title — a picture next to the text, never a backdrop under it — so the
113
- * composition fills the whole canvas like a poster: an early version kept
114
- * the left half a single flat colour "for the title" and rendered as a
115
- * mostly blank frame on every home page. `variant` names one of the flat
116
- * poster families (the same builders `coverArt` picks from, at hero size);
117
- * the older names (`mesh`, `geometric`, `diagonal`, `radial`, `dark`, `warm`)
118
- * stay as aliases so no blueprint needs to change.
119
- */
120
- export function heroArt(palette, variant = 'mesh', seed = 1) {
121
- const c = toRgb(palette);
122
- const tones = buildTones(c);
123
- const rng = mulberry32(seed);
124
- const family = HERO_FAMILY[variant];
125
- let layers;
126
- switch (family) {
127
- case 'grid':
128
- layers = gridNodeCover(c, tones, rng);
129
- break;
130
- case 'blocks':
131
- layers = colourBlockCover(c, tones, rng);
132
- break;
133
- case 'bands':
134
- layers = stripeBandCover(c, tones, rng);
135
- break;
136
- case 'rings':
137
- layers = concentricCover(c, tones, rng);
138
- break;
139
- case 'ink':
140
- layers = editorialMarkCover(c, tones, rng);
141
- break;
142
- default:
143
- layers = archSunCover(c, tones, rng);
144
- }
145
- return { width: HERO_WIDTH, height: HERO_HEIGHT, seed, layers };
146
- }
147
- // ---------------------------------------------------------------- cover art
148
- const COVER_WIDTH = 1200;
149
- const COVER_HEIGHT = 800;
150
- function colourBlockCover(c, tones, rng) {
151
- const angle = rng() < 0.5 ? 0 : 90;
152
- const threeTone = rng() < 0.5;
153
- const colors = threeTone ? [tones.pale, c.accent, tones.hue2] : [tones.pale, c.accent];
154
- const accentCenter = [0.24 + rng() * 0.1, 0.72 + rng() * 0.06];
155
- const accentIsDisc = rng() < 0.5;
156
- return [
157
- { kind: 'bands', angle, colors, count: colors.length },
158
- accentIsDisc
159
- ? { kind: 'disc', center: accentCenter, radius: 0.09, color: tones.deep, alpha: 1 }
160
- : {
161
- kind: 'rect',
162
- center: accentCenter,
163
- width: 0.16,
164
- height: 0.16,
165
- radius: 0.02,
166
- color: tones.deep,
167
- alpha: 1,
168
- },
169
- ];
170
- }
171
- function gridNodeCover(c, tones, rng) {
172
- const a = [0.28 + rng() * 0.08, 0.32 + rng() * 0.08];
173
- const b = [0.6 + rng() * 0.08, 0.5];
174
- const d = [0.46 + rng() * 0.08, 0.76 + rng() * 0.06];
175
- return [
176
- { kind: 'fill', color: c.bg },
177
- { kind: 'dots', spacing: 0.05, radius: 0.006, color: c.border, alpha: 0.55 },
178
- segment(a, b, COVER_WIDTH, COVER_HEIGHT, 0.004, c.mutedFg, 0.45),
179
- segment(b, d, COVER_WIDTH, COVER_HEIGHT, 0.004, c.mutedFg, 0.45),
180
- { kind: 'disc', center: b, radius: 0.045, color: c.accent, alpha: 1 },
181
- { kind: 'disc', center: a, radius: 0.02, color: tones.hue1, alpha: 1 },
182
- { kind: 'disc', center: d, radius: 0.02, color: tones.deep, alpha: 1 },
183
- ];
184
- }
185
- function stripeBandCover(_c, tones, rng) {
186
- const angles = [0, 45, 90, 135];
187
- const angle = angles[Math.floor(rng() * angles.length) % angles.length] ?? 0;
188
- return [
189
- {
190
- kind: 'bands',
191
- angle,
192
- colors: [tones.hue2, tones.hue1, tones.deep],
193
- count: 3 + Math.floor(rng() * 2),
194
- },
195
- ];
196
- }
197
- function concentricCover(_c, tones, rng) {
198
- const center = [0.62 + rng() * 0.08, 0.42 + rng() * 0.08];
199
- return [
200
- { kind: 'fill', color: tones.pale },
201
- { kind: 'ring', center, innerRadius: 0.16, outerRadius: 0.176, color: tones.deep, alpha: 1 },
202
- { kind: 'ring', center, innerRadius: 0.23, outerRadius: 0.242, color: tones.hue1, alpha: 1 },
203
- {
204
- kind: 'disc',
205
- center: [0.24 + rng() * 0.08, 0.74],
206
- radius: 0.07,
207
- color: tones.deep,
208
- alpha: 1,
209
- },
210
- ];
211
- }
212
- function editorialMarkCover(c, _tones, rng) {
213
- const markKind = Math.floor(rng() * 3) % 3;
214
- let mark;
215
- if (markKind === 0) {
216
- mark = {
217
- kind: 'polygon',
218
- center: [0.7, 0.62],
219
- radius: 0.18,
220
- sides: 3,
221
- rotation: 0,
222
- color: c.accent,
223
- alpha: 1,
224
- };
225
- }
226
- else if (markKind === 1) {
227
- // Centred exactly on the bottom edge: only the top half of the disc is on-canvas — a semicircle for free.
228
- mark = { kind: 'disc', center: [0.72, 1], radius: 0.32, color: c.accent, alpha: 1 };
229
- }
230
- else {
231
- // Centred exactly on a corner: only one quarter of the disc is on-canvas.
232
- mark = { kind: 'disc', center: [1, 1], radius: 0.4, color: c.accent, alpha: 1 };
233
- }
234
- return [
235
- { kind: 'fill', color: c.bg },
236
- segment([0.12, 0.18], [0.5, 0.18], COVER_WIDTH, COVER_HEIGHT, 0.004, c.fg, 0.8),
237
- mark,
238
- ];
239
- }
240
- function isometricStackCover(c, tones, rng) {
241
- const shadow = mix(c.fg, tones.pale, 0.7);
242
- const originX = 0.4 + rng() * 0.06;
243
- const originY = 0.4 + rng() * 0.06;
244
- const step = 0.12 + rng() * 0.03;
245
- const tilt = rng() < 0.5 ? 1 : -1;
246
- const positions = [
247
- [originX, originY],
248
- [originX + step, originY + step * 0.85],
249
- [originX + step * 2, originY + step * 1.7],
250
- ];
251
- const palette = [tones.hue2, c.accent, tones.deep];
252
- // A seed-driven rotation of which colour lands on which card, so two
253
- // seeds landing on this family don't just shift position — they read as
254
- // genuinely different stacks.
255
- const shift = Math.floor(rng() * palette.length);
256
- const colors = palette.map((_, i) => palette[(i + shift) % palette.length] ?? c.accent);
257
- const layers = [{ kind: 'fill', color: tones.pale }];
258
- positions.forEach((pos, i) => {
259
- const rotation = tilt * (i - 1) * (4 + rng() * 4);
260
- layers.push({
261
- kind: 'rect',
262
- center: [pos[0] + 0.015, pos[1] + 0.02],
263
- width: 0.34,
264
- height: 0.22,
265
- radius: 0.02,
266
- rotation,
267
- color: shadow,
268
- alpha: 0.3,
269
- }, {
270
- kind: 'rect',
271
- center: pos,
272
- width: 0.34,
273
- height: 0.22,
274
- radius: 0.02,
275
- rotation,
276
- color: colors[i] ?? c.accent,
277
- alpha: 1,
278
- });
279
- });
280
- return layers;
281
- }
282
- function archSunCover(c, tones, rng) {
283
- const center = [0.42 + rng() * 0.16, 0.5 + rng() * 0.12];
284
- const radius = 0.18 + rng() * 0.08;
285
- const sunColor = rng() < 0.5 ? c.accent : tones.hue1;
286
- const layers = [
287
- { kind: 'fill', color: tones.warm },
288
- { kind: 'disc', center, radius, color: sunColor, alpha: 1 },
289
- ];
290
- if (rng() < 0.7) {
291
- layers.push({
292
- kind: 'ring',
293
- center,
294
- innerRadius: radius + 0.04,
295
- outerRadius: radius + 0.05,
296
- color: tones.deep,
297
- alpha: 0.6,
298
- });
299
- }
300
- // The ground band crops the sun at ~60% of its radius below centre —
301
- // enough of an arch to read as sunrise/sunset, never the full disc.
302
- const groundHeight = 0.5;
303
- const horizonY = center[1] + radius * 0.6;
304
- layers.push({
305
- kind: 'rect',
306
- center: [0.5, horizonY + groundHeight / 2],
307
- width: 1.6,
308
- height: groundHeight,
309
- color: tones.deep,
310
- alpha: 1,
311
- });
312
- return layers;
313
- }
314
- function checkerCover(c, rng) {
315
- return [
316
- { kind: 'fill', color: c.bg },
317
- {
318
- kind: 'checker',
319
- center: [0.66, 0.5],
320
- width: 0.9,
321
- height: 1.05,
322
- cell: 0.045,
323
- rotation: rng() < 0.5 ? 0 : 8,
324
- color: c.accent,
325
- alpha: 0.85,
326
- },
327
- ];
328
- }
329
- function duotoneBlobCover(tones, rng) {
330
- const jitter = () => (rng() - 0.5) * 0.05;
331
- return [
332
- { kind: 'fill', color: tones.pale },
333
- {
334
- kind: 'blob',
335
- points: [
336
- { at: [0.32 + jitter(), 0.38 + jitter()], radius: 0.28 },
337
- { at: [0.5 + jitter(), 0.3 + jitter()], radius: 0.22 },
338
- { at: [0.4 + jitter(), 0.55 + jitter()], radius: 0.2 },
339
- ],
340
- smoothing: 0.09,
341
- color: tones.hue1,
342
- alpha: 1,
343
- },
344
- {
345
- kind: 'blob',
346
- points: [
347
- { at: [0.68 + jitter(), 0.62 + jitter()], radius: 0.26 },
348
- { at: [0.82 + jitter(), 0.7 + jitter()], radius: 0.2 },
349
- ],
350
- smoothing: 0.08,
351
- color: tones.deep,
352
- alpha: 1,
353
- },
354
- ];
355
- }
356
- const COVER_FAMILIES = [
357
- colourBlockCover,
358
- gridNodeCover,
359
- stripeBandCover,
360
- concentricCover,
361
- editorialMarkCover,
362
- isometricStackCover,
363
- archSunCover,
364
- (c, _tones, rng) => checkerCover(c, rng),
365
- (_c, tones, rng) => duotoneBlobCover(tones, rng),
366
- ];
367
- /** A family of ≥8 visibly different, flat layouts from one palette, picked deterministically by `seed` (D5: colour blocks, grid & node, stripe bands, concentric rings, editorial marks, isometric stacks, arch & sun, checker/half-tone, duotone blobs). */
368
- export function coverArt(palette, seed = 1) {
369
- const c = toRgb(palette);
370
- const tones = buildTones(c);
371
- const rng = mulberry32(seed);
372
- // The family is picked by the seed itself, not by the first random draw:
373
- // a blueprint seeds its covers with consecutive seeds (1, 2, 3…), and a
374
- // random pick sent three of eight blog posts to the same "arch & sun"
375
- // frame. Consecutive seeds now walk the families in order — eight
376
- // covers, eight different layouts — while `rng` still varies each one.
377
- const familyIndex = ((Math.floor(seed) % COVER_FAMILIES.length) + COVER_FAMILIES.length) % COVER_FAMILIES.length;
378
- const build = COVER_FAMILIES[familyIndex];
379
- const layers = build(c, tones, rng);
380
- return { width: COVER_WIDTH, height: COVER_HEIGHT, seed, layers };
381
- }
382
- // ---------------------------------------------------------------- avatar
383
- const AVATAR_SIZE = 600;
384
- /** A 600×600 abstract "person" mark: a disc (head) over a disc (shoulders, cropped by the frame edge) on a flat companion-tone field — never a real photo of a person. */
385
- export function avatarArt(palette, seed = 1) {
386
- const c = toRgb(palette);
387
- const tones = buildTones(c);
388
- const rng = mulberry32(seed);
389
- const field = rng() < 0.5 ? tones.pale : mix(c.muted, tones.hue2, 0.25);
390
- const mark = rng() < 0.5 ? c.accent : tones.deep;
391
- const layers = [
392
- { kind: 'fill', color: field },
393
- { kind: 'disc', center: [0.5, 1.08], radius: 0.52, color: mark, alpha: 1 },
394
- { kind: 'disc', center: [0.5, 0.4], radius: 0.2, color: mark, alpha: 1 },
395
- ];
396
- if (rng() < 0.5) {
397
- layers.push({
398
- kind: 'ring',
399
- center: [0.5, 0.5],
400
- innerRadius: 0.42,
401
- outerRadius: 0.432,
402
- color: c.border,
403
- alpha: 0.7,
404
- });
405
- }
406
- return { width: AVATAR_SIZE, height: AVATAR_SIZE, seed, layers };
407
- }
408
- // ---------------------------------------------------------------- logo
409
- const LOGO_WIDTH = 400;
410
- const LOGO_HEIGHT = 160;
411
- /** A 400×160 neutral, abstract wordmark stand-in — a geometric mark plus two bars, always in flat mid-grey so it reads as a client logo in a logo strip, never a real brand. 8 distinct marks across seeds. */
412
- export function logoArt(seed = 1) {
413
- const rng = mulberry32(seed);
414
- const grey = { r: 0.42, g: 0.44, b: 0.47 };
415
- const markKind = Math.floor(rng() * 8) % 8;
416
- const center = [0.16, 0.5];
417
- let mark;
418
- switch (markKind) {
419
- case 0:
420
- mark = {
421
- kind: 'polygon',
422
- center,
423
- radius: 0.24,
424
- sides: 3,
425
- rotation: 0,
426
- color: grey,
427
- alpha: 0.85,
428
- };
429
- break;
430
- case 1:
431
- mark = {
432
- kind: 'ring',
433
- center,
434
- innerRadius: 0.16,
435
- outerRadius: 0.24,
436
- color: grey,
437
- alpha: 0.85,
438
- };
439
- break;
440
- case 2:
441
- mark = {
442
- kind: 'rect',
443
- center,
444
- width: 0.32,
445
- height: 0.32,
446
- radius: 0.06,
447
- rotation: 8,
448
- color: grey,
449
- alpha: 0.85,
450
- };
451
- break;
452
- case 3:
453
- mark = {
454
- kind: 'polygon',
455
- center,
456
- radius: 0.24,
457
- sides: 6,
458
- rotation: 0,
459
- color: grey,
460
- alpha: 0.85,
461
- };
462
- break;
463
- case 4:
464
- mark = {
465
- kind: 'rect',
466
- center,
467
- width: 0.3,
468
- height: 0.3,
469
- radius: 0.02,
470
- rotation: 45,
471
- color: grey,
472
- alpha: 0.85,
473
- };
474
- break;
475
- case 5:
476
- mark = { kind: 'disc', center, radius: 0.22, color: grey, alpha: 0.85 };
477
- break;
478
- case 6:
479
- mark = {
480
- kind: 'polygon',
481
- center,
482
- radius: 0.24,
483
- sides: 3,
484
- rotation: 90,
485
- color: grey,
486
- alpha: 0.85,
487
- };
488
- break;
489
- default:
490
- mark = { kind: 'disc', center, radius: 0.22, color: grey, alpha: 0.85 };
491
- }
492
- const layers = [{ kind: 'fill', color: WHITE }, mark];
493
- if (markKind === 7) {
494
- // A white bar crops the disc into a "circle + bar" mark distinct from case 5's plain disc.
495
- layers.push({
496
- kind: 'rect',
497
- center,
498
- width: 0.07,
499
- height: 0.32,
500
- color: WHITE,
501
- alpha: 1,
502
- });
503
- }
504
- layers.push({
505
- kind: 'rect',
506
- center: [0.52, 0.42],
507
- width: 0.5,
508
- height: 0.14,
509
- radius: 0.02,
510
- color: grey,
511
- alpha: 0.55,
512
- }, {
513
- kind: 'rect',
514
- center: [0.46, 0.62],
515
- width: 0.36,
516
- height: 0.1,
517
- radius: 0.02,
518
- color: grey,
519
- alpha: 0.35,
520
- });
521
- return { width: LOGO_WIDTH, height: LOGO_HEIGHT, seed, layers };
522
- }
523
- // ---------------------------------------------------------------- product
524
- const PRODUCT_SIZE = 1000;
525
- /** A 1000×1000 centred, flat stylised object — a store's demo catalogue. Hard offset shadows (a darker solid shape, never blurred), a solid or two-tone backdrop, a floor line, one flat highlight shape per object. 6 shapes across seeds. */
526
- export function productArt(palette, seed = 1) {
527
- const c = toRgb(palette);
528
- const tones = buildTones(c);
529
- const rng = mulberry32(seed);
530
- const objectColor = rng() < 0.5 ? c.accent : tones.deep;
531
- const shape = Math.floor(rng() * 6) % 6;
532
- const backdrop = rng() < 0.5 ? c.muted : tones.pale;
533
- const twoTone = rng() < 0.5;
534
- const shadowColor = mix(c.fg, backdrop, 0.55);
535
- const layers = twoTone
536
- ? [{ kind: 'bands', angle: 90, colors: [backdrop, darken(backdrop, 0.06)], count: 2 }]
537
- : [{ kind: 'fill', color: backdrop }];
538
- layers.push(segment([0.14, 0.78], [0.86, 0.78], PRODUCT_SIZE, PRODUCT_SIZE, 0.004, c.border, 0.8));
539
- switch (shape) {
540
- case 0: {
541
- // Bottle/capsule.
542
- layers.push({
543
- kind: 'rect',
544
- center: [0.52, 0.55],
545
- width: 0.22,
546
- height: 0.58,
547
- radius: 0.11,
548
- color: shadowColor,
549
- alpha: 0.3,
550
- }, {
551
- kind: 'rect',
552
- center: [0.5, 0.53],
553
- width: 0.22,
554
- height: 0.58,
555
- radius: 0.11,
556
- color: objectColor,
557
- alpha: 1,
558
- }, {
559
- kind: 'rect',
560
- center: [0.5, 0.22],
561
- width: 0.09,
562
- height: 0.1,
563
- radius: 0.02,
564
- color: objectColor,
565
- alpha: 1,
566
- });
567
- break;
568
- }
569
- case 1: {
570
- // Box.
571
- layers.push({
572
- kind: 'rect',
573
- center: [0.53, 0.55],
574
- width: 0.42,
575
- height: 0.42,
576
- radius: 0.03,
577
- color: shadowColor,
578
- alpha: 0.3,
579
- }, {
580
- kind: 'rect',
581
- center: [0.5, 0.52],
582
- width: 0.42,
583
- height: 0.42,
584
- radius: 0.03,
585
- color: objectColor,
586
- alpha: 1,
587
- }, segment([0.5, 0.31], [0.5, 0.73], PRODUCT_SIZE, PRODUCT_SIZE, 0.006, lighten(objectColor, 0.3), 0.6));
588
- break;
589
- }
590
- case 2: {
591
- // Sphere, flattened, with a crescent highlight (a lighter disc partly re-covered by an object-coloured disc).
592
- const radius = 0.28;
593
- layers.push({ kind: 'disc', center: [0.53, 0.55], radius, color: shadowColor, alpha: 0.3 }, { kind: 'disc', center: [0.5, 0.52], radius, color: objectColor, alpha: 1 }, {
594
- kind: 'disc',
595
- center: [0.5 - radius * 0.35, 0.52 - radius * 0.35],
596
- radius: radius * 0.36,
597
- color: lighten(objectColor, 0.4),
598
- alpha: 1,
599
- }, {
600
- kind: 'disc',
601
- center: [0.5 - radius * 0.22, 0.52 - radius * 0.22],
602
- radius: radius * 0.3,
603
- color: objectColor,
604
- alpha: 1,
605
- });
606
- break;
607
- }
608
- case 3: {
609
- // Ring.
610
- layers.push({
611
- kind: 'ring',
612
- center: [0.53, 0.55],
613
- innerRadius: 0.2,
614
- outerRadius: 0.3,
615
- color: shadowColor,
616
- alpha: 0.3,
617
- }, {
618
- kind: 'ring',
619
- center: [0.5, 0.52],
620
- innerRadius: 0.2,
621
- outerRadius: 0.3,
622
- color: objectColor,
623
- alpha: 1,
624
- });
625
- break;
626
- }
627
- case 4: {
628
- // Stacked cards.
629
- const positions = [
630
- [0.44, 0.62],
631
- [0.5, 0.52],
632
- [0.56, 0.42],
633
- ];
634
- const colors = [tones.hue2, tones.hue1, objectColor];
635
- positions.forEach((pos, i) => {
636
- const rotation = (i - 1) * 6;
637
- layers.push({
638
- kind: 'rect',
639
- center: [pos[0] + 0.02, pos[1] + 0.03],
640
- width: 0.34,
641
- height: 0.22,
642
- radius: 0.02,
643
- rotation,
644
- color: shadowColor,
645
- alpha: 0.25,
646
- }, {
647
- kind: 'rect',
648
- center: pos,
649
- width: 0.34,
650
- height: 0.22,
651
- radius: 0.02,
652
- rotation,
653
- color: colors[i] ?? objectColor,
654
- alpha: 1,
655
- });
656
- });
657
- break;
658
- }
659
- default: {
660
- // Hexagonal tile.
661
- layers.push({
662
- kind: 'polygon',
663
- center: [0.53, 0.55],
664
- radius: 0.28,
665
- sides: 6,
666
- rotation: 0,
667
- color: shadowColor,
668
- alpha: 0.3,
669
- }, {
670
- kind: 'polygon',
671
- center: [0.5, 0.52],
672
- radius: 0.28,
673
- sides: 6,
674
- rotation: 0,
675
- color: objectColor,
676
- alpha: 1,
677
- }, {
678
- kind: 'polygon',
679
- center: [0.5, 0.52],
680
- radius: 0.12,
681
- sides: 6,
682
- rotation: 0,
683
- color: lighten(objectColor, 0.3),
684
- alpha: 1,
685
- });
686
- }
687
- }
688
- return { width: PRODUCT_SIZE, height: PRODUCT_SIZE, seed, layers };
689
- }
690
- //# sourceMappingURL=compositions.js.map