mimeforge 0.0.0-stage → 0.1.1
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/CHANGELOG.md +22 -0
- package/LICENSE +21 -0
- package/README.md +291 -2
- package/THIRD_PARTY.md +10 -0
- package/assets/bodies/archive.svg +13 -0
- package/assets/bodies/audio.svg +14 -0
- package/assets/bodies/code.svg +14 -0
- package/assets/bodies/data.svg +13 -0
- package/assets/bodies/database.svg +14 -0
- package/assets/bodies/design.svg +15 -0
- package/assets/bodies/disk.svg +16 -0
- package/assets/bodies/ebook.svg +13 -0
- package/assets/bodies/executable.svg +13 -0
- package/assets/bodies/font.svg +13 -0
- package/assets/bodies/game.svg +17 -0
- package/assets/bodies/generic.svg +13 -0
- package/assets/bodies/geo.svg +13 -0
- package/assets/bodies/image.svg +14 -0
- package/assets/bodies/mail.svg +13 -0
- package/assets/bodies/model3d.svg +14 -0
- package/assets/bodies/pdf.svg +13 -0
- package/assets/bodies/science.svg +13 -0
- package/assets/bodies/security.svg +14 -0
- package/assets/bodies/sheet.svg +13 -0
- package/assets/bodies/slides.svg +13 -0
- package/assets/bodies/text.svg +14 -0
- package/assets/bodies/video.svg +13 -0
- package/assets/bodies/word.svg +12 -0
- package/assets/fonts/monogram.json +106 -0
- package/dist/body.d.ts +24 -0
- package/dist/body.js +70 -0
- package/dist/categories.d.ts +10 -0
- package/dist/categories.js +122 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +127 -0
- package/dist/color.d.ts +12 -0
- package/dist/color.js +34 -0
- package/dist/config.d.ts +22 -0
- package/dist/config.js +30 -0
- package/dist/font.d.ts +47 -0
- package/dist/font.js +102 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +8 -0
- package/dist/label.d.ts +18 -0
- package/dist/label.js +36 -0
- package/dist/mime.d.ts +6 -0
- package/dist/mime.js +28 -0
- package/dist/palette.d.ts +13 -0
- package/dist/palette.js +32 -0
- package/dist/paths.d.ts +3 -0
- package/dist/paths.js +5 -0
- package/dist/png.d.ts +2 -0
- package/dist/png.js +11 -0
- package/dist/render.d.ts +26 -0
- package/dist/render.js +23 -0
- package/dist/testrun.d.ts +12 -0
- package/dist/testrun.js +70 -0
- package/docs/bodies.md +86 -0
- package/docs/categories.md +42 -0
- package/docs/fonts.md +63 -0
- package/docs/preview.svg +1 -0
- package/package.json +76 -4
- package/third_party/MIME-DB-LICENSE +23 -0
- package/third_party/monogram/CREDITS.txt +15 -0
- package/third_party/monogram/monogram.ttf +0 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
|
2
|
+
<g id="page">
|
|
3
|
+
<path d="M7.5 2.5H19.5L26.5 9.5V27.5A2 2 0 0 1 24.5 29.5H7.5A2 2 0 0 1 5.5 27.5V4.5A2 2 0 0 1 7.5 2.5Z" fill="#FFFFFF" stroke="#B8BFC7" stroke-linejoin="round"/>
|
|
4
|
+
</g>
|
|
5
|
+
<g id="emblem">
|
|
6
|
+
<path d="M13 11V10A3 3 0 0 1 19 10V11" fill="none" stroke="#FF00FF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
7
|
+
<rect x="9.5" y="11" width="13" height="7" rx="2" fill="#FF00FF"/>
|
|
8
|
+
<circle cx="16" cy="14.5" r="1.5" fill="#FFFFFF"/>
|
|
9
|
+
</g>
|
|
10
|
+
<g id="fold">
|
|
11
|
+
<path d="M19.5 2.5V7.5Q19.5 9.5 21.5 9.5H26.5Z" fill="#E3E7EB"/>
|
|
12
|
+
<path d="M19.5 2.5V7.5Q19.5 9.5 21.5 9.5H26.5Z" fill="none" stroke="#B8BFC7" stroke-linejoin="round"/>
|
|
13
|
+
</g>
|
|
14
|
+
</svg>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
|
2
|
+
<g id="page">
|
|
3
|
+
<path d="M7.5 2.5H19.5L26.5 9.5V27.5A2 2 0 0 1 24.5 29.5H7.5A2 2 0 0 1 5.5 27.5V4.5A2 2 0 0 1 7.5 2.5Z" fill="#FFFFFF" stroke="#B8BFC7" stroke-linejoin="round"/>
|
|
4
|
+
</g>
|
|
5
|
+
<g id="emblem">
|
|
6
|
+
<path d="M9 10h6v3H9z M17 10h6v3h-6z" fill="#FF00FF"/>
|
|
7
|
+
<path d="M9 15h6v3H9z M17 15h6v3h-6z" fill="#FF99FF"/>
|
|
8
|
+
</g>
|
|
9
|
+
<g id="fold">
|
|
10
|
+
<path d="M19.5 2.5V7.5Q19.5 9.5 21.5 9.5H26.5Z" fill="#E3E7EB"/>
|
|
11
|
+
<path d="M19.5 2.5V7.5Q19.5 9.5 21.5 9.5H26.5Z" fill="none" stroke="#B8BFC7" stroke-linejoin="round"/>
|
|
12
|
+
</g>
|
|
13
|
+
</svg>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
|
2
|
+
<g id="page">
|
|
3
|
+
<path d="M7.5 2.5H19.5L26.5 9.5V27.5A2 2 0 0 1 24.5 29.5H7.5A2 2 0 0 1 5.5 27.5V4.5A2 2 0 0 1 7.5 2.5Z" fill="#FFFFFF" stroke="#B8BFC7" stroke-linejoin="round"/>
|
|
4
|
+
</g>
|
|
5
|
+
<g id="emblem">
|
|
6
|
+
<rect x="9" y="9" width="14" height="9" rx="2" fill="#FF99FF"/>
|
|
7
|
+
<path d="M11 13h2v3h-2z M15 11.5h2v4.5h-2z M19 10h2v6h-2z" fill="#FF00FF"/>
|
|
8
|
+
</g>
|
|
9
|
+
<g id="fold">
|
|
10
|
+
<path d="M19.5 2.5V7.5Q19.5 9.5 21.5 9.5H26.5Z" fill="#E3E7EB"/>
|
|
11
|
+
<path d="M19.5 2.5V7.5Q19.5 9.5 21.5 9.5H26.5Z" fill="none" stroke="#B8BFC7" stroke-linejoin="round"/>
|
|
12
|
+
</g>
|
|
13
|
+
</svg>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
|
2
|
+
<g id="page">
|
|
3
|
+
<path d="M7.5 2.5H19.5L26.5 9.5V27.5A2 2 0 0 1 24.5 29.5H7.5A2 2 0 0 1 5.5 27.5V4.5A2 2 0 0 1 7.5 2.5Z" fill="#FFFFFF" stroke="#B8BFC7" stroke-linejoin="round"/>
|
|
4
|
+
</g>
|
|
5
|
+
<g id="emblem">
|
|
6
|
+
<rect x="9" y="6" width="10" height="2" rx="1" fill="#FF00FF"/>
|
|
7
|
+
<rect x="9" y="11" width="14" height="2" rx="1" fill="#FF00FF"/>
|
|
8
|
+
<rect x="9" y="16" width="8" height="2" rx="1" fill="#FF00FF"/>
|
|
9
|
+
</g>
|
|
10
|
+
<g id="fold">
|
|
11
|
+
<path d="M19.5 2.5V7.5Q19.5 9.5 21.5 9.5H26.5Z" fill="#E3E7EB"/>
|
|
12
|
+
<path d="M19.5 2.5V7.5Q19.5 9.5 21.5 9.5H26.5Z" fill="none" stroke="#B8BFC7" stroke-linejoin="round"/>
|
|
13
|
+
</g>
|
|
14
|
+
</svg>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
|
2
|
+
<g id="page">
|
|
3
|
+
<path d="M7.5 2.5H19.5L26.5 9.5V27.5A2 2 0 0 1 24.5 29.5H7.5A2 2 0 0 1 5.5 27.5V4.5A2 2 0 0 1 7.5 2.5Z" fill="#FFFFFF" stroke="#B8BFC7" stroke-linejoin="round"/>
|
|
4
|
+
</g>
|
|
5
|
+
<g id="emblem">
|
|
6
|
+
<rect x="9" y="9" width="14" height="9" rx="2.5" fill="#FF00FF"/>
|
|
7
|
+
<polygon points="14,10.8 19.5,13.5 14,16.2" fill="#FFFFFF"/>
|
|
8
|
+
</g>
|
|
9
|
+
<g id="fold">
|
|
10
|
+
<path d="M19.5 2.5V7.5Q19.5 9.5 21.5 9.5H26.5Z" fill="#E3E7EB"/>
|
|
11
|
+
<path d="M19.5 2.5V7.5Q19.5 9.5 21.5 9.5H26.5Z" fill="none" stroke="#B8BFC7" stroke-linejoin="round"/>
|
|
12
|
+
</g>
|
|
13
|
+
</svg>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
|
2
|
+
<g id="page">
|
|
3
|
+
<path d="M7.5 2.5H19.5L26.5 9.5V27.5A2 2 0 0 1 24.5 29.5H7.5A2 2 0 0 1 5.5 27.5V4.5A2 2 0 0 1 7.5 2.5Z" fill="#FFFFFF" stroke="#B8BFC7" stroke-linejoin="round"/>
|
|
4
|
+
</g>
|
|
5
|
+
<g id="emblem">
|
|
6
|
+
<path d="M10 7L12.7 17L16 10L19.3 17L22 7" fill="none" stroke="#FF00FF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
7
|
+
</g>
|
|
8
|
+
<g id="fold">
|
|
9
|
+
<path d="M19.5 2.5V7.5Q19.5 9.5 21.5 9.5H26.5Z" fill="#E3E7EB"/>
|
|
10
|
+
<path d="M19.5 2.5V7.5Q19.5 9.5 21.5 9.5H26.5Z" fill="none" stroke="#B8BFC7" stroke-linejoin="round"/>
|
|
11
|
+
</g>
|
|
12
|
+
</svg>
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "Monogram",
|
|
3
|
+
"author": "Vinícius Menézio (datagoblin)",
|
|
4
|
+
"license": "CC0-1.0",
|
|
5
|
+
"source": "https://datagoblin.itch.io/monogram",
|
|
6
|
+
"cols": 5,
|
|
7
|
+
"rows": 7,
|
|
8
|
+
"descender": 1,
|
|
9
|
+
"advance": 6,
|
|
10
|
+
"glyphs": {
|
|
11
|
+
"0": [ ".###.", "#...#", "#..##", "#.#.#", "##..#", "#...#", ".###.", "....." ],
|
|
12
|
+
"1": [ "..#..", ".##..", "..#..", "..#..", "..#..", "..#..", "#####", "....." ],
|
|
13
|
+
"2": [ ".###.", "#...#", "....#", "...#.", "..#..", ".#...", "#####", "....." ],
|
|
14
|
+
"3": [ ".###.", "#...#", "....#", "..##.", "....#", "#...#", ".###.", "....." ],
|
|
15
|
+
"4": [ ".#..#", ".#..#", "#...#", "#####", "....#", "....#", "....#", "....." ],
|
|
16
|
+
"5": [ "#####", "#....", "####.", "....#", "....#", "#...#", ".###.", "....." ],
|
|
17
|
+
"6": [ ".###.", "#....", "#....", "####.", "#...#", "#...#", ".###.", "....." ],
|
|
18
|
+
"7": [ "#####", "....#", "....#", "...#.", "..#..", "..#..", "..#..", "....." ],
|
|
19
|
+
"8": [ ".###.", "#...#", "#...#", ".###.", "#...#", "#...#", ".###.", "....." ],
|
|
20
|
+
"9": [ ".###.", "#...#", "#...#", ".####", "....#", "#...#", ".###.", "....." ],
|
|
21
|
+
"!": [ "..#..", "..#..", "..#..", "..#..", "..#..", ".....", "..#..", "....." ],
|
|
22
|
+
"\"": [ ".#.#.", ".#.#.", ".#.#.", ".....", ".....", ".....", ".....", "....." ],
|
|
23
|
+
"#": [ ".....", ".#.#.", "#####", ".#.#.", ".#.#.", "#####", ".#.#.", "....." ],
|
|
24
|
+
"$": [ "..#..", ".####", "#.#..", ".###.", "..#.#", "####.", "..#..", "....." ],
|
|
25
|
+
"%": [ "#...#", "#...#", "...#.", "..#..", ".#...", "#...#", "#...#", "....." ],
|
|
26
|
+
"&": [ ".##..", "#..#.", "#..#.", ".####", "#..#.", "#..#.", ".##.#", "....." ],
|
|
27
|
+
"'": [ "..#..", "..#..", "..#..", ".....", ".....", ".....", ".....", "....." ],
|
|
28
|
+
"(": [ "...#.", "..#..", "..#..", "..#..", "..#..", "..#..", "...#.", "....." ],
|
|
29
|
+
")": [ ".#...", "..#..", "..#..", "..#..", "..#..", "..#..", ".#...", "....." ],
|
|
30
|
+
"*": [ ".....", "..#..", "#.#.#", ".###.", "#.#.#", "..#..", ".....", "....." ],
|
|
31
|
+
"+": [ ".....", "..#..", "..#..", "#####", "..#..", "..#..", ".....", "....." ],
|
|
32
|
+
",": [ ".....", ".....", ".....", ".....", ".....", "..#..", "..#..", ".#..." ],
|
|
33
|
+
"-": [ ".....", ".....", ".....", "#####", ".....", ".....", ".....", "....." ],
|
|
34
|
+
".": [ ".....", ".....", ".....", ".....", ".....", "..#..", "..#..", "....." ],
|
|
35
|
+
"/": [ "....#", "....#", "...#.", "..#..", ".#...", "#....", "#....", "....." ],
|
|
36
|
+
":": [ ".....", "..#..", "..#..", ".....", ".....", "..#..", "..#..", "....." ],
|
|
37
|
+
";": [ ".....", "..#..", "..#..", ".....", ".....", "..#..", "..#..", ".#..." ],
|
|
38
|
+
"<": [ ".....", "...##", ".##..", "#....", ".##..", "...##", ".....", "....." ],
|
|
39
|
+
"=": [ ".....", ".....", "#####", ".....", "#####", ".....", ".....", "....." ],
|
|
40
|
+
">": [ ".....", "##...", "..##.", "....#", "..##.", "##...", ".....", "....." ],
|
|
41
|
+
"?": [ ".###.", "#...#", "....#", "...#.", "..#..", ".....", "..#..", "....." ],
|
|
42
|
+
"@": [ ".###.", "#..##", "#.#.#", "#.#.#", "#..##", "#....", ".###.", "....." ],
|
|
43
|
+
"A": [ ".###.", "#...#", "#...#", "#...#", "#####", "#...#", "#...#", "....." ],
|
|
44
|
+
"B": [ "####.", "#...#", "#...#", "####.", "#...#", "#...#", "####.", "....." ],
|
|
45
|
+
"C": [ ".###.", "#...#", "#....", "#....", "#....", "#...#", ".###.", "....." ],
|
|
46
|
+
"D": [ "####.", "#...#", "#...#", "#...#", "#...#", "#...#", "####.", "....." ],
|
|
47
|
+
"E": [ "#####", "#....", "#....", "####.", "#....", "#....", "#####", "....." ],
|
|
48
|
+
"F": [ "#####", "#....", "#....", "####.", "#....", "#....", "#....", "....." ],
|
|
49
|
+
"G": [ ".###.", "#...#", "#....", "#.###", "#...#", "#...#", ".###.", "....." ],
|
|
50
|
+
"H": [ "#...#", "#...#", "#...#", "#####", "#...#", "#...#", "#...#", "....." ],
|
|
51
|
+
"I": [ "#####", "..#..", "..#..", "..#..", "..#..", "..#..", "#####", "....." ],
|
|
52
|
+
"J": [ "....#", "....#", "....#", "....#", "#...#", "#...#", ".###.", "....." ],
|
|
53
|
+
"K": [ "#...#", "#..#.", "#.#..", "##...", "#.#..", "#..#.", "#...#", "....." ],
|
|
54
|
+
"L": [ "#....", "#....", "#....", "#....", "#....", "#....", "#####", "....." ],
|
|
55
|
+
"M": [ "#...#", "##.##", "#.#.#", "#...#", "#...#", "#...#", "#...#", "....." ],
|
|
56
|
+
"N": [ "#...#", "#...#", "##..#", "#.#.#", "#..##", "#...#", "#...#", "....." ],
|
|
57
|
+
"O": [ ".###.", "#...#", "#...#", "#...#", "#...#", "#...#", ".###.", "....." ],
|
|
58
|
+
"P": [ "####.", "#...#", "#...#", "####.", "#....", "#....", "#....", "....." ],
|
|
59
|
+
"Q": [ ".###.", "#...#", "#...#", "#...#", "#...#", "#...#", ".###.", "...##" ],
|
|
60
|
+
"R": [ "####.", "#...#", "#...#", "####.", "#...#", "#...#", "#...#", "....." ],
|
|
61
|
+
"S": [ ".###.", "#...#", "#....", ".###.", "....#", "#...#", ".###.", "....." ],
|
|
62
|
+
"T": [ "#####", "..#..", "..#..", "..#..", "..#..", "..#..", "..#..", "....." ],
|
|
63
|
+
"U": [ "#...#", "#...#", "#...#", "#...#", "#...#", "#...#", ".###.", "....." ],
|
|
64
|
+
"V": [ "#...#", "#...#", "#...#", "#...#", ".#.#.", ".#.#.", "..#..", "....." ],
|
|
65
|
+
"W": [ "#...#", "#...#", "#...#", "#...#", "#.#.#", "##.##", "#...#", "....." ],
|
|
66
|
+
"X": [ "#...#", "#...#", ".#.#.", "..#..", ".#.#.", "#...#", "#...#", "....." ],
|
|
67
|
+
"Y": [ "#...#", "#...#", ".#.#.", "..#..", "..#..", "..#..", "..#..", "....." ],
|
|
68
|
+
"Z": [ "#####", "....#", "...#.", "..#..", ".#...", "#....", "#####", "....." ],
|
|
69
|
+
"[": [ "..##.", "..#..", "..#..", "..#..", "..#..", "..#..", "..##.", "....." ],
|
|
70
|
+
"\\": [ "#....", "#....", ".#...", "..#..", "...#.", "....#", "....#", "....." ],
|
|
71
|
+
"]": [ ".##..", "..#..", "..#..", "..#..", "..#..", "..#..", ".##..", "....." ],
|
|
72
|
+
"^": [ "..#..", ".#.#.", "#...#", ".....", ".....", ".....", ".....", "....." ],
|
|
73
|
+
"_": [ ".....", ".....", ".....", ".....", ".....", ".....", "#####", "....." ],
|
|
74
|
+
"`": [ ".#...", "..#..", ".....", ".....", ".....", ".....", ".....", "....." ],
|
|
75
|
+
"a": [ ".....", ".....", ".####", "#...#", "#...#", "#...#", ".####", "....." ],
|
|
76
|
+
"b": [ "#....", "#....", "####.", "#...#", "#...#", "#...#", "####.", "....." ],
|
|
77
|
+
"c": [ ".....", ".....", ".###.", "#...#", "#....", "#...#", ".###.", "....." ],
|
|
78
|
+
"d": [ "....#", "....#", ".####", "#...#", "#...#", "#...#", ".####", "....." ],
|
|
79
|
+
"e": [ ".....", ".....", ".###.", "#...#", "#####", "#....", ".###.", "....." ],
|
|
80
|
+
"f": [ "..##.", ".#..#", ".#...", "####.", ".#...", ".#...", ".#...", "....." ],
|
|
81
|
+
"g": [ ".....", ".....", ".####", "#...#", "#...#", "#...#", ".####", "....#" ],
|
|
82
|
+
"h": [ "#....", "#....", "####.", "#...#", "#...#", "#...#", "#...#", "....." ],
|
|
83
|
+
"i": [ "..#..", ".....", ".##..", "..#..", "..#..", "..#..", "#####", "....." ],
|
|
84
|
+
"j": [ "....#", ".....", "...##", "....#", "....#", "....#", "....#", "#...#" ],
|
|
85
|
+
"k": [ "#....", "#....", "#...#", "#..#.", "###..", "#..#.", "#...#", "....." ],
|
|
86
|
+
"l": [ "##...", ".#...", ".#...", ".#...", ".#...", ".#...", "..###", "....." ],
|
|
87
|
+
"m": [ ".....", ".....", "####.", "#.#.#", "#.#.#", "#.#.#", "#.#.#", "....." ],
|
|
88
|
+
"n": [ ".....", ".....", "####.", "#...#", "#...#", "#...#", "#...#", "....." ],
|
|
89
|
+
"o": [ ".....", ".....", ".###.", "#...#", "#...#", "#...#", ".###.", "....." ],
|
|
90
|
+
"p": [ ".....", ".....", "####.", "#...#", "#...#", "#...#", "####.", "#...." ],
|
|
91
|
+
"q": [ ".....", ".....", ".####", "#...#", "#...#", "#...#", ".####", "....#" ],
|
|
92
|
+
"r": [ ".....", ".....", "#.##.", "##..#", "#....", "#....", "#....", "....." ],
|
|
93
|
+
"s": [ ".....", ".....", ".####", "#....", ".###.", "....#", "####.", "....." ],
|
|
94
|
+
"t": [ ".#...", ".#...", "####.", ".#...", ".#...", ".#...", "..###", "....." ],
|
|
95
|
+
"u": [ ".....", ".....", "#...#", "#...#", "#...#", "#...#", ".####", "....." ],
|
|
96
|
+
"v": [ ".....", ".....", "#...#", "#...#", "#...#", ".#.#.", "..#..", "....." ],
|
|
97
|
+
"w": [ ".....", ".....", "#...#", "#...#", "#.#.#", "#.#.#", ".#.#.", "....." ],
|
|
98
|
+
"x": [ ".....", ".....", "#...#", ".#.#.", "..#..", ".#.#.", "#...#", "....." ],
|
|
99
|
+
"y": [ ".....", ".....", "#...#", "#...#", "#...#", "#...#", ".####", "....#" ],
|
|
100
|
+
"z": [ ".....", ".....", "#####", "...#.", "..#..", ".#...", "#####", "....." ],
|
|
101
|
+
"{": [ "...#.", "..#..", "..#..", ".#...", "..#..", "..#..", "...#.", "....." ],
|
|
102
|
+
"|": [ "..#..", "..#..", "..#..", "..#..", "..#..", "..#..", "..#..", "....." ],
|
|
103
|
+
"}": [ ".#...", "..#..", "..#..", "...#.", "..#..", "..#..", ".#...", "....." ],
|
|
104
|
+
"~": [ ".....", ".....", ".#..#", "#.##.", ".....", ".....", ".....", "....." ]
|
|
105
|
+
}
|
|
106
|
+
}
|
package/dist/body.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type Category } from './categories.js';
|
|
2
|
+
export interface BodyTokens {
|
|
3
|
+
/** Literal color in body SVGs that is replaced by the accent. */
|
|
4
|
+
accent: string;
|
|
5
|
+
/** Literal color replaced by the lighter tint of the accent. */
|
|
6
|
+
tint: string;
|
|
7
|
+
}
|
|
8
|
+
export declare const DEFAULT_TOKENS: BodyTokens;
|
|
9
|
+
export interface BodySet {
|
|
10
|
+
source: string;
|
|
11
|
+
svg(category: Category): string;
|
|
12
|
+
warnings: string[];
|
|
13
|
+
/** Categories that have no own body and are drawn with the generic one. */
|
|
14
|
+
missing: Category[];
|
|
15
|
+
}
|
|
16
|
+
/** Check the body contract (see docs/BODY-SPEC.md). Returns human readable problems. */
|
|
17
|
+
export declare function validateBody(svg: string, name: string, tokens?: BodyTokens): string[];
|
|
18
|
+
/**
|
|
19
|
+
* `spec`: undefined → bundled bodies. A directory → `<category>.svg` files, missing ones fall back to the
|
|
20
|
+
* directory's `generic.svg`, then to the bundled body. A single `.svg` file → used for every category.
|
|
21
|
+
*/
|
|
22
|
+
export declare function loadBodies(spec?: string, tokens?: BodyTokens): BodySet;
|
|
23
|
+
/** Replace the color tokens in a body SVG. */
|
|
24
|
+
export declare function recolor(svg: string, accent: string, tokens?: BodyTokens, tintAmount?: number): string;
|
package/dist/body.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
|
|
2
|
+
import { basename, join } from 'node:path';
|
|
3
|
+
import { CATEGORIES, isCategory } from './categories.js';
|
|
4
|
+
import { tint } from './color.js';
|
|
5
|
+
import { assetPath } from './paths.js';
|
|
6
|
+
export const DEFAULT_TOKENS = { accent: '#FF00FF', tint: '#FF99FF' };
|
|
7
|
+
/** Check the body contract (see docs/BODY-SPEC.md). Returns human readable problems. */
|
|
8
|
+
export function validateBody(svg, name, tokens = DEFAULT_TOKENS) {
|
|
9
|
+
const problems = [];
|
|
10
|
+
if (!/<svg[^>]*viewBox="0 0 32 32"/.test(svg))
|
|
11
|
+
problems.push(`${name}: root <svg> must have viewBox="0 0 32 32"`);
|
|
12
|
+
if (!svg.toLowerCase().includes(tokens.accent.toLowerCase()))
|
|
13
|
+
problems.push(`${name}: no accent token ${tokens.accent} found (icon cannot be recolored)`);
|
|
14
|
+
if (!/<\/svg>\s*$/.test(svg))
|
|
15
|
+
problems.push(`${name}: must end with </svg>`);
|
|
16
|
+
return problems;
|
|
17
|
+
}
|
|
18
|
+
function readDirBodies(dir) {
|
|
19
|
+
const map = new Map();
|
|
20
|
+
for (const f of readdirSync(dir)) {
|
|
21
|
+
if (!f.endsWith('.svg'))
|
|
22
|
+
continue;
|
|
23
|
+
const cat = basename(f, '.svg');
|
|
24
|
+
if (isCategory(cat))
|
|
25
|
+
map.set(cat, readFileSync(join(dir, f), 'utf8').trim());
|
|
26
|
+
}
|
|
27
|
+
return map;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* `spec`: undefined → bundled bodies. A directory → `<category>.svg` files, missing ones fall back to the
|
|
31
|
+
* directory's `generic.svg`, then to the bundled body. A single `.svg` file → used for every category.
|
|
32
|
+
*/
|
|
33
|
+
export function loadBodies(spec, tokens = DEFAULT_TOKENS) {
|
|
34
|
+
const bundled = readDirBodies(assetPath('bodies'));
|
|
35
|
+
const warnings = [];
|
|
36
|
+
let custom;
|
|
37
|
+
let single;
|
|
38
|
+
if (spec) {
|
|
39
|
+
if (!existsSync(spec))
|
|
40
|
+
throw new Error(`Body path not found: ${spec}`);
|
|
41
|
+
if (statSync(spec).isDirectory())
|
|
42
|
+
custom = readDirBodies(spec);
|
|
43
|
+
else
|
|
44
|
+
single = readFileSync(spec, 'utf8').trim();
|
|
45
|
+
}
|
|
46
|
+
if (single)
|
|
47
|
+
warnings.push(...validateBody(single, spec, tokens));
|
|
48
|
+
if (custom)
|
|
49
|
+
for (const [c, s] of custom)
|
|
50
|
+
warnings.push(...validateBody(s, `${spec}/${c}.svg`, tokens));
|
|
51
|
+
const has = (c) => !!(single ?? custom?.get(c) ?? bundled.get(c));
|
|
52
|
+
return {
|
|
53
|
+
source: spec ?? 'bundled',
|
|
54
|
+
warnings,
|
|
55
|
+
missing: CATEGORIES.filter((c) => !has(c)),
|
|
56
|
+
svg(category) {
|
|
57
|
+
const s = single ?? custom?.get(category) ?? custom?.get('generic') ?? bundled.get(category) ?? bundled.get('generic');
|
|
58
|
+
if (!s)
|
|
59
|
+
throw new Error(`No body for category "${category}"`);
|
|
60
|
+
return s;
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
/** Replace the color tokens in a body SVG. */
|
|
65
|
+
export function recolor(svg, accent, tokens = DEFAULT_TOKENS, tintAmount = 0.55) {
|
|
66
|
+
const esc = (s) => s.replace(/[.*+?^${}()|[\]\\#]/g, '\\$&');
|
|
67
|
+
return svg
|
|
68
|
+
.replace(new RegExp(esc(tokens.accent), 'gi'), accent)
|
|
69
|
+
.replace(new RegExp(esc(tokens.tint), 'gi'), tint(accent, tintAmount));
|
|
70
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare const CATEGORIES: readonly ['generic', 'text', 'code', 'data', 'pdf', 'word', 'sheet', 'slides', 'image', 'audio', 'video', 'archive', 'font', 'ebook', 'database', 'executable', 'security', 'model3d', 'geo', 'mail', 'science', 'disk', 'design', 'game'];
|
|
2
|
+
export type Category = (typeof CATEGORIES)[number];
|
|
3
|
+
export declare const isCategory: (v: string) => v is Category;
|
|
4
|
+
export declare const EXT_OVERRIDES: ReadonlyMap<string, Category>;
|
|
5
|
+
export declare function classifyMime(mime: string): Category;
|
|
6
|
+
/**
|
|
7
|
+
* Category for an extension (without dot, any case). `mimes` are all mime types registered for it; an extension can
|
|
8
|
+
* have several (`mp4` is both application/mp4 and video/mp4), the first one that is not "generic" wins.
|
|
9
|
+
*/
|
|
10
|
+
export declare function classify(ext: string, mimes?: string | readonly string[]): Category;
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
export const CATEGORIES = [
|
|
2
|
+
'generic', 'text', 'code', 'data', 'pdf', 'word', 'sheet', 'slides', 'image', 'audio', 'video',
|
|
3
|
+
'archive', 'font', 'ebook', 'database', 'executable', 'security', 'model3d', 'geo', 'mail', 'science',
|
|
4
|
+
'disk', 'design', 'game',
|
|
5
|
+
];
|
|
6
|
+
export const isCategory = (v) => CATEGORIES.includes(v);
|
|
7
|
+
/** Extension → category for popular formats that mime types alone get wrong or do not know. Checked first. */
|
|
8
|
+
const EXT_GROUPS = {
|
|
9
|
+
text: 'txt text md markdown mdx log ini conf cfg rst adoc org tex lock gitignore editorconfig',
|
|
10
|
+
code: 'js mjs cjs jsx ts tsx mts cts vue svelte astro py pyw rb go rs java kt kts swift c h cc cpp cxx hpp hh cs fs php sh bash zsh fish ps1 bat cmd lua pl pm r scala dart ex exs erl hs clj groovy gradle makefile dockerfile html htm xhtml css scss sass less ipynb vb vbs asm wasm coffee elm nim zig',
|
|
11
|
+
data: 'json jsonc json5 jsonl ndjson yaml yml toml xml xsd xsl xslt rss atom plist env properties proto graphql gql ndjson csv-schema',
|
|
12
|
+
image: 'jpg jpeg png gif webp avif heic heif bmp tif tiff ico icns svg raw cr2 nef dng jxl apng',
|
|
13
|
+
archive: 'zip rar 7z tar gz tgz bz2 xz zst lz lz4 lzma cab jar war ear pkg deb rpm apk arc gtar ustar cfs msix msixbundle appx appxbundle sis sisx crx xpi oxt',
|
|
14
|
+
font: 'ttf otf woff woff2 eot',
|
|
15
|
+
ebook: 'epub mobi azw azw3 fb2 cbz cbr djvu lrf',
|
|
16
|
+
database: 'db sqlite sqlite3 mdb accdb sql dbf',
|
|
17
|
+
executable: 'exe msi app dll so dylib elf out com run class luac application xbap',
|
|
18
|
+
security: 'pem key crt cer der pub gpg pgp asc pfx p12 jks csr kdbx scq scs spq spp roa mft dssc',
|
|
19
|
+
model3d: 'glb gltf obj stl fbx dae 3ds blend step stp ply usdz',
|
|
20
|
+
geo: 'kml kmz gpx geojson shp dwg dxf gml mvt mgp',
|
|
21
|
+
mail: 'eml msg mbox ics vcf emlx',
|
|
22
|
+
science: 'hdf hdf5 h5 nc mat fits cif pdb sdf mol seed mseed dataless',
|
|
23
|
+
disk: 'iso dmg img toast vhd vhdx vmdk vdi qcow qcow2 ova ovf vbox vbox-extpack hdd nrg mdf wim',
|
|
24
|
+
design: 'psd psb ai sketch fig xd indd indt idml cdr afdesign afphoto afpub xcf kra ora vsd vsdx vss vst vsw vtx vdx qwd qwt qxb qxd qxl qxt fm frame maker book',
|
|
25
|
+
game: 'z1 z2 z3 z4 z5 z6 z7 z8 wad gam t3 sm smzip n-gage ngdat blb blorb c4d c4f c4g c4p c4u dir dcr dxr cst cct cxt w3d fgd swa aam aas swf rom nes smc sfc gba gbc gb nds n64 z64 v64 sav srm sms gg pce nsp xci pak vpk unity3d uasset umap',
|
|
26
|
+
};
|
|
27
|
+
/** Office-like and other formats mapped by function (no brand-specific bodies). */
|
|
28
|
+
const FUNCTION_MAP = {
|
|
29
|
+
// Google Workspace shortcut files
|
|
30
|
+
gdoc: 'word', gsheet: 'sheet', gslides: 'slides', gdraw: 'image', gscript: 'code', gmap: 'geo',
|
|
31
|
+
// StarOffice / StarDivision
|
|
32
|
+
sdw: 'word', sgl: 'word', vor: 'word', sdc: 'sheet', smf: 'sheet', sdd: 'slides', sda: 'image',
|
|
33
|
+
// MS Works, Project, Office theme, OneNote, XPS, HTML help
|
|
34
|
+
wps: 'word', wcm: 'word', wks: 'sheet', wdb: 'database', mpt: 'sheet', thmx: 'slides',
|
|
35
|
+
one: 'text', onea: 'text', onepkg: 'text', onetmp: 'text', onetoc: 'text', onetoc2: 'text', xps: 'pdf', chm: 'ebook',
|
|
36
|
+
// KDE Calligra / KOffice
|
|
37
|
+
kwd: 'word', kwt: 'word', ksp: 'sheet', kpr: 'slides', kpt: 'slides',
|
|
38
|
+
// misc
|
|
39
|
+
procreate: 'image', dra: 'image', dsf: 'image', xfdf: 'data', siv: 'code', sieve: 'code', texi: 'text', texinfo: 'text',
|
|
40
|
+
icc: 'data', icm: 'data', pcap: 'data', cap: 'data', dmp: 'data',
|
|
41
|
+
// formats found while triaging the generic list
|
|
42
|
+
wpd: 'word', abw: 'word', fdf: 'pdf', oxps: 'pdf',
|
|
43
|
+
saf: 'audio', spf: 'audio', mmf: 'audio', mlp: 'audio', rm: 'video', rmvb: 'video', mxf: 'video',
|
|
44
|
+
// Lotus, Gnumeric and other office-like formats
|
|
45
|
+
lwp: 'word', '123': 'sheet', gnumeric: 'sheet', pre: 'slides', apr: 'database', nsf: 'mail', see: 'mail',
|
|
46
|
+
// archives / packages
|
|
47
|
+
mar: 'archive', ipk: 'archive', joda: 'archive', portpkg: 'archive', ez3: 'archive',
|
|
48
|
+
// audio (Yamaha HV, karaoke, music notation), video, compiled code, DRM / crypto, dynamic geometry & math
|
|
49
|
+
hvp: 'audio', hvs: 'audio', mmd: 'audio', mus: 'audio', qam: 'video', mvb: 'video', elc: 'code', wmlsc: 'code',
|
|
50
|
+
irm: 'security', nns: 'security', cryptonote: 'security',
|
|
51
|
+
geo: 'science', i2g: 'science', cdy: 'science', dpg: 'science', kfo: 'science', nbp: 'science',
|
|
52
|
+
// old archive formats, Android App Bundle (zip), Windows Installer parts, WML, RDF/SPARQL/MARC, KDE/Micrografx drawing, math, misc
|
|
53
|
+
arj: 'archive', bcpio: 'archive', cpt: 'archive', dgc: 'archive', gca: 'archive', hqx: 'archive', sea: 'archive',
|
|
54
|
+
shar: 'archive', sv4cpio: 'archive', sv4crc: 'archive', udeb: 'archive', aab: 'archive',
|
|
55
|
+
osf: 'audio', hvd: 'audio', m13: 'video', m14: 'video', msm: 'executable', msp: 'executable', wmlc: 'code',
|
|
56
|
+
nq: 'data', nt: 'data', rq: 'data', trig: 'data', mrc: 'data',
|
|
57
|
+
flw: 'design', karbon: 'design', kon: 'design', flo: 'design', igx: 'design',
|
|
58
|
+
gqf: 'science', gqs: 'science', mc1: 'science', fzs: 'sheet', chrt: 'sheet', atx: 'game',
|
|
59
|
+
};
|
|
60
|
+
const DOC_GROUPS = {
|
|
61
|
+
pdf: 'pdf', ps: 'pdf', eps: 'pdf', dvi: 'pdf',
|
|
62
|
+
doc: 'word', docx: 'word', docm: 'word', dot: 'word', dotx: 'word', rtf: 'word', odt: 'word', pages: 'word',
|
|
63
|
+
xls: 'sheet', xlsx: 'sheet', xlsm: 'sheet', xlsb: 'sheet', csv: 'sheet', tsv: 'sheet', ods: 'sheet', numbers: 'sheet',
|
|
64
|
+
ppt: 'slides', pptx: 'slides', pptm: 'slides', pps: 'slides', ppsx: 'slides', odp: 'slides', key: 'slides',
|
|
65
|
+
};
|
|
66
|
+
export const EXT_OVERRIDES = (() => {
|
|
67
|
+
const m = new Map();
|
|
68
|
+
for (const [cat, list] of Object.entries(EXT_GROUPS))
|
|
69
|
+
for (const ext of list.split(/\s+/))
|
|
70
|
+
m.set(ext, cat);
|
|
71
|
+
for (const [ext, cat] of Object.entries(DOC_GROUPS))
|
|
72
|
+
m.set(ext, cat);
|
|
73
|
+
for (const [ext, cat] of Object.entries(FUNCTION_MAP))
|
|
74
|
+
m.set(ext, cat);
|
|
75
|
+
m.set('key', 'slides'); // Keynote wins over the generic "private key" meaning
|
|
76
|
+
return m;
|
|
77
|
+
})();
|
|
78
|
+
/** Mime rules, first match wins (order matters: specific formats before broad ones). */
|
|
79
|
+
const MIME_RULES = [
|
|
80
|
+
['pdf', /^application\/(pdf|postscript|x-dvi)$/],
|
|
81
|
+
['word', /msword|wordprocessingml|vnd\.ms-word|\/rtf$|opendocument\.text|vnd\.sun\.xml\.writer/],
|
|
82
|
+
['sheet', /ms-excel|spreadsheetml|opendocument\.(spreadsheet|chart|formula)|vnd\.sun\.xml\.calc|^text\/(csv|tab-separated)/],
|
|
83
|
+
['slides', /powerpoint|presentationml|opendocument\.presentation|vnd\.sun\.xml\.impress/],
|
|
84
|
+
['database', /sqlite|ms-access|x-msaccess|opendocument\.database|x-dbf|^application\/sql/],
|
|
85
|
+
['ebook', /epub|mobipocket|x-fictionbook|x-cbr|x-cbz|ebook/],
|
|
86
|
+
['font', /^font\/|font-|-font|ms-fontobject/],
|
|
87
|
+
['image', /^image\/|opendocument\.(graphics|image)|x-msmetafile|x-emf|x-wmf/],
|
|
88
|
+
['video', /^video\/|^application\/(mp4|mp21)$/],
|
|
89
|
+
['audio', /^audio\/|mpegurl|x-scpls/],
|
|
90
|
+
['model3d', /^model\/|x-shader/],
|
|
91
|
+
['security', /pkcs|x509|pgp|pkix|x-pem|ms-pki|x-pkcs|cert|keys?$|pkixcmp/],
|
|
92
|
+
['geo', /google-earth|gpx|vnd\.geo|dwg|dxf|shapefile|^application\/gml/],
|
|
93
|
+
['mail', /^message\/|mbox|ms-outlook|^text\/(calendar|vcard|x-vcard)|^application\/(ics|vcard)/],
|
|
94
|
+
['science', /^chemical\/|x-hdf|netcdf|^application\/fits|mathematica|matlab|x-cdf/],
|
|
95
|
+
['archive', /zip|x-tar|gzip|x-7z|x-rar|rar-compressed|x-bzip|x-xz|x-compress|java-archive|x-rpm|vnd\.debian|x-iso9660|diskimage|android\.package|x-lzh|x-cpio|x-stuffit|x-ace|vnd\.ms-cab|installer/],
|
|
96
|
+
['executable', /x-msdownload|x-msdos-program|portable-executable|x-executable|x-msi|x-dosexec|x-mach-binary/],
|
|
97
|
+
['code', /javascript|ecmascript|^text\/(html|css|x-|javascript|typescript|jsx|tsx)|^application\/(x-sh(?![a-z])|x-csh|x-python|x-httpd|x-perl|x-ruby|x-php|wasm|typescript|x-tcl|sieve)/],
|
|
98
|
+
['data', /json|xml|yaml|cbor|protobuf|toml|graphql|rdf|iccprofile|^application\/(x-ndjson|x-plist)/],
|
|
99
|
+
['text', /^text\/|x-texinfo/],
|
|
100
|
+
];
|
|
101
|
+
export function classifyMime(mime) {
|
|
102
|
+
for (const [cat, re] of MIME_RULES)
|
|
103
|
+
if (re.test(mime))
|
|
104
|
+
return cat;
|
|
105
|
+
return 'generic';
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Category for an extension (without dot, any case). `mimes` are all mime types registered for it; an extension can
|
|
109
|
+
* have several (`mp4` is both application/mp4 and video/mp4), the first one that is not "generic" wins.
|
|
110
|
+
*/
|
|
111
|
+
export function classify(ext, mimes = []) {
|
|
112
|
+
const e = ext.toLowerCase().replace(/^\./, '');
|
|
113
|
+
const override = EXT_OVERRIDES.get(e);
|
|
114
|
+
if (override)
|
|
115
|
+
return override;
|
|
116
|
+
for (const m of typeof mimes === 'string' ? [mimes] : mimes) {
|
|
117
|
+
const c = classifyMime(m);
|
|
118
|
+
if (c !== 'generic')
|
|
119
|
+
return c;
|
|
120
|
+
}
|
|
121
|
+
return 'generic';
|
|
122
|
+
}
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { join, resolve } from 'node:path';
|
|
4
|
+
import { parseArgs } from 'node:util';
|
|
5
|
+
import { loadBodies } from './body.js';
|
|
6
|
+
import { loadConfig } from './config.js';
|
|
7
|
+
import { renderPng } from './png.js';
|
|
8
|
+
import { loadFont } from './font.js';
|
|
9
|
+
import { allExtensions, categoryFor } from './mime.js';
|
|
10
|
+
import { createPalette } from './palette.js';
|
|
11
|
+
import { renderIcon } from './render.js';
|
|
12
|
+
import { testRun } from './testrun.js';
|
|
13
|
+
import { ROOT } from './paths.js';
|
|
14
|
+
const HELP = `MimeForge – generate file-type icons (SVG) from file extensions
|
|
15
|
+
|
|
16
|
+
Usage
|
|
17
|
+
mimeforge <ext...> [options] write <out>/<ext>.svg for the given extensions
|
|
18
|
+
mimeforge --all [options] every known extension (mime-db + extras) + manifest.json
|
|
19
|
+
mimeforge --test-run [options] write an HTML page with all body types at 16/48/128 px
|
|
20
|
+
|
|
21
|
+
Options
|
|
22
|
+
-o, --out <dir> output directory (default: ./out, test run: ./out/test-run)
|
|
23
|
+
--font <spec> "monogram" (default), a .ttf/.otf/.woff file, or a bitmap-font .json
|
|
24
|
+
--body <path> a directory of <category>.svg bodies, or one .svg used for every type
|
|
25
|
+
--color <k=#hex> override an accent; k = category (pdf) or extension (.docx); repeatable
|
|
26
|
+
--colors <file> JSON file {"pdf": "#d00", ".docx": "#06c"}
|
|
27
|
+
--accent <#hex> force one accent for all icons
|
|
28
|
+
--label-case <c> upper (default) | lower | none
|
|
29
|
+
--no-label bodies only
|
|
30
|
+
--png <sizes> also write PNGs, e.g. --png 16,32,48 → <out>/<size>/<ext>.png (needs @resvg/resvg-js)
|
|
31
|
+
--config <file> JSON config (default: ./mimeforge.config.json if present); flags override it
|
|
32
|
+
--list print "ext category" for every known extension
|
|
33
|
+
-h, --help this help
|
|
34
|
+
-v, --version
|
|
35
|
+
`;
|
|
36
|
+
function fail(msg) {
|
|
37
|
+
console.error(`error: ${msg}`);
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
async function main() {
|
|
41
|
+
const { values, positionals } = parseArgs({
|
|
42
|
+
allowPositionals: true,
|
|
43
|
+
options: {
|
|
44
|
+
'test-run': { type: 'boolean' }, all: { type: 'boolean' }, list: { type: 'boolean' },
|
|
45
|
+
out: { type: 'string', short: 'o' }, font: { type: 'string' }, body: { type: 'string' },
|
|
46
|
+
color: { type: 'string', multiple: true }, colors: { type: 'string' }, accent: { type: 'string' },
|
|
47
|
+
'label-case': { type: 'string' }, 'no-label': { type: 'boolean' }, png: { type: 'string' }, config: { type: 'string' },
|
|
48
|
+
help: { type: 'boolean', short: 'h' }, version: { type: 'boolean', short: 'v' },
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
if (values.version) {
|
|
52
|
+
console.log(JSON.parse(readFileSync(join(ROOT, 'package.json'), 'utf8')).version);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
if (values.help || (!positionals.length && !values.all && !values['test-run'] && !values.list)) {
|
|
56
|
+
console.log(HELP);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
if (values.list) {
|
|
60
|
+
for (const e of allExtensions())
|
|
61
|
+
console.log(`${e} ${categoryFor(e)}`);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
let cfg = {};
|
|
65
|
+
try {
|
|
66
|
+
cfg = loadConfig(values.config)?.config ?? {};
|
|
67
|
+
}
|
|
68
|
+
catch (e) {
|
|
69
|
+
fail(e.message);
|
|
70
|
+
}
|
|
71
|
+
const fontSpec = values.font ?? cfg.font;
|
|
72
|
+
const bodySpec = values.body ?? cfg.body;
|
|
73
|
+
const accent = values.accent ?? cfg.accent;
|
|
74
|
+
const overrides = { ...(cfg.colors ?? {}) };
|
|
75
|
+
if (values.colors)
|
|
76
|
+
Object.assign(overrides, JSON.parse(readFileSync(values.colors, 'utf8')));
|
|
77
|
+
for (const kv of values.color ?? []) {
|
|
78
|
+
const i = kv.indexOf('=');
|
|
79
|
+
if (i < 1)
|
|
80
|
+
fail(`--color expects key=#hex, got "${kv}"`);
|
|
81
|
+
overrides[kv.slice(0, i)] = kv.slice(i + 1);
|
|
82
|
+
}
|
|
83
|
+
const labelCase = values['label-case'] ?? cfg.labelCase ?? 'upper';
|
|
84
|
+
if (!['upper', 'lower', 'none'].includes(labelCase))
|
|
85
|
+
fail('--label-case must be upper, lower or none');
|
|
86
|
+
let opts;
|
|
87
|
+
let bodyInfo;
|
|
88
|
+
try {
|
|
89
|
+
const bodies = loadBodies(bodySpec);
|
|
90
|
+
bodyInfo = bodies;
|
|
91
|
+
opts = {
|
|
92
|
+
font: loadFont(fontSpec), bodies, palette: createPalette(overrides), accent,
|
|
93
|
+
label: values['no-label'] || cfg.label === false ? false : undefined, labelCase: labelCase,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
catch (e) {
|
|
97
|
+
fail(e.message);
|
|
98
|
+
}
|
|
99
|
+
for (const w of bodyInfo.warnings)
|
|
100
|
+
console.error(`warning: ${w}`);
|
|
101
|
+
if (values['test-run']) {
|
|
102
|
+
const out = resolve(values.out ?? 'out/test-run');
|
|
103
|
+
const file = testRun(out, opts, { font: fontSpec ?? 'monogram', body: bodyInfo.source, warnings: bodyInfo.warnings, missing: bodyInfo.missing });
|
|
104
|
+
console.log(`test run written: ${file}`);
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
const out = resolve(values.out ?? cfg.out ?? 'out');
|
|
108
|
+
mkdirSync(out, { recursive: true });
|
|
109
|
+
const pngSizes = (values.png ? values.png.split(',').map(Number) : cfg.png ?? []).filter((n) => Number.isFinite(n) && n > 0);
|
|
110
|
+
const exts = values.all ? allExtensions() : positionals.map((e) => e.toLowerCase().replace(/^\./, ''));
|
|
111
|
+
const manifest = {};
|
|
112
|
+
let bytes = 0;
|
|
113
|
+
for (const ext of exts) {
|
|
114
|
+
const r = renderIcon(ext, opts);
|
|
115
|
+
writeFileSync(join(out, `${ext}.svg`), r.svg);
|
|
116
|
+
for (const size of pngSizes) {
|
|
117
|
+
mkdirSync(join(out, String(size)), { recursive: true });
|
|
118
|
+
writeFileSync(join(out, String(size), `${ext}.png`), await renderPng(r.svg, size));
|
|
119
|
+
}
|
|
120
|
+
manifest[ext] = r.category;
|
|
121
|
+
bytes += r.svg.length;
|
|
122
|
+
}
|
|
123
|
+
if (values.all)
|
|
124
|
+
writeFileSync(join(out, 'manifest.json'), JSON.stringify(manifest, null, 1));
|
|
125
|
+
console.log(`${exts.length} icon(s) → ${out} (${(bytes / 1024).toFixed(0)} KB total, ${(bytes / exts.length / 1024).toFixed(1)} KB avg)`);
|
|
126
|
+
}
|
|
127
|
+
main().catch((e) => fail(e.message));
|
package/dist/color.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export type RGB = [number, number, number];
|
|
2
|
+
export declare function parseHex(input: string): RGB;
|
|
3
|
+
export declare function toHex([r, g, b]: RGB): string;
|
|
4
|
+
/** Normalize any accepted hex notation to `#RRGGBB`. */
|
|
5
|
+
export declare const normalizeHex: (input: string) => string;
|
|
6
|
+
/** Mix a color towards white by `t` (0 = unchanged, 1 = white). */
|
|
7
|
+
export declare function tint(hex: string, t: number): string;
|
|
8
|
+
/** WCAG relative luminance. */
|
|
9
|
+
export declare function luminance(hex: string): number;
|
|
10
|
+
export declare function contrast(a: string, b: string): number;
|
|
11
|
+
/** White label text unless the accent is too light for it; then a dark ink. */
|
|
12
|
+
export declare function labelTextColor(accent: string, light?: string, dark?: string, minContrast?: number): string;
|
package/dist/color.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export function parseHex(input) {
|
|
2
|
+
let h = input.trim().replace(/^#/, '');
|
|
3
|
+
if (h.length === 3)
|
|
4
|
+
h = h.split('').map((c) => c + c).join('');
|
|
5
|
+
if (!/^[0-9a-fA-F]{6}$/.test(h))
|
|
6
|
+
throw new Error(`Invalid hex color: "${input}"`);
|
|
7
|
+
return [parseInt(h.slice(0, 2), 16), parseInt(h.slice(2, 4), 16), parseInt(h.slice(4, 6), 16)];
|
|
8
|
+
}
|
|
9
|
+
export function toHex([r, g, b]) {
|
|
10
|
+
return '#' + [r, g, b].map((v) => Math.round(Math.max(0, Math.min(255, v))).toString(16).padStart(2, '0')).join('').toUpperCase();
|
|
11
|
+
}
|
|
12
|
+
/** Normalize any accepted hex notation to `#RRGGBB`. */
|
|
13
|
+
export const normalizeHex = (input) => toHex(parseHex(input));
|
|
14
|
+
/** Mix a color towards white by `t` (0 = unchanged, 1 = white). */
|
|
15
|
+
export function tint(hex, t) {
|
|
16
|
+
const [r, g, b] = parseHex(hex);
|
|
17
|
+
return toHex([r + (255 - r) * t, g + (255 - g) * t, b + (255 - b) * t]);
|
|
18
|
+
}
|
|
19
|
+
/** WCAG relative luminance. */
|
|
20
|
+
export function luminance(hex) {
|
|
21
|
+
const lin = parseHex(hex).map((v) => {
|
|
22
|
+
const s = v / 255;
|
|
23
|
+
return s <= 0.03928 ? s / 12.92 : Math.pow((s + 0.055) / 1.055, 2.4);
|
|
24
|
+
});
|
|
25
|
+
return 0.2126 * lin[0] + 0.7152 * lin[1] + 0.0722 * lin[2];
|
|
26
|
+
}
|
|
27
|
+
export function contrast(a, b) {
|
|
28
|
+
const [hi, lo] = [luminance(a), luminance(b)].sort((x, y) => y - x);
|
|
29
|
+
return (hi + 0.05) / (lo + 0.05);
|
|
30
|
+
}
|
|
31
|
+
/** White label text unless the accent is too light for it; then a dark ink. */
|
|
32
|
+
export function labelTextColor(accent, light = '#FFFFFF', dark = '#1B1F24', minContrast = 3) {
|
|
33
|
+
return contrast(accent, light) >= minContrast ? light : dark;
|
|
34
|
+
}
|