bookcover-core 0.7.0 → 0.9.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 (42) hide show
  1. package/dist/builtin_icons.d.ts +5 -0
  2. package/dist/builtin_icons.d.ts.map +1 -0
  3. package/dist/builtin_icons.js +18 -0
  4. package/dist/builtin_icons.js.map +1 -0
  5. package/dist/data_file.d.ts.map +1 -1
  6. package/dist/data_file.js +14 -7
  7. package/dist/data_file.js.map +1 -1
  8. package/dist/design.d.ts +29 -3
  9. package/dist/design.d.ts.map +1 -1
  10. package/dist/design.js +158 -41
  11. package/dist/design.js.map +1 -1
  12. package/dist/dimensions.d.ts +23 -4
  13. package/dist/dimensions.d.ts.map +1 -1
  14. package/dist/dimensions.js +3 -2
  15. package/dist/dimensions.js.map +1 -1
  16. package/dist/form_schema.d.ts +6 -21
  17. package/dist/form_schema.d.ts.map +1 -1
  18. package/dist/form_schema.js +18 -106
  19. package/dist/form_schema.js.map +1 -1
  20. package/dist/generated/builtin_bg_regions.d.ts +1 -1
  21. package/dist/generated/builtin_bg_regions.d.ts.map +1 -1
  22. package/dist/generated/icon_svgs_data.d.ts +4 -0
  23. package/dist/generated/icon_svgs_data.d.ts.map +1 -0
  24. package/dist/generated/icon_svgs_data.js +8 -0
  25. package/dist/generated/icon_svgs_data.js.map +1 -0
  26. package/dist/generated/templates_data.js +1 -1
  27. package/dist/generated/templates_data.js.map +1 -1
  28. package/dist/generated/vector_bg_images_data.d.ts.map +1 -1
  29. package/dist/generated/vector_bg_images_data.js +12 -11
  30. package/dist/generated/vector_bg_images_data.js.map +1 -1
  31. package/dist/icon_cache.d.ts +7 -1
  32. package/dist/icon_cache.d.ts.map +1 -1
  33. package/dist/icon_cache.js +19 -2
  34. package/dist/icon_cache.js.map +1 -1
  35. package/dist/image_regions.d.ts +1 -2
  36. package/dist/image_regions.d.ts.map +1 -1
  37. package/dist/image_regions.js.map +1 -1
  38. package/dist/index.d.ts +13 -6
  39. package/dist/index.d.ts.map +1 -1
  40. package/dist/index.js +17 -7
  41. package/dist/index.js.map +1 -1
  42. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"dimensions.js","sourceRoot":"","sources":["../src/dimensions.ts"],"names":[],"mappings":"AACA,wEAAwE;AAExE,OAAO,EAAC,WAAW,EAAE,qBAAqB,EAAC,MAAM,mBAAmB,CAAA;AAOpE,0CAA0C;AAC1C,SAAS,KAAK,CAAC,CAAQ,EAAE,IAAkB;IACvC,OAAO,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAA;AACzC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAAC,MAAkB;IACjD,IAAI,MAAM,CAAC,UAAU,KAAK,QAAQ,EAAE,CAAC;QACjC,MAAM,IAAI,GAAG,CAAC,MAAM,CAAC,WAAW,IAAI,IAAI,CAAkB,CAAA;QAC1D,MAAM,WAAW,GAA0C,MAAM,CAAC,OAAO;YACrE,CAAC,CAAC,MAAM,CAAC,OAAiB;YAC1B,CAAC,CAAC,EAAC,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,iBAAkB,EAAE,IAAI,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,kBAAmB,EAAE,IAAI,CAAC,EAAC,CAAA;QACtG,OAAO,qBAAqB,CAAC;YACzB,IAAI,EAAE,IAAI;YACV,IAAI,EAAE,WAAW;YACjB,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,YAAY,IAAI,CAAC,EAAE,IAAI,CAAC;YAC5C,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,YAAY,IAAI,CAAC,EAAE,IAAI,CAAC;SAC/C,CAAC,CAAA;IACN,CAAC;IAED,MAAM,OAAO,GAAG,WAAW,CAAC,MAAM,CAAC,UAA+C,CAAC,CAAA;IACnF,MAAM,SAAS,GAAqB;QAChC,IAAI,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,OAAiB;YAC3C,CAAC,CAAC,EAAC,IAAI,EAAE,MAAM,CAAC,WAA4B,EAAE,KAAK,EAAE,MAAM,CAAC,iBAAkB,EAAE,MAAM,EAAE,MAAM,CAAC,kBAAmB,EAAC;QACvH,KAAK,EAAE,MAAM,CAAC,UAAW;QACzB,YAAY,EAAE,MAAM,CAAC,YAA6B;QAClD,IAAI,EAAE,IAAI;KACb,CAAA;IACD,IAAI,OAAO,CAAC,yBAAyB,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;QACzD,SAAS,CAAC,UAAU,GAAG,MAAM,CAAC,UAAyB,CAAA;IAC3D,CAAC;IACD,IAAI,OAAO,CAAC,uBAAuB,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;QACrD,SAAS,CAAC,QAAQ,GAAG,MAAM,CAAC,QAAqB,CAAA;IACrD,CAAC;IACD,OAAO,OAAO,CAAC,cAAc,CAAC,SAAS,CAAC,CAAA;AAC5C,CAAC"}
1
+ {"version":3,"file":"dimensions.js","sourceRoot":"","sources":["../src/dimensions.ts"],"names":[],"mappings":"AACA,wEAAwE;AAExE,OAAO,EAAC,WAAW,EAAE,qBAAqB,EAAC,MAAM,mBAAmB,CAAA;AA0BpE,0CAA0C;AAC1C,SAAS,KAAK,CAAC,CAAQ,EAAE,IAAkB;IACvC,OAAO,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAA;AACzC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAAC,MAAsB;IACrD,IAAI,MAAM,CAAC,UAAU,KAAK,QAAQ,EAAE,CAAC;QACjC,MAAM,IAAI,GAAG,CAAC,MAAM,CAAC,WAAW,IAAI,IAAI,CAAkB,CAAA;QAC1D,MAAM,WAAW,GAA0C,MAAM,CAAC,OAAO;YACrE,CAAC,CAAC,MAAM,CAAC,OAAiB;YAC1B,CAAC,CAAC,EAAC,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,iBAAkB,EAAE,IAAI,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,kBAAmB,EAAE,IAAI,CAAC,EAAC,CAAA;QACtG,OAAO,qBAAqB,CAAC;YACzB,IAAI,EAAE,IAAI;YACV,IAAI,EAAE,WAAW;YACjB,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,YAAY,IAAI,CAAC,EAAE,IAAI,CAAC;YAC5C,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,YAAY,IAAI,CAAC,EAAE,IAAI,CAAC;SAC/C,CAAC,CAAA;IACN,CAAC;IAED,MAAM,OAAO,GAAG,WAAW,CAAC,MAAM,CAAC,UAA+C,CAAC,CAAA;IACnF,MAAM,SAAS,GAAqB;QAChC,IAAI,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,OAAiB;YAC3C,CAAC,CAAC,EAAC,IAAI,EAAE,MAAM,CAAC,WAA4B,EAAE,KAAK,EAAE,MAAM,CAAC,iBAAkB,EAAE,MAAM,EAAE,MAAM,CAAC,kBAAmB,EAAC;QACvH,KAAK,EAAE,MAAM,CAAC,UAAW;QACzB,YAAY,EAAE,MAAM,CAAC,YAA6B;QAClD,IAAI,EAAE,IAAI;KACb,CAAA;IACD,IAAI,OAAO,CAAC,yBAAyB,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;QACzD,SAAS,CAAC,UAAU,GAAG,MAAM,CAAC,UAAyB,CAAA;IAC3D,CAAC;IACD,IAAI,OAAO,CAAC,uBAAuB,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;QACrD,SAAS,CAAC,QAAQ,GAAG,MAAM,CAAC,QAAqB,CAAA;IACrD,CAAC;IACD,OAAO,OAAO,CAAC,cAAc,CAAC,SAAS,CAAC,CAAA;AAC5C,CAAC"}
@@ -1,30 +1,11 @@
1
1
  import type { FontStyle } from 'typst-fonts';
2
2
  import type { EmbedFormState } from './form_state.js';
3
- import type { RegionStats } from './design.js';
4
3
  /** Minimal shape build_schema needs to style custom fonts — typst-fonts' CustomFont is
5
4
  * assignable, so callers can pass their uploaded-font store directly */
6
5
  export interface CustomFontStyle {
7
6
  family: string;
8
7
  style: FontStyle;
9
8
  }
10
- /** Colors sampled from the background image, keyed by where they were sampled from.
11
- * front_top/front_bottom treat the whole image as the front panel — correct whenever the
12
- * image is confined to the front panel (any bg_image_coverage besides 'full'). Under 'full'
13
- * coverage the raw image is a full wrap (back+spine+front side by side), so front text should
14
- * only look at the front panel's own portion of it, not the back/spine content alongside it —
15
- * front_top_full/front_bottom_full and back/spine are that full-wrap interpretation, located
16
- * proportionally within the same image (see analyze_pixel_regions in image_regions.ts). Both
17
- * interpretations are always sampled together (when dims are resolvable) so switching
18
- * bg_image_coverage never needs a recompute — it just picks which pair to use. */
19
- export interface ImageRegions {
20
- front_top: RegionStats;
21
- front_bottom: RegionStats;
22
- front_top_full: RegionStats | null;
23
- front_bottom_full: RegionStats | null;
24
- back: RegionStats | null;
25
- spine: RegionStats | null;
26
- }
27
- export declare const VECTOR_BG_AUTO_COLOR = "#e1d1c4";
28
9
  /** Replace straight quotes with typographic curly quotes */
29
10
  export declare function curly_quotes(text: string): string;
30
11
  /** Parse a font family name from user input. Accepts:
@@ -34,6 +15,10 @@ export declare function curly_quotes(text: string): string;
34
15
  export declare function parse_font_family(text: string): string | null;
35
16
  /** Assemble the full flat schema object from form values. custom_fonts supplies the sniffed
36
17
  * serif/sans style of user-uploaded families (their bytes travel separately to the compiler).
37
- * Never reads a background image — that too is passed to the generators out-of-band. */
38
- export declare function build_schema(form: EmbedFormState, custom_fonts?: CustomFontStyle[], image_regions?: ImageRegions | null): Record<string, unknown>;
18
+ * Never reads a background image — that too is passed to the generators out-of-band.
19
+ * Color fields carry only explicit user overrides (converted from the form's raw hex to the
20
+ * schema's hsl() string format) — anything left unset stays unset here, for resolve_colors()
21
+ * (generator/src/design.ts, run downstream inside build()) to fill in from bg_color/spine_color
22
+ * and, when a background image is active, the sampled image regions. */
23
+ export declare function build_schema(form: EmbedFormState, custom_fonts?: CustomFontStyle[]): Record<string, unknown>;
39
24
  //# sourceMappingURL=form_schema.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"form_schema.d.ts","sourceRoot":"","sources":["../src/form_schema.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,aAAa,CAAA;AAC1C,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,iBAAiB,CAAA;AAInD,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,aAAa,CAAA;AAG5C;yEACyE;AACzE,MAAM,WAAW,eAAe;IAC5B,MAAM,EAAC,MAAM,CAAA;IACb,KAAK,EAAC,SAAS,CAAA;CAClB;AAED;;;;;;;;mFAQmF;AACnF,MAAM,WAAW,YAAY;IACzB,SAAS,EAAC,WAAW,CAAA;IACrB,YAAY,EAAC,WAAW,CAAA;IACxB,cAAc,EAAC,WAAW,GAAG,IAAI,CAAA;IACjC,iBAAiB,EAAC,WAAW,GAAG,IAAI,CAAA;IACpC,IAAI,EAAC,WAAW,GAAG,IAAI,CAAA;IACvB,KAAK,EAAC,WAAW,GAAG,IAAI,CAAA;CAC3B;AAMD,eAAO,MAAM,oBAAoB,YAAY,CAAA;AAwD7C,4DAA4D;AAC5D,wBAAgB,YAAY,CAAC,IAAI,EAAC,MAAM,GAAE,MAAM,CAU/C;AAaD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAC,MAAM,GAAE,MAAM,GAAG,IAAI,CAe3D;AAmBD;;yFAEyF;AACzF,wBAAgB,YAAY,CACxB,IAAI,EAAC,cAAc,EACnB,YAAY,CAAC,EAAC,eAAe,EAAE,EAC/B,aAAa,CAAC,EAAC,YAAY,GAAG,IAAI,GACpC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAmMxB"}
1
+ {"version":3,"file":"form_schema.d.ts","sourceRoot":"","sources":["../src/form_schema.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,aAAa,CAAA;AAC1C,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,iBAAiB,CAAA;AAInD;yEACyE;AACzE,MAAM,WAAW,eAAe;IAC5B,MAAM,EAAC,MAAM,CAAA;IACb,KAAK,EAAC,SAAS,CAAA;CAClB;AAED,4DAA4D;AAC5D,wBAAgB,YAAY,CAAC,IAAI,EAAC,MAAM,GAAE,MAAM,CAU/C;AAaD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAC,MAAM,GAAE,MAAM,GAAG,IAAI,CAe3D;AAmBD;;;;;;yEAMyE;AACzE,wBAAgB,YAAY,CACxB,IAAI,EAAC,cAAc,EACnB,YAAY,CAAC,EAAC,eAAe,EAAE,GACjC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAsJxB"}
@@ -1,59 +1,7 @@
1
1
  // Pure functions for building the cover generator schema from form state
2
2
  import { pm_to_typst } from 'pm-to-typst';
3
- import { derive_colors, hex_override_to_hsl, hex_to_hsl } from './colors.js';
4
- import { pick_vivid_tint, synthesize_fill, blend_regions, region_hex } from './design.js';
3
+ import { hex_override_to_hsl } from './colors.js';
5
4
  import { find_pattern } from './patterns.js';
6
- // Auto bg_color fallback for a vector background: there's no image to sample a complementary
7
- // color from, so this fixed neutral tan stands in instead of the imageless default of white.
8
- // Exported so callers previewing the auto color pre-generate (e.g. the widget's picker swatches)
9
- // can match this without duplicating the value
10
- export const VECTOR_BG_AUTO_COLOR = '#e1d1c4';
11
- /** Convert a hex color to a RegionStats triple (inverse of design.ts's region_hex) — a flat
12
- * synthesized color, so lightness_spread is 0 */
13
- function hex_to_region(hex) {
14
- const [h, s, l] = hex_to_hsl(hex);
15
- return { hue: h, saturation: s / 100, lightness: l / 100, lightness_spread: 0 };
16
- }
17
- // Baseline contrast target per position — bottom-positioned text (author, usually) tends to sit
18
- // over busier parts of a photo than top-positioned text (title, usually sky/simpler), so it
19
- // gets a higher floor before any per-image variance is even factored in
20
- const BASE_CONTRAST_BY_POSITION = { top: 5.5, middle: 6, bottom: 7 };
21
- // Scales a region's measured lightness_spread into additional required contrast — a region
22
- // that isn't visually uniform (text crossing both light and dark areas) needs more margin than
23
- // its average color alone would suggest
24
- const VARIANCE_CONTRAST_BOOST = 10;
25
- /** The contrast target for text at a given position against its resolved backdrop region */
26
- function min_contrast_for(position, backdrop) {
27
- return BASE_CONTRAST_BY_POSITION[position] + backdrop.lightness_spread * VARIANCE_CONTRAST_BOOST;
28
- }
29
- /** The front-panel top/bottom pair to use for text placement — the full-wrap interpretation
30
- * under 'full' coverage (when it was resolvable), else the whole-image interpretation that
31
- * every other coverage mode already needs */
32
- function front_pair(coverage, regions) {
33
- if (coverage === 'full' && regions.front_top_full && regions.front_bottom_full)
34
- return { top: regions.front_top_full, bottom: regions.front_bottom_full };
35
- return { top: regions.front_top, bottom: regions.front_bottom };
36
- }
37
- /**
38
- * Resolve which sampled region a piece of front-panel text should be checked for contrast
39
- * against, given its vertical position and how the background image covers the front panel:
40
- * 'painted'/'feature' insets don't fill the panel, so text is assumed to sit on bg_color
41
- * throughout; 'front_partial' only covers the bottom two-thirds, so top-positioned text sits
42
- * on bg_color while middle/bottom sits on the image; any full-bleed-front coverage maps
43
- * top/bottom/middle directly onto the sampled regions (see front_pair for the 'full' case).
44
- */
45
- function image_backdrop_for_position(position, coverage, regions, bg_region) {
46
- if (coverage === 'painted' || coverage === 'feature')
47
- return bg_region;
48
- if (coverage === 'front_partial')
49
- return position === 'top' ? bg_region : regions.front_bottom;
50
- const { top, bottom } = front_pair(coverage, regions);
51
- if (position === 'top')
52
- return top;
53
- if (position === 'bottom')
54
- return bottom;
55
- return blend_regions(top, bottom);
56
- }
57
5
  /** Replace straight quotes with typographic curly quotes */
58
6
  export function curly_quotes(text) {
59
7
  return text
@@ -108,49 +56,13 @@ function build_font_config(raw, custom_fonts) {
108
56
  }
109
57
  /** Assemble the full flat schema object from form values. custom_fonts supplies the sniffed
110
58
  * serif/sans style of user-uploaded families (their bytes travel separately to the compiler).
111
- * Never reads a background image — that too is passed to the generators out-of-band. */
112
- export function build_schema(form, custom_fonts, image_regions) {
59
+ * Never reads a background image — that too is passed to the generators out-of-band.
60
+ * Color fields carry only explicit user overrides (converted from the form's raw hex to the
61
+ * schema's hsl() string format) — anything left unset stays unset here, for resolve_colors()
62
+ * (generator/src/design.ts, run downstream inside build()) to fill in from bg_color/spine_color
63
+ * and, when a background image is active, the sampled image regions. */
64
+ export function build_schema(form, custom_fonts) {
113
65
  const q = curly_quotes;
114
- // bg_color's auto value: complements the background image when one is active; a vector
115
- // background has no pixels to sample, so it gets a fixed neutral tan instead; otherwise white —
116
- // resolved once here since it feeds both the schema's bg_color and the derive_colors() call
117
- const all_regions = image_regions
118
- ? [image_regions.front_top, image_regions.front_bottom,
119
- ...(image_regions.back ? [image_regions.back] : []),
120
- ...(image_regions.spine ? [image_regions.spine] : [])]
121
- : [];
122
- const effective_bg_color = form.bg_color ?? (all_regions.length ? synthesize_fill(all_regions)
123
- : form.bg_vector_id ? VECTOR_BG_AUTO_COLOR : '#ffffff');
124
- // A punchier accent than bg_color, for icon/pattern colors — fixed mid-lightness target
125
- const accent_color = all_regions.length ? synthesize_fill(all_regions, 0.45) : undefined;
126
- // The image is only actually visible behind the back panel/spine under full-wrap coverage —
127
- // otherwise blurb/spine keep deriving purely from bg_color, same as before this feature
128
- const image_backdrop = image_regions && form.bg_image_coverage === 'full'
129
- && image_regions.back && image_regions.spine
130
- ? { back: region_hex(image_regions.back), spine: region_hex(image_regions.spine) }
131
- : undefined;
132
- // Derive colors from bg_color + spine_color (+ the real image color behind blurb/spine,
133
- // when visible), then apply per-field overrides
134
- const colors = derive_colors(effective_bg_color, form.spine_color, image_backdrop);
135
- // Front text hue/contrast derived from the image, when active — falls back through to
136
- // today's flat defaults below when no image (or that field's own region) is available
137
- let title_image_color;
138
- let subtitle_image_color;
139
- let author_image_color;
140
- if (image_regions) {
141
- const bg_region = hex_to_region(effective_bg_color);
142
- const coverage = form.bg_image_coverage;
143
- const { top: front_top, bottom: front_bottom } = front_pair(coverage, image_regions);
144
- const candidates = [front_top, front_bottom];
145
- const backdrop_for = (position) => image_backdrop_for_position(position, coverage, image_regions, bg_region);
146
- const tint_for = (position) => {
147
- const backdrop = backdrop_for(position);
148
- return pick_vivid_tint(candidates, backdrop, min_contrast_for(position, backdrop));
149
- };
150
- title_image_color = hex_override_to_hsl(tint_for(form.title_position)) ?? undefined;
151
- subtitle_image_color = hex_override_to_hsl(tint_for(form.subtitle_position)) ?? undefined;
152
- author_image_color = hex_override_to_hsl(tint_for(form.author_position)) ?? undefined;
153
- }
154
66
  // Build size/print fields
155
67
  const size_fields = {};
156
68
  if (form.service_id === 'custom') {
@@ -194,7 +106,7 @@ export function build_schema(form, custom_fonts, image_regions) {
194
106
  title1_size: form.title1_size,
195
107
  title1_weight: form.title1_weight,
196
108
  title1_italic: form.title1_italic || undefined,
197
- title1_color: hex_override_to_hsl(form.title1_color) ?? title_image_color ?? undefined,
109
+ title1_color: hex_override_to_hsl(form.title1_color) ?? undefined,
198
110
  title2: q(form.title2) || undefined,
199
111
  title2_font: build_font_config(form.title2_font, custom_fonts),
200
112
  title2_size: form.title2_size,
@@ -217,7 +129,7 @@ export function build_schema(form, custom_fonts, image_regions) {
217
129
  subtitle_size: form.subtitle_size,
218
130
  subtitle_weight: form.subtitle_weight,
219
131
  subtitle_italic: form.subtitle_italic || undefined,
220
- subtitle_color: hex_override_to_hsl(form.subtitle_color) ?? subtitle_image_color ?? colors.front_subtitle,
132
+ subtitle_color: hex_override_to_hsl(form.subtitle_color) ?? undefined,
221
133
  subtitle_alignment: form.subtitle_alignment !== 'center' ? form.subtitle_alignment : undefined,
222
134
  subtitle_position: form.subtitle_position,
223
135
  subtitle_spacing: form.subtitle_spacing !== 1.5 ? form.subtitle_spacing : undefined,
@@ -228,7 +140,7 @@ export function build_schema(form, custom_fonts, image_regions) {
228
140
  author_size: form.author_size,
229
141
  author_weight: form.author_weight,
230
142
  author_italic: form.author_italic || undefined,
231
- author_color: hex_override_to_hsl(form.author_color) ?? author_image_color ?? colors.front_author,
143
+ author_color: hex_override_to_hsl(form.author_color) ?? undefined,
232
144
  author_alignment: form.author_alignment !== 'center' ? form.author_alignment : undefined,
233
145
  author_position: form.author_position,
234
146
  author_margin_top: form.author_margin_top !== 10 ? form.author_margin_top : undefined,
@@ -236,12 +148,12 @@ export function build_schema(form, custom_fonts, image_regions) {
236
148
  blurb: build_blurb(form.blurb),
237
149
  blurb_font: build_font_config(form.blurb_font, custom_fonts),
238
150
  blurb_size: form.blurb_size,
239
- blurb_color: hex_override_to_hsl(form.blurb_color) ?? colors.blurb,
151
+ blurb_color: hex_override_to_hsl(form.blurb_color) ?? undefined,
240
152
  blurb_bg_color: form.blurb_bg_color === undefined
241
- ? colors.blurb_background // auto-derived from bg_color
153
+ ? undefined // auto-derived downstream by resolve_colors()
242
154
  : form.blurb_bg_color === null
243
155
  ? null // transparent
244
- : hex_override_to_hsl(form.blurb_bg_color) ?? colors.blurb_background,
156
+ : hex_override_to_hsl(form.blurb_bg_color) ?? undefined,
245
157
  blurb_alignment: form.blurb_alignment !== 'left' ? form.blurb_alignment : undefined,
246
158
  blurb_padding: form.blurb_padding !== 7 ? form.blurb_padding : undefined,
247
159
  blurb_width: form.blurb_width !== 80 ? form.blurb_width : undefined,
@@ -251,13 +163,13 @@ export function build_schema(form, custom_fonts, image_regions) {
251
163
  spine_title_size: form.spine_title_size,
252
164
  spine_title_weight: form.spine_title_weight,
253
165
  spine_title_italic: form.spine_title_italic || undefined,
254
- spine_title_color: hex_override_to_hsl(form.spine_title_color) ?? colors.spine_title,
166
+ spine_title_color: hex_override_to_hsl(form.spine_title_color) ?? undefined,
255
167
  spine_author: form.spine_author || undefined,
256
168
  spine_author_font: build_font_config(form.spine_author_font, custom_fonts),
257
169
  spine_author_size: form.spine_author_size,
258
170
  spine_author_weight: form.spine_author_weight,
259
171
  spine_author_italic: form.spine_author_italic || undefined,
260
- spine_author_color: hex_override_to_hsl(form.spine_author_color) ?? colors.spine_author,
172
+ spine_author_color: hex_override_to_hsl(form.spine_author_color) ?? undefined,
261
173
  // Size & print
262
174
  service_id: form.service_id === 'custom' ? 'custom' : form.service_id,
263
175
  binding_type: form.service_id === 'custom' ? 'paperback' : form.binding_type,
@@ -269,16 +181,16 @@ export function build_schema(form, custom_fonts, image_regions) {
269
181
  home_print_margin: form.home_print_margin || undefined,
270
182
  // Background
271
183
  bg_image_coverage: form.bg_image_coverage,
272
- bg_color: colors.front_background,
184
+ bg_color: hex_override_to_hsl(form.bg_color) ?? undefined,
273
185
  bg_color_gradient: form.bg_color_gradient || undefined,
274
186
  ...(form.icon_id ? { icon_id: form.icon_id, icon_mode: form.icon_mode } : {}),
275
187
  icon_size: form.icon_size !== 1 ? form.icon_size : undefined,
276
- icon_color: form.icon_color || accent_color || undefined,
188
+ icon_color: form.icon_color || undefined,
277
189
  icon_spine: form.icon_spine || undefined,
278
190
  ...pattern_fields,
279
191
  pattern_color: form.pattern_color || undefined,
280
192
  bg_vector_id: form.bg_vector_id || undefined,
281
- spine_color: colors.spine_background,
193
+ spine_color: hex_override_to_hsl(form.spine_color),
282
194
  // Other
283
195
  isbn: form.isbn || undefined,
284
196
  cjk_variant: form.cjk_variant !== 'auto' ? form.cjk_variant : undefined,
@@ -1 +1 @@
1
- {"version":3,"file":"form_schema.js","sourceRoot":"","sources":["../src/form_schema.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"form_schema.js","sourceRoot":"","sources":["../src/form_schema.ts"],"names":[],"mappings":"AACA,yEAAyE;AAEzE,OAAO,EAAC,WAAW,EAAC,MAAM,aAAa,CAAA;AAIvC,OAAO,EAAC,mBAAmB,EAAC,MAAM,aAAa,CAAA;AAC/C,OAAO,EAAC,YAAY,EAAC,MAAM,eAAe,CAAA;AAS1C,4DAA4D;AAC5D,MAAM,UAAU,YAAY,CAAC,IAAW;IACpC,OAAO,IAAI;QACP,4EAA4E;SAC3E,OAAO,CAAC,sBAAsB,EAAE,KAAK,CAAC;QACvC,yCAAyC;SACxC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC;QACnB,4EAA4E;SAC3E,OAAO,CAAC,sBAAsB,EAAE,KAAK,CAAC;QACvC,mEAAmE;SAClE,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAA;AAC3B,CAAC;AAED;;;;;GAKG;AACH,SAAS,WAAW,CAAC,GAAS;IAC1B,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,CAAC,CAAA;IAC9B,OAAO,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAA;AAC3C,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAAW;IACzC,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,CAAA;IAElB,+DAA+D;IAC/D,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,yCAAyC,CAAC,CAAA;IACtE,IAAI,QAAQ,EAAE,CAAC;QACX,OAAO,kBAAkB,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAA;IAC9D,CAAC;IAED,iDAAiD;IACjD,IAAI,4BAA4B,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAC1C,OAAO,IAAI,CAAA;IACf,CAAC;IAED,OAAO,IAAI,CAAA;AACf,CAAC;AAED;oEACoE;AACpE,SAAS,iBAAiB,CACtB,GAAU,EACV,YAA+B;IAE/B,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE;QACX,OAAO,SAAS,CAAA;IACpB,MAAM,MAAM,GAAG,iBAAiB,CAAC,GAAG,CAAC,CAAA;IACrC,IAAI,CAAC,MAAM;QACP,OAAO,SAAS,CAAA;IACpB,sFAAsF;IACtF,8EAA8E;IAC9E,MAAM,MAAM,GAAG,YAAY,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,CAAA;IAC3D,OAAO,MAAM,CAAC,CAAC,CAAC,EAAC,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAC,CAAC,CAAC,CAAC,EAAC,MAAM,EAAC,CAAA;AAC5D,CAAC;AAED;;;;;;yEAMyE;AACzE,MAAM,UAAU,YAAY,CACxB,IAAmB,EACnB,YAA+B;IAE/B,MAAM,CAAC,GAAG,YAAY,CAAA;IAEtB,0BAA0B;IAC1B,MAAM,WAAW,GAA2B,EAAE,CAAA;IAC9C,IAAI,IAAI,CAAC,UAAU,KAAK,QAAQ,EAAE,CAAC;QAC/B,WAAW,CAAC,cAAc,CAAC,GAAG,IAAI,CAAC,YAAY,CAAA;QAC/C,WAAW,CAAC,cAAc,CAAC,GAAG,IAAI,CAAC,YAAY,CAAA;QAC/C,WAAW,CAAC,aAAa,CAAC,GAAG,IAAI,CAAC,WAAW,CAAA;QAC7C,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACf,WAAW,CAAC,SAAS,CAAC,GAAG,IAAI,CAAC,OAAO,CAAA;QACzC,CAAC;aAAM,CAAC;YACJ,WAAW,CAAC,mBAAmB,CAAC,GAAG,IAAI,CAAC,iBAAiB,CAAA;YACzD,WAAW,CAAC,oBAAoB,CAAC,GAAG,IAAI,CAAC,kBAAkB,CAAA;QAC/D,CAAC;IACL,CAAC;SAAM,CAAC;QACJ,WAAW,CAAC,YAAY,CAAC,GAAG,IAAI,CAAC,UAAU,CAAA;QAC3C,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACf,WAAW,CAAC,SAAS,CAAC,GAAG,IAAI,CAAC,OAAO,CAAA;QACzC,CAAC;aAAM,CAAC;YACJ,WAAW,CAAC,mBAAmB,CAAC,GAAG,IAAI,CAAC,iBAAiB,CAAA;YACzD,WAAW,CAAC,oBAAoB,CAAC,GAAG,IAAI,CAAC,kBAAkB,CAAA;YAC3D,WAAW,CAAC,aAAa,CAAC,GAAG,IAAI,CAAC,WAAW,CAAA;QACjD,CAAC;IACL,CAAC;IAED,oEAAoE;IACpE,IAAI,cAAc,GAA2B,EAAE,CAAA;IAC/C,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;QAClB,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;QACzC,IAAI,GAAG,EAAE,CAAC;YACN,cAAc,GAAG;gBACb,OAAO,EAAE,IAAI,CAAC,UAAU;gBACxB,eAAe,EAAE,GAAG,CAAC,OAAO,GAAG,IAAI,CAAC,aAAa;aACpD,CAAA;QACL,CAAC;IACL,CAAC;IAED,OAAO;QACH,OAAO;QACP,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,SAAS;QACnC,WAAW,EAAE,iBAAiB,CAAC,IAAI,CAAC,WAAW,EAAE,YAAY,CAAC;QAC9D,WAAW,EAAE,IAAI,CAAC,WAAW;QAC7B,aAAa,EAAE,IAAI,CAAC,aAAa;QACjC,aAAa,EAAE,IAAI,CAAC,aAAa,IAAI,SAAS;QAC9C,YAAY,EAAE,mBAAmB,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,SAAS;QAEjE,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,SAAS;QACnC,WAAW,EAAE,iBAAiB,CAAC,IAAI,CAAC,WAAW,EAAE,YAAY,CAAC;QAC9D,WAAW,EAAE,IAAI,CAAC,WAAW;QAC7B,aAAa,EAAE,IAAI,CAAC,aAAa;QACjC,aAAa,EAAE,IAAI,CAAC,aAAa,IAAI,SAAS;QAC9C,YAAY,EAAE,mBAAmB,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,SAAS;QAEjE,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,SAAS;QACnC,WAAW,EAAE,iBAAiB,CAAC,IAAI,CAAC,WAAW,EAAE,YAAY,CAAC;QAC9D,WAAW,EAAE,IAAI,CAAC,WAAW;QAC7B,aAAa,EAAE,IAAI,CAAC,aAAa;QACjC,aAAa,EAAE,IAAI,CAAC,aAAa,IAAI,SAAS;QAC9C,YAAY,EAAE,mBAAmB,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,SAAS;QAEjE,eAAe,EAAE,IAAI,CAAC,eAAe,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC,SAAS;QACrF,cAAc,EAAE,IAAI,CAAC,cAAc;QACnC,aAAa,EAAE,IAAI,CAAC,aAAa,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,SAAS;QACxE,gBAAgB,EAAE,IAAI,CAAC,gBAAgB,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC,SAAS;QAClF,mBAAmB,EAAE,IAAI,CAAC,mBAAmB,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC,CAAC,SAAS;QAE3F,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,SAAS;QACtD,aAAa,EAAE,iBAAiB,CAAC,IAAI,CAAC,aAAa,EAAE,YAAY,CAAC;QAClE,aAAa,EAAE,IAAI,CAAC,aAAa;QACjC,eAAe,EAAE,IAAI,CAAC,eAAe;QACrC,eAAe,EAAE,IAAI,CAAC,eAAe,IAAI,SAAS;QAClD,cAAc,EAAE,mBAAmB,CAAC,IAAI,CAAC,cAAc,CAAC,IAAI,SAAS;QACrE,kBAAkB,EAAE,IAAI,CAAC,kBAAkB,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC,CAAC,SAAS;QAC9F,iBAAiB,EAAE,IAAI,CAAC,iBAAiB;QACzC,gBAAgB,EAAE,IAAI,CAAC,gBAAgB,KAAK,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC,SAAS;QACnF,mBAAmB,EAAE,IAAI,CAAC,mBAAmB,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC,CAAC,SAAS;QAC3F,sBAAsB,EAAE,IAAI,CAAC,sBAAsB,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC,CAAC,SAAS;QAEpG,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS;QAChD,WAAW,EAAE,iBAAiB,CAAC,IAAI,CAAC,WAAW,EAAE,YAAY,CAAC;QAC9D,WAAW,EAAE,IAAI,CAAC,WAAW;QAC7B,aAAa,EAAE,IAAI,CAAC,aAAa;QACjC,aAAa,EAAE,IAAI,CAAC,aAAa,IAAI,SAAS;QAC9C,YAAY,EAAE,mBAAmB,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,SAAS;QACjE,gBAAgB,EAAE,IAAI,CAAC,gBAAgB,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC,SAAS;QACxF,eAAe,EAAE,IAAI,CAAC,eAAe;QACrC,iBAAiB,EAAE,IAAI,CAAC,iBAAiB,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC,CAAC,SAAS;QACrF,oBAAoB,EAAE,IAAI,CAAC,oBAAoB,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC,CAAC,SAAS;QAE9F,KAAK,EAAE,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC;QAC9B,UAAU,EAAE,iBAAiB,CAAC,IAAI,CAAC,UAAU,EAAE,YAAY,CAAC;QAC5D,UAAU,EAAE,IAAI,CAAC,UAAU;QAC3B,WAAW,EAAE,mBAAmB,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,SAAS;QAC/D,cAAc,EAAE,IAAI,CAAC,cAAc,KAAK,SAAS;YAC7C,CAAC,CAAC,SAAS,CAAE,8CAA8C;YAC3D,CAAC,CAAC,IAAI,CAAC,cAAc,KAAK,IAAI;gBAC1B,CAAC,CAAC,IAAI,CAAE,cAAc;gBACtB,CAAC,CAAC,mBAAmB,CAAC,IAAI,CAAC,cAAc,CAAC,IAAI,SAAS;QAC/D,eAAe,EAAE,IAAI,CAAC,eAAe,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC,SAAS;QACnF,aAAa,EAAE,IAAI,CAAC,aAAa,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,SAAS;QACxE,WAAW,EAAE,IAAI,CAAC,WAAW,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS;QACnE,aAAa,EAAE,IAAI,CAAC,aAAa,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,SAAS;QAExE,WAAW,EAAE,IAAI,CAAC,WAAW,IAAI,SAAS;QAC1C,gBAAgB,EAAE,iBAAiB,CAAC,IAAI,CAAC,gBAAgB,EAAE,YAAY,CAAC;QACxE,gBAAgB,EAAE,IAAI,CAAC,gBAAgB;QACvC,kBAAkB,EAAE,IAAI,CAAC,kBAAkB;QAC3C,kBAAkB,EAAE,IAAI,CAAC,kBAAkB,IAAI,SAAS;QACxD,iBAAiB,EAAE,mBAAmB,CAAC,IAAI,CAAC,iBAAiB,CAAC,IAAI,SAAS;QAE3E,YAAY,EAAE,IAAI,CAAC,YAAY,IAAI,SAAS;QAC5C,iBAAiB,EAAE,iBAAiB,CAAC,IAAI,CAAC,iBAAiB,EAAE,YAAY,CAAC;QAC1E,iBAAiB,EAAE,IAAI,CAAC,iBAAiB;QACzC,mBAAmB,EAAE,IAAI,CAAC,mBAAmB;QAC7C,mBAAmB,EAAE,IAAI,CAAC,mBAAmB,IAAI,SAAS;QAC1D,kBAAkB,EAAE,mBAAmB,CAAC,IAAI,CAAC,kBAAkB,CAAC,IAAI,SAAS;QAE7E,eAAe;QACf,UAAU,EAAE,IAAI,CAAC,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU;QACrE,YAAY,EAAE,IAAI,CAAC,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY;QAC5E,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,SAAS;QACpC,UAAU,EAAE,IAAI,CAAC,UAAU,IAAI,SAAS;QACxC,GAAG,WAAW;QAEd,YAAY,EAAE,IAAI,CAAC,YAAY,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,SAAS;QACtE,WAAW,EAAE,IAAI,CAAC,WAAW,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS;QACnE,iBAAiB,EAAE,IAAI,CAAC,iBAAiB,IAAI,SAAS;QAEtD,aAAa;QACb,iBAAiB,EAAE,IAAI,CAAC,iBAAiB;QACzC,QAAQ,EAAE,mBAAmB,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,SAAS;QACzD,iBAAiB,EAAE,IAAI,CAAC,iBAAiB,IAAI,SAAS;QAEtD,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAC,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3E,SAAS,EAAE,IAAI,CAAC,SAAS,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS;QAC5D,UAAU,EAAE,IAAI,CAAC,UAAU,IAAI,SAAS;QACxC,UAAU,EAAE,IAAI,CAAC,UAAU,IAAI,SAAS;QAExC,GAAG,cAAc;QACjB,aAAa,EAAE,IAAI,CAAC,aAAa,IAAI,SAAS;QAE9C,YAAY,EAAE,IAAI,CAAC,YAAY,IAAI,SAAS;QAE5C,WAAW,EAAE,mBAAmB,CAAC,IAAI,CAAC,WAAW,CAAC;QAElD,QAAQ;QACR,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,SAAS;QAC5B,WAAW,EAAE,IAAI,CAAC,WAAW,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS;KAC1E,CAAA;AACL,CAAC"}
@@ -1,4 +1,4 @@
1
- import type { ImageRegions } from '../form_schema.js';
1
+ import type { ImageRegions } from '../design.js';
2
2
  /** Precomputed ImageRegions for every builtin background photo (assets/backgrounds/<key>),
3
3
  * sampled against a fixed reference size — a 6in x 9in trim, 250-page Lulu paperback — rather
4
4
  * than the actual cover being generated, since the source photo's dominant colors barely shift
@@ -1 +1 @@
1
- {"version":3,"file":"builtin_bg_regions.d.ts","sourceRoot":"","sources":["../../src/generated/builtin_bg_regions.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,mBAAmB,CAAA;AAEnD;;;;;;;;oCAQoC;AACpC,eAAO,MAAM,kBAAkB,EAAC,MAAM,CAAC,MAAM,EAAE;IAAC,IAAI,EAAC,MAAM,CAAC;IAAC,OAAO,EAAC,YAAY,CAAA;CAAC,CA4DjF,CAAA"}
1
+ {"version":3,"file":"builtin_bg_regions.d.ts","sourceRoot":"","sources":["../../src/generated/builtin_bg_regions.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,cAAc,CAAA;AAE9C;;;;;;;;oCAQoC;AACpC,eAAO,MAAM,kBAAkB,EAAC,MAAM,CAAC,MAAM,EAAE;IAAC,IAAI,EAAC,MAAM,CAAC;IAAC,OAAO,EAAC,YAAY,CAAA;CAAC,CA4DjF,CAAA"}
@@ -0,0 +1,4 @@
1
+ /** Raw SVG text for each bundled built-in icon, keyed by id, using currentColor so
2
+ * resolve_icon() in icon_cache.ts can recolor them like any Iconify icon */
3
+ export declare const BUILTIN_ICON_SVGS: Record<string, string>;
4
+ //# sourceMappingURL=icon_svgs_data.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"icon_svgs_data.d.ts","sourceRoot":"","sources":["../../src/generated/icon_svgs_data.ts"],"names":[],"mappings":"AAGA;6EAC6E;AAC7E,eAAO,MAAM,iBAAiB,EAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAEnD,CAAA"}
@@ -0,0 +1,8 @@
1
+ // GENERATED by .bin/gen_icon_svgs from generator/icon_svgs/*.svg — do not edit
2
+ // Edit the source .svg files instead, then rerun the generator build
3
+ /** Raw SVG text for each bundled built-in icon, keyed by id, using currentColor so
4
+ * resolve_icon() in icon_cache.ts can recolor them like any Iconify icon */
5
+ export const BUILTIN_ICON_SVGS = {
6
+ "cross": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\">\n\t<path d=\"M0 0h24v24H0z\" fill=\"none\" />\n\t<path fill=\"currentColor\" d=\"m 11,2 h 2 v 5 h 5 V 9 H 13 V 22 H 11 V 9 H 6 V 7 h 5 z\" />\n</svg>",
7
+ };
8
+ //# sourceMappingURL=icon_svgs_data.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"icon_svgs_data.js","sourceRoot":"","sources":["../../src/generated/icon_svgs_data.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,qEAAqE;AAErE;6EAC6E;AAC7E,MAAM,CAAC,MAAM,iBAAiB,GAA0B;IACpD,OAAO,EAAE,wNAAwN;CACpO,CAAA"}
@@ -2,7 +2,7 @@
2
2
  // Edit the source .typ files instead, then rerun the generator build
3
3
  /** The cover.typ / _helpers.typ templates baked in at this package version */
4
4
  export const DEFAULT_TEMPLATES = {
5
- cover: "\n// Book cover template — receives all variables from _data.typ\n#import \"_data.typ\": *\n\n// -- Page setup --\n#set page(\n width: total_width,\n height: total_height,\n margin: 0mm,\n)\n\n#set text(font: font_body_family)\n#set quote(block: true)\n#show quote: set pad(x: 3em)\n\n// ============================================================\n// Helper functions\n// ============================================================\n\n#import \"_helpers.typ\": *\n\n\n// ============================================================\n// Layer 1 — Background fills\n// ============================================================\n\n#place(\n top + left,\n rect(\n width: total_width,\n height: total_height,\n fill: if has_front_gradient {\n gradient.linear(\n (color_front_gradient_start, 0%),\n (color_front_bg, 30%),\n (color_front_bg, 45%),\n (color_front_gradient_end, 100%),\n angle: -40deg,\n )\n } else {\n color_front_bg\n },\n ),\n)\n\n\n#if has_pattern {\n let tiled = pattern(\n size: (pattern_tile_w, pattern_tile_h),\n image(\"pattern.svg\", width: pattern_tile_w, height: pattern_tile_h),\n )\n place(\n top + left,\n rect(width: total_width, height: total_height, fill: tiled),\n )\n}\n\n\n// ============================================================\n// Layer 2 — Background image\n// ============================================================\n\n#if has_image {\n if image_coverage == \"full\" {\n // Full spread: image covers entire canvas (back + spine + front)\n place(\n top + left,\n dx: 0mm,\n dy: 0mm,\n image(\n image_filename,\n width: total_width,\n height: total_height,\n fit: if image_is_vector { \"stretch\" } else { \"cover\" },\n ),\n )\n } else if image_coverage == \"painted\" {\n // Painted: image inset on front cover — 15mm margin horizontally and from bottom, 50% trim height\n let painted_w = face_width - 30mm\n let painted_h = face_height * 0.5 // sync: index.ts painted_h = face_height * 0.5\n place(\n top + left,\n dx: front_x + 15mm,\n dy: bleed + face_height * (1/3),\n image(\n image_filename,\n width: painted_w,\n height: painted_h,\n fit: \"stretch\", // Should be exact but just in case, ensures full frame visible\n ),\n )\n } else if image_coverage == \"feature\" {\n // Feature: same size and position as painted but with rounded corners via box clip\n let painted_w = face_width - 30mm\n let painted_h = face_height * 0.5\n place(\n top + left,\n dx: front_x + 15mm,\n dy: bleed + face_height * (1/3),\n box(\n width: painted_w,\n height: painted_h,\n radius: 6mm,\n clip: true,\n image(\n image_filename,\n width: painted_w,\n height: painted_h,\n fit: \"cover\",\n ),\n ),\n )\n } else if image_coverage == \"front_partial\" {\n // Lower 2/3: image covers the full front face width, bottom 2/3 of height, flush to the bottom trim edge\n let w = face_width\n let h = face_height * (2 / 3)\n place(\n top + left,\n dx: front_x,\n dy: bleed + face_height - h,\n image(\n image_filename,\n width: w,\n height: h,\n fit: \"cover\",\n ),\n )\n } else {\n // Front only: image covers the front panel including all bleed margins\n place(\n top + left,\n dx: front_x,\n dy: 0mm,\n image(\n image_filename,\n width: face_width + bleed,\n height: total_height,\n fit: if image_is_vector { \"stretch\" } else { \"cover\" },\n ),\n )\n }\n}\n\n\n// ============================================================\n// Layer 2b — Icon on front cover\n// ============================================================\n\n#if has_icon {\n let icon_size = calc.min(face_height * 0.4, face_width * 0.8)\n if icon_mode != \"center\" {\n icon_size = calc.min(face_height * 0.5, face_width * 0.8)\n }\n // Apply user-controlled size multiplier (1.0 = default)\n icon_size = icon_size * icon_size_mod\n let offset_icon_dx = front_x + (face_width - icon_size) + (icon_size * 0.15)\n let offset_icon_dy = bleed * -1 + (icon_size * 0.15)\n\n // Place ghost icons first (if any) so below main one\n if icon_mode == \"echo\" {\n place(\n bottom + left,\n dx: offset_icon_dx + icon_size * 0.1,\n dy: offset_icon_dy - icon_size * 0.15,\n image(\"icon_ghost.svg\", width: icon_size, height: icon_size),\n )\n place(\n bottom + left,\n dx: offset_icon_dx - icon_size * 0.1,\n dy: offset_icon_dy + icon_size * 0.1,\n image(\"icon_ghost2.svg\", width: icon_size, height: icon_size),\n )\n }\n\n if icon_mode == \"center\" {\n // Center — large icon centered horizontally, positioned in the lower/mid section\n place(\n bottom + left,\n dx: front_x + (face_width - icon_size) / 2,\n dy: (face_height * 0.2 + bleed) * -1,\n image(\"icon_main.svg\", width: icon_size, height: icon_size),\n )\n } else {\n // Offset/echo: large icon offset towards bottom-right, slightly overflowing the trim edge\n place(\n bottom + left,\n dx: offset_icon_dx,\n dy: offset_icon_dy,\n image(\"icon_main.svg\", width: icon_size, height: icon_size),\n )\n }\n}\n\n\n// ============================================================\n// Layer 2c — Spine background\n// ============================================================\n\n// Spine background (only when spread has a spine) — drawn after pattern so it covers it\n#if has_spine and color_spine_bg != none {\n place(\n top + left,\n dx: spine_x,\n dy: 0mm,\n rect(width: spine_width, height: total_height, fill: color_spine_bg),\n )\n}\n\n\n// ============================================================\n// Layer 3 — Back panel content\n// ============================================================\n\n// back_margin, back_content_w imported from _data.typ\n\n// Blurb container box with background color and 1cm padding around the text\n// Content is scaled down if it would overflow the available back-panel height\n#if has_blurb {\n let blurb_content = {\n set par(justify: true) if blurb_justify\n set par(leading: 1em * blurb_spacing)\n // Compact heading styles for a small blurb box — size and leading are hardcoded\n // independent of blurb_spacing, since headings rarely wrap and need tight leading\n show heading.where(level: 1): it => {\n set block(above: 2em, below: 1em)\n set text(size: 1.3em)\n set par(leading: 0.5em)\n it\n }\n show heading.where(level: 2): it => {\n set block(above: 2em, below: 1em)\n set text(size: 1.15em)\n set par(leading: 0.5em)\n it\n }\n align(blurb_alignment, text(\n font: font_blurb_family,\n size: fs_back_blurb * blurb_size,\n fill: color_blurb,\n blurb,\n ))\n }\n let blurb_max_h = back_content_h - blurb_padding * 2\n place(\n top + left,\n dx: back_x + (face_width - blurb_width) / 2,\n dy: bleed + back_margin,\n rect(\n width: blurb_width,\n fill: color_blurb_bg,\n stroke: none,\n radius: 1.5mm,\n inset: blurb_padding,\n context {\n let avail_w = blurb_width - blurb_padding * 2\n let natural = measure(blurb_content, width: avail_w)\n if natural.height > blurb_max_h {\n let ratio = blurb_max_h / natural.height\n scale(x: ratio * 100%, y: ratio * 100%, reflow: true, blurb_content)\n } else {\n blurb_content\n }\n },\n ),\n )\n}\n\n// ISBN barcode — position and size from printing-services cover_region_barcode\n#if has_barcode {\n place(\n bottom + left,\n dx: barcode_x,\n dy: barcode_y * -1,\n image(\"barcode.svg\", width: barcode_w, height: barcode_h),\n )\n}\n\n// ============================================================\n// Layer 4 — Spine content\n// ============================================================\n\n#if has_spine and has_spine_text and fs_spine_title > 0mm {\n // Title rotated along spine (bottom-to-top reading direction).\n // place() anchors the unrotated bounding box top-left, so we subtract\n // half the unrotated dimensions to keep the visual center on the spine.\n place(\n top + left,\n dx: spine_x + spine_width * 0.5 - face_height * 0.4,\n dy: bleed + face_height * 0.5 - spine_width * 0.5,\n rotate(\n 90deg,\n origin: center,\n box(\n width: face_height * 0.8,\n height: spine_width,\n align(\n center + horizon,\n // Shrink title + author together as one unit if they overflow the spine length\n shrink-to-width(\n stack(\n dir: ltr,\n spacing: spine_width * 0.3,\n text(\n font: font_spine_title_family,\n size: fs_spine_title * spine_title_size_mod,\n fill: color_spine_title,\n weight: spine_title_weight,\n style: if spine_title_italic { \"italic\" } else { \"normal\" },\n spine_title,\n ),\n if has_author and fs_spine_author > 0mm {\n text(\n font: font_spine_author_family,\n size: fs_spine_author * spine_author_size_mod,\n fill: color_spine_author,\n weight: spine_author_weight,\n style: if spine_author_italic { \"italic\" } else { \"normal\" },\n spine_author,\n )\n },\n ),\n ),\n ),\n ),\n ),\n )\n}\n\n// Small icon at the bottom of the spine (when spine is wide enough for text)\n#if has_spine_icon {\n let spine_icon_size = spine_width * 0.7\n let spine_icon_side_margin = (spine_width - spine_icon_size) / 2\n // Bottom margin is twice the side margin\n let spine_icon_bottom_margin = spine_icon_side_margin * 2\n place(\n top + left,\n dx: spine_x + spine_icon_side_margin,\n dy: bleed + face_height - spine_icon_size - spine_icon_bottom_margin,\n image(\"icon_spine.svg\", width: spine_icon_size, height: spine_icon_size),\n )\n}\n\n// ============================================================\n// Layer 5 — Front panel content\n// ============================================================\n\n// front_margin, front_content_w imported from _data.typ\n\n// Base font size — proportional to trim height, multiplied by per-title size modifier\n#let fs_title_base = face_height * 0.05\n#let fs_subtitle = face_height * 0.025 * subtitle_size\n#let fs_author = face_height * 0.035 * author_size\n\n// Render a single title line with its per-title styling, shrunk to fit if too wide\n#let render_title(txt, font_family, size_mod, weight, is_italic, color) = {\n if txt != \"\" {\n box(\n width: front_content_w,\n shrink-to-width(\n align(title_alignment, text(\n font: font_family,\n size: fs_title_base * size_mod,\n fill: color,\n weight: weight,\n style: if is_italic { \"italic\" } else { \"normal\" },\n txt,\n )),\n ),\n )\n }\n}\n\n// Render each title line, then filter out empty ones so they don't create stack spacing gaps\n#let title_lines_rendered = stack(\n dir: ttb,\n spacing: face_height * title_spacing / 100,\n ..(\n render_title(title1, font_title1_family, title1_size, title1_weight, title1_italic, color_front_title1),\n render_title(title2, font_title2_family, title2_size, title2_weight, title2_italic, color_front_title2),\n render_title(title3, font_title3_family, title3_size, title3_weight, title3_italic, color_front_title3),\n ).filter(x => x != none),\n)\n\n// All subtitle lines in one container — shrink-to-width scales them all together uniformly\n// text() wraps shrink-to-width so inner content inherits the scaled size (no explicit size inside)\n#let subtitle_lines_rendered = text(\n font: font_subtitle_family,\n size: fs_subtitle,\n weight: subtitle_weight,\n style: if subtitle_italic { \"italic\" } else { \"normal\" },\n fill: color_front_subtitle,\n box(\n width: front_content_w,\n shrink-to-width(\n stack(\n dir: ttb,\n spacing: face_height * subtitle_spacing / 100,\n ..subtitle_lines.map(line => align(subtitle_alignment, line)),\n )\n ),\n ),\n)\n\n// Author styled text — single line, shrunk to fit width\n#let author_rendered = box(\n width: front_content_w,\n shrink-to-width(\n align(author_alignment, text(\n font: font_author_family,\n size: fs_author,\n fill: color_front_author,\n weight: author_weight,\n style: if author_italic { \"italic\" } else { \"normal\" },\n author,\n )),\n ),\n)\n\n// Title group: stacked title lines\n#let title_group = stack(\n dir: ttb,\n spacing: face_height * title_spacing / 100,\n title_lines_rendered,\n)\n\n// Check which elements belong to each position box\n#let top_has_title = title_position == \"top\"\n#let top_has_subtitle = has_subtitle and subtitle_position == \"top\"\n#let top_has_author = has_author and author_position == \"top\"\n#let top_has_any = top_has_title or top_has_subtitle or top_has_author\n\n#let mid_has_title = title_position == \"middle\"\n#let mid_has_subtitle = has_subtitle and subtitle_position == \"middle\"\n#let mid_has_author = has_author and author_position == \"middle\"\n#let mid_has_any = mid_has_title or mid_has_subtitle or mid_has_author\n\n#let bot_has_title = title_position == \"bottom\"\n#let bot_has_subtitle = has_subtitle and subtitle_position == \"bottom\"\n#let bot_has_author = has_author and author_position == \"bottom\"\n#let bot_has_any = bot_has_title or bot_has_subtitle or bot_has_author\n\n// Build a content group for a position box (order within box: title | subtitle | author)\n// Each element gets its own vertical margins inside the box\n#let make_pos_box(has_t, has_s, has_a) = box(\n width: front_content_w,\n stack(\n dir: ttb,\n if has_t { v(face_height * title_margin_top / 100) },\n if has_t { title_group },\n if has_t { v(face_height * title_margin_bottom / 100) },\n if has_s { v(face_height * subtitle_margin_top / 100) },\n if has_s { subtitle_lines_rendered },\n if has_s { v(face_height * subtitle_margin_bottom / 100) },\n if has_a { v(face_height * author_margin_top / 100) },\n if has_a { author_rendered },\n if has_a { v(face_height * author_margin_bottom / 100) },\n ),\n)\n\n// Place each non-empty position box on the front panel (within panel margin on all sides)\n#if top_has_any {\n place(\n top + left,\n dx: front_x + front_margin,\n dy: bleed + front_margin,\n make_pos_box(top_has_title, top_has_subtitle, top_has_author),\n )\n}\n#if mid_has_any {\n place(\n horizon + left,\n dx: front_x + front_margin,\n make_pos_box(mid_has_title, mid_has_subtitle, mid_has_author),\n )\n}\n#if bot_has_any {\n place(\n bottom + left,\n dx: front_x + front_margin,\n dy: (bleed + front_margin) * -1,\n make_pos_box(bot_has_title, bot_has_subtitle, bot_has_author),\n )\n}\n\n\n// ============================================================\n// Layer 6 — Home-print margin (white rounded borders for home inkjet printers)\n// ============================================================\n\n#if home_print {\n // Render the back and front as two separate rounded cards, each with a white\n // border, so a home printer that can't reach the paper edge still prints\n // cleanly. One white matte covers the spread with two rounded windows punched\n // out — one per trim face, each inset by `home_margin` on all sides with\n // `home_corner`-rounded corners. Windows track the trim faces (not a spine-\n // centre split), so the bleed and spine become white gutter and every side —\n // including the inner/gutter edge — gets a real margin. A single matte (vs.\n // two abutting ones) avoids a hairline seam down the gutter.\n let m = home_margin\n let win_w = face_width - 2 * m\n let win_h = face_height - 2 * m\n let win_y = bleed + m\n let back_win = (bleed + m, win_y, win_w, win_h)\n let front_win = (total_width - bleed - face_width + m, win_y, win_w, win_h)\n home-print-matte(total_width, total_height, (back_win, front_win), home_corner)\n}\n",
5
+ cover: "\n// Book cover template — receives all variables from _data.typ\n#import \"_data.typ\": *\n\n// -- Page setup --\n#set page(\n width: total_width,\n height: total_height,\n margin: 0mm,\n)\n\n#set text(font: font_body_family)\n#set quote(block: true)\n#show quote: set pad(x: 3em)\n\n// ============================================================\n// Helper functions\n// ============================================================\n\n#import \"_helpers.typ\": *\n\n\n// ============================================================\n// Layer 1 — Background fills\n// ============================================================\n\n#place(\n top + left,\n rect(\n width: total_width,\n height: total_height,\n fill: if has_front_gradient {\n gradient.linear(\n (color_front_gradient_start, 0%),\n (color_front_bg, 30%),\n (color_front_bg, 45%),\n (color_front_gradient_end, 100%),\n angle: -40deg,\n )\n } else {\n color_front_bg\n },\n ),\n)\n\n\n#if has_pattern {\n let tiled = pattern(\n size: (pattern_tile_w, pattern_tile_h),\n image(\"pattern.svg\", width: pattern_tile_w, height: pattern_tile_h),\n )\n place(\n top + left,\n rect(width: total_width, height: total_height, fill: tiled),\n )\n}\n\n\n// ============================================================\n// Layer 2 — Background image\n// ============================================================\n\n#if has_image {\n if image_coverage == \"full\" {\n // Full spread: image covers entire canvas (back + spine + front)\n place(\n top + left,\n dx: 0mm,\n dy: 0mm,\n image(\n image_filename,\n width: total_width,\n height: total_height,\n fit: if image_is_vector { \"stretch\" } else { \"cover\" },\n ),\n )\n } else if image_coverage == \"painted\" {\n // Painted: image inset on front cover — 15mm margin horizontally and from bottom, 50% trim height\n let painted_w = face_width - 30mm\n let painted_h = face_height * 0.5 // sync: index.ts painted_h = face_height * 0.5\n place(\n top + left,\n dx: front_x + 15mm,\n dy: bleed + face_height * (1/3),\n image(\n image_filename,\n width: painted_w,\n height: painted_h,\n fit: \"stretch\", // Should be exact but just in case, ensures full frame visible\n ),\n )\n } else if image_coverage == \"feature\" {\n // Feature: same size and position as painted but with rounded corners via box clip\n let painted_w = face_width - 30mm\n let painted_h = face_height * 0.5\n place(\n top + left,\n dx: front_x + 15mm,\n dy: bleed + face_height * (1/3),\n box(\n width: painted_w,\n height: painted_h,\n radius: 6mm,\n clip: true,\n image(\n image_filename,\n width: painted_w,\n height: painted_h,\n fit: \"cover\",\n ),\n ),\n )\n } else if image_coverage == \"front_partial\" {\n // Lower 2/3: image covers the full front face width, bottom 2/3 of height, flush to the bottom trim edge\n let w = face_width\n let h = face_height * (2 / 3)\n place(\n top + left,\n dx: front_x,\n dy: bleed + face_height - h,\n image(\n image_filename,\n width: w,\n height: h,\n fit: \"cover\",\n ),\n )\n } else {\n // Front only: image covers the front panel including all bleed margins\n place(\n top + left,\n dx: front_x,\n dy: 0mm,\n image(\n image_filename,\n width: face_width + bleed,\n height: total_height,\n fit: if image_is_vector { \"stretch\" } else { \"cover\" },\n ),\n )\n }\n}\n\n\n// ============================================================\n// Layer 2b — Icon on front cover\n// ============================================================\n\n#if has_icon {\n let icon_size = calc.min(face_height * 0.4, face_width * 0.8)\n if icon_mode != \"center\" {\n icon_size = calc.min(face_height * 0.5, face_width * 0.8)\n }\n // Apply user-controlled size multiplier (1.0 = default)\n icon_size = icon_size * icon_size_mod\n let offset_icon_dx = front_x + (face_width - icon_size) + (icon_size * 0.15)\n let offset_icon_dy = bleed * -1 + (icon_size * 0.15)\n\n // Place ghost icons first (if any) so below main one\n if icon_mode == \"echo\" {\n place(\n bottom + left,\n dx: offset_icon_dx + icon_size * 0.1,\n dy: offset_icon_dy - icon_size * 0.15,\n image(\"icon_ghost.svg\", width: icon_size, height: icon_size),\n )\n place(\n bottom + left,\n dx: offset_icon_dx - icon_size * 0.1,\n dy: offset_icon_dy + icon_size * 0.1,\n image(\"icon_ghost2.svg\", width: icon_size, height: icon_size),\n )\n }\n\n if icon_mode == \"center\" {\n // Center — large icon centered horizontally, positioned in the lower/mid section\n place(\n bottom + left,\n dx: front_x + (face_width - icon_size) / 2,\n dy: (face_height * 0.2 + bleed) * -1,\n image(\"icon_main.svg\", width: icon_size, height: icon_size),\n )\n } else {\n // Offset/echo: large icon offset towards bottom-right, slightly overflowing the trim edge\n place(\n bottom + left,\n dx: offset_icon_dx,\n dy: offset_icon_dy,\n image(\"icon_main.svg\", width: icon_size, height: icon_size),\n )\n }\n}\n\n\n// ============================================================\n// Layer 2c — Spine background\n// ============================================================\n\n// Spine background (only when spread has a spine) — drawn after pattern so it covers it\n#if has_spine and color_spine_bg != none {\n place(\n top + left,\n dx: spine_x,\n dy: 0mm,\n rect(width: spine_width, height: total_height, fill: color_spine_bg),\n )\n}\n\n\n// ============================================================\n// Layer 3 — Back panel content\n// ============================================================\n\n// back_margin, back_content_w, spiral_margin imported from _data.typ\n\n// Blurb container box with background color and 1cm padding around the text\n// Content is scaled down if it would overflow the available back-panel height\n#if has_blurb {\n let blurb_content = {\n set par(justify: true) if blurb_justify\n set par(leading: 1em * blurb_spacing)\n // Compact heading styles for a small blurb box — size and leading are hardcoded\n // independent of blurb_spacing, since headings rarely wrap and need tight leading\n show heading.where(level: 1): it => {\n set block(above: 2em, below: 1em)\n set text(size: 1.3em)\n set par(leading: 0.5em)\n it\n }\n show heading.where(level: 2): it => {\n set block(above: 2em, below: 1em)\n set text(size: 1.15em)\n set par(leading: 0.5em)\n it\n }\n align(blurb_alignment, text(\n font: font_blurb_family,\n size: fs_back_blurb * blurb_size,\n fill: color_blurb,\n blurb,\n ))\n }\n let blurb_max_h = back_content_h - blurb_padding * 2\n place(\n top + left,\n // Centered within the safe area, not the full face — spiral_margin is 0 for other bindings\n dx: back_x + (face_width - spiral_margin - blurb_width) / 2,\n dy: bleed + back_margin,\n rect(\n width: blurb_width,\n fill: color_blurb_bg,\n stroke: none,\n radius: 1.5mm,\n inset: blurb_padding,\n context {\n let avail_w = blurb_width - blurb_padding * 2\n let natural = measure(blurb_content, width: avail_w)\n if natural.height > blurb_max_h {\n let ratio = blurb_max_h / natural.height\n scale(x: ratio * 100%, y: ratio * 100%, reflow: true, blurb_content)\n } else {\n blurb_content\n }\n },\n ),\n )\n}\n\n// ISBN barcode — position and size from printing-services cover_region_barcode\n#if has_barcode {\n place(\n bottom + left,\n dx: barcode_x,\n dy: barcode_y * -1,\n image(\"barcode.svg\", width: barcode_w, height: barcode_h),\n )\n}\n\n// ============================================================\n// Layer 4 — Spine content\n// ============================================================\n\n#if has_spine and has_spine_text and fs_spine_title > 0mm {\n // Title rotated along spine (bottom-to-top reading direction).\n // place() anchors the unrotated bounding box top-left, so we subtract\n // half the unrotated dimensions to keep the visual center on the spine.\n place(\n top + left,\n dx: spine_x + spine_width * 0.5 - face_height * 0.4,\n dy: bleed + face_height * 0.5 - spine_width * 0.5,\n rotate(\n 90deg,\n origin: center,\n box(\n width: face_height * 0.8,\n height: spine_width,\n align(\n center + horizon,\n // Shrink title + author together as one unit if they overflow the spine length\n shrink-to-width(\n stack(\n dir: ltr,\n spacing: spine_width * 0.3,\n text(\n font: font_spine_title_family,\n size: fs_spine_title * spine_title_size_mod,\n fill: color_spine_title,\n weight: spine_title_weight,\n style: if spine_title_italic { \"italic\" } else { \"normal\" },\n spine_title,\n ),\n if has_author and fs_spine_author > 0mm {\n text(\n font: font_spine_author_family,\n size: fs_spine_author * spine_author_size_mod,\n fill: color_spine_author,\n weight: spine_author_weight,\n style: if spine_author_italic { \"italic\" } else { \"normal\" },\n spine_author,\n )\n },\n ),\n ),\n ),\n ),\n ),\n )\n}\n\n// Small icon at the bottom of the spine (when spine is wide enough for text)\n#if has_spine_icon {\n let spine_icon_size = spine_width * 0.7\n let spine_icon_side_margin = (spine_width - spine_icon_size) / 2\n // Bottom margin is twice the side margin\n let spine_icon_bottom_margin = spine_icon_side_margin * 2\n place(\n top + left,\n dx: spine_x + spine_icon_side_margin,\n dy: bleed + face_height - spine_icon_size - spine_icon_bottom_margin,\n image(\"icon_spine.svg\", width: spine_icon_size, height: spine_icon_size),\n )\n}\n\n// ============================================================\n// Layer 5 — Front panel content\n// ============================================================\n\n// front_margin, front_content_w, spiral_margin imported from _data.typ\n\n// Base font size — proportional to trim height, multiplied by per-title size modifier\n#let fs_title_base = face_height * 0.05\n#let fs_subtitle = face_height * 0.025 * subtitle_size\n#let fs_author = face_height * 0.035 * author_size\n\n// Render a single title line with its per-title styling, shrunk to fit if too wide\n#let render_title(txt, font_family, size_mod, weight, is_italic, color) = {\n if txt != \"\" {\n box(\n width: front_content_w,\n shrink-to-width(\n align(title_alignment, text(\n font: font_family,\n size: fs_title_base * size_mod,\n fill: color,\n weight: weight,\n style: if is_italic { \"italic\" } else { \"normal\" },\n txt,\n )),\n ),\n )\n }\n}\n\n// Render each title line, then filter out empty ones so they don't create stack spacing gaps\n#let title_lines_rendered = stack(\n dir: ttb,\n spacing: face_height * title_spacing / 100,\n ..(\n render_title(title1, font_title1_family, title1_size, title1_weight, title1_italic, color_front_title1),\n render_title(title2, font_title2_family, title2_size, title2_weight, title2_italic, color_front_title2),\n render_title(title3, font_title3_family, title3_size, title3_weight, title3_italic, color_front_title3),\n ).filter(x => x != none),\n)\n\n// All subtitle lines in one container — shrink-to-width scales them all together uniformly\n// text() wraps shrink-to-width so inner content inherits the scaled size (no explicit size inside)\n#let subtitle_lines_rendered = text(\n font: font_subtitle_family,\n size: fs_subtitle,\n weight: subtitle_weight,\n style: if subtitle_italic { \"italic\" } else { \"normal\" },\n fill: color_front_subtitle,\n box(\n width: front_content_w,\n shrink-to-width(\n stack(\n dir: ttb,\n spacing: face_height * subtitle_spacing / 100,\n ..subtitle_lines.map(line => align(subtitle_alignment, line)),\n )\n ),\n ),\n)\n\n// Author styled text — single line, shrunk to fit width\n#let author_rendered = box(\n width: front_content_w,\n shrink-to-width(\n align(author_alignment, text(\n font: font_author_family,\n size: fs_author,\n fill: color_front_author,\n weight: author_weight,\n style: if author_italic { \"italic\" } else { \"normal\" },\n author,\n )),\n ),\n)\n\n// Title group: stacked title lines\n#let title_group = stack(\n dir: ttb,\n spacing: face_height * title_spacing / 100,\n title_lines_rendered,\n)\n\n// Check which elements belong to each position box\n#let top_has_title = title_position == \"top\"\n#let top_has_subtitle = has_subtitle and subtitle_position == \"top\"\n#let top_has_author = has_author and author_position == \"top\"\n#let top_has_any = top_has_title or top_has_subtitle or top_has_author\n\n#let mid_has_title = title_position == \"middle\"\n#let mid_has_subtitle = has_subtitle and subtitle_position == \"middle\"\n#let mid_has_author = has_author and author_position == \"middle\"\n#let mid_has_any = mid_has_title or mid_has_subtitle or mid_has_author\n\n#let bot_has_title = title_position == \"bottom\"\n#let bot_has_subtitle = has_subtitle and subtitle_position == \"bottom\"\n#let bot_has_author = has_author and author_position == \"bottom\"\n#let bot_has_any = bot_has_title or bot_has_subtitle or bot_has_author\n\n// Build a content group for a position box (order within box: title | subtitle | author)\n// Each element gets its own vertical margins inside the box\n#let make_pos_box(has_t, has_s, has_a) = box(\n width: front_content_w,\n stack(\n dir: ttb,\n if has_t { v(face_height * title_margin_top / 100) },\n if has_t { title_group },\n if has_t { v(face_height * title_margin_bottom / 100) },\n if has_s { v(face_height * subtitle_margin_top / 100) },\n if has_s { subtitle_lines_rendered },\n if has_s { v(face_height * subtitle_margin_bottom / 100) },\n if has_a { v(face_height * author_margin_top / 100) },\n if has_a { author_rendered },\n if has_a { v(face_height * author_margin_bottom / 100) },\n ),\n)\n\n// Place each non-empty position box on the front panel (within panel margin on all sides)\n#if top_has_any {\n place(\n top + left,\n dx: front_x + front_margin + spiral_margin,\n dy: bleed + front_margin,\n make_pos_box(top_has_title, top_has_subtitle, top_has_author),\n )\n}\n#if mid_has_any {\n place(\n horizon + left,\n dx: front_x + front_margin + spiral_margin,\n make_pos_box(mid_has_title, mid_has_subtitle, mid_has_author),\n )\n}\n#if bot_has_any {\n place(\n bottom + left,\n dx: front_x + front_margin + spiral_margin,\n dy: (bleed + front_margin) * -1,\n make_pos_box(bot_has_title, bot_has_subtitle, bot_has_author),\n )\n}\n\n\n// ============================================================\n// Layer 6 — Home-print margin (white rounded borders for home inkjet printers)\n// ============================================================\n\n#if home_print {\n // Render the back and front as two separate rounded cards, each with a white\n // border, so a home printer that can't reach the paper edge still prints\n // cleanly. One white matte covers the spread with two rounded windows punched\n // out — one per trim face, each inset by `home_margin` on all sides with\n // `home_corner`-rounded corners. Windows track the trim faces (not a spine-\n // centre split), so the bleed and spine become white gutter and every side —\n // including the inner/gutter edge — gets a real margin. A single matte (vs.\n // two abutting ones) avoids a hairline seam down the gutter.\n let m = home_margin\n let win_w = face_width - 2 * m\n let win_h = face_height - 2 * m\n let win_y = bleed + m\n let back_win = (bleed + m, win_y, win_w, win_h)\n let front_win = (total_width - bleed - face_width + m, win_y, win_w, win_h)\n home-print-matte(total_width, total_height, (back_win, front_win), home_corner)\n}\n",
6
6
  helpers: "\n// Shared helper functions for the cover template\n\n// Scale content so it fits within the container width using geometric scaling.\n// Uses scale() rather than text(size: ...) so inner explicit font sizes are also scaled.\n#let fit-to-width(max-text-size: auto, min-text-size: 2mm, it) = context {\n let effective-max = if max-text-size == auto { text.size * 3 } else { max-text-size }\n let effective-min = if min-text-size == auto { text.size * 0.33 } else { min-text-size }\n // Express size limits as dimensionless ratios relative to current text size\n let max-ratio = effective-max / text.size\n let min-ratio = effective-min / text.size\n let contentsize = measure(it)\n layout(size => {\n if contentsize.width > 0mm {\n let ratio = calc.min(size.width / contentsize.width, size.height / contentsize.height)\n let clamped = calc.max(calc.min(ratio, max-ratio), min-ratio)\n if clamped != 1.0 {\n scale(x: clamped * 100%, y: clamped * 100%, reflow: true, it)\n } else {\n it\n }\n }\n })\n}\n\n// Variant that only shrinks (never grows beyond current text size)\n#let shrink-to-width(min-text-size: auto, it) = context {\n fit-to-width(max-text-size: text.size, min-text-size: min-text-size, it)\n}\n\n// Curve segments tracing one rounded rectangle (absolute coords), for compositing\n// into a larger path. Corners use a cubic-bezier handle (0.5523r) to approximate arcs.\n#let rounded-subpath(x, y, w, h, r) = {\n let k = 0.5523 * r\n (\n curve.move((x + r, y), relative: false),\n curve.line((x + w - r, y), relative: false),\n curve.cubic((x + w - r + k, y), (x + w, y + r - k), (x + w, y + r), relative: false),\n curve.line((x + w, y + h - r), relative: false),\n curve.cubic((x + w, y + h - r + k), (x + w - r + k, y + h), (x + w - r, y + h), relative: false),\n curve.line((x + r, y + h), relative: false),\n curve.cubic((x + r - k, y + h), (x, y + h - r + k), (x, y + h - r), relative: false),\n curve.line((x, y + r), relative: false),\n curve.cubic((x, y + r - k), (x + r - k, y), (x + r, y), relative: false),\n curve.close(),\n )\n}\n\n// White \"matte\" covering the whole canvas (cw × ch) with rounded-rect windows\n// punched out via even-odd fill, so the artwork shows through each window as a\n// rounded card surrounded by a white border. Used for home-print margins on\n// edge-to-edge-incapable printers. `windows` is an array of (x, y, w, h) tuples.\n#let home-print-matte(cw, ch, windows, r) = {\n let segs = (\n curve.move((0mm, 0mm), relative: false),\n curve.line((cw, 0mm), relative: false),\n curve.line((cw, ch), relative: false),\n curve.line((0mm, ch), relative: false),\n curve.close(),\n )\n for win in windows {\n segs = segs + rounded-subpath(win.at(0), win.at(1), win.at(2), win.at(3), r)\n }\n place(top + left, curve(fill: white, fill-rule: \"even-odd\", ..segs))\n}\n",
7
7
  };
8
8
  //# sourceMappingURL=templates_data.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"templates_data.js","sourceRoot":"","sources":["../../src/generated/templates_data.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,qEAAqE;AAIrE,8EAA8E;AAC9E,MAAM,CAAC,MAAM,iBAAiB,GAAa;IACvC,KAAK,EAAE,qjkBAAqjkB;IAC5jkB,OAAO,EAAE,ygGAAygG;CACrhG,CAAA"}
1
+ {"version":3,"file":"templates_data.js","sourceRoot":"","sources":["../../src/generated/templates_data.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,qEAAqE;AAIrE,8EAA8E;AAC9E,MAAM,CAAC,MAAM,iBAAiB,GAAa;IACvC,KAAK,EAAE,wvkBAAwvkB;IAC/vkB,OAAO,EAAE,ygGAAygG;CACrhG,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"vector_bg_images_data.d.ts","sourceRoot":"","sources":["../../src/generated/vector_bg_images_data.ts"],"names":[],"mappings":"AAGA;qGACqG;AACrG,eAAO,MAAM,oBAAoB,EAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CA2BtD,CAAA"}
1
+ {"version":3,"file":"vector_bg_images_data.d.ts","sourceRoot":"","sources":["../../src/generated/vector_bg_images_data.ts"],"names":[],"mappings":"AAGA;qGACqG;AACrG,eAAO,MAAM,oBAAoB,EAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CA4BtD,CAAA"}