👁️
5
fork

Configure Feed

Select the types of activity you want to include in your feed.

card set symbols!

+150 -433
+1
.gitignore
··· 15 15 public/data/ 16 16 public/symbols/ 17 17 src/lib/card-manifest.ts 18 + src/lib/set-symbols.ts 18 19 .cache/ 19 20 20 21 tsconfig.tsbuildinfo
+19 -4
scripts/download-scryfall.ts
··· 1039 1039 } 1040 1040 1041 1041 // Process: extract mappings from cached CSS 1042 + // CSS has comma-separated selectors like: 1043 + // .ss-grn:before, 1044 + // .ss-gk1:before { 1045 + // content: "\e94b"; 1046 + // } 1042 1047 const css = await readFile(cssCachePath, "utf-8"); 1043 1048 1044 1049 const mappings: Record<string, number> = {}; 1045 - const regex = /\.ss-([a-z0-9]+):before\s*\{\s*content:\s*"\\([0-9a-f]+)"/gi; 1050 + 1051 + // Match rule blocks: selectors { content: "\xxxx"; } 1052 + const blockRegex = 1053 + /((?:\.ss-[a-z0-9]+:before[,\s]*)+)\s*\{\s*content:\s*"\\([0-9a-f]+)"/gi; 1054 + const selectorRegex = /\.ss-([a-z0-9]+):before/g; 1046 1055 let match: RegExpExecArray | null; 1047 1056 1048 - while ((match = regex.exec(css)) !== null) { 1049 - const [, setCode, codepoint] = match; 1050 - mappings[setCode] = parseInt(codepoint, 16); 1057 + while ((match = blockRegex.exec(css)) !== null) { 1058 + const [, selectors, codepoint] = match; 1059 + const cp = parseInt(codepoint, 16); 1060 + 1061 + // Extract all set codes from the selector list 1062 + let selectorMatch: RegExpExecArray | null; 1063 + while ((selectorMatch = selectorRegex.exec(selectors)) !== null) { 1064 + mappings[selectorMatch[1]] = cp; 1065 + } 1051 1066 } 1052 1067 1053 1068 const setCount = Object.keys(mappings).length;
+78 -26
src/components/CardWireframe.tsx
··· 19 19 import type { Card, CardFace, Rarity } from "@/lib/scryfall-types"; 20 20 import { ManaCost } from "./ManaCost"; 21 21 import { OracleText } from "./OracleText"; 22 + import { SetSymbol } from "./SetSymbol"; 22 23 23 24 interface CardWireframeProps { 24 25 card: Card; ··· 61 62 }, 62 63 }; 63 64 64 - // Rarity symbols (approximate) 65 - const RARITY_SYMBOL: Record<Rarity, string> = { 66 - common: "●", 67 - uncommon: "◆", 68 - rare: "★", 69 - mythic: "✦", 70 - special: "✧", 71 - bonus: "✦", 72 - }; 73 - 74 65 interface FaceContentProps { 75 66 face: CardFace; 76 67 showStats?: boolean; 68 + setCode?: string; 69 + rarity?: Rarity; 77 70 } 78 71 79 - function FaceContent({ face, showStats = true }: FaceContentProps) { 72 + function FaceContent({ 73 + face, 74 + showStats = true, 75 + setCode, 76 + rarity, 77 + }: FaceContentProps) { 80 78 const hasPT = face.power !== undefined && face.toughness !== undefined; 81 79 const hasLoyalty = face.loyalty !== undefined; 82 80 const hasDefense = face.defense !== undefined; ··· 87 85 <div className="flex flex-col h-full"> 88 86 {/* Type Line */} 89 87 {face.type_line && ( 90 - <div className="px-[4cqw] py-[1.5cqw] text-[5cqw] tracking-tight text-gray-700 dark:text-gray-300 border-b border-gray-300 dark:border-slate-600 truncate bg-gray-100 dark:bg-slate-700"> 91 - {face.type_line} 88 + <div className="flex items-center justify-between px-[4cqw] py-[1.5cqw] text-[5cqw] tracking-tight text-gray-700 dark:text-gray-300 border-b border-gray-300 dark:border-slate-600 bg-gray-100 dark:bg-slate-700"> 89 + <span className="truncate">{face.type_line}</span> 90 + {setCode && rarity && ( 91 + <SetSymbol 92 + setCode={setCode} 93 + rarity={SET_SYMBOL_RARITY[rarity]} 94 + className="text-[7cqw] shrink-0" 95 + /> 96 + )} 92 97 </div> 93 98 )} 94 99 ··· 160 165 ); 161 166 } 162 167 168 + const SET_SYMBOL_RARITY: Record< 169 + Rarity, 170 + "common" | "uncommon" | "rare" | "mythic" | "timeshifted" 171 + > = { 172 + common: "common", 173 + uncommon: "uncommon", 174 + rare: "rare", 175 + mythic: "mythic", 176 + special: "timeshifted", 177 + bonus: "mythic", 178 + }; 179 + 163 180 function CardFooter({ card }: { card: Card }) { 164 181 const rarity = card.rarity || "common"; 165 - const rarityStyle = RARITY_COLORS[rarity] || RARITY_COLORS.common; 166 182 const collectorNum = card.collector_number; 183 + const setCode = card.set; 167 184 168 185 return ( 169 186 <div className="flex items-center justify-between px-[3cqw] py-[1.5cqw] text-[3.5cqw] tracking-tight text-gray-600 dark:text-gray-400 border-t border-gray-300 dark:border-slate-600 bg-gray-50 dark:bg-slate-800"> 170 187 <div className="flex items-center gap-[1.5cqw]"> 171 - <span className={rarityStyle.text}>{RARITY_SYMBOL[rarity]}</span> 172 - <span>{card.set?.toUpperCase()}</span> 188 + {setCode && ( 189 + <SetSymbol 190 + setCode={setCode} 191 + rarity={SET_SYMBOL_RARITY[rarity]} 192 + className="text-[6cqw]" 193 + /> 194 + )} 195 + <span>{setCode?.toUpperCase()}</span> 173 196 {collectorNum && <span>#{collectorNum}</span>} 174 197 </div> 175 198 {card.artist && <span className="truncate italic">{card.artist}</span>} ··· 234 257 )} 235 258 </div> 236 259 <div className="flex-1 overflow-hidden"> 237 - <FaceContent face={topFace} /> 260 + <FaceContent face={topFace} setCode={card.set} rarity={rarity} /> 238 261 </div> 239 262 </div> 240 263 ··· 256 279 )} 257 280 </div> 258 281 <div className="flex-1 overflow-hidden"> 259 - <FaceContent face={bottomFace} showStats={false} /> 282 + <FaceContent 283 + face={bottomFace} 284 + showStats={false} 285 + setCode={card.set} 286 + rarity={rarity} 287 + /> 260 288 </div> 261 289 </div> 262 290 </div> ··· 308 336 )} 309 337 </div> 310 338 <div className="flex-1 overflow-hidden"> 311 - <FaceContent face={faces[0]} showStats={false} /> 339 + <FaceContent 340 + face={faces[0]} 341 + showStats={false} 342 + setCode={card.set} 343 + rarity={rarity} 344 + /> 312 345 </div> 313 346 </div> 314 347 ··· 330 363 )} 331 364 </div> 332 365 <div className="flex-1 overflow-hidden"> 333 - <FaceContent face={faces[1]} showStats={false} /> 366 + <FaceContent 367 + face={faces[1]} 368 + showStats={false} 369 + setCode={card.set} 370 + rarity={rarity} 371 + /> 334 372 </div> 335 373 <CardFooter card={card} /> 336 374 </div> ··· 372 410 )} 373 411 </div> 374 412 <div className="flex-1 overflow-hidden"> 375 - <FaceContent face={faces[0]} showStats={false} /> 413 + <FaceContent 414 + face={faces[0]} 415 + showStats={false} 416 + setCode={card.set} 417 + rarity={rarity} 418 + /> 376 419 </div> 377 420 </div> 378 421 ··· 400 443 /> 401 444 )} 402 445 </div> 403 - <FaceContent face={faces[1]} showStats={false} /> 446 + <FaceContent 447 + face={faces[1]} 448 + showStats={false} 449 + setCode={card.set} 450 + rarity={rarity} 451 + /> 404 452 </div> 405 453 </div> 406 454 </div> ··· 459 507 460 508 {/* Main card body */} 461 509 <div className="flex-1 flex flex-col overflow-hidden"> 462 - <FaceContent face={mainFace} /> 510 + <FaceContent face={mainFace} setCode={card.set} rarity={rarity} /> 463 511 </div> 464 512 465 513 <CardFooter card={card} /> ··· 504 552 </span> 505 553 </div> 506 554 <div className="flex-1 flex flex-col overflow-hidden"> 507 - <FaceContent face={faces[0]} /> 555 + <FaceContent face={faces[0]} setCode={card.set} rarity={rarity} /> 508 556 </div> 509 557 <CardFooter card={card} /> 510 558 </div> ··· 538 586 </span> 539 587 </div> 540 588 <div className="flex-1 flex flex-col overflow-hidden"> 541 - <FaceContent face={faces[1] || faces[0]} /> 589 + <FaceContent 590 + face={faces[1] || faces[0]} 591 + setCode={card.set} 592 + rarity={rarity} 593 + /> 542 594 </div> 543 595 <CardFooter card={card} /> 544 596 </div> ··· 584 636 585 637 {/* Card body */} 586 638 <div className="flex-1 flex flex-col overflow-hidden"> 587 - <FaceContent face={faces[0]} /> 639 + <FaceContent face={faces[0]} setCode={card.set} rarity={rarity} /> 588 640 </div> 589 641 590 642 <CardFooter card={card} />
+52
src/components/SetSymbol.tsx
··· 1 + /** 2 + * Renders an MTG set symbol using the Keyrune font 3 + * 4 + * https://github.com/andrewgioia/keyrune 5 + */ 6 + 7 + import { getSetSymbol } from "@/lib/set-symbols"; 8 + 9 + interface SetSymbolProps { 10 + setCode: string; 11 + rarity?: "common" | "uncommon" | "rare" | "mythic" | "timeshifted"; 12 + size?: "small" | "medium" | "large"; 13 + className?: string; 14 + } 15 + 16 + const SIZE_CLASSES = { 17 + small: "text-base", 18 + medium: "text-xl", 19 + large: "text-2xl", 20 + }; 21 + 22 + const RARITY_CLASSES = { 23 + common: "text-rarity-common dark:text-gray-400", 24 + uncommon: "text-rarity-uncommon", 25 + rare: "text-rarity-rare", 26 + mythic: "text-rarity-mythic", 27 + timeshifted: "text-rarity-timeshifted", 28 + }; 29 + 30 + export function SetSymbol({ 31 + setCode, 32 + rarity = "common", 33 + size = "medium", 34 + className, 35 + }: SetSymbolProps) { 36 + const symbol = getSetSymbol(setCode); 37 + 38 + if (!symbol) { 39 + return null; 40 + } 41 + 42 + return ( 43 + <span 44 + role="img" 45 + className={`font-['Keyrune'] ${SIZE_CLASSES[size]} ${RARITY_CLASSES[rarity]} ${className ?? ""}`} 46 + title={`${setCode.toUpperCase()} (${rarity})`} 47 + aria-label={`Set: ${setCode.toUpperCase()}, Rarity: ${rarity}`} 48 + > 49 + {symbol} 50 + </span> 51 + ); 52 + }
-403
src/lib/set-symbols.ts
··· 1 - /** 2 - * Keyrune set symbol unicode mappings 3 - * Auto-generated by scripts/download-scryfall.ts from Keyrune v3.18.0 4 - * https://github.com/andrewgioia/keyrune 5 - * 6 - * Font licensed under SIL OFL 1.1 7 - */ 8 - 9 - export const SET_SYMBOLS: Record<string, number> = { 10 - "10e": 0xe60b, 11 - "1e": 0xe947, 12 - "2e": 0xe948, 13 - "2ed": 0xe602, 14 - "2u": 0xe949, 15 - "2x2": 0xe99c, 16 - "2xm": 0xe96e, 17 - "30a": 0xe9aa, 18 - "3e": 0xe94a, 19 - "3ed": 0xe603, 20 - "40k": 0xe998, 21 - "4ed": 0xe604, 22 - "5dn": 0xe633, 23 - "5ed": 0xe606, 24 - "6ed": 0xe607, 25 - "7ed": 0xe608, 26 - "8ed": 0xe609, 27 - "9ed": 0xe60a, 28 - "a25": 0xe93d, 29 - "acr": 0xe9ce, 30 - "aer": 0xe90f, 31 - "afc": 0xe981, 32 - "afr": 0xe972, 33 - "akh": 0xe914, 34 - "akr": 0xe970, 35 - "ala": 0xe641, 36 - "all": 0xe61a, 37 - "ann": 0xe92d, 38 - "apc": 0xe62a, 39 - "arb": 0xe643, 40 - "arc": 0xe657, 41 - "arn": 0xe613, 42 - "ath": 0xe65f, 43 - "atq": 0xe614, 44 - "avr": 0xe64c, 45 - "azorius": 0xe94e, 46 - "bbd": 0xe942, 47 - "bcore": 0xe612, 48 - "bfz": 0xe699, 49 - "big": 0xe9d6, 50 - "blb": 0xe9cd, 51 - "blc": 0xe9d4, 52 - "bng": 0xe651, 53 - "bok": 0xe635, 54 - "boros": 0xe94f, 55 - "bot": 0xe99e, 56 - "br": 0xe9c1, 57 - "brb": 0xe660, 58 - "brc": 0xe99f, 59 - "bro": 0xe99d, 60 - "brr": 0xe9a0, 61 - "btd": 0xe661, 62 - "c13": 0xe65b, 63 - "c14": 0xe65d, 64 - "c15": 0xe900, 65 - "c16": 0xe9e5, 66 - "c17": 0xe934, 67 - "c18": 0xe946, 68 - "c19": 0xe95f, 69 - "c20": 0xe966, 70 - "c21": 0xe97e, 71 - "cc1": 0xe968, 72 - "cc2": 0xe987, 73 - "chk": 0xe634, 74 - "chr": 0xe65e, 75 - "clb": 0xe991, 76 - "clu": 0xe9cb, 77 - "cm1": 0xe65a, 78 - "cm2": 0xe940, 79 - "cma": 0xe916, 80 - "cmc": 0xe969, 81 - "cmd": 0xe658, 82 - "cmm": 0xe9b5, 83 - "cn2": 0xe904, 84 - "cns": 0xe65c, 85 - "con": 0xe642, 86 - "csp": 0xe61b, 87 - "dd2": 0xe66a, 88 - "ddc": 0xe66b, 89 - "ddd": 0xe66c, 90 - "dde": 0xe66d, 91 - "ddf": 0xe66e, 92 - "ddg": 0xe66f, 93 - "ddh": 0xe670, 94 - "ddi": 0xe671, 95 - "ddj": 0xe672, 96 - "ddk": 0xe673, 97 - "ddl": 0xe674, 98 - "ddm": 0xe675, 99 - "ddn": 0xe676, 100 - "ddo": 0xe677, 101 - "ddp": 0xe698, 102 - "ddq": 0xe908, 103 - "ddr": 0xe90d, 104 - "dds": 0xe921, 105 - "ddt": 0xe933, 106 - "ddu": 0xe93e, 107 - "dft": 0xe9e0, 108 - "dgm": 0xe64f, 109 - "dimir": 0xe950, 110 - "dis": 0xe639, 111 - "dka": 0xe64b, 112 - "dkm": 0xe662, 113 - "dmc": 0xe994, 114 - "dmr": 0xe9a4, 115 - "dmu": 0xe993, 116 - "dom": 0xe93f, 117 - "dpa": 0xe689, 118 - "drb": 0xe678, 119 - "drc": 0xe9e8, 120 - "drk": 0xe616, 121 - "dsc": 0xe9dc, 122 - "dsk": 0xe9d7, 123 - "dst": 0xe632, 124 - "dtk": 0xe693, 125 - "dvk": 0xea03, 126 - "e02": 0xe931, 127 - "ea1": 0xe9b4, 128 - "ecc": 0xea11, 129 - "ecl": 0xea04, 130 - "eld": 0xe95e, 131 - "ema": 0xe903, 132 - "emn": 0xe90b, 133 - "eoc": 0xe9f6, 134 - "eoe": 0xe9f0, 135 - "eos": 0xea00, 136 - "eve": 0xe640, 137 - "evg": 0xe669, 138 - "exo": 0xe621, 139 - "exp": 0xe69a, 140 - "fca": 0xe9f8, 141 - "fdc": 0xe9e4, 142 - "fdn": 0xe9d8, 143 - "fem": 0xe617, 144 - "fic": 0xe9f5, 145 - "fin": 0xe9ed, 146 - "frf": 0xe654, 147 - "fut": 0xe63c, 148 - "gk1": 0xe94b, 149 - "gk2": 0xe959, 150 - "gn2": 0xe964, 151 - "gn3": 0xe9a5, 152 - "gnt": 0xe94d, 153 - "golgari": 0xe951, 154 - "gpt": 0xe638, 155 - "gruul": 0xe952, 156 - "gs1": 0xe945, 157 - "gtc": 0xe64e, 158 - "h09": 0xe67f, 159 - "h17": 0xe938, 160 - "ha1": 0xe96b, 161 - "hbg": 0xe9a6, 162 - "hml": 0xe618, 163 - "hop": 0xe656, 164 - "hou": 0xe924, 165 - "htr17": 0xe687, 166 - "ice": 0xe619, 167 - "ice2": 0xe925, 168 - "iko": 0xe962, 169 - "ima": 0xe935, 170 - "inr": 0xe9e2, 171 - "inv": 0xe628, 172 - "isd": 0xe64a, 173 - "izzet": 0xe953, 174 - "j20": 0xe96a, 175 - "j21": 0xe983, 176 - "j22": 0xe9ad, 177 - "j25": 0xe9df, 178 - "j25a": 0xe9db, 179 - "jmp": 0xe96f, 180 - "jou": 0xe652, 181 - "jud": 0xe62d, 182 - "khc": 0xe97d, 183 - "khm": 0xe974, 184 - "kld": 0xe90e, 185 - "klr": 0xe97c, 186 - "ktk": 0xe653, 187 - "lcc": 0xe9c7, 188 - "lci": 0xe9c2, 189 - "lea": 0xe600, 190 - "leb": 0xe601, 191 - "leg": 0xe615, 192 - "lgn": 0xe62f, 193 - "lrw": 0xe63d, 194 - "ltc": 0xe9b6, 195 - "ltr": 0xe9af, 196 - "m10": 0xe60c, 197 - "m11": 0xe60d, 198 - "m12": 0xe60e, 199 - "m13": 0xe60f, 200 - "m14": 0xe610, 201 - "m15": 0xe611, 202 - "m19": 0xe941, 203 - "m20": 0xe95d, 204 - "m21": 0xe960, 205 - "m3c": 0xe9d0, 206 - "mar": 0xe9f9, 207 - "mat": 0xe9a3, 208 - "mb1": 0xe971, 209 - "mb2": 0xe9d9, 210 - "mbs": 0xe648, 211 - "md1": 0xe682, 212 - "me1": 0xe68d, 213 - "me2": 0xe68e, 214 - "me3": 0xe68f, 215 - "me4": 0xe690, 216 - "med": 0xe94c, 217 - "mh1": 0xe95b, 218 - "mh2": 0xe97b, 219 - "mh3": 0xe9cf, 220 - "mic": 0xe985, 221 - "mid": 0xe978, 222 - "mir": 0xe61c, 223 - "mkc": 0xe9ca, 224 - "mkm": 0xe9c9, 225 - "mm2": 0xe695, 226 - "mm3": 0xe912, 227 - "mma": 0xe663, 228 - "mmq": 0xe625, 229 - "moc": 0xe9a9, 230 - "mom": 0xe9a2, 231 - "mor": 0xe63e, 232 - "mp1": 0xe913, 233 - "mp2": 0xe922, 234 - "mrd": 0xe631, 235 - "mul": 0xe9ba, 236 - "ncc": 0xe98e, 237 - "nec": 0xe98d, 238 - "neo": 0xe98c, 239 - "nms": 0xe626, 240 - "nph": 0xe649, 241 - "ody": 0xe62b, 242 - "ogw": 0xe901, 243 - "om1": 0xea0e, 244 - "omb": 0xea10, 245 - "onc": 0xe9a8, 246 - "one": 0xe9a1, 247 - "ons": 0xe62e, 248 - "ori": 0xe697, 249 - "orzhov": 0xe954, 250 - "otc": 0xe9d2, 251 - "otj": 0xe9cc, 252 - "otp": 0xe9d5, 253 - "papac": 0xe92a, 254 - "parl": 0xe688, 255 - "parl2": 0xe68c, 256 - "parl3": 0xe943, 257 - "past": 0xe68b, 258 - "pbook": 0xe68a, 259 - "pc2": 0xe659, 260 - "pca": 0xe911, 261 - "pcy": 0xe627, 262 - "pd2": 0xe680, 263 - "pd3": 0xe681, 264 - "pdep": 0xe93a, 265 - "pdrc": 0xe932, 266 - "peuro": 0xe92b, 267 - "pfnm": 0xe937, 268 - "pgru": 0xe683, 269 - "pheart": 0xe936, 270 - "pidw": 0xe92c, 271 - "pio": 0xe9e7, 272 - "pip": 0xe9c3, 273 - "plc": 0xe63b, 274 - "pleaf": 0xe686, 275 - "pls": 0xe629, 276 - "pm2": 0xea02, 277 - "pma": 0xea01, 278 - "pmodo": 0xe91b, 279 - "pmps": 0xe919, 280 - "pmpu": 0xe91a, 281 - "pmtg1": 0xe684, 282 - "pmtg2": 0xe685, 283 - "po2": 0xe665, 284 - "por": 0xe664, 285 - "psalvat05": 0xe909, 286 - "psalvat11": 0xe90a, 287 - "psega": 0xe93b, 288 - "psld": 0xe687, 289 - "psum": 0xe605, 290 - "ptg": 0xe965, 291 - "ptk": 0xe666, 292 - "ptsa": 0xe93c, 293 - "pxbox": 0xe915, 294 - "pz2": 0xe91f, 295 - "pza": 0xea08, 296 - "rakdos": 0xe955, 297 - "rav": 0xe637, 298 - "rex": 0xe9c4, 299 - "rix": 0xe92f, 300 - "roe": 0xe646, 301 - "rtr": 0xe64d, 302 - "rvr": 0xe9bb, 303 - "s00": 0xe668, 304 - "s99": 0xe667, 305 - "scd": 0xe9ab, 306 - "scg": 0xe630, 307 - "selesnya": 0xe956, 308 - "shm": 0xe63f, 309 - "simic": 0xe957, 310 - "sir": 0xe9b1, 311 - "sis": 0xe9b2, 312 - "sld": 0xe687, 313 - "sld2": 0xe9bc, 314 - "slu": 0xe687, 315 - "snc": 0xe98b, 316 - "soi": 0xe902, 317 - "sok": 0xe636, 318 - "som": 0xe647, 319 - "spe": 0xe9f3, 320 - "spg": 0xe9c8, 321 - "spm": 0xe9f1, 322 - "ss1": 0xe944, 323 - "ss2": 0xe95c, 324 - "ss3": 0xe96d, 325 - "sta": 0xe980, 326 - "sth": 0xe620, 327 - "stx": 0xe975, 328 - "td2": 0xe91c, 329 - "tdc": 0xe9f4, 330 - "tdm": 0xe9e6, 331 - "thb": 0xe961, 332 - "ths": 0xe650, 333 - "tla": 0xe9fb, 334 - "tle": 0xea0b, 335 - "tmc": 0xea15, 336 - "tmp": 0xe61f, 337 - "tmt": 0xea06, 338 - "tor": 0xe62c, 339 - "tpr": 0xe694, 340 - "tsp": 0xe63a, 341 - "tsr": 0xe976, 342 - "uds": 0xe624, 343 - "ugl": 0xe691, 344 - "ulg": 0xe623, 345 - "uma": 0xe958, 346 - "una": 0xe9be, 347 - "und": 0xe96c, 348 - "unf": 0xe98a, 349 - "unh": 0xe692, 350 - "usg": 0xe622, 351 - "ust": 0xe930, 352 - "v09": 0xe679, 353 - "v0x": 0xe920, 354 - "v10": 0xe67a, 355 - "v11": 0xe67b, 356 - "v12": 0xe67c, 357 - "v13": 0xe67d, 358 - "v14": 0xe67e, 359 - "v15": 0xe905, 360 - "v16": 0xe906, 361 - "v17": 0xe939, 362 - "van": 0xe655, 363 - "vis": 0xe61d, 364 - "vma": 0xe696, 365 - "voc": 0xe986, 366 - "vow": 0xe977, 367 - "w16": 0xe907, 368 - "w17": 0xe923, 369 - "war": 0xe95a, 370 - "who": 0xe9b0, 371 - "woc": 0xe9b9, 372 - "woe": 0xe9ae, 373 - "wot": 0xe9c0, 374 - "wth": 0xe61e, 375 - "wwk": 0xe645, 376 - "x2ps": 0xe928, 377 - "x4ea": 0xe929, 378 - "xcle": 0xe926, 379 - "xduels": 0xe91d, 380 - "xice": 0xe927, 381 - "xlcu": 0xe90c, 382 - "xln": 0xe92e, 383 - "xmods": 0xe91e, 384 - "xren": 0xe917, 385 - "xrin": 0xe918, 386 - "yeoe": 0xe9da, 387 - "yone": 0xe9a7, 388 - "ysnc": 0xe989, 389 - "ywoe": 0xe9bd, 390 - "zen": 0xe644, 391 - "znc": 0xe967, 392 - "zne": 0xe97a, 393 - "znr": 0xe963, 394 - }; 395 - 396 - /** 397 - * Get unicode character for a set symbol 398 - * Returns empty string if set not found 399 - */ 400 - export function getSetSymbol(setCode: string): string { 401 - const codepoint = SET_SYMBOLS[setCode.toLowerCase()]; 402 - return codepoint ? String.fromCodePoint(codepoint) : ""; 403 - }