@domainellipticlanguage/mtg-crucible 0.2.0 → 0.2.4
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.
- package/README.md +137 -386
- package/dist/constants/constants/index.d.ts +3 -0
- package/dist/constants/constants/index.d.ts.map +1 -0
- package/dist/constants/constants/index.js +14 -0
- package/dist/constants/constants/index.js.map +1 -0
- package/dist/constants/index.d.ts +3 -0
- package/dist/constants/index.d.ts.map +1 -0
- package/dist/constants/index.js +14 -0
- package/dist/constants/index.js.map +1 -0
- package/dist/constants/types.d.ts +166 -0
- package/dist/constants/types.d.ts.map +1 -0
- package/dist/constants/types.js +19 -0
- package/dist/constants/types.js.map +1 -0
- package/dist/helpers.d.ts +0 -6
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js +0 -54
- package/dist/helpers.js.map +1 -1
- package/dist/index.d.ts +5 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +23 -10
- package/dist/index.js.map +1 -1
- package/dist/parser/index.d.ts +4 -0
- package/dist/parser/index.d.ts.map +1 -0
- package/dist/parser/index.js +28 -0
- package/dist/parser/index.js.map +1 -0
- package/dist/parser/layout.d.ts +1355 -0
- package/dist/parser/layout.d.ts.map +1 -0
- package/dist/parser/layout.js +339 -0
- package/dist/parser/layout.js.map +1 -0
- package/dist/parser/parser/index.d.ts +4 -0
- package/dist/parser/parser/index.d.ts.map +1 -0
- package/dist/parser/parser/index.js +28 -0
- package/dist/parser/parser/index.js.map +1 -0
- package/dist/parser/parser.d.ts +30 -0
- package/dist/parser/parser.d.ts.map +1 -0
- package/dist/parser/parser.js +1321 -0
- package/dist/parser/parser.js.map +1 -0
- package/dist/parser/types.d.ts +166 -0
- package/dist/parser/types.d.ts.map +1 -0
- package/dist/parser/types.js +19 -0
- package/dist/parser/types.js.map +1 -0
- package/dist/parser.d.ts.map +1 -1
- package/dist/parser.js +64 -66
- package/dist/parser.js.map +1 -1
- package/dist/react/react/MtgCard.d.ts +2 -2
- package/dist/react/react/MtgCard.d.ts.map +1 -1
- package/dist/react/react/MtgCard.js +15 -15
- package/dist/react/react/MtgCard.js.map +1 -1
- package/dist/react/react/index.d.ts +1 -1
- package/dist/react/react/index.d.ts.map +1 -1
- package/dist/react/types.d.ts +21 -15
- package/dist/react/types.d.ts.map +1 -1
- package/dist/react/types.js +16 -0
- package/dist/react/types.js.map +1 -1
- package/dist/renderers/render.d.ts.map +1 -1
- package/dist/renderers/render.js +4 -19
- package/dist/renderers/render.js.map +1 -1
- package/dist/types.d.ts +21 -15
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +16 -0
- package/dist/types.js.map +1 -1
- package/package.json +12 -2
package/README.md
CHANGED
|
@@ -5,470 +5,221 @@ A TypeScript library for rendering Magic: The Gathering card images as PNGs.
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
npm install mtg-crucible
|
|
8
|
+
npm install @domainellipticlanguage/mtg-crucible
|
|
9
9
|
```
|
|
10
10
|
|
|
11
11
|
## Quick Start
|
|
12
12
|
|
|
13
|
+
### From text
|
|
14
|
+
|
|
13
15
|
```typescript
|
|
14
|
-
import {
|
|
16
|
+
import { renderCard } from '@domainellipticlanguage/mtg-crucible';
|
|
15
17
|
import { writeFileSync } from 'fs';
|
|
16
18
|
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
19
|
+
const result = await renderCard(`
|
|
20
|
+
Crucible of Legends {3}
|
|
21
|
+
Art URL: https://example.com/art.png
|
|
22
|
+
Rarity: Mythic Rare
|
|
23
|
+
Legendary Artifact
|
|
24
|
+
Whenever a legendary creature you control dies, return it to your hand at the beginning of the next end step.
|
|
25
|
+
Flavor Text: Every great story begins with fire.
|
|
24
26
|
`);
|
|
25
27
|
|
|
26
|
-
writeFileSync('crucible-of-legends.png',
|
|
28
|
+
writeFileSync('crucible-of-legends.png', result.frontFace);
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### From structured data
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
import { renderCard } from '@domainellipticlanguage/mtg-crucible';
|
|
35
|
+
|
|
36
|
+
const result = await renderCard({
|
|
37
|
+
name: 'Crucible of Legends',
|
|
38
|
+
manaCost: '{3}',
|
|
39
|
+
supertypes: ['legendary'],
|
|
40
|
+
types: ['artifact'],
|
|
41
|
+
rarity: 'mythic',
|
|
42
|
+
abilities: 'Whenever a legendary creature you control dies, return it to your hand at the beginning of the next end step.',
|
|
43
|
+
flavorText: 'Every great story begins with fire.',
|
|
44
|
+
});
|
|
27
45
|
```
|
|
28
46
|
|
|
29
47
|
<img src="logo/crucible-of-legends.png" alt="Crucible of Legends" width="300">
|
|
30
48
|
|
|
31
49
|
## API
|
|
32
50
|
|
|
33
|
-
### `
|
|
51
|
+
### `renderCard(input: CardData | string): Promise<RenderedCard>`
|
|
34
52
|
|
|
35
|
-
Parse a text-format
|
|
53
|
+
Parse and render a card. Accepts either a text-format string or a `CardData` object. Returns a `RenderedCard` with `frontFace` (PNG buffer), optional `backFace`, orientation info, and rotation data for multi-face cards.
|
|
36
54
|
|
|
37
|
-
### `parseCard(text: string):
|
|
55
|
+
### `parseCard(text: string): CardData`
|
|
38
56
|
|
|
39
|
-
Parse a text-format card definition into a `
|
|
57
|
+
Parse a text-format card definition into a `CardData` object.
|
|
40
58
|
|
|
41
|
-
### `
|
|
59
|
+
### `formatCard(card: CardData): string`
|
|
42
60
|
|
|
43
|
-
|
|
61
|
+
Convert a `CardData` object back to text format (round-trips with `parseCard`).
|
|
44
62
|
|
|
45
|
-
###
|
|
63
|
+
### `normalizeCard(card: CardData): NormalizedCardData`
|
|
46
64
|
|
|
47
|
-
|
|
65
|
+
Normalize a `CardData` into `NormalizedCardData` with all fields resolved (frame colors derived, abilities parsed, defaults filled in).
|
|
48
66
|
|
|
49
|
-
|
|
50
|
-
- `renderPlaneswalker(card: PlaneswalkerData): Promise<Buffer>`
|
|
51
|
-
- `renderSaga(card: SagaData): Promise<Buffer>`
|
|
52
|
-
- `renderBattle(card: BattleData): Promise<Buffer>`
|
|
67
|
+
### `getArtDimensions(card: CardData, template?: TemplateName, linked?: boolean): { width: number; height: number }`
|
|
53
68
|
|
|
54
|
-
|
|
69
|
+
Get the expected art image dimensions for a given card and template. Useful for generating or resizing art to fit correctly.
|
|
55
70
|
|
|
56
|
-
|
|
57
|
-
For the full grammar, metadata reference, and corner cases, see [docs/text-format.md](docs/text-format.md).
|
|
71
|
+
### `renderCardImage(card: NormalizedCardData, templateOverride?: string): Promise<Buffer>`
|
|
58
72
|
|
|
59
|
-
|
|
73
|
+
Low-level renderer. Renders a single face to PNG. Requires pre-normalized card data.
|
|
60
74
|
|
|
61
|
-
|
|
62
|
-
Name {mana cost}
|
|
63
|
-
Art: <art url> (Optional)
|
|
64
|
-
Rarity: <rarity> (Optional)
|
|
65
|
-
Type Line
|
|
66
|
-
Rules text line 1
|
|
67
|
-
Rules text line 2
|
|
68
|
-
Power/Toughness
|
|
69
|
-
*Flavor text*
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
Each line of rules text becomes a separate paragraph on the rendered card. Mana symbols use curly brace notation: `{W}`, `{U}`, `{B}`, `{R}`, `{G}`, `{C}`, `{T}`, `{1}`, `{2}`, etc. Hybrid and phyrexian mana are supported: `{G/U}`, `{G/P}`.
|
|
75
|
+
## Text Format
|
|
73
76
|
|
|
74
|
-
|
|
77
|
+
Cards are defined in a plain text format. For the full grammar see [docs/text-format.md](docs/text-format.md).
|
|
75
78
|
|
|
76
|
-
|
|
79
|
+
### Standard cards
|
|
77
80
|
|
|
78
81
|
```
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
*Legend speaks of the Creators' rage at their most prized creation.*
|
|
82
|
+
Lightning Bolt {R}
|
|
83
|
+
Instant
|
|
84
|
+
Lightning Bolt deals 3 damage to any target.
|
|
83
85
|
```
|
|
84
86
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
### Extended Text Spoiler Format
|
|
88
|
-
|
|
89
|
-
An art image URL can be specified between the name and type line, amont other things
|
|
87
|
+
### Creatures
|
|
90
88
|
|
|
91
89
|
```
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
Flash
|
|
97
|
-
Flying, vigilance
|
|
98
|
-
4/4
|
|
99
|
-
*Some flavor text*
|
|
90
|
+
Tarmogoyf {1}{G}
|
|
91
|
+
Creature -- Lhurgoyf
|
|
92
|
+
Tarmogoyf's power is equal to the number of card types among cards in all graveyards and its toughness is equal to that number plus 1.
|
|
93
|
+
*/1+*
|
|
100
94
|
```
|
|
101
95
|
|
|
102
96
|
### Planeswalkers
|
|
103
97
|
|
|
104
98
|
```
|
|
105
99
|
Liliana of the Veil {1}{B}{B}
|
|
106
|
-
Legendary Planeswalker
|
|
100
|
+
Legendary Planeswalker -- Liliana
|
|
107
101
|
+1: Each player discards a card.
|
|
108
102
|
-2: Target player sacrifices a creature.
|
|
109
103
|
-6: Separate all permanents target player controls into two piles.
|
|
110
104
|
Loyalty: 3
|
|
111
105
|
```
|
|
112
106
|
|
|
113
|
-
Abilities prefixed with `+N:`, `-N:`, or `0:` are parsed as loyalty abilities. Lines without a cost prefix are treated as static abilities.
|
|
114
|
-
|
|
115
107
|
### Sagas
|
|
116
108
|
|
|
117
109
|
```
|
|
118
110
|
The Eldest Reborn {4}{B}
|
|
119
|
-
Enchantment
|
|
120
|
-
I
|
|
121
|
-
II
|
|
122
|
-
III
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
Chapter numerals (I through VI) are parsed automatically. Combined chapters are supported:
|
|
126
|
-
|
|
127
|
-
```
|
|
128
|
-
I, II — Create a 1/1 red Goblin creature token.
|
|
129
|
-
III — Creatures you control get +2/+0 until end of turn.
|
|
111
|
+
Enchantment -- Saga
|
|
112
|
+
I -- Each opponent sacrifices a creature or planeswalker.
|
|
113
|
+
II -- Each opponent discards a card.
|
|
114
|
+
III -- Put target creature or planeswalker card from a graveyard onto the battlefield under your control.
|
|
130
115
|
```
|
|
131
116
|
|
|
132
117
|
### Battles
|
|
133
118
|
|
|
134
119
|
```
|
|
135
120
|
Invasion of Gobakhan {1}{W}
|
|
136
|
-
Battle
|
|
137
|
-
When Invasion of Gobakhan enters
|
|
121
|
+
Battle -- Siege
|
|
122
|
+
When Invasion of Gobakhan enters, look at target opponent's hand.
|
|
138
123
|
Defense: 3
|
|
139
124
|
```
|
|
140
125
|
|
|
141
|
-
###
|
|
142
|
-
|
|
143
|
-
```
|
|
144
|
-
Command Tower
|
|
145
|
-
Land
|
|
146
|
-
{T}: Add one mana of any color in your commander's color identity.
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
## Frame Color
|
|
150
|
-
|
|
151
|
-
The frame color is automatically derived:
|
|
152
|
-
|
|
153
|
-
| Condition | Frame |
|
|
154
|
-
|---|---|
|
|
155
|
-
| Type includes "Vehicle" | `v` (vehicle) |
|
|
156
|
-
| Type includes "Land" + no mana cost | `l` (land) |
|
|
157
|
-
| No colored mana symbols | `a` (artifact/colorless) |
|
|
158
|
-
| One color in mana cost | That color (`w`, `u`, `b`, `r`, `g`) |
|
|
159
|
-
| Two or more colors | `m` (multicolor/gold) |
|
|
160
|
-
|
|
161
|
-
Colors are extracted from all mana symbols including hybrid (`{G/U}`) and phyrexian (`{G/P}`).
|
|
162
|
-
|
|
163
|
-
## Card Dimensions
|
|
164
|
-
|
|
165
|
-
| Card Type | Width | Height |
|
|
166
|
-
|---|---|---|
|
|
167
|
-
| Standard | 2010 | 2814 |
|
|
168
|
-
| Planeswalker | 1500 | 2100 |
|
|
169
|
-
| Saga | 1500 | 2100 |
|
|
170
|
-
| Battle | 2814 | 2010 (landscape) |
|
|
171
|
-
|
|
172
|
-
## Development
|
|
173
|
-
|
|
174
|
-
```bash
|
|
175
|
-
npm test # run tests (vitest)
|
|
176
|
-
npm run build # compile TypeScript
|
|
177
|
-
npm run dev # start local dev server on port 3000
|
|
178
|
-
npm run spike # render test cards to output/
|
|
179
|
-
```
|
|
180
|
-
|
|
181
|
-
## Deploy to AWS Lambda
|
|
182
|
-
|
|
183
|
-
The project uses [SST](https://sst.dev) to deploy a Lambda function that serves the same UI as the dev server.
|
|
184
|
-
|
|
185
|
-
**Prerequisites:** AWS credentials configured (`~/.aws/credentials` or environment variables).
|
|
186
|
-
|
|
187
|
-
```bash
|
|
188
|
-
npx sst deploy --stage dev # deploy
|
|
189
|
-
npx sst remove --stage dev # tear down
|
|
190
|
-
```
|
|
191
|
-
|
|
192
|
-
The deploy output will print a function URL you can open in a browser. The Lambda is configured with 2 GB memory, 30s timeout, and x86_64 architecture (required for `@napi-rs/canvas`).
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
## TODO
|
|
196
|
-
|
|
197
|
-
- Improve set symbol generation with logo
|
|
198
|
-
- Fix missing rarity on sagas
|
|
199
|
-
- Test limits of parser leniency
|
|
200
|
-
- Test reminder text without asterisks
|
|
201
|
-
- Test multiple lines of flavor text
|
|
202
|
-
- Investigate card dimensions
|
|
203
|
-
- [X] Add blurb about Extended Text Spoiler format
|
|
204
|
-
- Update readme examples to be custom
|
|
205
|
-
- Add a carddata example to quickstart
|
|
206
|
-
- Add Class enchantment to spike
|
|
207
|
-
- Support Level Up https://scryfall.com/card/c13/43/echo-mage
|
|
208
|
-
- note how this affect P/T assumptions...
|
|
209
|
-
- Support more hybrid mana
|
|
210
|
-
- Phyrexian hybrid
|
|
211
|
-
- colorless/color hybrid
|
|
212
|
-
- 2/color hybrid
|
|
213
|
-
- Support multi-cards
|
|
214
|
-
- Enchantment Rooms
|
|
215
|
-
- https://scryfall.com/card/dsk/43/bottomless-pool-locker-room
|
|
216
|
-
- Fuse cards
|
|
217
|
-
- Adventures
|
|
218
|
-
- MDFC
|
|
219
|
-
- Kamigawa flip cards
|
|
220
|
-
- Flip cards (Werewolf, etc.)
|
|
221
|
-
- Support Varying P/T
|
|
222
|
-
- Leveler Cards
|
|
223
|
-
- Prototype
|
|
224
|
-
- Support Mutate
|
|
225
|
-
- Test harness
|
|
226
|
-
- Optimize asset size
|
|
227
|
-
- Downsample everything - it's excessive right now
|
|
228
|
-
- 744 × 1039 and jpeg to match mtg.design
|
|
229
|
-
- 672 × 936 to match scryfall
|
|
230
|
-
- Alternatively, procedurally generate textures + frames
|
|
231
|
-
- Can we get away with a single frame/format to serve Class, Saga, Case?
|
|
232
|
-
- Think we just need an AccentColor enum
|
|
233
|
-
- Ponder if card template should be more coarse grained
|
|
234
|
-
- Finalize the schema
|
|
235
|
-
- Support color indicator
|
|
236
|
-
- Support saga creature
|
|
237
|
-
- Support the MDFC / Transform triangle indicator.
|
|
238
|
-
- Figure out what default set/sequence/collection should be
|
|
239
|
-
- Support {11} to {20}
|
|
240
|
-
- Support untap symbol {Q}
|
|
241
|
-
- Support two color accents & crowns - does CC have these?
|
|
242
|
-
- does not look like it...perhaps can use them as masks applied to other renders
|
|
243
|
-
- Support the wedge for MDFC or transform cards
|
|
244
|
-
|
|
245
|
-
## Bugs
|
|
246
|
-
- Fix planeswalker ability spacing
|
|
247
|
-
- Four ability planeswalkers seem to have a different template?
|
|
248
|
-
- Fix planeswalker templates to have transparency
|
|
249
|
-
- pretty sure our current setup can handle this
|
|
250
|
-
- Revamp parser - Flavor Text: or Flavor: X
|
|
251
|
-
- More lenient parsing - we can ...
|
|
252
|
-
- Fix planeswalker art render positioning X
|
|
253
|
-
- Fix common set logo
|
|
254
|
-
- Fix colored artifacts using wrong border (do we support accents though?)
|
|
255
|
-
- Fix land accents - why is command tower gold?
|
|
256
|
-
- Ok I think if it produces multiple colors, the accent changes. Colorless lands have no accent
|
|
257
|
-
- This complicates our enums...
|
|
258
|
-
- Archway of Innovation - example of other. Same with basics...
|
|
259
|
-
- Oh and dryad arbor
|
|
260
|
-
- Reminder text does not get rendered in italics. (Anything in parens can be assumed to be reminder text)
|
|
261
|
-
- Legendary crown is missing a shadow on the side
|
|
262
|
-
- Asterisked text in flavor text should be normal faced, not italic
|
|
263
|
-
- Nit: Saga reminder text could be formatted a little better
|
|
264
|
-
- For gold and hybrid frames, still use the default for the name and typeline. Same for P/T box
|
|
265
|
-
- Lands have distinctive text box backgrounds?
|
|
266
|
-
- Make hybrid mana parsing more lenient -
|
|
267
|
-
- but normalize it to the correct order. Similar for phyrexian mana
|
|
268
|
-
- Update Flavortext parsing
|
|
269
|
-
- Multicolored artifact
|
|
270
|
-
- Urza's Saga - just straight up broken
|
|
271
|
-
- Shrewed Hatchling - P/T box wrong color
|
|
272
|
-
- Drayad Arbor - no P/T box. name and type box are wrong color
|
|
273
|
-
|
|
274
|
-
- An Unearthly Child - where does that little golden bit come from?
|
|
275
|
-
- Ability to override text size??
|
|
276
|
-
|
|
277
|
-
- Parsing - should the unstructured PW ability use the PW ability template with '' cost?
|
|
278
|
-
|
|
279
|
-
## Design Decisions
|
|
280
|
-
- should we support multicolored as an alias for gold?
|
|
281
|
-
- '' single letters as aliases?
|
|
282
|
-
- should normalize sort mana values?
|
|
283
|
-
- do we support styling in the text format? too complicated and people should just use the JSON format
|
|
284
|
-
- Should we infer missing hyphens in type line?
|
|
285
|
-
- anything unrecognized is assumed to be a subtype?
|
|
286
|
-
|
|
287
|
-
# TODO
|
|
288
|
-
- test with LTS
|
|
289
|
-
- Add {S} symbol
|
|
290
|
-
- Should we expose the name and type line colors? Yeah might as well.
|
|
291
|
-
|
|
292
|
-
- Expose function to Derive the art boundaries.
|
|
293
|
-
|
|
294
|
-
## Future Features And Blockers
|
|
295
|
-
- Pass in FrameModifier / FrameVariant / FrameStyle
|
|
296
|
-
- can be a list or single value. List will round robin??
|
|
297
|
-
- Support devoid borders
|
|
298
|
-
- Support nyx borders
|
|
299
|
-
- Support Snow borders
|
|
300
|
-
- Support miracle borders
|
|
301
|
-
|
|
302
|
-
- Support various borders
|
|
303
|
-
- Support hybrid mana borders
|
|
304
|
-
- Support composite cards
|
|
305
|
-
- mdfc is fine
|
|
306
|
-
- transform - double the crowns?
|
|
307
|
-
- flip - another 5 templates. no pinlines?
|
|
308
|
-
- Tokens?
|
|
309
|
-
- Flavor Name (nickname)
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
# bugs
|
|
313
|
-
Arni Slays the Troll - saga drawable areay is rectangle...
|
|
314
|
-
|
|
315
|
-
- fix reminder text italics
|
|
316
|
-
|
|
317
|
-
https://cran.r-project.org/web/packages/scryr/vignettes/frames.html
|
|
318
|
-
- Frame Effects
|
|
319
|
-
|
|
320
|
-
- Lesson
|
|
321
|
-
|
|
322
|
-
Only way to avoid combinatorial explosion - separate layers
|
|
323
|
-
|
|
324
|
-
## Supported
|
|
325
|
-
Battles (only the front face)
|
|
326
|
-
|
|
327
|
-
## Maybe
|
|
328
|
-
- Keyrune integration https://keyrune.andrewgioia.com/icons.html
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
# API's
|
|
332
|
-
```typescript
|
|
333
|
-
renderCard(cardData: CardData): RenderedCard
|
|
334
|
-
|
|
335
|
-
renderCard(text: string): RenderedCard
|
|
336
|
-
|
|
337
|
-
parseCard(text: string): CardData
|
|
338
|
-
formatCard(cardData: CardData): string
|
|
339
|
-
|
|
340
|
-
normalizeCard(cardData: CardData): CardData
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
// TODO maybe make a class and there should just be an aspectRatio property that does the division.
|
|
344
|
-
getArtDimensions(cardTemplate: CardTemplate): { widthPixels: number; heightPixels: number; aspectRatioWidth: number; aspectRatioHeight: number; }
|
|
345
|
-
```
|
|
346
|
-
|
|
347
|
-
# Plan
|
|
348
|
-
Note: IF you need to refer to any borders and assets, you cannot do that yet. they are downloading. But at some point you will be able to refer to Card Conjurer
|
|
349
|
-
|
|
350
|
-
1. [X] Fix everything to use the new apis and types. i.e. fix the build
|
|
351
|
-
1. [X (supposedly)] Harden the text parser
|
|
352
|
-
1. Create test framework whereby the AI can query scryfall for the text, json, art crop, and rendered card. Then we render our own card (using the scryfall art crop for art), then we concatenate our card with the scryfall card and the AI can view them side by side in a single image to allow for excruciating detail comparison.
|
|
353
|
-
1. Add support for composite cards. In the text format as well
|
|
354
|
-
|
|
355
|
-
# Decisions
|
|
356
|
-
Card normalization - do we help them out with boilerplate reminder text for sagas and classes?
|
|
357
|
-
Could we support hybrid borders via draw tools? linear gradient to shift between them?
|
|
358
|
-
|
|
359
|
-
# Tech Debt
|
|
360
|
-
- The Class level one is still messed up - tried changing it and it got messed up. I think we do need to parse it as level one?
|
|
361
|
-
|
|
362
|
-
# React Component
|
|
363
|
-
- Let's create a react component for displaying a RenderedCard object
|
|
364
|
-
- rotations - you can click likee on scryfall
|
|
365
|
-
- Card name rendered in an invisible span so that people can ctrl+F
|
|
366
|
-
- right click to copy scryfall text, copy crucible text, copy scryfall json, copy crucible json, copy card image (the face you are currently looking at)
|
|
367
|
-
- some way to control zoom/scale (maybe that's just regular styling and the component doesn't need to care?)
|
|
368
|
-
- This should be structured so that people can independently import the component or the parser or the renderer. The react component will have a peer dependency on react.
|
|
126
|
+
### Multi-face cards
|
|
369
127
|
|
|
370
|
-
|
|
128
|
+
Use `--linkType--` delimiters between faces:
|
|
371
129
|
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
Normal form - abilities should be converted to an array?
|
|
375
|
-
- should have a Null StructuredAbilities new type of Parsed
|
|
376
|
-
|
|
377
|
-
# Parser
|
|
378
|
-
Explicit legendCrown field (Has Legend Crown: true/false?)
|
|
379
|
-
Refactor stuff under flavor or style
|
|
380
|
-
|
|
381
|
-
Maybe CardData should have an Art Alt text. For storing art description?
|
|
382
|
-
- Or Art Description
|
|
383
|
-
- Should Art be Art URL?
|
|
384
|
-
Embed linktype in separator
|
|
385
|
-
Define normalized form
|
|
386
|
-
|
|
387
|
-
Remove support for asterisk-bounded flavor text
|
|
388
|
-
|
|
389
|
-
Normal form - I guess we should use caps for everything? titel case? Avoid conversions...
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
# Renderer
|
|
393
|
-
gradient for adventure book thing - that's the accent colors
|
|
394
|
-
Support split cards - no mask, just clip
|
|
395
|
-
Support MDFC
|
|
396
|
-
Fix battles?
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
# Optimization
|
|
400
|
-
Convert from O(M*N) to O(M+N)
|
|
401
|
-
Extract wubrgla textures
|
|
402
|
-
Extract pinlines, etc.
|
|
403
|
-
|
|
404
|
-
# Scope Down
|
|
405
|
-
- Remove colorless frame
|
|
406
|
-
- How to deprecate levelers, etc.?
|
|
407
|
-
- Deprecate battles
|
|
408
|
-
|
|
409
|
-
# Cruscible Text Format
|
|
410
130
|
```
|
|
411
131
|
Huntmaster of the Fells {2}{R}{G}
|
|
412
|
-
Creature
|
|
132
|
+
Creature -- Human Werewolf
|
|
413
133
|
Whenever this creature enters or transforms into Huntmaster of the Fells, create a 2/2 green Wolf creature token and you gain 2 life.
|
|
414
|
-
At the beginning of each upkeep, if no spells were cast last turn, transform this creature.
|
|
415
134
|
2/2
|
|
416
|
-
Flavor Text: He's a pretty cool guy
|
|
417
|
-
wraps around to next line
|
|
418
|
-
Art URL: ...
|
|
419
|
-
Art Description: ...
|
|
420
|
-
Artist: Chris Rain
|
|
421
|
-
Has Legend Crown: false
|
|
422
|
-
Frame Effect: Normal
|
|
423
|
-
Frame Color: Gold
|
|
424
|
-
Accent Color: Red and Green
|
|
425
|
-
Name Line Color: Gold
|
|
426
|
-
Type Line Color: Gold
|
|
427
|
-
Set Code: INR
|
|
428
|
-
Collector Number: 205
|
|
429
|
-
Designer: Mark Rosewater?
|
|
430
135
|
--transform--
|
|
431
136
|
Ravager of the Fells
|
|
432
|
-
Art URL: ...
|
|
433
|
-
Art Description: ...
|
|
434
137
|
Color Indicator: Red and Green
|
|
435
|
-
Creature
|
|
138
|
+
Creature -- Werewolf
|
|
436
139
|
Trample
|
|
437
|
-
Whenever this creature transforms into Ravager of the Fells, it deals 2 damage to target opponent or planeswalker and 2 damage to up to one target creature that player or that planeswalker's controller controls.
|
|
438
|
-
At the beginning of each upkeep, if a player cast two or more spells last turn, transform this creature.
|
|
439
140
|
4/4
|
|
440
|
-
Flavor Text: Another flavor text
|
|
441
141
|
```
|
|
442
142
|
|
|
443
|
-
|
|
444
|
-
accents for lands - also look for basic land names like Forest, etc. So fetch lands will work
|
|
445
|
-
Let's support keyword abilities vs. ability words. Ability words are like "Landfall" and are italicized. Keyword abilities are like "Exhaust" and are not italicized. They both appear before hyphens (or M dashes). We will infer which is which by the presence of reminder text. if no reminder text, then it must be an ability word, otherwise it must be a keyword ability. Examples:
|
|
446
|
-
"Landfall - Whenever you cast a land spell, this creature gets +1/+1 until end of turn."
|
|
447
|
-
- since no reminder text, this is an ability word and the "Landfall" should be italicized
|
|
448
|
-
"Exhaust - {3}{R}: Put two +1/+1 counters on this creature. (Activate each exhaust ability only once.)"
|
|
449
|
-
- since there is reminder text, this is a keyword ability and the "Exhaust" should not be italicized
|
|
450
|
-
- fuse cards need to blend in the bottom reminder text
|
|
143
|
+
Supported link types: `--transform--`, `--mdfc--`, `--split--`, `--fuse--`, `--flip--`, `--adventure--`, `--aftermath--`
|
|
451
144
|
|
|
452
|
-
|
|
453
|
-
- fix colorless frames
|
|
145
|
+
A bare `----` delimiter will infer the link type from card content (e.g. "Fuse" in text, both sides being instants/sorceries, presence of "transform" keyword).
|
|
454
146
|
|
|
455
|
-
|
|
456
|
-
- for split cards with hybrid mana, do the red white and blue thing...
|
|
457
|
-
- Fuse needs to use the mask and write the reminder text
|
|
147
|
+
### Metadata fields
|
|
458
148
|
|
|
459
|
-
|
|
460
|
-
- make parser more lenient (line order, allow and, oxfoard comma,)
|
|
461
|
-
- typeline color / name line color are not independent?
|
|
462
|
-
- P/T box color should match with frame
|
|
149
|
+
These can appear on any line (order doesn't matter):
|
|
463
150
|
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
151
|
+
| Field | Example |
|
|
152
|
+
|---|---|
|
|
153
|
+
| `Art URL:` | `Art URL: https://example.com/art.png` |
|
|
154
|
+
| `Art Description:` | `Art Description: A fiery landscape` |
|
|
155
|
+
| `Rarity:` | `Rarity: Mythic Rare` |
|
|
156
|
+
| `Flavor Text:` | `Flavor Text: Some italic text` |
|
|
157
|
+
| `Frame Color:` | `Frame Color: Red and Blue` |
|
|
158
|
+
| `Accent Color:` | `Accent Color: Green` |
|
|
159
|
+
| `Frame Effect:` | `Frame Effect: Nyx` |
|
|
160
|
+
| `Color Indicator:` | `Color Indicator: Red and Green` |
|
|
161
|
+
| `Has Legend Crown:` | `Has Legend Crown: true` |
|
|
162
|
+
| `Set Code:` | `Set Code: MH3` |
|
|
163
|
+
| `Collector Number:` | `Collector Number: 205` |
|
|
164
|
+
| `Artist:` | `Artist: Chris Rahn` |
|
|
165
|
+
| `Designer:` | `Designer: Mark Rosewater` |
|
|
166
|
+
|
|
167
|
+
### Mana symbols
|
|
168
|
+
|
|
169
|
+
Use curly brace notation: `{W}`, `{U}`, `{B}`, `{R}`, `{G}`, `{C}`, `{T}`, `{1}`, `{2}`, etc.
|
|
170
|
+
|
|
171
|
+
Hybrid: `{G/U}`, `{W/B}`. Phyrexian: `{G/P}`, `{R/P}`.
|
|
172
|
+
|
|
173
|
+
## Supported Templates
|
|
174
|
+
|
|
175
|
+
- Standard (including colorless/Eldrazi full-bleed art)
|
|
176
|
+
- Planeswalker (3 and 4 ability variants)
|
|
177
|
+
- Saga
|
|
178
|
+
- Class
|
|
179
|
+
- Battle
|
|
180
|
+
- Adventure
|
|
181
|
+
- Transform (front and back)
|
|
182
|
+
- MDFC / Modal DFC (front and back)
|
|
183
|
+
- Split
|
|
184
|
+
- Fuse
|
|
185
|
+
- Flip (Kamigawa-style)
|
|
186
|
+
- Aftermath
|
|
187
|
+
- Mutate
|
|
188
|
+
- Prototype
|
|
189
|
+
- Leveler
|
|
190
|
+
|
|
191
|
+
## React Component
|
|
192
|
+
|
|
193
|
+
```tsx
|
|
194
|
+
import { MtgCard } from '@domainellipticlanguage/mtg-crucible/react';
|
|
195
|
+
|
|
196
|
+
<MtgCard
|
|
197
|
+
card={renderedCardDisplay}
|
|
198
|
+
cardText="searchable text for ctrl+f"
|
|
199
|
+
rotateWidgetStyle={{ display: 'none' }} // optional: hide rotation arrow
|
|
200
|
+
/>
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
The component supports:
|
|
204
|
+
- Click to cycle through rotations (transform, flip, split, etc.)
|
|
205
|
+
- Rotation arrow widget (Scryfall-style) with hover/click animation
|
|
206
|
+
- Right-click context menu: download, copy image, copy text formats
|
|
207
|
+
- Invisible searchable text overlay for Ctrl+F
|
|
208
|
+
- CSS 3D transforms for card flipping
|
|
468
209
|
|
|
210
|
+
## Development
|
|
469
211
|
|
|
212
|
+
```bash
|
|
213
|
+
npm test # run tests (vitest)
|
|
214
|
+
npm run build # compile TypeScript
|
|
215
|
+
npm run dev # start local dev server with hot reload
|
|
216
|
+
```
|
|
470
217
|
|
|
471
|
-
|
|
218
|
+
## Publishing
|
|
472
219
|
|
|
473
|
-
|
|
474
|
-
|
|
220
|
+
```bash
|
|
221
|
+
npm login
|
|
222
|
+
npm version patch # or minor/major
|
|
223
|
+
npm run build
|
|
224
|
+
npm publish --access public
|
|
225
|
+
```
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { RARITIES, TEMPLATE_NAMES, COLORS, FRAME_COLORS, FRAME_EFFECTS, SUPERTYPES_LIST, CARD_TYPES, LINK_TYPES, } from '../types';
|
|
2
|
+
export type { Rarity, TemplateName, Color, AccentColor, FrameColor, FrameEffect, Supertype, Type, Subtype, LinkType, PlaneswalkerAbilities, SagaAbilities, ClassAbilities, LevelerAbilities, CaseAbilities, PrototypeAbilities, MutateAbilities, NoneAbilities, StructuredAbilities, ParsedAbilities, CardData, NormalizedCardData, Rotation, RenderedCard, MtgCardDisplayData, } from '../types';
|
|
3
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/constants/index.ts"],"names":[],"mappings":"AACA,OAAO,EACL,QAAQ,EAAE,cAAc,EAAE,MAAM,EAAE,YAAY,EAAE,aAAa,EAC7D,eAAe,EAAE,UAAU,EAAE,UAAU,GACxC,MAAM,UAAU,CAAC;AAElB,YAAY,EACV,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,WAAW,EACjE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAClC,qBAAqB,EAAE,aAAa,EAAE,cAAc,EAAE,gBAAgB,EACtE,aAAa,EAAE,kBAAkB,EAAE,eAAe,EAAE,aAAa,EACjE,mBAAmB,EAAE,eAAe,EACpC,QAAQ,EAAE,kBAAkB,EAAE,QAAQ,EAAE,YAAY,EACpD,kBAAkB,GACnB,MAAM,UAAU,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LINK_TYPES = exports.CARD_TYPES = exports.SUPERTYPES_LIST = exports.FRAME_EFFECTS = exports.FRAME_COLORS = exports.COLORS = exports.TEMPLATE_NAMES = exports.RARITIES = void 0;
|
|
4
|
+
// Browser-safe constants module — no Node dependencies
|
|
5
|
+
var types_1 = require("../types");
|
|
6
|
+
Object.defineProperty(exports, "RARITIES", { enumerable: true, get: function () { return types_1.RARITIES; } });
|
|
7
|
+
Object.defineProperty(exports, "TEMPLATE_NAMES", { enumerable: true, get: function () { return types_1.TEMPLATE_NAMES; } });
|
|
8
|
+
Object.defineProperty(exports, "COLORS", { enumerable: true, get: function () { return types_1.COLORS; } });
|
|
9
|
+
Object.defineProperty(exports, "FRAME_COLORS", { enumerable: true, get: function () { return types_1.FRAME_COLORS; } });
|
|
10
|
+
Object.defineProperty(exports, "FRAME_EFFECTS", { enumerable: true, get: function () { return types_1.FRAME_EFFECTS; } });
|
|
11
|
+
Object.defineProperty(exports, "SUPERTYPES_LIST", { enumerable: true, get: function () { return types_1.SUPERTYPES_LIST; } });
|
|
12
|
+
Object.defineProperty(exports, "CARD_TYPES", { enumerable: true, get: function () { return types_1.CARD_TYPES; } });
|
|
13
|
+
Object.defineProperty(exports, "LINK_TYPES", { enumerable: true, get: function () { return types_1.LINK_TYPES; } });
|
|
14
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/constants/index.ts"],"names":[],"mappings":";;;AAAA,uDAAuD;AACvD,kCAGkB;AAFhB,iGAAA,QAAQ,OAAA;AAAE,uGAAA,cAAc,OAAA;AAAE,+FAAA,MAAM,OAAA;AAAE,qGAAA,YAAY,OAAA;AAAE,sGAAA,aAAa,OAAA;AAC7D,wGAAA,eAAe,OAAA;AAAE,mGAAA,UAAU,OAAA;AAAE,mGAAA,UAAU,OAAA"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { RARITIES, TEMPLATE_NAMES, COLORS, FRAME_COLORS, FRAME_EFFECTS, SUPERTYPES_LIST, CARD_TYPES, LINK_TYPES, } from '../types';
|
|
2
|
+
export type { Rarity, TemplateName, Color, AccentColor, FrameColor, FrameEffect, Supertype, Type, Subtype, LinkType, PlaneswalkerAbilities, SagaAbilities, ClassAbilities, LevelerAbilities, CaseAbilities, PrototypeAbilities, MutateAbilities, NoneAbilities, StructuredAbilities, ParsedAbilities, CardData, NormalizedCardData, Rotation, RenderedCard, MtgCardDisplayData, } from '../types';
|
|
3
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/constants/index.ts"],"names":[],"mappings":"AACA,OAAO,EACL,QAAQ,EAAE,cAAc,EAAE,MAAM,EAAE,YAAY,EAAE,aAAa,EAC7D,eAAe,EAAE,UAAU,EAAE,UAAU,GACxC,MAAM,UAAU,CAAC;AAElB,YAAY,EACV,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,WAAW,EACjE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAClC,qBAAqB,EAAE,aAAa,EAAE,cAAc,EAAE,gBAAgB,EACtE,aAAa,EAAE,kBAAkB,EAAE,eAAe,EAAE,aAAa,EACjE,mBAAmB,EAAE,eAAe,EACpC,QAAQ,EAAE,kBAAkB,EAAE,QAAQ,EAAE,YAAY,EACpD,kBAAkB,GACnB,MAAM,UAAU,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LINK_TYPES = exports.CARD_TYPES = exports.SUPERTYPES_LIST = exports.FRAME_EFFECTS = exports.FRAME_COLORS = exports.COLORS = exports.TEMPLATE_NAMES = exports.RARITIES = void 0;
|
|
4
|
+
// Browser-safe constants module — no Node dependencies
|
|
5
|
+
var types_1 = require("../types");
|
|
6
|
+
Object.defineProperty(exports, "RARITIES", { enumerable: true, get: function () { return types_1.RARITIES; } });
|
|
7
|
+
Object.defineProperty(exports, "TEMPLATE_NAMES", { enumerable: true, get: function () { return types_1.TEMPLATE_NAMES; } });
|
|
8
|
+
Object.defineProperty(exports, "COLORS", { enumerable: true, get: function () { return types_1.COLORS; } });
|
|
9
|
+
Object.defineProperty(exports, "FRAME_COLORS", { enumerable: true, get: function () { return types_1.FRAME_COLORS; } });
|
|
10
|
+
Object.defineProperty(exports, "FRAME_EFFECTS", { enumerable: true, get: function () { return types_1.FRAME_EFFECTS; } });
|
|
11
|
+
Object.defineProperty(exports, "SUPERTYPES_LIST", { enumerable: true, get: function () { return types_1.SUPERTYPES_LIST; } });
|
|
12
|
+
Object.defineProperty(exports, "CARD_TYPES", { enumerable: true, get: function () { return types_1.CARD_TYPES; } });
|
|
13
|
+
Object.defineProperty(exports, "LINK_TYPES", { enumerable: true, get: function () { return types_1.LINK_TYPES; } });
|
|
14
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/constants/index.ts"],"names":[],"mappings":";;;AAAA,uDAAuD;AACvD,kCAGkB;AAFhB,iGAAA,QAAQ,OAAA;AAAE,uGAAA,cAAc,OAAA;AAAE,+FAAA,MAAM,OAAA;AAAE,qGAAA,YAAY,OAAA;AAAE,sGAAA,aAAa,OAAA;AAC7D,wGAAA,eAAe,OAAA;AAAE,mGAAA,UAAU,OAAA;AAAE,mGAAA,UAAU,OAAA"}
|