@cardanvil/frame-kit 0.6.0 → 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 (66) hide show
  1. package/README.md +75 -1
  2. package/dist/authoring/collectorInfo.d.ts +17 -0
  3. package/dist/authoring/collectorInfo.js +62 -0
  4. package/dist/authoring/index.d.ts +2 -0
  5. package/dist/authoring/index.js +2 -0
  6. package/dist/authoring/layoutDefaults.d.ts +20 -2
  7. package/dist/authoring/layoutDefaults.js +48 -13
  8. package/dist/authoring/opacityStops.d.ts +8 -0
  9. package/dist/authoring/opacityStops.js +17 -0
  10. package/dist/cli/index.d.ts +1 -0
  11. package/dist/cli/index.js +1 -0
  12. package/dist/cli/loadFrame.js +1 -1
  13. package/dist/cli/main.js +72 -0
  14. package/dist/cli/studio/boxSets.js +11 -8
  15. package/dist/cli/studio/serve.js +4 -1
  16. package/dist/cli/trim.d.ts +62 -0
  17. package/dist/cli/trim.js +251 -0
  18. package/dist/layout/border.d.ts +27 -0
  19. package/dist/layout/border.js +31 -0
  20. package/dist/layout/boxOverrides.d.ts +28 -0
  21. package/dist/layout/boxOverrides.js +95 -0
  22. package/dist/layout/color.d.ts +23 -0
  23. package/dist/layout/color.js +53 -0
  24. package/dist/layout/cutouts.d.ts +28 -0
  25. package/dist/layout/cutouts.js +29 -0
  26. package/dist/layout/decorations.d.ts +1 -6
  27. package/dist/layout/decorations.js +7 -11
  28. package/dist/layout/frameLayers.d.ts +29 -2
  29. package/dist/layout/frameLayers.js +145 -3
  30. package/dist/layout/index.d.ts +17 -6
  31. package/dist/layout/index.js +17 -6
  32. package/dist/layout/paint.d.ts +36 -0
  33. package/dist/layout/paint.js +148 -0
  34. package/dist/layout/powerToughnessRow.d.ts +26 -0
  35. package/dist/layout/powerToughnessRow.js +13 -0
  36. package/dist/layout/resolveFrameAsset.d.ts +6 -0
  37. package/dist/layout/resolveFrameAsset.js +8 -0
  38. package/dist/layout/rulesSlide.d.ts +13 -0
  39. package/dist/layout/rulesSlide.js +33 -0
  40. package/dist/layout/settings.d.ts +26 -0
  41. package/dist/layout/settings.js +62 -0
  42. package/dist/layout/textStyle.d.ts +29 -0
  43. package/dist/layout/textStyle.js +28 -0
  44. package/dist/layout/turn.d.ts +54 -0
  45. package/dist/layout/turn.js +107 -0
  46. package/dist/manifest/contract.d.ts +6074 -9392
  47. package/dist/manifest/contract.js +46 -1
  48. package/dist/manifest/testFrame.js +9 -0
  49. package/dist/schema/collectorInfo.d.ts +95 -0
  50. package/dist/schema/collectorInfo.js +80 -0
  51. package/dist/schema/condition.d.ts +67 -0
  52. package/dist/schema/condition.js +47 -0
  53. package/dist/schema/frame.d.ts +17291 -25435
  54. package/dist/schema/frame.js +402 -14
  55. package/dist/schema/frameAssets.d.ts +53 -33
  56. package/dist/schema/frameAssets.js +77 -5
  57. package/dist/schema/index.d.ts +3 -0
  58. package/dist/schema/index.js +3 -0
  59. package/dist/schema/layout.d.ts +29 -4
  60. package/dist/schema/layout.js +27 -2
  61. package/dist/schema/paint.d.ts +202 -0
  62. package/dist/schema/paint.js +109 -0
  63. package/dist/studio/assets/index-BnhNwm_r.js +74 -0
  64. package/dist/studio/index.html +1 -1
  65. package/package.json +5 -3
  66. package/dist/studio/assets/index-CNzyV22G.js +0 -76
@@ -12,8 +12,12 @@ export declare const SCRYFALL_LAYOUTS: readonly ["normal", "split", "flip", "tra
12
12
  export type ScryfallLayoutName = (typeof SCRYFALL_LAYOUTS)[number];
13
13
  /**
14
14
  * Card shapes that Scryfall does not distinguish but frames must: a
15
- * planeswalker and a Case are `normal`/`class` to Scryfall, yet need their own
16
- * box geometry. Crossed with every Scryfall layout to form `<layout>_<special>`.
15
+ * planeswalker is `normal` to Scryfall, yet needs its own box geometry. Crossed
16
+ * with every Scryfall layout to form `<layout>_<special>`.
17
+ *
18
+ * `case` is here for compatibility only. Scryfall files a Case under a `case`
19
+ * layout of its own (see `additionalLayouts`), and Card Anvil keys every Case
20
+ * card there, so a `<layout>_case` key is never looked up.
17
21
  */
18
22
  export declare const specialLayouts: readonly ["planeswalker", "case"];
19
23
  /**
@@ -21,9 +25,26 @@ export declare const specialLayouts: readonly ["planeswalker", "case"];
21
25
  * `saga_transform`/`saga_transform_creature` cover a transform DFC where EITHER
22
26
  * face is a Saga — which physical side it is on varies per card and is resolved
23
27
  * at render time, not by separate keys.
28
+ *
29
+ * `omen` and `prepare` join `adventure` as the layouts that print a second
30
+ * spell inset in the text box (see the `inset*` boxes in `frame.ts`). Scryfall
31
+ * files Omen cards under `adventure`, told apart only by the inset's type
32
+ * line, but their frame art differs, so frames key them separately. `prepare`
33
+ * is a layout Scryfall does return; it is listed here rather than in
34
+ * `SCRYFALL_LAYOUTS` because `@scryfall/api-types` predates it, and that list
35
+ * has to match the package.
36
+ *
37
+ * `aftermath` is to `split` what `omen` is to `adventure`: Scryfall files an
38
+ * aftermath card under `split`, told apart only by its Aftermath keyword, but
39
+ * it prints its first half upright and its second sideways where a split card
40
+ * turns both.
41
+ *
42
+ * `case` is a layout Scryfall returns for Case cards (e.g. Case of the Burning
43
+ * Masks), listed here rather than in `SCRYFALL_LAYOUTS` for the reason
44
+ * `prepare` is: `@scryfall/api-types` predates it.
24
45
  */
25
- export declare const additionalLayouts: readonly ["saga_creature", "saga_transform", "saga_transform_creature"];
26
- export declare const layouts: readonly ["normal", "split", "flip", "transform", "modal_dfc", "meld", "leveler", "class", "saga", "adventure", "mutate", "prototype", "battle", "planar", "scheme", "vanguard", "token", "double_faced_token", "emblem", "augment", "host", "art_series", "reversible_card", ...("transform_planeswalker" | "transform_case" | "saga_planeswalker" | "saga_case" | "normal_planeswalker" | "normal_case" | "split_planeswalker" | "split_case" | "flip_planeswalker" | "flip_case" | "modal_dfc_planeswalker" | "modal_dfc_case" | "meld_planeswalker" | "meld_case" | "leveler_planeswalker" | "leveler_case" | "class_planeswalker" | "class_case" | "adventure_planeswalker" | "adventure_case" | "mutate_planeswalker" | "mutate_case" | "prototype_planeswalker" | "prototype_case" | "battle_planeswalker" | "battle_case" | "planar_planeswalker" | "planar_case" | "scheme_planeswalker" | "scheme_case" | "vanguard_planeswalker" | "vanguard_case" | "token_planeswalker" | "token_case" | "double_faced_token_planeswalker" | "double_faced_token_case" | "emblem_planeswalker" | "emblem_case" | "augment_planeswalker" | "augment_case" | "host_planeswalker" | "host_case" | "art_series_planeswalker" | "art_series_case" | "reversible_card_planeswalker" | "reversible_card_case")[], "saga_creature", "saga_transform", "saga_transform_creature"];
46
+ export declare const additionalLayouts: readonly ["saga_creature", "saga_transform", "saga_transform_creature", "omen", "prepare", "aftermath", "case"];
47
+ export declare const layouts: readonly ["normal", "split", "flip", "transform", "modal_dfc", "meld", "leveler", "class", "saga", "adventure", "mutate", "prototype", "battle", "planar", "scheme", "vanguard", "token", "double_faced_token", "emblem", "augment", "host", "art_series", "reversible_card", ...("transform_planeswalker" | "transform_case" | "saga_planeswalker" | "saga_case" | "normal_planeswalker" | "normal_case" | "split_planeswalker" | "split_case" | "flip_planeswalker" | "flip_case" | "modal_dfc_planeswalker" | "modal_dfc_case" | "meld_planeswalker" | "meld_case" | "leveler_planeswalker" | "leveler_case" | "class_planeswalker" | "class_case" | "adventure_planeswalker" | "adventure_case" | "mutate_planeswalker" | "mutate_case" | "prototype_planeswalker" | "prototype_case" | "battle_planeswalker" | "battle_case" | "planar_planeswalker" | "planar_case" | "scheme_planeswalker" | "scheme_case" | "vanguard_planeswalker" | "vanguard_case" | "token_planeswalker" | "token_case" | "double_faced_token_planeswalker" | "double_faced_token_case" | "emblem_planeswalker" | "emblem_case" | "augment_planeswalker" | "augment_case" | "host_planeswalker" | "host_case" | "art_series_planeswalker" | "art_series_case" | "reversible_card_planeswalker" | "reversible_card_case")[], "saga_creature", "saga_transform", "saga_transform_creature", "omen", "prepare", "aftermath", "case"];
27
48
  export declare const LayoutEnum: z.ZodEnum<{
28
49
  transform: "transform";
29
50
  saga: "saga";
@@ -48,6 +69,7 @@ export declare const LayoutEnum: z.ZodEnum<{
48
69
  host: "host";
49
70
  art_series: "art_series";
50
71
  reversible_card: "reversible_card";
72
+ case: "case";
51
73
  transform_planeswalker: "transform_planeswalker";
52
74
  transform_case: "transform_case";
53
75
  saga_planeswalker: "saga_planeswalker";
@@ -97,5 +119,8 @@ export declare const LayoutEnum: z.ZodEnum<{
97
119
  saga_creature: "saga_creature";
98
120
  saga_transform: "saga_transform";
99
121
  saga_transform_creature: "saga_transform_creature";
122
+ omen: "omen";
123
+ prepare: "prepare";
124
+ aftermath: "aftermath";
100
125
  }>;
101
126
  export type Layout = z.infer<typeof LayoutEnum>;
@@ -35,8 +35,12 @@ export const SCRYFALL_LAYOUTS = [
35
35
  ];
36
36
  /**
37
37
  * Card shapes that Scryfall does not distinguish but frames must: a
38
- * planeswalker and a Case are `normal`/`class` to Scryfall, yet need their own
39
- * box geometry. Crossed with every Scryfall layout to form `<layout>_<special>`.
38
+ * planeswalker is `normal` to Scryfall, yet needs its own box geometry. Crossed
39
+ * with every Scryfall layout to form `<layout>_<special>`.
40
+ *
41
+ * `case` is here for compatibility only. Scryfall files a Case under a `case`
42
+ * layout of its own (see `additionalLayouts`), and Card Anvil keys every Case
43
+ * card there, so a `<layout>_case` key is never looked up.
40
44
  */
41
45
  export const specialLayouts = ["planeswalker", "case"];
42
46
  const customLayouts = SCRYFALL_LAYOUTS.flatMap((layout) => specialLayouts.map((special) => `${layout}_${special}`));
@@ -45,11 +49,32 @@ const customLayouts = SCRYFALL_LAYOUTS.flatMap((layout) => specialLayouts.map((s
45
49
  * `saga_transform`/`saga_transform_creature` cover a transform DFC where EITHER
46
50
  * face is a Saga — which physical side it is on varies per card and is resolved
47
51
  * at render time, not by separate keys.
52
+ *
53
+ * `omen` and `prepare` join `adventure` as the layouts that print a second
54
+ * spell inset in the text box (see the `inset*` boxes in `frame.ts`). Scryfall
55
+ * files Omen cards under `adventure`, told apart only by the inset's type
56
+ * line, but their frame art differs, so frames key them separately. `prepare`
57
+ * is a layout Scryfall does return; it is listed here rather than in
58
+ * `SCRYFALL_LAYOUTS` because `@scryfall/api-types` predates it, and that list
59
+ * has to match the package.
60
+ *
61
+ * `aftermath` is to `split` what `omen` is to `adventure`: Scryfall files an
62
+ * aftermath card under `split`, told apart only by its Aftermath keyword, but
63
+ * it prints its first half upright and its second sideways where a split card
64
+ * turns both.
65
+ *
66
+ * `case` is a layout Scryfall returns for Case cards (e.g. Case of the Burning
67
+ * Masks), listed here rather than in `SCRYFALL_LAYOUTS` for the reason
68
+ * `prepare` is: `@scryfall/api-types` predates it.
48
69
  */
49
70
  export const additionalLayouts = [
50
71
  "saga_creature",
51
72
  "saga_transform",
52
73
  "saga_transform_creature",
74
+ "omen",
75
+ "prepare",
76
+ "aftermath",
77
+ "case",
53
78
  ];
54
79
  export const layouts = [
55
80
  ...SCRYFALL_LAYOUTS,
@@ -0,0 +1,202 @@
1
+ import { z } from "zod";
2
+ /** A color written `#rrggbb`. */
3
+ export declare const HexColorSchema: z.ZodString;
4
+ /**
5
+ * A color taken from the card: its pinline colors, each looked up in
6
+ * `palette`.
7
+ *
8
+ * A card with one pinline color paints that color. A two-color card paints
9
+ * its two colors in their printed order (`WU`, `GW`…), and a three-color card
10
+ * its three (`GWU`, `URW`…) — but only up to `maxParts`; a card with more
11
+ * colors than that paints the palette's gold (`m`). Artifact, colorless, land
12
+ * and vehicle pinlines paint `a`, `c`, `l` and `v`. A color the palette leaves
13
+ * out paints nothing.
14
+ */
15
+ export declare const CardColorsSchema: z.ZodObject<{
16
+ palette: z.ZodRecord<z.ZodEnum<{
17
+ a: "a";
18
+ w: "w";
19
+ u: "u";
20
+ b: "b";
21
+ r: "r";
22
+ g: "g";
23
+ m: "m";
24
+ c: "c";
25
+ l: "l";
26
+ v: "v";
27
+ }> & z.core.$partial, z.ZodString>;
28
+ maxParts: z.ZodOptional<z.ZodNumber>;
29
+ }, z.core.$strip>;
30
+ export type CardColors = z.infer<typeof CardColorsSchema>;
31
+ /** One opacity along a gradient: `opacity` (0–1) at `at` (0 start, 1 end). */
32
+ export declare const OpacityStopSchema: z.ZodObject<{
33
+ at: z.ZodNumber;
34
+ opacity: z.ZodNumber;
35
+ }, z.core.$strip>;
36
+ export type OpacityStop = z.infer<typeof OpacityStopSchema>;
37
+ /**
38
+ * The boxes a `rulesSlide` can move, and a gradient can be anchored to: the
39
+ * header above the rules text.
40
+ */
41
+ export declare const SlidingBoxEnum: z.ZodEnum<{
42
+ type: "type";
43
+ title: "title";
44
+ mana: "mana";
45
+ setSymbol: "setSymbol";
46
+ }>;
47
+ export type SlidingBox = z.infer<typeof SlidingBoxEnum>;
48
+ /**
49
+ * A rectangle painted on the sheet over the art and under the text, its
50
+ * opacity changing along `direction` by `opacity`, which needs at least two
51
+ * stops. Drawn in the order listed, above the frame art.
52
+ *
53
+ * `color` is one color, or the card's: for those, the rectangle is split
54
+ * along `direction` into one equal part per color, blended over `blend` px
55
+ * where two meet.
56
+ */
57
+ export declare const GradientSchema: z.ZodObject<{
58
+ x: z.ZodNumber;
59
+ y: z.ZodNumber;
60
+ width: z.ZodNumber;
61
+ height: z.ZodNumber;
62
+ direction: z.ZodOptional<z.ZodEnum<{
63
+ vertical: "vertical";
64
+ horizontal: "horizontal";
65
+ }>>;
66
+ color: z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
67
+ palette: z.ZodRecord<z.ZodEnum<{
68
+ a: "a";
69
+ w: "w";
70
+ u: "u";
71
+ b: "b";
72
+ r: "r";
73
+ g: "g";
74
+ m: "m";
75
+ c: "c";
76
+ l: "l";
77
+ v: "v";
78
+ }> & z.core.$partial, z.ZodString>;
79
+ maxParts: z.ZodOptional<z.ZodNumber>;
80
+ blend: z.ZodOptional<z.ZodNumber>;
81
+ }, z.core.$strip>]>;
82
+ opacity: z.ZodReadonly<z.ZodArray<z.ZodObject<{
83
+ at: z.ZodNumber;
84
+ opacity: z.ZodNumber;
85
+ }, z.core.$strip>>>;
86
+ anchor: z.ZodOptional<z.ZodEnum<{
87
+ type: "type";
88
+ title: "title";
89
+ mana: "mana";
90
+ setSymbol: "setSymbol";
91
+ }>>;
92
+ slides: z.ZodOptional<z.ZodBoolean>;
93
+ when: z.ZodOptional<z.ZodObject<{
94
+ border: z.ZodOptional<z.ZodUnion<readonly [z.ZodEnum<{
95
+ default: "default";
96
+ light: "light";
97
+ dark: "dark";
98
+ none: "none";
99
+ }>, z.ZodReadonly<z.ZodArray<z.ZodEnum<{
100
+ default: "default";
101
+ light: "light";
102
+ dark: "dark";
103
+ none: "none";
104
+ }>>>]>>;
105
+ settings: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodBoolean, z.ZodString, z.ZodNumber]>>>;
106
+ frameColor: z.ZodOptional<z.ZodUnion<readonly [z.ZodEnum<{
107
+ a: "a";
108
+ w: "w";
109
+ u: "u";
110
+ b: "b";
111
+ r: "r";
112
+ g: "g";
113
+ m: "m";
114
+ c: "c";
115
+ l: "l";
116
+ v: "v";
117
+ }>, z.ZodReadonly<z.ZodArray<z.ZodEnum<{
118
+ a: "a";
119
+ w: "w";
120
+ u: "u";
121
+ b: "b";
122
+ r: "r";
123
+ g: "g";
124
+ m: "m";
125
+ c: "c";
126
+ l: "l";
127
+ v: "v";
128
+ }>>>]>>;
129
+ }, z.core.$strip>>;
130
+ }, z.core.$strip>;
131
+ export type Gradient = z.infer<typeof GradientSchema>;
132
+ /**
133
+ * An image whose own color is replaced, keeping its alpha — a shape the frame
134
+ * ships once and paints per card. Drawn at its own size unless given one.
135
+ */
136
+ export declare const TintedImageSchema: z.ZodObject<{
137
+ src: z.ZodString;
138
+ x: z.ZodNumber;
139
+ y: z.ZodNumber;
140
+ width: z.ZodOptional<z.ZodNumber>;
141
+ height: z.ZodOptional<z.ZodNumber>;
142
+ color: z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
143
+ palette: z.ZodRecord<z.ZodEnum<{
144
+ a: "a";
145
+ w: "w";
146
+ u: "u";
147
+ b: "b";
148
+ r: "r";
149
+ g: "g";
150
+ m: "m";
151
+ c: "c";
152
+ l: "l";
153
+ v: "v";
154
+ }> & z.core.$partial, z.ZodString>;
155
+ maxParts: z.ZodOptional<z.ZodNumber>;
156
+ part: z.ZodOptional<z.ZodNumber>;
157
+ }, z.core.$strip>]>;
158
+ showWith: z.ZodOptional<z.ZodLiteral<"powerToughness">>;
159
+ ptSlot: z.ZodOptional<z.ZodEnum<{
160
+ power: "power";
161
+ toughness: "toughness";
162
+ }>>;
163
+ slides: z.ZodOptional<z.ZodBoolean>;
164
+ when: z.ZodOptional<z.ZodObject<{
165
+ border: z.ZodOptional<z.ZodUnion<readonly [z.ZodEnum<{
166
+ default: "default";
167
+ light: "light";
168
+ dark: "dark";
169
+ none: "none";
170
+ }>, z.ZodReadonly<z.ZodArray<z.ZodEnum<{
171
+ default: "default";
172
+ light: "light";
173
+ dark: "dark";
174
+ none: "none";
175
+ }>>>]>>;
176
+ settings: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodBoolean, z.ZodString, z.ZodNumber]>>>;
177
+ frameColor: z.ZodOptional<z.ZodUnion<readonly [z.ZodEnum<{
178
+ a: "a";
179
+ w: "w";
180
+ u: "u";
181
+ b: "b";
182
+ r: "r";
183
+ g: "g";
184
+ m: "m";
185
+ c: "c";
186
+ l: "l";
187
+ v: "v";
188
+ }>, z.ZodReadonly<z.ZodArray<z.ZodEnum<{
189
+ a: "a";
190
+ w: "w";
191
+ u: "u";
192
+ b: "b";
193
+ r: "r";
194
+ g: "g";
195
+ m: "m";
196
+ c: "c";
197
+ l: "l";
198
+ v: "v";
199
+ }>>>]>>;
200
+ }, z.core.$strip>>;
201
+ }, z.core.$strip>;
202
+ export type TintedImage = z.infer<typeof TintedImageSchema>;
@@ -0,0 +1,109 @@
1
+ import { z } from "zod";
2
+ import { AssetUrlSchema } from "./assetUrl.js";
3
+ import { RenderConditionSchema } from "./condition.js";
4
+ import { FrameColorEnum } from "./frameAssets.js";
5
+ /** A color written `#rrggbb`. */
6
+ export const HexColorSchema = z
7
+ .string()
8
+ .regex(/^#[0-9a-fA-F]{6}$/, "expected a color written #rrggbb");
9
+ /**
10
+ * A color taken from the card: its pinline colors, each looked up in
11
+ * `palette`.
12
+ *
13
+ * A card with one pinline color paints that color. A two-color card paints
14
+ * its two colors in their printed order (`WU`, `GW`…), and a three-color card
15
+ * its three (`GWU`, `URW`…) — but only up to `maxParts`; a card with more
16
+ * colors than that paints the palette's gold (`m`). Artifact, colorless, land
17
+ * and vehicle pinlines paint `a`, `c`, `l` and `v`. A color the palette leaves
18
+ * out paints nothing.
19
+ */
20
+ export const CardColorsSchema = z.object({
21
+ palette: z.partialRecord(FrameColorEnum, HexColorSchema),
22
+ /**
23
+ * The most colors painted side by side, 1–3. Defaults to 1, where any
24
+ * multicolor card is gold.
25
+ */
26
+ maxParts: z.number().int().min(1).max(3).optional(),
27
+ });
28
+ /** One opacity along a gradient: `opacity` (0–1) at `at` (0 start, 1 end). */
29
+ export const OpacityStopSchema = z.object({
30
+ at: z.number().min(0).max(1),
31
+ opacity: z.number().min(0).max(1),
32
+ });
33
+ /**
34
+ * The boxes a `rulesSlide` can move, and a gradient can be anchored to: the
35
+ * header above the rules text.
36
+ */
37
+ export const SlidingBoxEnum = z.enum(["mana", "title", "type", "setSymbol"]);
38
+ /**
39
+ * A rectangle painted on the sheet over the art and under the text, its
40
+ * opacity changing along `direction` by `opacity`, which needs at least two
41
+ * stops. Drawn in the order listed, above the frame art.
42
+ *
43
+ * `color` is one color, or the card's: for those, the rectangle is split
44
+ * along `direction` into one equal part per color, blended over `blend` px
45
+ * where two meet.
46
+ */
47
+ export const GradientSchema = z.object({
48
+ x: z.number(),
49
+ y: z.number(),
50
+ width: z.number(),
51
+ height: z.number(),
52
+ /** Which way `opacity` runs: down the sheet, or across it. Defaults to vertical. */
53
+ direction: z.enum(["vertical", "horizontal"]).optional(),
54
+ color: z.union([
55
+ HexColorSchema,
56
+ CardColorsSchema.extend({
57
+ /** Px over which two of the card's colors blend into each other. */
58
+ blend: z.number().nonnegative().optional(),
59
+ }),
60
+ ]),
61
+ opacity: z.array(OpacityStopSchema).min(2).readonly(),
62
+ /**
63
+ * The box the gradient moves with. Its `y` is then measured from that box's
64
+ * top in the boxes the card is drawn with — whichever set the layout picks
65
+ * for it (a planeswalker's by how many abilities it has) and wherever a
66
+ * `rulesSlide` has moved them. Takes the place of `slides`, which it
67
+ * ignores.
68
+ */
69
+ anchor: SlidingBoxEnum.optional(),
70
+ /** Moves with the boxes when the layout's `rulesSlide` moves them. */
71
+ slides: z.boolean().optional(),
72
+ /** Drawn only on a render where this holds, e.g. a template setting's value. */
73
+ when: RenderConditionSchema.optional(),
74
+ });
75
+ /**
76
+ * An image whose own color is replaced, keeping its alpha — a shape the frame
77
+ * ships once and paints per card. Drawn at its own size unless given one.
78
+ */
79
+ export const TintedImageSchema = z.object({
80
+ src: AssetUrlSchema,
81
+ x: z.number(),
82
+ y: z.number(),
83
+ width: z.number().optional(),
84
+ height: z.number().optional(),
85
+ /**
86
+ * One color, or one of the card's (see `CardColorsSchema`): `part` 0 is
87
+ * the first, and a card with fewer colors paints its last.
88
+ */
89
+ color: z.union([
90
+ HexColorSchema,
91
+ CardColorsSchema.extend({ part: z.number().int().min(0).optional() }),
92
+ ]),
93
+ /** Drawn only on a card with power and toughness, as the P/T is. */
94
+ showWith: z.literal("powerToughness").optional(),
95
+ /**
96
+ * Placed beside a number of the layout's `powerToughnessRow`: the image's
97
+ * right edge goes the row's `gap` left of that number, so it moves with
98
+ * the number as it widens, and `x` only nudges it from there (usually 0).
99
+ * Without a row, `x` places it as usual.
100
+ */
101
+ ptSlot: z.enum(["power", "toughness"]).optional(),
102
+ /** Moves with the boxes when the layout's `rulesSlide` moves them. */
103
+ slides: z.boolean().optional(),
104
+ /**
105
+ * Drawn only on a render where this holds — a frame offering two sets of
106
+ * symbols lists both, each under the value of the setting that picks it.
107
+ */
108
+ when: RenderConditionSchema.optional(),
109
+ });