1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
import { blob } from "https://esm.town/v/std/blob";
import { icons, IconStyle } from "npm:@phosphor-icons/core";
import { Font, FontEditor, woff2 } from "npm:fonteditor-core";
const CDN_BASE_URL = "https://unpkg.com/@phosphor-icons";
type SemVer = `${number}.${number}.${number}`;
type IconStyleMap = Partial<Record<IconStyle, string[]>>;
type FontFormatMap = Partial<Record<FontEditor.FontType, ArrayBuffer>>;
type SerialFontFormatMap = Partial<Record<FontEditor.FontType, string>>;
type FontPack = {
fonts: FontFormatMap;
css: string;
};
type SerialFontPack = {
fonts: SerialFontFormatMap;
css: string;
};
type IcoMoonSelection = {
IcoMoonType: "selection";
icons: {
icon: {
paths: string[];
attrs: Record<string, string>[];
isMulticolor: boolean;
isMulticolor2: boolean;
grid: number;
tags: string[];
};
attrs: Record<string, string>[];
properties: {
order: number;
id: number;
name: string;
prevSize: number;
code: number;
codes?: number[];
ligatures: string;
};
setIdx: number;
setId: number;
iconIdx: number;
}[];
};
const CACHE = new (class {
constructor() {}
private packageVersion(version: SemVer): string {
return !!version ? `web@${version}` : "web";
}
private fontName(weight: IconStyle): string {
return weight === "regular"
? "Phosphor"
: `Phosphor-${weight.replace(/^\w/, (c) => c.toUpperCase())}`;
}
private fontURL(weight: IconStyle, version: SemVer): string {
return `${CDN_BASE_URL}/${
this.packageVersion(
version,
)
}/src/${weight}/${this.fontName(weight)}.ttf`;
}
private cssURL(weight: IconStyle, version: SemVer): string {
return `${CDN_BASE_URL}/${
this.packageVersion(
version,
)
}/src/${weight}/style.css`;
}
private selectionURL(weight: IconStyle, version: SemVer) {
return `${CDN_BASE_URL}/${
this.packageVersion(
version,
)
}/src/${weight}/selection.json`;
}
async getSelection(weight: IconStyle, version: SemVer = "2.1.1"): Promise<IcoMoonSelection> {
let selection = await blob.getJSON(`ph-${version}-selection-${weight}`);
if (!selection) {
const res = await fetch(this.selectionURL(weight, version), {
method: "GET",
mode: "cors",
cache: "default",
});
if (res.status !== 200) {
throw new Error(res.statusText);
}
selection = (await res.json()) as IcoMoonSelection;
await blob.setJSON(`ph-${version}-selection-${weight}`, selection);
}
return selection;