@liiift-studio/sanity-font-manager 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +263 -0
- package/dist/index.js +3699 -0
- package/dist/index.mjs +3622 -0
- package/package.json +77 -0
- package/src/components/BatchUploadFonts.jsx +639 -0
- package/src/components/FontScriptUploaderComponent.jsx +463 -0
- package/src/components/GenerateCollectionsPairsComponent.jsx +259 -0
- package/src/components/PriceInput.jsx +26 -0
- package/src/components/RegenerateSubfamiliesComponent.jsx +185 -0
- package/src/components/SingleUploaderTool.jsx +673 -0
- package/src/components/StatusDisplay.jsx +26 -0
- package/src/components/UpdateScriptsComponent.jsx +76 -0
- package/src/components/UploadButton.jsx +43 -0
- package/src/components/UploadScriptsComponent.jsx +537 -0
- package/src/hooks/useSanityClient.js +9 -0
- package/src/index.js +56 -0
- package/src/utils/generateCssFile.js +197 -0
- package/src/utils/generateFontData.js +145 -0
- package/src/utils/generateFontFile.js +38 -0
- package/src/utils/generateKeywords.js +185 -0
- package/src/utils/generateSubset.js +45 -0
- package/src/utils/getEmptyFontKit.js +99 -0
- package/src/utils/parseVariableFontInstances.js +211 -0
- package/src/utils/processFontFiles.js +477 -0
- package/src/utils/regenerateFontData.js +146 -0
- package/src/utils/sanitizeForSanityId.js +65 -0
- package/src/utils/updateFontPrices.js +94 -0
- package/src/utils/updateTypefaceDocument.js +160 -0
- package/src/utils/uploadFontFiles.js +316 -0
- package/src/utils/utils.js +16 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,3622 @@
|
|
|
1
|
+
// src/components/BatchUploadFonts.jsx
|
|
2
|
+
import React4, { useCallback, useState as useState2, useMemo as useMemo2, useRef, useEffect } from "react";
|
|
3
|
+
import { Card, Box as Box2, Flex as Flex3, Grid, Text as Text4, Label, Switch, Button as Button2, Spinner, Tooltip, Stack as Stack2 } from "@sanity/ui";
|
|
4
|
+
import { ControlsIcon, InfoOutlineIcon, TrashIcon, UploadIcon, WarningOutlineIcon } from "@sanity/icons";
|
|
5
|
+
import { useFormValue as useFormValue2 } from "sanity";
|
|
6
|
+
|
|
7
|
+
// src/hooks/useSanityClient.js
|
|
8
|
+
import { useMemo } from "react";
|
|
9
|
+
import { useClient } from "sanity";
|
|
10
|
+
function useSanityClient() {
|
|
11
|
+
const client = useClient({ apiVersion: "2021-10-23" });
|
|
12
|
+
return useMemo(() => client, [client]);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// src/utils/processFontFiles.js
|
|
16
|
+
import * as fontkit from "fontkit";
|
|
17
|
+
import { nanoid } from "nanoid";
|
|
18
|
+
|
|
19
|
+
// src/utils/generateKeywords.js
|
|
20
|
+
var coreWeights = ["Hairline", "ExtraThin", "Thin", "Mager", "Maigre", "ExtraLight", "Light", "Chiaro", "Lite", "Leicht", "Demi", "Book", "Buch", "Regular", "Normal", "Medium", "Stark", "Thick", "Kr\xE4ftig", "Viertelfett", "Halbfett", "Dreiviertelfett", "Dark", "Bold", "Neretto", "Gras", "Fett", "Extrafett", "Black", "Nero", "Heavy", "Nerissimo", "Ultra", "Fat", "Poster"];
|
|
21
|
+
var modifiers = ["Demi", "Semi", "Extra", "Ultra", "Super", "Plus"];
|
|
22
|
+
var coreItalics = ["Italic", "Slant", "Oblique", "Cursive", "Rotalic", "Reverse", "Crab Claw", "Crabclaw", "South Paw", "Southpaw", "Backwards", "Backslant", "Backslanted", "Back Slant"];
|
|
23
|
+
var alternativeSpelling = {
|
|
24
|
+
Backslant: ["Bsl"],
|
|
25
|
+
Backwards: ["Bck"],
|
|
26
|
+
Black: ["Blak", "Blk"],
|
|
27
|
+
Bold: ["Bd", "Bld"],
|
|
28
|
+
// B omitted — too ambiguous
|
|
29
|
+
Condensed: ["Cond", "Cnd"],
|
|
30
|
+
Crabclaw: ["Crab", "Claw"],
|
|
31
|
+
Cursive: ["Cur"],
|
|
32
|
+
Dark: ["Drk"],
|
|
33
|
+
Expanded: ["Exp"],
|
|
34
|
+
Extra: ["Xt", "Xtra", "Xtr", "X"],
|
|
35
|
+
// X omitted as standalone — too ambiguous
|
|
36
|
+
ExtraBlack: ["Xblk", "XBlk", "Xblck", "XBlck"],
|
|
37
|
+
ExtraBold: ["Xbd", "XBd", "Xbld", "XBld", "Xbold", "XBold", "ExBold", "Exbold", "Exbd", "ExBd", "Exbld", "ExBld"],
|
|
38
|
+
ExtraCondensed: ["XCond", "Xcnd"],
|
|
39
|
+
ExtraExpanded: ["XExp"],
|
|
40
|
+
ExtraLight: ["Xlight", "XLight", "Xlt", "XLt", "Xlgt", "XLgt", "Xl", "XL", "Xlght", "XLght"],
|
|
41
|
+
ExtraThin: ["Xthin", "Xthn", "Xth", "XThin", "XThn", "XTh", "XT"],
|
|
42
|
+
Extended: ["Ext"],
|
|
43
|
+
Hairline: ["Hl", "Hln", "Hlnn", "Hlnne", "Hlnnne"],
|
|
44
|
+
Italic: ["Ital", "It"],
|
|
45
|
+
Light: ["Lt", "Lght"],
|
|
46
|
+
Medium: ["Med", "Md", "md", "med"],
|
|
47
|
+
Oblique: ["Obl"],
|
|
48
|
+
Plus: ["Pls"],
|
|
49
|
+
Regular: ["Reg", "Rg"],
|
|
50
|
+
Reverse: ["Rev"],
|
|
51
|
+
Rotalic: ["Rot"],
|
|
52
|
+
SemiBold: ["SmBd", "Sb", "Sbd", "Sbld", "Sbold", "Semibd", "SemiBd", "Semibld", "SemiBld", "semiBd", "semiBld"],
|
|
53
|
+
Slant: ["Sl"],
|
|
54
|
+
Southpaw: ["South", "Paw"],
|
|
55
|
+
Super: ["Supr"],
|
|
56
|
+
Thin: ["Thn"],
|
|
57
|
+
Ultra: ["Ult", "Ultre", "Ul", "Ulta"],
|
|
58
|
+
XX: ["XXt", "XXtra", "XXtr", "XX"],
|
|
59
|
+
XXBlack: ["XXblk", "XXBlk", "XXblck", "XXBlck"],
|
|
60
|
+
XXLight: ["XXlight", "XXLight", "XXlt", "XXLt", "XXlgt", "XXLgt", "XXl", "XXL", "XXlght", "XXLght"],
|
|
61
|
+
XXX: ["XXXt", "XXXtra", "XXXtr", "XXX"],
|
|
62
|
+
XXXLight: ["XXXlight", "XXXLight", "XXXlt", "XXXLt", "XXXlgt", "XXXLgt", "XXXl", "XXXL", "XXXlght", "XXXLght"]
|
|
63
|
+
};
|
|
64
|
+
function reverseSpellingLookup(str) {
|
|
65
|
+
let exactMatch = "";
|
|
66
|
+
Object.keys(alternativeSpelling).forEach(function(key) {
|
|
67
|
+
alternativeSpelling[key].forEach(function(alternative) {
|
|
68
|
+
if (str === alternative) {
|
|
69
|
+
exactMatch = key;
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
if (exactMatch) return exactMatch;
|
|
74
|
+
let result = "";
|
|
75
|
+
let longestMatch = 0;
|
|
76
|
+
Object.keys(alternativeSpelling).forEach(function(key) {
|
|
77
|
+
alternativeSpelling[key].forEach(function(alternative) {
|
|
78
|
+
const regex = new RegExp(`\\b${alternative}\\b`);
|
|
79
|
+
if (regex.test(str) && alternative.length > longestMatch) {
|
|
80
|
+
result = key;
|
|
81
|
+
longestMatch = alternative.length;
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
return result;
|
|
86
|
+
}
|
|
87
|
+
function expandAbbreviations(str) {
|
|
88
|
+
if (!str) return str;
|
|
89
|
+
return str.split(" ").map((word) => {
|
|
90
|
+
const expanded = reverseSpellingLookup(word);
|
|
91
|
+
return expanded || word;
|
|
92
|
+
}).join(" ");
|
|
93
|
+
}
|
|
94
|
+
function removeWeightNames(str) {
|
|
95
|
+
if (!str) return str;
|
|
96
|
+
return str.split(" ").map((word) => {
|
|
97
|
+
coreWeights.forEach((weight) => {
|
|
98
|
+
if (word === weight) word = "";
|
|
99
|
+
modifiers.forEach((modifier) => {
|
|
100
|
+
if (word === modifier || modifier + weight === word) word = "";
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
const expanded = reverseSpellingLookup(word);
|
|
104
|
+
if (expanded) return "";
|
|
105
|
+
return word;
|
|
106
|
+
}).join(" ").trim();
|
|
107
|
+
}
|
|
108
|
+
function generateStyleKeywords() {
|
|
109
|
+
let weightKeywordList = [];
|
|
110
|
+
let italicKeywordList = [];
|
|
111
|
+
weightKeywordList = [...coreWeights];
|
|
112
|
+
modifiers.forEach((modifier) => {
|
|
113
|
+
coreWeights.forEach((weight) => {
|
|
114
|
+
weightKeywordList.push(modifier + weight);
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
weightKeywordList = [...weightKeywordList, ...modifiers];
|
|
118
|
+
italicKeywordList = [...coreItalics];
|
|
119
|
+
weightKeywordList = weightKeywordList.map(function(el) {
|
|
120
|
+
var newEls = [];
|
|
121
|
+
Object.keys(alternativeSpelling).forEach(function(key) {
|
|
122
|
+
if (el.indexOf(key) !== -1) {
|
|
123
|
+
alternativeSpelling[key].forEach(function(alternative) {
|
|
124
|
+
let newSpelling = el.replace(key, alternative);
|
|
125
|
+
newEls.push(newSpelling);
|
|
126
|
+
Object.keys(alternativeSpelling).forEach(function(key2) {
|
|
127
|
+
if (newSpelling.indexOf(key2) !== -1) {
|
|
128
|
+
alternativeSpelling[key2].forEach(function(alternative2) {
|
|
129
|
+
let newSpelling2 = newSpelling.replace(key2, alternative2);
|
|
130
|
+
newEls.push(newSpelling2);
|
|
131
|
+
Object.keys(alternativeSpelling).forEach(function(key3) {
|
|
132
|
+
if (newSpelling2.indexOf(key3) !== -1) {
|
|
133
|
+
alternativeSpelling[key3].forEach(function(alternative3) {
|
|
134
|
+
newEls.push(newSpelling2.replace(key3, alternative3));
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
newEls.push(el);
|
|
145
|
+
return newEls;
|
|
146
|
+
}).reduce(function(a, b) {
|
|
147
|
+
return a.concat(b);
|
|
148
|
+
});
|
|
149
|
+
italicKeywordList = italicKeywordList.map(function(el) {
|
|
150
|
+
var newEls = [];
|
|
151
|
+
Object.keys(alternativeSpelling).forEach(function(key) {
|
|
152
|
+
if (el.indexOf(key) !== -1) {
|
|
153
|
+
alternativeSpelling[key].forEach(function(alternative) {
|
|
154
|
+
newEls.push(el.replace(key, alternative));
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
newEls.push(el);
|
|
159
|
+
return newEls;
|
|
160
|
+
}).reduce(function(a, b) {
|
|
161
|
+
return a.concat(b);
|
|
162
|
+
});
|
|
163
|
+
weightKeywordList = weightKeywordList.sort((a, b) => b.length - a.length);
|
|
164
|
+
italicKeywordList = italicKeywordList.sort((a, b) => b.length - a.length);
|
|
165
|
+
weightKeywordList = weightKeywordList.filter((item, pos) => weightKeywordList.indexOf(item) === pos);
|
|
166
|
+
italicKeywordList = italicKeywordList.filter((item, pos) => italicKeywordList.indexOf(item) === pos);
|
|
167
|
+
return { weightKeywordList, italicKeywordList };
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// src/utils/sanitizeForSanityId.js
|
|
171
|
+
import slugify from "slugify";
|
|
172
|
+
function sanitizeForSanityId(str) {
|
|
173
|
+
if (!str || typeof str !== "string") {
|
|
174
|
+
return "font-" + Date.now();
|
|
175
|
+
}
|
|
176
|
+
let sanitized = str.toLowerCase().trim();
|
|
177
|
+
sanitized = sanitized.replace(/\+/g, "plus");
|
|
178
|
+
sanitized = sanitized.replace(/&/g, "and");
|
|
179
|
+
sanitized = sanitized.replace(/@/g, "at");
|
|
180
|
+
sanitized = slugify(sanitized, {
|
|
181
|
+
replacement: "-",
|
|
182
|
+
remove: /[^\w\s-]/g,
|
|
183
|
+
lower: true,
|
|
184
|
+
strict: true,
|
|
185
|
+
locale: "en",
|
|
186
|
+
trim: true
|
|
187
|
+
});
|
|
188
|
+
sanitized = sanitized.replace(/[^a-z0-9\-_]/g, "-");
|
|
189
|
+
sanitized = sanitized.replace(/-+/g, "-");
|
|
190
|
+
sanitized = sanitized.replace(/^[-_]+|[-_]+$/g, "");
|
|
191
|
+
if (sanitized && !/^[a-z_]/.test(sanitized)) {
|
|
192
|
+
sanitized = "font_" + sanitized;
|
|
193
|
+
}
|
|
194
|
+
if (!sanitized) {
|
|
195
|
+
sanitized = "font_" + Date.now();
|
|
196
|
+
}
|
|
197
|
+
if (sanitized.length > 128) {
|
|
198
|
+
const hash = Math.random().toString(36).substring(2, 8);
|
|
199
|
+
sanitized = sanitized.substring(0, 120) + "_" + hash;
|
|
200
|
+
}
|
|
201
|
+
if (!/^[a-z_][a-z0-9\-_]*$/.test(sanitized)) {
|
|
202
|
+
console.warn(`ID sanitization produced invalid result: "${sanitized}", using fallback`);
|
|
203
|
+
sanitized = "font_" + Date.now();
|
|
204
|
+
}
|
|
205
|
+
return sanitized;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// src/utils/processFontFiles.js
|
|
209
|
+
var readFontFile = (file) => {
|
|
210
|
+
return new Promise((resolve, reject) => {
|
|
211
|
+
const reader = new FileReader();
|
|
212
|
+
reader.onload = (event) => {
|
|
213
|
+
resolve(new Uint8Array(event.target.result));
|
|
214
|
+
};
|
|
215
|
+
reader.onerror = (error) => {
|
|
216
|
+
reject(error);
|
|
217
|
+
};
|
|
218
|
+
reader.readAsArrayBuffer(file);
|
|
219
|
+
});
|
|
220
|
+
};
|
|
221
|
+
var processFontFiles = async (files, title, weightKeywordList, italicKeywordList, setStatus, preserveShortenedNames = false, preserveFileNames = false) => {
|
|
222
|
+
let failedFiles = [];
|
|
223
|
+
let subfamilies = {};
|
|
224
|
+
let fontsObjects = {};
|
|
225
|
+
let newPreferredStyle = { weight: -100, style: "Italic", _ref: "" };
|
|
226
|
+
for (let i = 0; i < files.length; i++) {
|
|
227
|
+
const file = files[i];
|
|
228
|
+
const fontBuffer = await readFontFile(file);
|
|
229
|
+
const font = fontkit.create(fontBuffer);
|
|
230
|
+
console.log("File name: ", file.name);
|
|
231
|
+
if (file.name.endsWith(".woff2") || file.name.endsWith(".woff")) {
|
|
232
|
+
await handleWebfontMetadata(file, font, files);
|
|
233
|
+
}
|
|
234
|
+
const { weightName, subfamilyName, fontTitle, style, italicKW, variableFont } = extractFontMetadata(
|
|
235
|
+
font,
|
|
236
|
+
title,
|
|
237
|
+
weightKeywordList,
|
|
238
|
+
italicKeywordList,
|
|
239
|
+
preserveShortenedNames
|
|
240
|
+
);
|
|
241
|
+
const id = sanitizeForSanityId(fontTitle);
|
|
242
|
+
let originalFilename = null;
|
|
243
|
+
if (preserveFileNames) {
|
|
244
|
+
originalFilename = file.name.replace(/\.(ttf|otf|woff2?|eot|svg)$/i, "");
|
|
245
|
+
}
|
|
246
|
+
logFontInfo(id, fontTitle, font, file.name, subfamilyName, style, weightName, variableFont, italicKW);
|
|
247
|
+
subfamilies[id] = subfamilyName;
|
|
248
|
+
if (fontsObjects[id]) {
|
|
249
|
+
fontsObjects[id].files = [...fontsObjects[id].files, file];
|
|
250
|
+
if (preserveFileNames && originalFilename) {
|
|
251
|
+
fontsObjects[id].originalFilename = originalFilename;
|
|
252
|
+
}
|
|
253
|
+
} else {
|
|
254
|
+
fontsObjects[id] = createFontObject(
|
|
255
|
+
id,
|
|
256
|
+
fontTitle,
|
|
257
|
+
title,
|
|
258
|
+
font,
|
|
259
|
+
variableFont,
|
|
260
|
+
weightName,
|
|
261
|
+
subfamilyName,
|
|
262
|
+
file,
|
|
263
|
+
preserveFileNames ? originalFilename : null
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
fontsObjects = sortFontObjects(fontsObjects);
|
|
268
|
+
const uniqueSubfamilies = [...new Set(Object.values(subfamilies))];
|
|
269
|
+
console.log("Subfamilies:", subfamilies);
|
|
270
|
+
console.log("Unique subfamilies:", uniqueSubfamilies, uniqueSubfamilies.length);
|
|
271
|
+
console.log("Font objects:", fontsObjects);
|
|
272
|
+
return { fontsObjects, subfamilies, uniqueSubfamilies, newPreferredStyle, failedFiles };
|
|
273
|
+
};
|
|
274
|
+
var handleWebfontMetadata = async (file, font, files) => {
|
|
275
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
276
|
+
if (!((_b = (_a = font == null ? void 0 : font.name) == null ? void 0 : _a.records) == null ? void 0 : _b.fullName) || ((_d = (_c = font == null ? void 0 : font.name) == null ? void 0 : _c.records) == null ? void 0 : _d.fullName) === "" || !/^[A-Z0-9]+$/.test((_f = (_e = font == null ? void 0 : font.name) == null ? void 0 : _e.records) == null ? void 0 : _f.fullName)) {
|
|
277
|
+
const ttfFile = files.find((f) => f.name === file.name.replace(".woff2", ".ttf").replace(".woff", ".ttf"));
|
|
278
|
+
if (ttfFile) {
|
|
279
|
+
const ttfFileBuffer = await readFontFile(ttfFile);
|
|
280
|
+
const ttfFileData = fontkit.create(ttfFileBuffer);
|
|
281
|
+
if (ttfFileData) font.name.records = (_g = ttfFileData == null ? void 0 : ttfFileData.name) == null ? void 0 : _g.records;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
};
|
|
285
|
+
var extractFontMetadata = (font, title, weightKeywordList, italicKeywordList, preserveShortenedNames = false) => {
|
|
286
|
+
var _a, _b, _c, _d, _e, _f;
|
|
287
|
+
let weightName = extractWeightName(font, italicKeywordList);
|
|
288
|
+
if (!preserveShortenedNames) {
|
|
289
|
+
weightName = expandAbbreviations(weightName);
|
|
290
|
+
}
|
|
291
|
+
if ((weightName === "" || weightName.toLowerCase() === "roman") && ((_b = (_a = font == null ? void 0 : font.name) == null ? void 0 : _a.records) == null ? void 0 : _b.fullName)) {
|
|
292
|
+
weightName = extractWeightFromFullName(font, title);
|
|
293
|
+
if (!preserveShortenedNames) {
|
|
294
|
+
weightName = expandAbbreviations(weightName);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
const variableFont = (font == null ? void 0 : font.variationAxes) && Object.keys(font.variationAxes).length > 0;
|
|
298
|
+
let subfamilyName = ((_f = (_e = (_d = (_c = font == null ? void 0 : font.name) == null ? void 0 : _c.records) == null ? void 0 : _d.fullName) == null ? void 0 : _e.en) == null ? void 0 : _f.replace(title.trim(), "").trim()) || font.subfamilyName.trim().replace(title.trim(), "").trim();
|
|
299
|
+
if (!preserveShortenedNames) {
|
|
300
|
+
subfamilyName = expandAbbreviations(subfamilyName);
|
|
301
|
+
}
|
|
302
|
+
let fontTitle = font == null ? void 0 : font.fullName.trim();
|
|
303
|
+
let style = (font == null ? void 0 : font.italicAngle) !== 0 || (font == null ? void 0 : font.fullName.toLowerCase().includes("italic")) ? "Italic" : "Regular";
|
|
304
|
+
const italicKW = processItalicKeywords(font, fontTitle, italicKeywordList);
|
|
305
|
+
subfamilyName = processSubfamilyName(subfamilyName, weightKeywordList, italicKW, preserveShortenedNames);
|
|
306
|
+
fontTitle = formatFontTitle(fontTitle, preserveShortenedNames);
|
|
307
|
+
subfamilyName = subfamilyName === "" ? "Regular" : subfamilyName.replace(/\s+/g, " ").trim();
|
|
308
|
+
if (subfamilyName !== "") {
|
|
309
|
+
weightName = weightName.replace(`${subfamilyName} `, "").replace(` ${subfamilyName}`, "").trim();
|
|
310
|
+
}
|
|
311
|
+
if (variableFont) {
|
|
312
|
+
if (!fontTitle.toLowerCase().includes("vf")) {
|
|
313
|
+
fontTitle = fontTitle + " VF";
|
|
314
|
+
}
|
|
315
|
+
subfamilyName = "";
|
|
316
|
+
}
|
|
317
|
+
if (!(variableFont && fontTitle.toLowerCase().includes("italic"))) {
|
|
318
|
+
fontTitle = addItalicToFontTitle(font, fontTitle, italicKW, style, preserveShortenedNames);
|
|
319
|
+
}
|
|
320
|
+
return { weightName, subfamilyName, fontTitle, style, italicKW, variableFont };
|
|
321
|
+
};
|
|
322
|
+
var extractWeightName = (font, italicKW) => {
|
|
323
|
+
var _a, _b, _c, _d;
|
|
324
|
+
let weightName = ((_b = (_a = font == null ? void 0 : font.name) == null ? void 0 : _a.records) == null ? void 0 : _b.preferredSubfamily) || ((_d = (_c = font == null ? void 0 : font.name) == null ? void 0 : _c.records) == null ? void 0 : _d.fontSubfamily);
|
|
325
|
+
if (typeof weightName === "object") {
|
|
326
|
+
weightName = (weightName == null ? void 0 : weightName.en) || (weightName.constructor === Object ? weightName[Object.keys(weightName)[0]] : null);
|
|
327
|
+
}
|
|
328
|
+
if ((font == null ? void 0 : font.variationAxes) && Object.keys(font.variationAxes).length > 0) {
|
|
329
|
+
return "Variable";
|
|
330
|
+
}
|
|
331
|
+
if (italicKW) {
|
|
332
|
+
italicKW.forEach((keyword) => {
|
|
333
|
+
const kwRegex = new RegExp(`\\b${keyword.trim()}\\b`, "i");
|
|
334
|
+
if (kwRegex.test(weightName)) {
|
|
335
|
+
weightName = weightName.replace(kwRegex, "").trim();
|
|
336
|
+
}
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
return weightName == null ? void 0 : weightName.toString().replace("Italic", "").replace("It", "").replace("Slanted", "").replace("Slant", "").replace("Backslant", "").trim();
|
|
340
|
+
};
|
|
341
|
+
var extractWeightFromFullName = (font, title) => {
|
|
342
|
+
var _a, _b;
|
|
343
|
+
let weightName = (_b = (_a = font == null ? void 0 : font.name) == null ? void 0 : _a.records) == null ? void 0 : _b.fullName;
|
|
344
|
+
weightName = (weightName == null ? void 0 : weightName.en) ? weightName.en : (weightName == null ? void 0 : weightName.constructor) === Object ? weightName[Object.keys(weightName)[0]] : weightName;
|
|
345
|
+
weightName = weightName == null ? void 0 : weightName.replace(title + " ", "").replace(title, "").trim();
|
|
346
|
+
weightName = weightName == null ? void 0 : weightName.replace("Italic", "").replace("It", "").replace("Slanted", "").replace("Slant", "").trim();
|
|
347
|
+
return weightName;
|
|
348
|
+
};
|
|
349
|
+
var processSubfamilyName = (subfamilyName, weightKeywordList, italicKeywordList, preserveShortenedNames = false) => {
|
|
350
|
+
weightKeywordList.forEach((keyword) => {
|
|
351
|
+
const kwRegex = new RegExp(`\\b${keyword.trim()}\\b`, "i");
|
|
352
|
+
if (kwRegex.test(subfamilyName)) {
|
|
353
|
+
subfamilyName = subfamilyName.replace(kwRegex, "").trim();
|
|
354
|
+
}
|
|
355
|
+
subfamilyName = removeWeightNames(subfamilyName) || subfamilyName;
|
|
356
|
+
if (!preserveShortenedNames) {
|
|
357
|
+
subfamilyName = expandAbbreviations(subfamilyName);
|
|
358
|
+
}
|
|
359
|
+
});
|
|
360
|
+
italicKeywordList.forEach((keyword) => {
|
|
361
|
+
const kwRegex = new RegExp(`\\b${keyword.trim()}\\b`, "i");
|
|
362
|
+
if (kwRegex.test(subfamilyName)) {
|
|
363
|
+
subfamilyName = subfamilyName.replace(kwRegex, "").trim();
|
|
364
|
+
}
|
|
365
|
+
});
|
|
366
|
+
return subfamilyName;
|
|
367
|
+
};
|
|
368
|
+
var processItalicKeywords = (font, fontTitle, italicKeywordList) => {
|
|
369
|
+
let italicKW = [];
|
|
370
|
+
italicKeywordList.forEach((keyword) => {
|
|
371
|
+
const kw = keyword.trim();
|
|
372
|
+
const kwRegex = new RegExp(`\\b${kw}\\b`, "i");
|
|
373
|
+
if (kwRegex.test(fontTitle)) {
|
|
374
|
+
fontTitle = fontTitle.replace(kwRegex, "").trim();
|
|
375
|
+
italicKW.push(kw);
|
|
376
|
+
}
|
|
377
|
+
if ((font == null ? void 0 : font.fullName) && typeof font.fullName === "string" && font.fullName.toLowerCase().includes(kw.toLowerCase())) {
|
|
378
|
+
if (!italicKW.includes(kw)) italicKW.push(kw);
|
|
379
|
+
}
|
|
380
|
+
});
|
|
381
|
+
return italicKW;
|
|
382
|
+
};
|
|
383
|
+
var formatFontTitle = (fontTitle, preserveShortenedNames = false) => {
|
|
384
|
+
const hasItalic = fontTitle.toLowerCase().includes("italic");
|
|
385
|
+
fontTitle = fontTitle.replace(/-/g, " ");
|
|
386
|
+
return fontTitle.replace(/\s+/g, " ").trim().split(" ").map((word) => {
|
|
387
|
+
if (hasItalic && word.toLowerCase() === "italic") return "Italic";
|
|
388
|
+
let fullWord = word;
|
|
389
|
+
if (!preserveShortenedNames) {
|
|
390
|
+
fullWord = reverseSpellingLookup(word) || word;
|
|
391
|
+
}
|
|
392
|
+
return fullWord[0].toUpperCase() + fullWord.slice(1);
|
|
393
|
+
}).join(" ");
|
|
394
|
+
};
|
|
395
|
+
var addItalicToFontTitle = (font, fontTitle, italicKW, style, preserveShortenedNames = false) => {
|
|
396
|
+
const hasItalicAngle = (font == null ? void 0 : font.italicAngle) !== 0;
|
|
397
|
+
const hasItalicInName = font == null ? void 0 : font.fullName.toLowerCase().includes("italic");
|
|
398
|
+
if (italicKW.length > 0 || hasItalicAngle || hasItalicInName) {
|
|
399
|
+
italicKW = [...new Set(italicKW)];
|
|
400
|
+
if (italicKW.length === 0 && (hasItalicAngle || hasItalicInName)) {
|
|
401
|
+
italicKW = ["Italic"];
|
|
402
|
+
}
|
|
403
|
+
if (!preserveShortenedNames) {
|
|
404
|
+
italicKW = italicKW.map((item) => reverseSpellingLookup(item) || item);
|
|
405
|
+
}
|
|
406
|
+
italicKW = [...new Set(italicKW)];
|
|
407
|
+
if (italicKW.length > 1 && italicKW.includes("Italic")) {
|
|
408
|
+
italicKW = ["Italic"];
|
|
409
|
+
}
|
|
410
|
+
const fontTitleLower = fontTitle.toLowerCase();
|
|
411
|
+
italicKW = italicKW.filter((keyword) => {
|
|
412
|
+
const keywordLower = keyword.toLowerCase();
|
|
413
|
+
const kwRegex = new RegExp(`\\b${keywordLower}\\b`);
|
|
414
|
+
const isSubstring = fontTitleLower.split(" ").some(
|
|
415
|
+
(word) => word.includes(keywordLower) || keywordLower.includes(word)
|
|
416
|
+
);
|
|
417
|
+
return !kwRegex.test(fontTitleLower) && !isSubstring;
|
|
418
|
+
});
|
|
419
|
+
if (italicKW.length > 0) {
|
|
420
|
+
fontTitle = fontTitle.trim() + " " + italicKW.join(" ");
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
return fontTitle;
|
|
424
|
+
};
|
|
425
|
+
var createFontObject = (id, fontTitle, title, font, variableFont, weightName, subfamilyName, file, originalFilename = null) => {
|
|
426
|
+
const fontObject = {
|
|
427
|
+
_key: nanoid(),
|
|
428
|
+
_id: id,
|
|
429
|
+
title: fontTitle,
|
|
430
|
+
slug: { _type: "slug", current: id },
|
|
431
|
+
typefaceName: title,
|
|
432
|
+
style: (font == null ? void 0 : font.italicAngle) !== 0 || (font == null ? void 0 : font.fullName.toLowerCase().includes("italic")) ? "Italic" : "Regular",
|
|
433
|
+
variableFont,
|
|
434
|
+
weightName,
|
|
435
|
+
subfamily: subfamilyName,
|
|
436
|
+
normalWeight: true,
|
|
437
|
+
weight: Number(determineWeight(font, weightName)),
|
|
438
|
+
fileInput: {},
|
|
439
|
+
files: [file],
|
|
440
|
+
fontKit: font
|
|
441
|
+
};
|
|
442
|
+
if (originalFilename) {
|
|
443
|
+
fontObject.originalFilename = originalFilename;
|
|
444
|
+
}
|
|
445
|
+
return fontObject;
|
|
446
|
+
};
|
|
447
|
+
var determineWeight = (font, weightName) => {
|
|
448
|
+
var _a;
|
|
449
|
+
if ((_a = font["OS/2"]) == null ? void 0 : _a.usWeightClass) {
|
|
450
|
+
return Number(font["OS/2"].usWeightClass);
|
|
451
|
+
}
|
|
452
|
+
const wn = (weightName == null ? void 0 : weightName.toLowerCase()) || "";
|
|
453
|
+
if (/hairline|extra thin|extrathin/.test(wn)) return 100;
|
|
454
|
+
if (/thin|extra light|extralight/.test(wn)) return 200;
|
|
455
|
+
if (/light|book/.test(wn)) return 300;
|
|
456
|
+
if (/regular|normal/.test(wn)) return 400;
|
|
457
|
+
if (/medium/.test(wn)) return 500;
|
|
458
|
+
if (/semi bold|semibold/.test(wn)) return 600;
|
|
459
|
+
if (/extra bold|extrabold/.test(wn)) return 800;
|
|
460
|
+
if (/bold/.test(wn)) return 700;
|
|
461
|
+
if (/black|ultra/.test(wn)) return 900;
|
|
462
|
+
return 400;
|
|
463
|
+
};
|
|
464
|
+
var sortFontObjects = (fontsObjects) => {
|
|
465
|
+
return Object.fromEntries(
|
|
466
|
+
Object.entries(fontsObjects).sort((a, b) => {
|
|
467
|
+
const weightA = Number(a[1].weight);
|
|
468
|
+
const weightB = Number(b[1].weight);
|
|
469
|
+
if (weightA === weightB) {
|
|
470
|
+
if (a[1].style === "Regular" && b[1].style === "Italic") return -1;
|
|
471
|
+
if (a[1].style === "Italic" && b[1].style === "Regular") return 1;
|
|
472
|
+
return 0;
|
|
473
|
+
}
|
|
474
|
+
return weightA - weightB;
|
|
475
|
+
})
|
|
476
|
+
);
|
|
477
|
+
};
|
|
478
|
+
var logFontInfo = (id, fontTitle, font, fileName, subfamilyName, style, weightName, variableFont, italicKW) => {
|
|
479
|
+
console.log("=== Font Info ====");
|
|
480
|
+
console.log("Font id: ", id);
|
|
481
|
+
console.log("Font title: ", fontTitle);
|
|
482
|
+
console.log("Fontkit fullName: ", font.fullName);
|
|
483
|
+
console.log("Fontkit family name: ", font.familyName);
|
|
484
|
+
console.log("File name: ", fileName);
|
|
485
|
+
console.log("Subfamily: ", subfamilyName);
|
|
486
|
+
console.log("Style: ", style);
|
|
487
|
+
console.log("Weight: ", weightName);
|
|
488
|
+
console.log("Variable: ", variableFont);
|
|
489
|
+
console.log("italicKW: ", italicKW);
|
|
490
|
+
console.log("Font italic angle: ", (font == null ? void 0 : font.italicAngle) !== 0 || (font == null ? void 0 : font.fullName.toLowerCase().includes("italic")) ? "Italic" : "Regular");
|
|
491
|
+
console.log("=======");
|
|
492
|
+
};
|
|
493
|
+
|
|
494
|
+
// src/utils/uploadFontFiles.js
|
|
495
|
+
import { nanoid as nanoid3 } from "nanoid";
|
|
496
|
+
|
|
497
|
+
// src/utils/generateCssFile.js
|
|
498
|
+
import base64 from "base-64";
|
|
499
|
+
import { Buffer } from "buffer";
|
|
500
|
+
import * as fontkit2 from "fontkit";
|
|
501
|
+
function _arrayBufferToBase64(buffer) {
|
|
502
|
+
var binary = "";
|
|
503
|
+
var bytes = new Uint8Array(buffer);
|
|
504
|
+
var len = bytes.byteLength;
|
|
505
|
+
for (var i = 0; i < len; i++) {
|
|
506
|
+
binary += String.fromCharCode(bytes[i]);
|
|
507
|
+
}
|
|
508
|
+
return base64.encode(binary);
|
|
509
|
+
}
|
|
510
|
+
function buildVFDescriptors(font) {
|
|
511
|
+
const cssAxes = {};
|
|
512
|
+
const skipped = [];
|
|
513
|
+
try {
|
|
514
|
+
const va = font.variationAxes;
|
|
515
|
+
if (!va) return { descriptors: "", skipped: [] };
|
|
516
|
+
for (const [tag, axis] of Object.entries(va)) {
|
|
517
|
+
const lo = Math.min(axis.min, axis.max);
|
|
518
|
+
const hi = Math.max(axis.min, axis.max);
|
|
519
|
+
if (lo === hi) {
|
|
520
|
+
skipped.push(tag);
|
|
521
|
+
continue;
|
|
522
|
+
}
|
|
523
|
+
if (tag === "wght") {
|
|
524
|
+
cssAxes["font-weight"] = `${lo} ${hi}`;
|
|
525
|
+
} else if (tag === "wdth") {
|
|
526
|
+
cssAxes["font-stretch"] = `${lo}% ${hi}%`;
|
|
527
|
+
} else if (tag === "slnt") {
|
|
528
|
+
cssAxes["font-style"] = `oblique ${lo}deg ${hi}deg`;
|
|
529
|
+
} else if (tag === "ital" && !cssAxes["font-style"]) {
|
|
530
|
+
if (hi > 0) cssAxes["font-style"] = "italic";
|
|
531
|
+
else skipped.push(tag);
|
|
532
|
+
} else {
|
|
533
|
+
skipped.push(tag);
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
} catch (_) {
|
|
537
|
+
}
|
|
538
|
+
const descriptors = Object.entries(cssAxes).map(([k, v]) => `${k}:${v}`).join(";") + (Object.keys(cssAxes).length ? ";" : "");
|
|
539
|
+
return { descriptors, skipped };
|
|
540
|
+
}
|
|
541
|
+
var FALLBACK_STACKS = {
|
|
542
|
+
"sans-serif": "local('Arial'), local('Helvetica Neue'), local('Roboto'), local('Liberation Sans')",
|
|
543
|
+
"serif": "local('Georgia'), local('Times New Roman'), local('Times')",
|
|
544
|
+
"monospace": "local('Courier New'), local('Courier'), local('Menlo'), local('Monaco')",
|
|
545
|
+
// Display and script fonts have no universally suitable system fallback; default to sans-serif
|
|
546
|
+
"default": "local('Arial'), local('Helvetica Neue'), local('Roboto'), local('Liberation Sans')"
|
|
547
|
+
};
|
|
548
|
+
var FAMILY_CLASS_MAP = {
|
|
549
|
+
1: "serif",
|
|
550
|
+
2: "serif",
|
|
551
|
+
3: "serif",
|
|
552
|
+
4: "serif",
|
|
553
|
+
5: "serif",
|
|
554
|
+
7: "serif",
|
|
555
|
+
8: "sans-serif"
|
|
556
|
+
};
|
|
557
|
+
function detectFontCategory(font) {
|
|
558
|
+
var _a, _b;
|
|
559
|
+
try {
|
|
560
|
+
const familyClass = ((_b = (_a = font._tables) == null ? void 0 : _a["OS/2"]) == null ? void 0 : _b.sFamilyClass) ?? 0;
|
|
561
|
+
const highByte = familyClass >> 8 & 255;
|
|
562
|
+
return FAMILY_CLASS_MAP[highByte] ?? "default";
|
|
563
|
+
} catch {
|
|
564
|
+
return "default";
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
function calcFallbackData(arrayBuffer) {
|
|
568
|
+
try {
|
|
569
|
+
let font = fontkit2.create(Buffer.from(arrayBuffer));
|
|
570
|
+
let upm = font.unitsPerEm;
|
|
571
|
+
let category = detectFontCategory(font);
|
|
572
|
+
return {
|
|
573
|
+
fallbackSrc: FALLBACK_STACKS[category],
|
|
574
|
+
ascentOverride: `${(font.ascent / upm * 100).toFixed(2)}%`,
|
|
575
|
+
descentOverride: `${(Math.abs(font.descent) / upm * 100).toFixed(2)}%`,
|
|
576
|
+
lineGapOverride: `${(font.lineGap / upm * 100).toFixed(2)}%`
|
|
577
|
+
};
|
|
578
|
+
} catch (err) {
|
|
579
|
+
console.error("Failed to extract fallback font data:", err);
|
|
580
|
+
return {
|
|
581
|
+
fallbackSrc: FALLBACK_STACKS["default"],
|
|
582
|
+
ascentOverride: "100%",
|
|
583
|
+
descentOverride: "0%",
|
|
584
|
+
lineGapOverride: "0%"
|
|
585
|
+
};
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
async function generateCssFile({
|
|
589
|
+
woff2File,
|
|
590
|
+
fileInput,
|
|
591
|
+
language = null,
|
|
592
|
+
fileName,
|
|
593
|
+
fontName,
|
|
594
|
+
variableFont,
|
|
595
|
+
weight,
|
|
596
|
+
style = "Normal",
|
|
597
|
+
client
|
|
598
|
+
}) {
|
|
599
|
+
try {
|
|
600
|
+
let arrayBuffer = await woff2File.arrayBuffer();
|
|
601
|
+
let b64 = _arrayBufferToBase64(arrayBuffer);
|
|
602
|
+
let fontkitFont = fontkit2.create(Buffer.from(arrayBuffer));
|
|
603
|
+
let { fallbackSrc, ascentOverride, descentOverride, lineGapOverride } = calcFallbackData(arrayBuffer);
|
|
604
|
+
let cssString;
|
|
605
|
+
if (variableFont) {
|
|
606
|
+
let { descriptors, skipped } = buildVFDescriptors(fontkitFont);
|
|
607
|
+
let skipComment = skipped.length ? `/* axes present but have no @font-face descriptor: ${skipped.join(", ")}` + (skipped.includes("opsz") ? " \u2014 add font-optical-sizing:auto to your element CSS" : "") + " */" : "";
|
|
608
|
+
cssString = `${skipComment}@font-face{font-family:'${fontName}';src:url(data:application/font-woff2;charset=utf-8;base64,${b64})format('woff2-variations');${descriptors}font-display:swap;}`;
|
|
609
|
+
} else {
|
|
610
|
+
let fontStyle = style === "Italic" ? "italic" : "normal";
|
|
611
|
+
cssString = `@font-face{font-family:'${fontName}';src:url(data:application/font-woff2;charset=utf-8;base64,${b64})format('woff2');font-weight:${weight};font-style:${fontStyle};font-display:swap;}`;
|
|
612
|
+
}
|
|
613
|
+
let fallbackCssString = `@font-face{font-family:'${fontName} Fallback';src:${fallbackSrc};ascent-override:${ascentOverride};descent-override:${descentOverride};line-gap-override:${lineGapOverride};}`;
|
|
614
|
+
let uploadBuffer = Buffer.from(cssString + fallbackCssString, "utf-8");
|
|
615
|
+
let doc = await client.assets.upload("file", uploadBuffer, { filename: fileName + ".css" });
|
|
616
|
+
let newFileInput = language == null ? {
|
|
617
|
+
...fileInput,
|
|
618
|
+
css: {
|
|
619
|
+
_type: "file",
|
|
620
|
+
asset: {
|
|
621
|
+
_type: "reference",
|
|
622
|
+
_ref: doc._id
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
} : {
|
|
626
|
+
...fileInput,
|
|
627
|
+
[language]: {
|
|
628
|
+
...fileInput[language],
|
|
629
|
+
css: {
|
|
630
|
+
_type: "file",
|
|
631
|
+
asset: {
|
|
632
|
+
_type: "reference",
|
|
633
|
+
_ref: doc._id
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
};
|
|
638
|
+
return newFileInput;
|
|
639
|
+
} catch (err) {
|
|
640
|
+
console.error(err);
|
|
641
|
+
throw err;
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
// src/utils/generateFontData.js
|
|
646
|
+
import { Buffer as Buffer2 } from "buffer";
|
|
647
|
+
import * as fontkit3 from "fontkit";
|
|
648
|
+
async function generateFontData({ fileInput, url, fontKit, fontId, client, commit = true }) {
|
|
649
|
+
var _a;
|
|
650
|
+
if (fontId.startsWith("drafts.")) {
|
|
651
|
+
fontId = fontId.replace("drafts.", "");
|
|
652
|
+
}
|
|
653
|
+
console.log("generate-font-data ", fontId, commit);
|
|
654
|
+
let srcUrl;
|
|
655
|
+
if (!url || url == null) {
|
|
656
|
+
srcUrl = await client.fetch(`*[_id == $id]{url}`, { id: fileInput.ttf.asset._ref });
|
|
657
|
+
console.log("src url ", srcUrl);
|
|
658
|
+
srcUrl = srcUrl[0].url;
|
|
659
|
+
} else {
|
|
660
|
+
srcUrl = url;
|
|
661
|
+
}
|
|
662
|
+
let font = fontKit;
|
|
663
|
+
if (!fontKit || fontKit == null) {
|
|
664
|
+
let buffer = await fetch(srcUrl);
|
|
665
|
+
buffer = await buffer.arrayBuffer();
|
|
666
|
+
buffer = Buffer2.from(buffer);
|
|
667
|
+
font = fontkit3.create(buffer);
|
|
668
|
+
}
|
|
669
|
+
let variableAxes;
|
|
670
|
+
try {
|
|
671
|
+
variableAxes = font.variationAxes;
|
|
672
|
+
} catch (err) {
|
|
673
|
+
console.error("err: ", err);
|
|
674
|
+
}
|
|
675
|
+
let variableInstances;
|
|
676
|
+
try {
|
|
677
|
+
variableInstances = font.namedVariations;
|
|
678
|
+
} catch (e) {
|
|
679
|
+
console.log("variable instances 2 error : ", e.message);
|
|
680
|
+
let fvar = (_a = font == null ? void 0 : font.fvar) == null ? void 0 : _a.instance;
|
|
681
|
+
fvar == null ? void 0 : fvar.forEach((fv) => {
|
|
682
|
+
var _a2, _b, _c, _d, _e, _f;
|
|
683
|
+
if ((fv == null ? void 0 : fv.nameID) === 2) fv.name = (_c = (_b = (_a2 = font == null ? void 0 : font._tables) == null ? void 0 : _a2.name) == null ? void 0 : _b.records) == null ? void 0 : _c.fontSubfamily;
|
|
684
|
+
if ((fv == null ? void 0 : fv.nameID) === 17) fv.name = (_f = (_e = (_d = font == null ? void 0 : font._tables) == null ? void 0 : _d.name) == null ? void 0 : _e.records) == null ? void 0 : _f.preferredSubfamily;
|
|
685
|
+
});
|
|
686
|
+
variableInstances = {};
|
|
687
|
+
fvar.forEach((v) => {
|
|
688
|
+
let key = v.name;
|
|
689
|
+
if (typeof key === "object") {
|
|
690
|
+
key = Object.values(key)[0];
|
|
691
|
+
}
|
|
692
|
+
let coordKeys = Object.keys(variableAxes);
|
|
693
|
+
let coord = {};
|
|
694
|
+
coordKeys.forEach((ck, ckIndex) => {
|
|
695
|
+
coord[ck] = v.coord[ckIndex];
|
|
696
|
+
});
|
|
697
|
+
variableInstances[key] = coord;
|
|
698
|
+
});
|
|
699
|
+
}
|
|
700
|
+
console.log("font : ", font);
|
|
701
|
+
console.log("variable instances : ", variableInstances);
|
|
702
|
+
console.log("variable axes : ", variableAxes);
|
|
703
|
+
let opentypeFeatures = font.availableFeatures;
|
|
704
|
+
let glyphCount = font.numGlyphs;
|
|
705
|
+
let characterSet = font.characterSet;
|
|
706
|
+
let metaData = {
|
|
707
|
+
postscriptName: font.postscriptName,
|
|
708
|
+
fullName: font.fullName,
|
|
709
|
+
familyName: font.familyName,
|
|
710
|
+
subfamilyName: font.subfamilyName,
|
|
711
|
+
copyright: font.copyright,
|
|
712
|
+
version: font.version.replaceAll("Version ", ""),
|
|
713
|
+
genDate: (/* @__PURE__ */ new Date()).toISOString()
|
|
714
|
+
};
|
|
715
|
+
let metrics = {
|
|
716
|
+
unitsPerEm: font.unitsPerEm,
|
|
717
|
+
ascender: font.ascent,
|
|
718
|
+
descender: font.descent,
|
|
719
|
+
lineGap: font.lineGap,
|
|
720
|
+
underlinePosition: font.underlinePosition,
|
|
721
|
+
underlineThickness: font.underlineThickness,
|
|
722
|
+
italicAngle: font.italicAngle,
|
|
723
|
+
capHeight: font.capHeight,
|
|
724
|
+
xHeight: font.xHeight,
|
|
725
|
+
boundingBox: font.bbox
|
|
726
|
+
};
|
|
727
|
+
let variableFont = false;
|
|
728
|
+
if (variableAxes && variableAxes != null && Object.keys(variableAxes).length > 0 && variableInstances && variableInstances != null && Object.keys(variableInstances).length > 0) {
|
|
729
|
+
variableFont = true;
|
|
730
|
+
}
|
|
731
|
+
let patch = {
|
|
732
|
+
metrics,
|
|
733
|
+
metaData,
|
|
734
|
+
variableFont,
|
|
735
|
+
variableAxes: JSON.stringify(variableAxes),
|
|
736
|
+
variableInstances: JSON.stringify(variableInstances),
|
|
737
|
+
glyphCount,
|
|
738
|
+
opentypeFeatures: { chars: opentypeFeatures },
|
|
739
|
+
characterSet: { chars: characterSet }
|
|
740
|
+
};
|
|
741
|
+
console.log("data : ", patch);
|
|
742
|
+
if (commit) patch = await client.patch(fontId).set(patch).commit({ autoGenerateArrayKeys: true });
|
|
743
|
+
return patch;
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
// src/utils/parseVariableFontInstances.js
|
|
747
|
+
import { nanoid as nanoid2 } from "nanoid";
|
|
748
|
+
var parseVariableFontInstances = async (font, client) => {
|
|
749
|
+
if (!font.variableFont || !font.variableInstances) return [];
|
|
750
|
+
let variableInstances;
|
|
751
|
+
try {
|
|
752
|
+
variableInstances = JSON.parse(font.variableInstances);
|
|
753
|
+
} catch (err) {
|
|
754
|
+
console.error("Error parsing variable instances:", err);
|
|
755
|
+
variableInstances = {};
|
|
756
|
+
}
|
|
757
|
+
if (Object.keys(variableInstances).length === 0) return [];
|
|
758
|
+
let staticFonts;
|
|
759
|
+
const typeface = await client.fetch(
|
|
760
|
+
`*[_type == 'typeface' && title == $typefaceName][0]{
|
|
761
|
+
'fonts': styles.fonts[]-> {
|
|
762
|
+
_id,
|
|
763
|
+
title,
|
|
764
|
+
subfamily,
|
|
765
|
+
style,
|
|
766
|
+
weight,
|
|
767
|
+
weightName,
|
|
768
|
+
metaData,
|
|
769
|
+
variableFont
|
|
770
|
+
}
|
|
771
|
+
}`,
|
|
772
|
+
{ typefaceName: font.typefaceName }
|
|
773
|
+
);
|
|
774
|
+
if ((typeface == null ? void 0 : typeface.fonts) && typeface.fonts.length > 0) {
|
|
775
|
+
staticFonts = typeface.fonts.filter((f) => !f.variableFont);
|
|
776
|
+
console.log("Using curated typeface fonts list:", staticFonts.length, "fonts");
|
|
777
|
+
} else {
|
|
778
|
+
console.warn("Typeface not found or no fonts in curated list, falling back to all fonts query");
|
|
779
|
+
staticFonts = await client.fetch(
|
|
780
|
+
`*[_type == 'font' && typefaceName == $typefaceName && variableFont != true]{
|
|
781
|
+
_id,
|
|
782
|
+
title,
|
|
783
|
+
subfamily,
|
|
784
|
+
style,
|
|
785
|
+
weight,
|
|
786
|
+
weightName,
|
|
787
|
+
metaData
|
|
788
|
+
}`,
|
|
789
|
+
{ typefaceName: font.typefaceName }
|
|
790
|
+
);
|
|
791
|
+
}
|
|
792
|
+
console.log("Variable font instances:", Object.keys(variableInstances));
|
|
793
|
+
console.log("Available static fonts:", staticFonts.map((sf) => sf.title));
|
|
794
|
+
const instanceMappings = [];
|
|
795
|
+
Object.keys(variableInstances).forEach((instanceName) => {
|
|
796
|
+
let matchingFont = null;
|
|
797
|
+
matchingFont = staticFonts.find((sf) => sf.title === instanceName);
|
|
798
|
+
if (!matchingFont && staticFonts.some((sf) => {
|
|
799
|
+
var _a;
|
|
800
|
+
return (_a = sf.metaData) == null ? void 0 : _a.fullName;
|
|
801
|
+
})) {
|
|
802
|
+
matchingFont = staticFonts.find((sf) => {
|
|
803
|
+
var _a, _b, _c, _d;
|
|
804
|
+
if (!((_a = sf.metaData) == null ? void 0 : _a.fullName)) return false;
|
|
805
|
+
let fullName = sf.metaData.fullName;
|
|
806
|
+
const WORDS_TO_REMOVE = ["VF", "var", "variable", "VAR", "vf"];
|
|
807
|
+
const variableName = (_c = (_b = font.metaData) == null ? void 0 : _b.familyName) == null ? void 0 : _c.replace(new RegExp(`\\b(${WORDS_TO_REMOVE.join("|")})\\b`, "gi"), "").replace(/\s{2,}/g, " ").trim();
|
|
808
|
+
if (variableName && fullName.startsWith(variableName)) {
|
|
809
|
+
fullName = fullName.substring(variableName.length).trim();
|
|
810
|
+
}
|
|
811
|
+
if (variableName) {
|
|
812
|
+
const words = variableName.split(/\s+/).map((w) => w.trim()).filter(Boolean);
|
|
813
|
+
if (words.length > 0) {
|
|
814
|
+
const regex = new RegExp(`\\b(${words.join("|")})\\b`, "gi");
|
|
815
|
+
const stripped = fullName.replace(regex, "").replace(/\s{2,}/g, " ").trim();
|
|
816
|
+
if (stripped !== "") fullName = stripped;
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
if (fullName.startsWith(font.typefaceName)) {
|
|
820
|
+
fullName = fullName.substring(font.typefaceName.length).trim();
|
|
821
|
+
}
|
|
822
|
+
if (((_d = sf.style) == null ? void 0 : _d.toLowerCase()) === "italic" && !fullName.toLowerCase().endsWith("italic") && !fullName.toLowerCase().endsWith("slanted")) {
|
|
823
|
+
fullName = fullName + " Italic";
|
|
824
|
+
}
|
|
825
|
+
if (fullName.trim().toLowerCase().endsWith("regular")) {
|
|
826
|
+
if (instanceName.trim().toLowerCase() + " regular" === fullName.trim().toLowerCase()) return true;
|
|
827
|
+
}
|
|
828
|
+
if (fullName.trim().toLowerCase().startsWith("regular")) {
|
|
829
|
+
if ("regular " + instanceName.trim().toLowerCase() === fullName.trim().toLowerCase()) return true;
|
|
830
|
+
}
|
|
831
|
+
if (fullName.trim().toLowerCase().endsWith("italic")) {
|
|
832
|
+
if (instanceName.trim().toLowerCase().endsWith("italic")) {
|
|
833
|
+
const k = instanceName.trim().toLowerCase().slice(0, -6).trim() + " regular italic";
|
|
834
|
+
if (k === fullName.trim().toLowerCase()) return true;
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
return fullName.trim().toLowerCase() === instanceName.trim().toLowerCase();
|
|
838
|
+
});
|
|
839
|
+
}
|
|
840
|
+
if (!matchingFont) {
|
|
841
|
+
const expandedName = instanceName.split(" ").map((word) => expandAbbreviations(word)).join(" ");
|
|
842
|
+
matchingFont = staticFonts.find((sf) => {
|
|
843
|
+
const nameWithoutTypeface = sf.title.replace(font.typefaceName, "").trim();
|
|
844
|
+
return nameWithoutTypeface === expandedName;
|
|
845
|
+
});
|
|
846
|
+
}
|
|
847
|
+
if (!matchingFont) {
|
|
848
|
+
const isItalic = instanceName.toLowerCase().includes("italic");
|
|
849
|
+
const weightTerms = [
|
|
850
|
+
{ term: "thin", weight: "100" },
|
|
851
|
+
{ term: "extralight", weight: "200" },
|
|
852
|
+
{ term: "extra light", weight: "200" },
|
|
853
|
+
{ term: "light", weight: "300" },
|
|
854
|
+
{ term: "regular", weight: "400" },
|
|
855
|
+
{ term: "normal", weight: "400" },
|
|
856
|
+
{ term: "medium", weight: "500" },
|
|
857
|
+
{ term: "semibold", weight: "600" },
|
|
858
|
+
{ term: "semi bold", weight: "600" },
|
|
859
|
+
{ term: "bold", weight: "700" },
|
|
860
|
+
{ term: "extrabold", weight: "800" },
|
|
861
|
+
{ term: "extra bold", weight: "800" },
|
|
862
|
+
{ term: "black", weight: "900" },
|
|
863
|
+
{ term: "heavy", weight: "900" }
|
|
864
|
+
];
|
|
865
|
+
let instanceWeight = "400";
|
|
866
|
+
for (const { term, weight } of weightTerms) {
|
|
867
|
+
if (instanceName.toLowerCase().includes(term)) {
|
|
868
|
+
instanceWeight = weight;
|
|
869
|
+
break;
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
matchingFont = staticFonts.find(
|
|
873
|
+
(sf) => sf.weight === instanceWeight && (isItalic && sf.style === "Italic" || !isItalic && sf.style === "Regular")
|
|
874
|
+
);
|
|
875
|
+
}
|
|
876
|
+
if (!matchingFont) {
|
|
877
|
+
matchingFont = staticFonts.find((sf) => {
|
|
878
|
+
if (!sf.weightName) return false;
|
|
879
|
+
const cleanInstance = instanceName.toLowerCase().replace(/italic/i, "").trim();
|
|
880
|
+
const cleanWeight = sf.weightName.toLowerCase().replace(/italic/i, "").trim();
|
|
881
|
+
return cleanInstance === cleanWeight;
|
|
882
|
+
});
|
|
883
|
+
}
|
|
884
|
+
if (!matchingFont && staticFonts.some((sf) => {
|
|
885
|
+
var _a;
|
|
886
|
+
return (_a = sf.metaData) == null ? void 0 : _a.fullName;
|
|
887
|
+
})) {
|
|
888
|
+
matchingFont = staticFonts.find((sf) => {
|
|
889
|
+
var _a;
|
|
890
|
+
if (!((_a = sf.metaData) == null ? void 0 : _a.fullName)) return false;
|
|
891
|
+
const typefacePattern = new RegExp(`^${font.typefaceName}\\s+`, "i");
|
|
892
|
+
const stylePart = sf.metaData.fullName.replace(typefacePattern, "").trim();
|
|
893
|
+
return instanceName.toLowerCase() === stylePart.toLowerCase();
|
|
894
|
+
});
|
|
895
|
+
}
|
|
896
|
+
console.log(`Instance "${instanceName}" matched with:`, matchingFont ? matchingFont.title : "No match found");
|
|
897
|
+
instanceMappings.push({
|
|
898
|
+
key: instanceName,
|
|
899
|
+
value: matchingFont ? { _type: "reference", _ref: matchingFont._id, _weak: true } : null,
|
|
900
|
+
_key: nanoid2()
|
|
901
|
+
});
|
|
902
|
+
});
|
|
903
|
+
return instanceMappings;
|
|
904
|
+
};
|
|
905
|
+
var parseVariableFontInstances_default = parseVariableFontInstances;
|
|
906
|
+
|
|
907
|
+
// src/utils/uploadFontFiles.js
|
|
908
|
+
var uploadFontFiles = async (fontsObjects, subfamilies, client, inputPrice, stylesObject, setStatus, setError) => {
|
|
909
|
+
let fontRefs = [];
|
|
910
|
+
let variableRefs = [];
|
|
911
|
+
let failedFiles = [];
|
|
912
|
+
const fontObjectKeys = Object.keys(fontsObjects);
|
|
913
|
+
for (let i = 0; i < fontObjectKeys.length; i++) {
|
|
914
|
+
const id = fontObjectKeys[i];
|
|
915
|
+
const fontObject = fontsObjects[id];
|
|
916
|
+
const files = fontObject.files;
|
|
917
|
+
const fontKit = fontObject.fontKit;
|
|
918
|
+
let newFileInput = fontObject.fileInput;
|
|
919
|
+
fontObject.subfamily = subfamilies[id] ? subfamilies[id] : "";
|
|
920
|
+
fontObject.price = Number(inputPrice) ? Number(inputPrice) : 0;
|
|
921
|
+
if (fontObject.price > 0) fontObject.sell = true;
|
|
922
|
+
for (let j = 0; j < files.length; j++) {
|
|
923
|
+
const file = files[j];
|
|
924
|
+
const fileType = determineFileType(file);
|
|
925
|
+
console.log(`[${i + 1}/${fontObjectKeys.length}][${j + 1}/${files.length}] Uploading font file: ${fontObject._id}.${fileType}`);
|
|
926
|
+
setStatus(`[${i + 1}/${fontObjectKeys.length}][${j + 1}/${files.length}] Uploading font file: ${fontObject._id}.${fileType}`);
|
|
927
|
+
try {
|
|
928
|
+
const baseAsset = await client.assets.upload("file", file, { filename: fontObject._id + "." + fileType });
|
|
929
|
+
newFileInput[fileType] = {
|
|
930
|
+
_type: "file",
|
|
931
|
+
asset: { _ref: baseAsset._id, _type: "reference" }
|
|
932
|
+
};
|
|
933
|
+
if (fileType === "woff2") {
|
|
934
|
+
console.log(`[${i + 1}/${fontObjectKeys.length}][${j + 1}/${files.length}] Generating CSS for: ${fontObject.title}`);
|
|
935
|
+
setStatus(`[${i + 1}/${fontObjectKeys.length}][${j + 1}/${files.length}] Generating CSS for: ${fontObject.title}`);
|
|
936
|
+
newFileInput = await generateCssFile({
|
|
937
|
+
woff2File: file,
|
|
938
|
+
fileInput: newFileInput,
|
|
939
|
+
fontName: fontObject.title,
|
|
940
|
+
fileName: fontObject._id,
|
|
941
|
+
variableFont: fontObject.variableFont,
|
|
942
|
+
weight: fontObject.weight,
|
|
943
|
+
client,
|
|
944
|
+
style: fontObject.style
|
|
945
|
+
});
|
|
946
|
+
}
|
|
947
|
+
if (fileType === "ttf") {
|
|
948
|
+
console.log(`[${i + 1}/${fontObjectKeys.length}][${j + 1}/${files.length}] Generating font data for: ${fontObject.title}`);
|
|
949
|
+
setStatus(`[${i + 1}/${fontObjectKeys.length}][${j + 1}/${files.length}] Generating font data for: ${fontObject.title}`);
|
|
950
|
+
const metadata = await generateFontData({
|
|
951
|
+
fontId: fontObject._id,
|
|
952
|
+
url: baseAsset.url,
|
|
953
|
+
fontKit,
|
|
954
|
+
client,
|
|
955
|
+
commit: false
|
|
956
|
+
});
|
|
957
|
+
Object.assign(fontObject, metadata);
|
|
958
|
+
}
|
|
959
|
+
} catch (err) {
|
|
960
|
+
console.error("Error uploading font: ", fontObject.title, err.message);
|
|
961
|
+
setStatus("Error uploading font: " + err.message);
|
|
962
|
+
setError(true);
|
|
963
|
+
failedFiles.push({ name: file.name, fk: fontKit });
|
|
964
|
+
continue;
|
|
965
|
+
}
|
|
966
|
+
fontObject.fileInput = newFileInput;
|
|
967
|
+
fontsObjects[id] = fontObject;
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
console.log("Creating/updating Sanity font documents:", fontsObjects);
|
|
971
|
+
for (let i = 0; i < fontObjectKeys.length; i++) {
|
|
972
|
+
const fontId = fontObjectKeys[i];
|
|
973
|
+
const font = fontsObjects[fontId];
|
|
974
|
+
const fontRef = await createOrUpdateFontDocument(font, client, setError);
|
|
975
|
+
if (fontRef) {
|
|
976
|
+
if (!font.variableFont) {
|
|
977
|
+
addToFontRefs(fontRef, font, fontRefs, stylesObject);
|
|
978
|
+
} else {
|
|
979
|
+
addToVariableRefs(fontRef, font, variableRefs, stylesObject);
|
|
980
|
+
}
|
|
981
|
+
console.log(`[${i + 1}/${fontObjectKeys.length}] ${fontRef._ref} created`);
|
|
982
|
+
setStatus(`[${i + 1}/${fontObjectKeys.length}] ${fontRef._ref} created`);
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
return { fontRefs, variableRefs, failedFiles };
|
|
986
|
+
};
|
|
987
|
+
var determineFileType = (file) => {
|
|
988
|
+
if (file.name.endsWith(".otf")) return "otf";
|
|
989
|
+
if (file.name.endsWith(".ttf")) return "ttf";
|
|
990
|
+
if (file.name.endsWith(".woff")) return "woff";
|
|
991
|
+
if (file.name.endsWith(".woff2")) return "woff2";
|
|
992
|
+
if (file.name.endsWith(".eot")) return "eot";
|
|
993
|
+
if (file.name.endsWith(".svg")) return "svg";
|
|
994
|
+
return "";
|
|
995
|
+
};
|
|
996
|
+
var createOrUpdateFontDocument = async (font, client, setError) => {
|
|
997
|
+
try {
|
|
998
|
+
const existingFont = await client.fetch(
|
|
999
|
+
`*[_type == 'font' && _id == $fontId]{
|
|
1000
|
+
fileInput,
|
|
1001
|
+
description,
|
|
1002
|
+
metaData,
|
|
1003
|
+
metrics,
|
|
1004
|
+
opentypeFeatures,
|
|
1005
|
+
characterSet,
|
|
1006
|
+
subfamily,
|
|
1007
|
+
scriptFileInput,
|
|
1008
|
+
variableInstanceReferences
|
|
1009
|
+
}`,
|
|
1010
|
+
{ fontId: font._id }
|
|
1011
|
+
).then((res) => res[0]);
|
|
1012
|
+
const { files, fontKit } = font;
|
|
1013
|
+
delete font.files;
|
|
1014
|
+
delete font.fontKit;
|
|
1015
|
+
delete font.originalFilename;
|
|
1016
|
+
if (font.variableFont && font.variableInstances) {
|
|
1017
|
+
const instanceMappings = await parseVariableFontInstances(font, client);
|
|
1018
|
+
if (instanceMappings.length > 0) {
|
|
1019
|
+
font.variableInstanceReferences = instanceMappings;
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
let fontResponse;
|
|
1023
|
+
if (existingFont) {
|
|
1024
|
+
fontResponse = await updateExistingFont(font, existingFont, client);
|
|
1025
|
+
} else {
|
|
1026
|
+
fontResponse = await createNewFont(font, client);
|
|
1027
|
+
}
|
|
1028
|
+
return {
|
|
1029
|
+
_key: nanoid3(),
|
|
1030
|
+
_type: "reference",
|
|
1031
|
+
_ref: fontResponse._id,
|
|
1032
|
+
_weak: true
|
|
1033
|
+
};
|
|
1034
|
+
} catch (e) {
|
|
1035
|
+
console.error("Error creating font: ", font.title, font.subfamily, e);
|
|
1036
|
+
setError(true);
|
|
1037
|
+
return null;
|
|
1038
|
+
}
|
|
1039
|
+
};
|
|
1040
|
+
var updateExistingFont = async (font, existingFont, client) => {
|
|
1041
|
+
if (existingFont.fileInput) {
|
|
1042
|
+
const newFileInput = { ...font.fileInput };
|
|
1043
|
+
Object.keys(existingFont.fileInput).forEach((key) => {
|
|
1044
|
+
if (!newFileInput[key]) newFileInput[key] = existingFont.fileInput[key];
|
|
1045
|
+
});
|
|
1046
|
+
font.fileInput = newFileInput;
|
|
1047
|
+
}
|
|
1048
|
+
font.metaData = !(font == null ? void 0 : font.metaData) || Object.keys((font == null ? void 0 : font.metaData) || {}).length === 0 ? (existingFont == null ? void 0 : existingFont.metaData) || {} : font.metaData;
|
|
1049
|
+
font.metrics = !(font == null ? void 0 : font.metrics) || Object.keys((font == null ? void 0 : font.metrics) || {}).length === 0 ? (existingFont == null ? void 0 : existingFont.metrics) || {} : font.metrics;
|
|
1050
|
+
font.opentypeFeatures = !(font == null ? void 0 : font.opentypeFeatures) || Object.keys((font == null ? void 0 : font.opentypeFeatures) || {}).length === 0 ? (existingFont == null ? void 0 : existingFont.opentypeFeatures) || {} : font.opentypeFeatures;
|
|
1051
|
+
font.characterSet = !(font == null ? void 0 : font.characterSet) || Object.keys((font == null ? void 0 : font.characterSet) || {}).length === 0 ? (existingFont == null ? void 0 : existingFont.characterSet) || {} : font.characterSet;
|
|
1052
|
+
font.scriptFileInput = (existingFont == null ? void 0 : existingFont.scriptFileInput) || {};
|
|
1053
|
+
cleanMetadataValues(font);
|
|
1054
|
+
if (font.variableFont && (existingFont == null ? void 0 : existingFont.variableInstanceReferences) && (!font.variableInstanceReferences || font.variableInstanceReferences.length === 0)) {
|
|
1055
|
+
font.variableInstanceReferences = existingFont.variableInstanceReferences;
|
|
1056
|
+
}
|
|
1057
|
+
console.log("Updating existing font: ", font._id, font.title);
|
|
1058
|
+
const patchObject = {
|
|
1059
|
+
fileInput: font.fileInput,
|
|
1060
|
+
subfamily: font.subfamily,
|
|
1061
|
+
weight: font.weight
|
|
1062
|
+
};
|
|
1063
|
+
if (font.variableInstanceReferences) {
|
|
1064
|
+
patchObject.variableInstanceReferences = font.variableInstanceReferences;
|
|
1065
|
+
}
|
|
1066
|
+
return await client.patch(font._id).set(patchObject).commit();
|
|
1067
|
+
};
|
|
1068
|
+
var createNewFont = async (font, client) => {
|
|
1069
|
+
console.log("Creating new font: ", font._id, font.title);
|
|
1070
|
+
if (font.metaData) cleanMetadataValues(font);
|
|
1071
|
+
const newDocument = {
|
|
1072
|
+
_key: nanoid3(),
|
|
1073
|
+
_id: font._id,
|
|
1074
|
+
_type: "font",
|
|
1075
|
+
...font
|
|
1076
|
+
};
|
|
1077
|
+
return await client.createOrReplace(newDocument);
|
|
1078
|
+
};
|
|
1079
|
+
var cleanMetadataValues = (font) => {
|
|
1080
|
+
if (!font.metaData) return;
|
|
1081
|
+
Object.keys(font.metaData).forEach((key) => {
|
|
1082
|
+
if (font.metaData[key] == null) {
|
|
1083
|
+
font.metaData[key] = "";
|
|
1084
|
+
} else {
|
|
1085
|
+
font.metaData[key] = font.metaData[key].replace(/[-]/g, "");
|
|
1086
|
+
}
|
|
1087
|
+
});
|
|
1088
|
+
};
|
|
1089
|
+
var addToFontRefs = (fontRef, font, fontRefs, stylesObject) => {
|
|
1090
|
+
if (stylesObject.fonts && stylesObject.fonts.length > 0) {
|
|
1091
|
+
const fontExists = stylesObject.fonts.findIndex((f) => f._ref === fontRef._ref);
|
|
1092
|
+
const inFontRefs = fontRefs.findIndex((f) => f._ref === fontRef._ref);
|
|
1093
|
+
if (fontExists === -1 && inFontRefs === -1) fontRefs.push(fontRef);
|
|
1094
|
+
} else {
|
|
1095
|
+
fontRefs.push(fontRef);
|
|
1096
|
+
}
|
|
1097
|
+
};
|
|
1098
|
+
var addToVariableRefs = (fontRef, font, variableRefs, stylesObject) => {
|
|
1099
|
+
var _a;
|
|
1100
|
+
if ((_a = stylesObject == null ? void 0 : stylesObject.variableFont) == null ? void 0 : _a.length) {
|
|
1101
|
+
const vfExists = stylesObject.variableFont.findIndex((f) => f._ref === fontRef._ref);
|
|
1102
|
+
const inVariableRefs = variableRefs.findIndex((f) => f._ref === fontRef._ref);
|
|
1103
|
+
if (vfExists === -1 && inVariableRefs === -1 && font.variableFont) variableRefs.push(fontRef);
|
|
1104
|
+
} else {
|
|
1105
|
+
variableRefs.push(fontRef);
|
|
1106
|
+
}
|
|
1107
|
+
};
|
|
1108
|
+
|
|
1109
|
+
// src/utils/updateTypefaceDocument.js
|
|
1110
|
+
import { nanoid as nanoid4 } from "nanoid";
|
|
1111
|
+
var updateTypefaceDocument = async (doc_id, fontRefs, variableRefs, subfamilies, uniqueSubfamilies, subfamiliesArray, preferredStyleRef, newPreferredStyle, stylesObject, client, setStatus, setError) => {
|
|
1112
|
+
console.log("Updating typeface document with new fonts:", { fontRefs, variableRefs, subfamilies, uniqueSubfamilies });
|
|
1113
|
+
setStatus("Updating typeface references...");
|
|
1114
|
+
let patch = {
|
|
1115
|
+
styles: {
|
|
1116
|
+
fonts: stylesObject.fonts ? [...stylesObject.fonts, ...fontRefs] : [...fontRefs],
|
|
1117
|
+
subfamilies: uniqueSubfamilies.length > 1 ? uniqueSubfamilies : [],
|
|
1118
|
+
variableFont: (stylesObject == null ? void 0 : stylesObject.variableFont) ? [...stylesObject.variableFont, ...variableRefs] : [...variableRefs]
|
|
1119
|
+
}
|
|
1120
|
+
};
|
|
1121
|
+
setStatus("Organising font subfamilies...");
|
|
1122
|
+
subfamiliesArray = subfamiliesArray || [];
|
|
1123
|
+
uniqueSubfamilies.forEach((subfamilyName) => {
|
|
1124
|
+
if (!subfamiliesArray.find((sf) => sf.title === subfamilyName)) {
|
|
1125
|
+
subfamiliesArray.push({
|
|
1126
|
+
title: subfamilyName,
|
|
1127
|
+
_key: nanoid4(),
|
|
1128
|
+
_type: "object",
|
|
1129
|
+
fonts: []
|
|
1130
|
+
});
|
|
1131
|
+
}
|
|
1132
|
+
});
|
|
1133
|
+
if (subfamiliesArray.length > 0) {
|
|
1134
|
+
Object.entries(subfamilies).forEach(([id, subfamilyName]) => {
|
|
1135
|
+
if (id.toLowerCase().includes("vf")) return;
|
|
1136
|
+
const subfamilyIndex = subfamiliesArray.findIndex((sf) => sf.title === subfamilyName);
|
|
1137
|
+
if (subfamilyIndex !== -1) {
|
|
1138
|
+
subfamiliesArray[subfamilyIndex].fonts.push({
|
|
1139
|
+
_ref: id,
|
|
1140
|
+
_key: nanoid4(),
|
|
1141
|
+
_type: "reference",
|
|
1142
|
+
_weak: true
|
|
1143
|
+
});
|
|
1144
|
+
}
|
|
1145
|
+
});
|
|
1146
|
+
subfamiliesArray = subfamiliesArray.map((subfamily) => ({
|
|
1147
|
+
...subfamily,
|
|
1148
|
+
fonts: subfamily.fonts.filter(
|
|
1149
|
+
(font, index, self) => index === self.findIndex((f) => f._ref === font._ref)
|
|
1150
|
+
)
|
|
1151
|
+
}));
|
|
1152
|
+
}
|
|
1153
|
+
patch.styles.subfamilies = subfamiliesArray;
|
|
1154
|
+
await updatePreferredStyle(doc_id, preferredStyleRef, newPreferredStyle, patch, client);
|
|
1155
|
+
console.log("doc_id: ", doc_id);
|
|
1156
|
+
console.log("Typeface patch: ", patch);
|
|
1157
|
+
console.log("New preferred style: ", newPreferredStyle);
|
|
1158
|
+
console.log("SubfamiliesArray:", subfamiliesArray);
|
|
1159
|
+
try {
|
|
1160
|
+
await client.patch(doc_id).set(patch).commit();
|
|
1161
|
+
console.log(`Updated document: ${doc_id}`);
|
|
1162
|
+
if (doc_id.startsWith("drafts.")) {
|
|
1163
|
+
await updatePublishedDocument(doc_id, patch, client);
|
|
1164
|
+
}
|
|
1165
|
+
} catch (err) {
|
|
1166
|
+
console.error("Error updating document:", err.message);
|
|
1167
|
+
setStatus("Error updating typeface");
|
|
1168
|
+
setError(true);
|
|
1169
|
+
}
|
|
1170
|
+
};
|
|
1171
|
+
var updatePreferredStyle = async (doc_id, preferredStyleRef, newPreferredStyle, patch, client) => {
|
|
1172
|
+
var _a;
|
|
1173
|
+
if ((preferredStyleRef == null ? void 0 : preferredStyleRef._ref) && preferredStyleRef._ref !== "" && preferredStyleRef._ref !== null && newPreferredStyle._ref !== preferredStyleRef._ref) {
|
|
1174
|
+
const prefStyleResult = await client.fetch(
|
|
1175
|
+
`*[_id == $docId]{ preferredStyle->{ weight, style, _id } }`,
|
|
1176
|
+
{ docId: doc_id }
|
|
1177
|
+
);
|
|
1178
|
+
const prefStyle = (_a = prefStyleResult[0]) == null ? void 0 : _a.preferredStyle;
|
|
1179
|
+
if (!(prefStyle == null ? void 0 : prefStyle.weight) || prefStyle === null || prefStyle.weight < newPreferredStyle.weight) {
|
|
1180
|
+
patch.preferredStyle = {
|
|
1181
|
+
_type: "reference",
|
|
1182
|
+
_ref: newPreferredStyle._ref,
|
|
1183
|
+
_weak: true
|
|
1184
|
+
};
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
};
|
|
1188
|
+
var updatePublishedDocument = async (doc_id, patch, client) => {
|
|
1189
|
+
const publishedId = doc_id.replace("drafts.", "");
|
|
1190
|
+
const publishedDoc = await client.fetch(`*[_id == $publishedId]`, { publishedId }).then((res) => res[0]);
|
|
1191
|
+
if (publishedDoc) {
|
|
1192
|
+
await client.patch(publishedId).set(patch).commit();
|
|
1193
|
+
console.log(`Updated published document: ${publishedId}`);
|
|
1194
|
+
} else {
|
|
1195
|
+
console.log(`No published document found for ${publishedId}, skipping`);
|
|
1196
|
+
}
|
|
1197
|
+
};
|
|
1198
|
+
|
|
1199
|
+
// src/utils/regenerateFontData.js
|
|
1200
|
+
import * as fontkit4 from "fontkit";
|
|
1201
|
+
var renameFontDocuments = async ({
|
|
1202
|
+
client,
|
|
1203
|
+
typefaceName,
|
|
1204
|
+
slug,
|
|
1205
|
+
weightKeywordList,
|
|
1206
|
+
italicKeywordList,
|
|
1207
|
+
preserveShortenedNames = false,
|
|
1208
|
+
setStatus,
|
|
1209
|
+
setError
|
|
1210
|
+
}) => {
|
|
1211
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
|
|
1212
|
+
try {
|
|
1213
|
+
if (!typefaceName) {
|
|
1214
|
+
setStatus("Typeface needs a title");
|
|
1215
|
+
setError(true);
|
|
1216
|
+
console.error("Typeface needs title");
|
|
1217
|
+
return { success: false, message: "Typeface needs title" };
|
|
1218
|
+
}
|
|
1219
|
+
setStatus("Fetching font documents...");
|
|
1220
|
+
const slugCurrent = (slug == null ? void 0 : slug.current) || typefaceName;
|
|
1221
|
+
const query = await client.fetch(
|
|
1222
|
+
`*[_type == "typeface" && slug.current == $slugCurrent][0]{
|
|
1223
|
+
"fonts": styles.fonts[]->{ _id, title, subfamily, fileInput, style, weight, _key }
|
|
1224
|
+
}`,
|
|
1225
|
+
{ slugCurrent }
|
|
1226
|
+
);
|
|
1227
|
+
const fontDocuments = (query == null ? void 0 : query.fonts) || [];
|
|
1228
|
+
if (fontDocuments.length === 0) {
|
|
1229
|
+
setStatus("No font documents found for this typeface");
|
|
1230
|
+
setError(true);
|
|
1231
|
+
return { success: false, message: "No font documents found for this typeface" };
|
|
1232
|
+
}
|
|
1233
|
+
setStatus(`Found ${fontDocuments.length} font documents to process...`);
|
|
1234
|
+
let updatedCount = 0;
|
|
1235
|
+
let skippedCount = 0;
|
|
1236
|
+
let errorCount = 0;
|
|
1237
|
+
for (let i = 0; i < fontDocuments.length; i++) {
|
|
1238
|
+
const fontDoc = fontDocuments[i];
|
|
1239
|
+
setStatus(`Processing font ${i + 1}/${fontDocuments.length}: ${fontDoc.title}`);
|
|
1240
|
+
if (!((_c = (_b = (_a = fontDoc.fileInput) == null ? void 0 : _a.ttf) == null ? void 0 : _b.asset) == null ? void 0 : _c._ref) && !((_f = (_e = (_d = fontDoc.fileInput) == null ? void 0 : _d.otf) == null ? void 0 : _e.asset) == null ? void 0 : _f._ref)) {
|
|
1241
|
+
console.log(`Skipping ${fontDoc.title} \u2014 no TTF or OTF file found`);
|
|
1242
|
+
skippedCount++;
|
|
1243
|
+
continue;
|
|
1244
|
+
}
|
|
1245
|
+
try {
|
|
1246
|
+
let file;
|
|
1247
|
+
let ttfAsset;
|
|
1248
|
+
let otfAsset;
|
|
1249
|
+
if ((_i = (_h = (_g = fontDoc.fileInput) == null ? void 0 : _g.ttf) == null ? void 0 : _h.asset) == null ? void 0 : _i._ref) {
|
|
1250
|
+
ttfAsset = await client.getDocument(fontDoc.fileInput.ttf.asset._ref);
|
|
1251
|
+
}
|
|
1252
|
+
if (!(ttfAsset == null ? void 0 : ttfAsset.url)) {
|
|
1253
|
+
if ((_l = (_k = (_j = fontDoc.fileInput) == null ? void 0 : _j.otf) == null ? void 0 : _k.asset) == null ? void 0 : _l._ref) {
|
|
1254
|
+
otfAsset = await client.getDocument(fontDoc.fileInput.otf.asset._ref);
|
|
1255
|
+
}
|
|
1256
|
+
if (!(otfAsset == null ? void 0 : otfAsset.url)) {
|
|
1257
|
+
console.log(`Skipping ${fontDoc.title} \u2014 no TTF or OTF URL`);
|
|
1258
|
+
skippedCount++;
|
|
1259
|
+
continue;
|
|
1260
|
+
}
|
|
1261
|
+
setStatus(`Fetching OTF file for ${fontDoc.title}...`);
|
|
1262
|
+
const res = await fetch(otfAsset.url);
|
|
1263
|
+
file = await res.blob();
|
|
1264
|
+
} else {
|
|
1265
|
+
setStatus(`Fetching TTF file for ${fontDoc.title}...`);
|
|
1266
|
+
const res = await fetch(ttfAsset.url);
|
|
1267
|
+
file = await res.blob();
|
|
1268
|
+
}
|
|
1269
|
+
const fontBuffer = await readFontFile(file);
|
|
1270
|
+
const font = fontkit4.create(fontBuffer);
|
|
1271
|
+
const { weightName, subfamilyName, fontTitle } = extractFontMetadata(
|
|
1272
|
+
font,
|
|
1273
|
+
typefaceName,
|
|
1274
|
+
weightKeywordList,
|
|
1275
|
+
italicKeywordList,
|
|
1276
|
+
preserveShortenedNames
|
|
1277
|
+
);
|
|
1278
|
+
const slugValue = fontTitle.toLowerCase().replace(/\s+/g, "-");
|
|
1279
|
+
setStatus(`Updating font ${i + 1}/${fontDocuments.length}: ${fontTitle}`);
|
|
1280
|
+
await client.patch(fontDoc._id).set({
|
|
1281
|
+
title: fontTitle,
|
|
1282
|
+
slug: { _type: "slug", current: slugValue },
|
|
1283
|
+
weightName,
|
|
1284
|
+
subfamily: subfamilyName
|
|
1285
|
+
}).commit();
|
|
1286
|
+
updatedCount++;
|
|
1287
|
+
} catch (err) {
|
|
1288
|
+
console.error(`Error processing ${fontDoc.title}:`, err);
|
|
1289
|
+
errorCount++;
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
const resultMessage = `Renamed ${updatedCount} of ${fontDocuments.length} font documents (${skippedCount} skipped, ${errorCount} errors)`;
|
|
1293
|
+
setStatus(resultMessage);
|
|
1294
|
+
return {
|
|
1295
|
+
success: true,
|
|
1296
|
+
message: resultMessage,
|
|
1297
|
+
stats: { total: fontDocuments.length, updated: updatedCount, skipped: skippedCount, errors: errorCount }
|
|
1298
|
+
};
|
|
1299
|
+
} catch (err) {
|
|
1300
|
+
console.error("Error renaming font documents:", err);
|
|
1301
|
+
setError(true);
|
|
1302
|
+
setStatus(`Error: ${err.message}`);
|
|
1303
|
+
return { success: false, message: `Error: ${err.message}` };
|
|
1304
|
+
}
|
|
1305
|
+
};
|
|
1306
|
+
|
|
1307
|
+
// src/utils/updateFontPrices.js
|
|
1308
|
+
var updateFontPrices = async ({
|
|
1309
|
+
client,
|
|
1310
|
+
title,
|
|
1311
|
+
slug,
|
|
1312
|
+
inputPrice,
|
|
1313
|
+
doc_id,
|
|
1314
|
+
setStatus,
|
|
1315
|
+
setError
|
|
1316
|
+
}) => {
|
|
1317
|
+
var _a, _b;
|
|
1318
|
+
try {
|
|
1319
|
+
if (!title) {
|
|
1320
|
+
setStatus("Typeface needs a title");
|
|
1321
|
+
setError(true);
|
|
1322
|
+
console.error("Typeface needs title");
|
|
1323
|
+
return { success: false, message: "Typeface needs title" };
|
|
1324
|
+
}
|
|
1325
|
+
if (!(slug == null ? void 0 : slug.current)) {
|
|
1326
|
+
setStatus("Typeface needs a slug");
|
|
1327
|
+
setError(true);
|
|
1328
|
+
console.error("Typeface needs slug");
|
|
1329
|
+
return { success: false, message: "Typeface needs slug" };
|
|
1330
|
+
}
|
|
1331
|
+
const price = Number(inputPrice);
|
|
1332
|
+
if (isNaN(price)) {
|
|
1333
|
+
setStatus("Invalid price value");
|
|
1334
|
+
setError(true);
|
|
1335
|
+
console.error("Invalid price value");
|
|
1336
|
+
return { success: false, message: "Invalid price value" };
|
|
1337
|
+
}
|
|
1338
|
+
setStatus("Fetching typeface document...");
|
|
1339
|
+
const typeface = await client.fetch(
|
|
1340
|
+
`*[_type == "typeface" && slug.current == $slug][0]`,
|
|
1341
|
+
{ slug: slug.current }
|
|
1342
|
+
);
|
|
1343
|
+
if (!typeface) {
|
|
1344
|
+
setStatus("Typeface not found");
|
|
1345
|
+
setError(true);
|
|
1346
|
+
console.error("Typeface not found");
|
|
1347
|
+
return { success: false, message: "Typeface not found" };
|
|
1348
|
+
}
|
|
1349
|
+
if (!((_b = (_a = typeface.styles) == null ? void 0 : _a.fonts) == null ? void 0 : _b.length)) {
|
|
1350
|
+
setStatus("No fonts found in typeface");
|
|
1351
|
+
setError(true);
|
|
1352
|
+
console.error("No fonts found in typeface");
|
|
1353
|
+
return { success: false, message: "No fonts found in typeface" };
|
|
1354
|
+
}
|
|
1355
|
+
const fontRefs = typeface.styles.fonts;
|
|
1356
|
+
setStatus(`Updating prices for ${fontRefs.length} fonts...`);
|
|
1357
|
+
let updatedCount = 0;
|
|
1358
|
+
for (let i = 0; i < fontRefs.length; i++) {
|
|
1359
|
+
try {
|
|
1360
|
+
await client.patch(fontRefs[i]._ref).set({ price, sell: price > 0 }).commit();
|
|
1361
|
+
updatedCount++;
|
|
1362
|
+
setStatus(`Updated ${updatedCount}/${fontRefs.length} fonts...`);
|
|
1363
|
+
} catch (err) {
|
|
1364
|
+
console.error(`Error updating font ${fontRefs[i]._ref}:`, err);
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1367
|
+
const successMessage = `Successfully updated prices for ${updatedCount} fonts to $${price}`;
|
|
1368
|
+
setStatus(successMessage);
|
|
1369
|
+
console.log(successMessage);
|
|
1370
|
+
return { success: true, message: successMessage, updatedCount };
|
|
1371
|
+
} catch (err) {
|
|
1372
|
+
const errorMessage = `Error: ${err.message}`;
|
|
1373
|
+
console.error("Error updating font prices:", err);
|
|
1374
|
+
setError(true);
|
|
1375
|
+
setStatus(errorMessage);
|
|
1376
|
+
return { success: false, message: errorMessage };
|
|
1377
|
+
}
|
|
1378
|
+
};
|
|
1379
|
+
|
|
1380
|
+
// src/components/StatusDisplay.jsx
|
|
1381
|
+
import React from "react";
|
|
1382
|
+
import { Flex, Text } from "@sanity/ui";
|
|
1383
|
+
var StatusDisplay = ({ status, error, action }) => {
|
|
1384
|
+
return /* @__PURE__ */ React.createElement(Flex, { paddingTop: 1, paddingBottom: 3, align: "center", justify: "space-between" }, /* @__PURE__ */ React.createElement(Flex, { align: "center", gap: 2 }, /* @__PURE__ */ React.createElement(Text, { size: 1 }, "Status:"), /* @__PURE__ */ React.createElement(Text, { size: 1, style: { color: error ? "red" : "green" } }, status)), action && action);
|
|
1385
|
+
};
|
|
1386
|
+
var StatusDisplay_default = StatusDisplay;
|
|
1387
|
+
|
|
1388
|
+
// src/components/PriceInput.jsx
|
|
1389
|
+
import React2 from "react";
|
|
1390
|
+
import { Flex as Flex2, Text as Text2 } from "@sanity/ui";
|
|
1391
|
+
var PriceInput = ({ inputPrice, handleInputChange }) => /* @__PURE__ */ React2.createElement(Flex2, { align: "center", gap: 2 }, /* @__PURE__ */ React2.createElement(Text2, { size: 1, muted: true }, "Price:"), /* @__PURE__ */ React2.createElement(Text2, { size: 1, muted: true }, "$"), /* @__PURE__ */ React2.createElement(
|
|
1392
|
+
"input",
|
|
1393
|
+
{
|
|
1394
|
+
value: inputPrice,
|
|
1395
|
+
onChange: handleInputChange,
|
|
1396
|
+
type: "number",
|
|
1397
|
+
style: { textAlign: "end", padding: "5px", maxWidth: "75px" }
|
|
1398
|
+
}
|
|
1399
|
+
), /* @__PURE__ */ React2.createElement(Text2, { size: 1, muted: true }, "per style"));
|
|
1400
|
+
var PriceInput_default = PriceInput;
|
|
1401
|
+
|
|
1402
|
+
// src/components/RegenerateSubfamiliesComponent.jsx
|
|
1403
|
+
import React3, { useState } from "react";
|
|
1404
|
+
import { nanoid as nanoid5 } from "nanoid";
|
|
1405
|
+
import { Button, Text as Text3, Stack, Box } from "@sanity/ui";
|
|
1406
|
+
import { useFormValue } from "sanity";
|
|
1407
|
+
var RegenerateSubfamiliesComponent = () => {
|
|
1408
|
+
const [status, setStatus] = useState("");
|
|
1409
|
+
const [ready, setReady] = useState(true);
|
|
1410
|
+
const [error, setError] = useState(false);
|
|
1411
|
+
const client = useSanityClient();
|
|
1412
|
+
const doc_id = useFormValue(["_id"]);
|
|
1413
|
+
const title = useFormValue(["title"]);
|
|
1414
|
+
const slug = useFormValue(["slug"]);
|
|
1415
|
+
const stylesObject = useFormValue(["styles"]) || { fonts: [], variableFont: [] };
|
|
1416
|
+
const handleClick = () => {
|
|
1417
|
+
regenerateSubfamilies({ title, stylesObject, slug, doc_id, client, setStatus, setReady, setError });
|
|
1418
|
+
};
|
|
1419
|
+
return /* @__PURE__ */ React3.createElement(React3.Fragment, null, status && /* @__PURE__ */ React3.createElement(Box, { padding: 3, style: { borderRadius: "4px", marginBottom: "10px" } }, /* @__PURE__ */ React3.createElement(Text3, { size: 1, style: { color: error ? "red" : "green" } }, status)), /* @__PURE__ */ React3.createElement(Button, { mode: "ghost", tone: "primary", width: "fill", padding: 3, onClick: handleClick, disabled: !ready }, /* @__PURE__ */ React3.createElement(Stack, { space: 2 }, /* @__PURE__ */ React3.createElement(Text3, { align: "center" }, "Regenerate Subfamilies"))));
|
|
1420
|
+
};
|
|
1421
|
+
var regenerateSubfamilies = async ({ title, stylesObject, slug, doc_id, client, setStatus, setReady, setError }) => {
|
|
1422
|
+
try {
|
|
1423
|
+
setStatus("Regenerating subfamilies...");
|
|
1424
|
+
setReady(false);
|
|
1425
|
+
setError(false);
|
|
1426
|
+
const allFonts = await fetchFonts(title, slug, client);
|
|
1427
|
+
if (!allFonts || allFonts.length === 0) {
|
|
1428
|
+
setStatus("No fonts found for this typeface");
|
|
1429
|
+
setReady(true);
|
|
1430
|
+
setError(true);
|
|
1431
|
+
return;
|
|
1432
|
+
}
|
|
1433
|
+
console.log("Found fonts:", allFonts.length);
|
|
1434
|
+
setStatus(`Found ${allFonts.length} fonts. Processing...`);
|
|
1435
|
+
const subfamilies = groupFontsBySubfamily(allFonts);
|
|
1436
|
+
const newSubfamiliesArray = createSubfamiliesArray(subfamilies);
|
|
1437
|
+
console.log("New subfamilies:", newSubfamiliesArray);
|
|
1438
|
+
setStatus(`Created ${newSubfamiliesArray.length} subfamily groups`);
|
|
1439
|
+
await updateTypefaceSubfamilies(doc_id, stylesObject, newSubfamiliesArray, client, setStatus, setError);
|
|
1440
|
+
setStatus("Subfamilies regenerated successfully");
|
|
1441
|
+
} catch (e) {
|
|
1442
|
+
console.error(e.message);
|
|
1443
|
+
setStatus("Error regenerating subfamilies");
|
|
1444
|
+
setError(true);
|
|
1445
|
+
}
|
|
1446
|
+
setReady(true);
|
|
1447
|
+
};
|
|
1448
|
+
var fetchFonts = async (title, slug, client) => {
|
|
1449
|
+
const slugCurrent = (slug == null ? void 0 : slug.current) || title;
|
|
1450
|
+
const query = await client.fetch(
|
|
1451
|
+
`*[_type == "typeface" && slug.current == $slugCurrent][0]{
|
|
1452
|
+
"fonts": styles.fonts[]->{ _id, title, subfamily, style, weight, _key }
|
|
1453
|
+
}`,
|
|
1454
|
+
{ slugCurrent }
|
|
1455
|
+
);
|
|
1456
|
+
return (query == null ? void 0 : query.fonts) || [];
|
|
1457
|
+
};
|
|
1458
|
+
var groupFontsBySubfamily = (fonts) => {
|
|
1459
|
+
const subfamilies = {};
|
|
1460
|
+
fonts.forEach((font) => {
|
|
1461
|
+
var _a;
|
|
1462
|
+
if ((_a = font.title) == null ? void 0 : _a.includes("VF")) {
|
|
1463
|
+
subfamilies[`VF_${font.title}`] = [font];
|
|
1464
|
+
} else {
|
|
1465
|
+
const subfamilyName = font.subfamily ? expandAbbreviations(font.subfamily) : "Regular";
|
|
1466
|
+
if (!subfamilies[subfamilyName]) subfamilies[subfamilyName] = [];
|
|
1467
|
+
subfamilies[subfamilyName].push(font);
|
|
1468
|
+
}
|
|
1469
|
+
});
|
|
1470
|
+
return subfamilies;
|
|
1471
|
+
};
|
|
1472
|
+
var createSubfamiliesArray = (subfamilies) => {
|
|
1473
|
+
return Object.keys(subfamilies).map((subfamilyName) => ({
|
|
1474
|
+
title: subfamilyName,
|
|
1475
|
+
_key: nanoid5(),
|
|
1476
|
+
_type: "object",
|
|
1477
|
+
fonts: subfamilies[subfamilyName].map((font) => ({
|
|
1478
|
+
_ref: font._id,
|
|
1479
|
+
_key: nanoid5(),
|
|
1480
|
+
_type: "reference",
|
|
1481
|
+
_weak: true
|
|
1482
|
+
}))
|
|
1483
|
+
}));
|
|
1484
|
+
};
|
|
1485
|
+
var updateTypefaceSubfamilies = async (doc_id, stylesObject, newSubfamiliesArray, client, setStatus, setError) => {
|
|
1486
|
+
const patch = {
|
|
1487
|
+
styles: {
|
|
1488
|
+
...stylesObject,
|
|
1489
|
+
subfamilies: newSubfamiliesArray
|
|
1490
|
+
}
|
|
1491
|
+
};
|
|
1492
|
+
try {
|
|
1493
|
+
await client.patch(doc_id).set(patch).commit();
|
|
1494
|
+
console.log(`Updated document: ${doc_id}`);
|
|
1495
|
+
if (doc_id.startsWith("drafts.")) {
|
|
1496
|
+
const publishedId = doc_id.replace("drafts.", "");
|
|
1497
|
+
const publishedDoc = await client.fetch(`*[_id == $publishedId]`, { publishedId }).then((res) => res[0]);
|
|
1498
|
+
if (publishedDoc) {
|
|
1499
|
+
await client.patch(publishedId).set(patch).commit();
|
|
1500
|
+
console.log(`Updated published document: ${publishedId}`);
|
|
1501
|
+
} else {
|
|
1502
|
+
console.log(`No published document found for ${publishedId}, skipping`);
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
} catch (err) {
|
|
1506
|
+
console.error("Error updating document:", err.message);
|
|
1507
|
+
setStatus("Error regenerating subfamilies");
|
|
1508
|
+
setError(true);
|
|
1509
|
+
}
|
|
1510
|
+
};
|
|
1511
|
+
|
|
1512
|
+
// src/components/BatchUploadFonts.jsx
|
|
1513
|
+
var ACCEPTED_EXTENSIONS = ["ttf", "otf", "woff", "woff2", "eot", "svg"];
|
|
1514
|
+
var formatElapsed = (s) => {
|
|
1515
|
+
const m = Math.floor(s / 60);
|
|
1516
|
+
const sec = s % 60;
|
|
1517
|
+
return m > 0 ? `${m}m ${sec}s` : `${sec}s`;
|
|
1518
|
+
};
|
|
1519
|
+
var BatchUploadFonts = () => {
|
|
1520
|
+
const [status, setStatus] = useState2("ready");
|
|
1521
|
+
const [ready, setReady] = useState2(true);
|
|
1522
|
+
const [inputPrice, setInputPrice] = useState2("0");
|
|
1523
|
+
const [error, setError] = useState2(false);
|
|
1524
|
+
const [preserveShortenedNames, setPreserveShortenedNames] = useState2(true);
|
|
1525
|
+
const [preserveFileNames, setPreserveFileNames] = useState2(false);
|
|
1526
|
+
const [showUtilities, setShowUtilities] = useState2(false);
|
|
1527
|
+
const [pendingFiles, setPendingFiles] = useState2([]);
|
|
1528
|
+
const [isDragging, setIsDragging] = useState2(false);
|
|
1529
|
+
const [elapsedSeconds, setElapsedSeconds] = useState2(0);
|
|
1530
|
+
const fileInputRef = useRef(null);
|
|
1531
|
+
const elapsedTimerRef = useRef(null);
|
|
1532
|
+
const wakeLockRef = useRef(null);
|
|
1533
|
+
const client = useSanityClient();
|
|
1534
|
+
const doc_id = useFormValue2(["_id"]);
|
|
1535
|
+
const title = useFormValue2(["title"]);
|
|
1536
|
+
const preferredStyleRef = useFormValue2(["preferredStyle"]);
|
|
1537
|
+
const slug = useFormValue2(["slug"]);
|
|
1538
|
+
const stylesObject = useFormValue2(["styles"]) || { fonts: [], variableFont: [] };
|
|
1539
|
+
const subfamiliesArray = (stylesObject == null ? void 0 : stylesObject.subfamilies) || [];
|
|
1540
|
+
const { weightKeywordList, italicKeywordList } = useMemo2(() => generateStyleKeywords(), []);
|
|
1541
|
+
useEffect(() => {
|
|
1542
|
+
if (ready !== true) {
|
|
1543
|
+
setElapsedSeconds(0);
|
|
1544
|
+
elapsedTimerRef.current = setInterval(() => {
|
|
1545
|
+
setElapsedSeconds((s) => s + 1);
|
|
1546
|
+
}, 1e3);
|
|
1547
|
+
} else {
|
|
1548
|
+
clearInterval(elapsedTimerRef.current);
|
|
1549
|
+
}
|
|
1550
|
+
return () => clearInterval(elapsedTimerRef.current);
|
|
1551
|
+
}, [ready]);
|
|
1552
|
+
useEffect(() => {
|
|
1553
|
+
if (ready !== true) {
|
|
1554
|
+
const handler = (e) => {
|
|
1555
|
+
e.preventDefault();
|
|
1556
|
+
e.returnValue = "";
|
|
1557
|
+
};
|
|
1558
|
+
window.addEventListener("beforeunload", handler);
|
|
1559
|
+
return () => window.removeEventListener("beforeunload", handler);
|
|
1560
|
+
}
|
|
1561
|
+
}, [ready]);
|
|
1562
|
+
useEffect(() => {
|
|
1563
|
+
var _a;
|
|
1564
|
+
if (ready !== true) {
|
|
1565
|
+
(_a = navigator.wakeLock) == null ? void 0 : _a.request("screen").then((lock) => {
|
|
1566
|
+
wakeLockRef.current = lock;
|
|
1567
|
+
}).catch(() => {
|
|
1568
|
+
});
|
|
1569
|
+
} else if (wakeLockRef.current) {
|
|
1570
|
+
wakeLockRef.current.release().catch(() => {
|
|
1571
|
+
});
|
|
1572
|
+
wakeLockRef.current = null;
|
|
1573
|
+
}
|
|
1574
|
+
}, [ready]);
|
|
1575
|
+
const validateInputs = (title2, inputPrice2) => {
|
|
1576
|
+
const price = Number(inputPrice2);
|
|
1577
|
+
if (!title2) {
|
|
1578
|
+
setStatus("Typeface needs a title");
|
|
1579
|
+
setError(true);
|
|
1580
|
+
return false;
|
|
1581
|
+
}
|
|
1582
|
+
if (isNaN(price) || typeof price !== "number") {
|
|
1583
|
+
setStatus("Invalid price \u2014 please refresh and try again");
|
|
1584
|
+
setError(true);
|
|
1585
|
+
return false;
|
|
1586
|
+
}
|
|
1587
|
+
return true;
|
|
1588
|
+
};
|
|
1589
|
+
const sortFilesByType = (files) => {
|
|
1590
|
+
if (!files) return [];
|
|
1591
|
+
const typeOrder = ["ttf", "otf", "eot", "svg", "woff", "woff2"];
|
|
1592
|
+
return Array.from(files).sort((a, b) => {
|
|
1593
|
+
const aIndex = typeOrder.indexOf(a.name.split(".").pop().toLowerCase());
|
|
1594
|
+
const bIndex = typeOrder.indexOf(b.name.split(".").pop().toLowerCase());
|
|
1595
|
+
if (aIndex === bIndex) return a.name.localeCompare(b.name);
|
|
1596
|
+
return aIndex - bIndex;
|
|
1597
|
+
});
|
|
1598
|
+
};
|
|
1599
|
+
const filterFontFiles = (files) => Array.from(files).filter((f) => ACCEPTED_EXTENSIONS.includes(f.name.split(".").pop().toLowerCase()));
|
|
1600
|
+
const handleCompletionStatus = (failedFiles, setError2, setStatus2) => {
|
|
1601
|
+
if (failedFiles.length > 0) {
|
|
1602
|
+
console.error("Failed uploads:", {
|
|
1603
|
+
files: failedFiles,
|
|
1604
|
+
names: failedFiles.map((f) => f.name),
|
|
1605
|
+
metadata: failedFiles.map((f) => {
|
|
1606
|
+
var _a, _b;
|
|
1607
|
+
return (_b = (_a = f == null ? void 0 : f.fk) == null ? void 0 : _a.name) == null ? void 0 : _b.records;
|
|
1608
|
+
})
|
|
1609
|
+
});
|
|
1610
|
+
setError2(true);
|
|
1611
|
+
setStatus2(`Upload completed with errors. Failed files: ${failedFiles.map((f) => f.name).join(", ")}`);
|
|
1612
|
+
} else {
|
|
1613
|
+
setError2(false);
|
|
1614
|
+
setStatus2("Upload completed successfully");
|
|
1615
|
+
}
|
|
1616
|
+
};
|
|
1617
|
+
const handleFileSelect = useCallback((e) => {
|
|
1618
|
+
const files = filterFontFiles(e.target.files);
|
|
1619
|
+
if (files.length > 0) setPendingFiles((prev) => [...prev, ...files]);
|
|
1620
|
+
e.target.value = "";
|
|
1621
|
+
}, []);
|
|
1622
|
+
const handleRemoveFile = useCallback((file) => {
|
|
1623
|
+
setPendingFiles((prev) => prev.filter((f) => f !== file));
|
|
1624
|
+
}, []);
|
|
1625
|
+
const handleDragEnter = useCallback((e) => {
|
|
1626
|
+
e.preventDefault();
|
|
1627
|
+
setIsDragging(true);
|
|
1628
|
+
}, []);
|
|
1629
|
+
const handleDragOver = useCallback((e) => {
|
|
1630
|
+
e.preventDefault();
|
|
1631
|
+
}, []);
|
|
1632
|
+
const handleDragLeave = useCallback((e) => {
|
|
1633
|
+
e.preventDefault();
|
|
1634
|
+
setIsDragging(false);
|
|
1635
|
+
}, []);
|
|
1636
|
+
const handleDrop = useCallback((e) => {
|
|
1637
|
+
e.preventDefault();
|
|
1638
|
+
setIsDragging(false);
|
|
1639
|
+
const files = filterFontFiles(e.dataTransfer.files);
|
|
1640
|
+
if (files.length > 0) setPendingFiles((prev) => [...prev, ...files]);
|
|
1641
|
+
}, []);
|
|
1642
|
+
const handleConfirmUpload = useCallback(async () => {
|
|
1643
|
+
try {
|
|
1644
|
+
setStatus("Uploading font files...");
|
|
1645
|
+
setReady("upload");
|
|
1646
|
+
setError(false);
|
|
1647
|
+
if (!validateInputs(title, inputPrice)) {
|
|
1648
|
+
setReady(true);
|
|
1649
|
+
return false;
|
|
1650
|
+
}
|
|
1651
|
+
const sortedFiles = sortFilesByType(pendingFiles);
|
|
1652
|
+
setPendingFiles([]);
|
|
1653
|
+
const { fontsObjects, subfamilies, uniqueSubfamilies, newPreferredStyle, failedFiles } = await processFontFiles(
|
|
1654
|
+
sortedFiles,
|
|
1655
|
+
title,
|
|
1656
|
+
weightKeywordList,
|
|
1657
|
+
italicKeywordList,
|
|
1658
|
+
setStatus,
|
|
1659
|
+
preserveShortenedNames,
|
|
1660
|
+
preserveFileNames
|
|
1661
|
+
);
|
|
1662
|
+
const { fontRefs, variableRefs } = await uploadFontFiles(
|
|
1663
|
+
fontsObjects,
|
|
1664
|
+
subfamilies,
|
|
1665
|
+
client,
|
|
1666
|
+
inputPrice,
|
|
1667
|
+
stylesObject,
|
|
1668
|
+
setStatus,
|
|
1669
|
+
setError
|
|
1670
|
+
);
|
|
1671
|
+
await updateTypefaceDocument(
|
|
1672
|
+
doc_id,
|
|
1673
|
+
fontRefs,
|
|
1674
|
+
variableRefs,
|
|
1675
|
+
subfamilies,
|
|
1676
|
+
uniqueSubfamilies,
|
|
1677
|
+
subfamiliesArray,
|
|
1678
|
+
preferredStyleRef,
|
|
1679
|
+
newPreferredStyle,
|
|
1680
|
+
stylesObject,
|
|
1681
|
+
client,
|
|
1682
|
+
setStatus,
|
|
1683
|
+
setError
|
|
1684
|
+
);
|
|
1685
|
+
handleCompletionStatus(failedFiles, setError, setStatus);
|
|
1686
|
+
} catch (e) {
|
|
1687
|
+
console.error(e.message);
|
|
1688
|
+
setError(true);
|
|
1689
|
+
setStatus("Error uploading font");
|
|
1690
|
+
}
|
|
1691
|
+
setReady(true);
|
|
1692
|
+
setError(false);
|
|
1693
|
+
}, [pendingFiles, stylesObject, title, slug, doc_id, inputPrice, weightKeywordList, italicKeywordList, client, preferredStyleRef, subfamiliesArray, preserveShortenedNames, preserveFileNames]);
|
|
1694
|
+
const handleRenameExistingFonts = useCallback(async () => {
|
|
1695
|
+
try {
|
|
1696
|
+
setStatus("Processing font documents...");
|
|
1697
|
+
setReady("rename");
|
|
1698
|
+
setError(false);
|
|
1699
|
+
if (!title) {
|
|
1700
|
+
setStatus("Typeface needs a title");
|
|
1701
|
+
setError(true);
|
|
1702
|
+
setReady(true);
|
|
1703
|
+
return false;
|
|
1704
|
+
}
|
|
1705
|
+
const result = await renameFontDocuments({
|
|
1706
|
+
client,
|
|
1707
|
+
typefaceName: title,
|
|
1708
|
+
slug,
|
|
1709
|
+
weightKeywordList,
|
|
1710
|
+
italicKeywordList,
|
|
1711
|
+
preserveShortenedNames,
|
|
1712
|
+
setStatus,
|
|
1713
|
+
setError
|
|
1714
|
+
});
|
|
1715
|
+
if (!result.success) setError(true);
|
|
1716
|
+
} catch (err) {
|
|
1717
|
+
console.error("Error renaming font documents:", err);
|
|
1718
|
+
setError(true);
|
|
1719
|
+
setStatus(`Error: ${err.message}`);
|
|
1720
|
+
}
|
|
1721
|
+
setReady(true);
|
|
1722
|
+
}, [title, client, slug, weightKeywordList, italicKeywordList, preserveShortenedNames]);
|
|
1723
|
+
const handleChangeFontPrice = useCallback(async () => {
|
|
1724
|
+
setStatus("Updating font prices...");
|
|
1725
|
+
setReady("price");
|
|
1726
|
+
setError(false);
|
|
1727
|
+
await updateFontPrices({ client, title, slug, inputPrice, doc_id, setStatus, setError });
|
|
1728
|
+
setReady(true);
|
|
1729
|
+
}, [title, slug, client, doc_id, inputPrice]);
|
|
1730
|
+
const handleRegenerateCssFiles = useCallback(async () => {
|
|
1731
|
+
var _a, _b, _c, _d;
|
|
1732
|
+
try {
|
|
1733
|
+
setStatus("Regenerating CSS files...");
|
|
1734
|
+
setReady("css");
|
|
1735
|
+
setError(false);
|
|
1736
|
+
if (!title) {
|
|
1737
|
+
setStatus("Typeface needs a title");
|
|
1738
|
+
setError(true);
|
|
1739
|
+
setReady(true);
|
|
1740
|
+
return false;
|
|
1741
|
+
}
|
|
1742
|
+
if (!(slug == null ? void 0 : slug.current)) {
|
|
1743
|
+
setStatus("Typeface needs a slug");
|
|
1744
|
+
setError(true);
|
|
1745
|
+
setReady(true);
|
|
1746
|
+
return false;
|
|
1747
|
+
}
|
|
1748
|
+
const typeface = await client.fetch(
|
|
1749
|
+
`*[_type == "typeface" && slug.current == $slug][0]`,
|
|
1750
|
+
{ slug: slug.current }
|
|
1751
|
+
);
|
|
1752
|
+
if (!typeface) {
|
|
1753
|
+
setStatus("Typeface not found");
|
|
1754
|
+
setError(true);
|
|
1755
|
+
setReady(true);
|
|
1756
|
+
return false;
|
|
1757
|
+
}
|
|
1758
|
+
if (!((_b = (_a = typeface.styles) == null ? void 0 : _a.fonts) == null ? void 0 : _b.length)) {
|
|
1759
|
+
setStatus("No fonts found in typeface");
|
|
1760
|
+
setError(true);
|
|
1761
|
+
setReady(true);
|
|
1762
|
+
return false;
|
|
1763
|
+
}
|
|
1764
|
+
const fontRefs = typeface.styles.fonts;
|
|
1765
|
+
setStatus(`Regenerating CSS for ${fontRefs.length} fonts...`);
|
|
1766
|
+
let updatedCount = 0;
|
|
1767
|
+
let errorCount = 0;
|
|
1768
|
+
for (let i = 0; i < fontRefs.length; i++) {
|
|
1769
|
+
try {
|
|
1770
|
+
const fontDoc = await client.fetch(`*[_id == $id][0]`, { id: fontRefs[i]._ref });
|
|
1771
|
+
if (!fontDoc) {
|
|
1772
|
+
errorCount++;
|
|
1773
|
+
continue;
|
|
1774
|
+
}
|
|
1775
|
+
if (!((_d = (_c = fontDoc.fileInput) == null ? void 0 : _c.woff2) == null ? void 0 : _d.asset)) {
|
|
1776
|
+
errorCount++;
|
|
1777
|
+
continue;
|
|
1778
|
+
}
|
|
1779
|
+
const woff2Asset = await client.fetch(`*[_id == $id][0]`, { id: fontDoc.fileInput.woff2.asset._ref });
|
|
1780
|
+
if (!(woff2Asset == null ? void 0 : woff2Asset.url)) {
|
|
1781
|
+
errorCount++;
|
|
1782
|
+
continue;
|
|
1783
|
+
}
|
|
1784
|
+
const woff2Response = await fetch(woff2Asset.url);
|
|
1785
|
+
const woff2Blob = await woff2Response.blob();
|
|
1786
|
+
const woff2File = new File([woff2Blob], `${fontDoc._id}.woff2`, { type: "font/woff2" });
|
|
1787
|
+
setStatus(`Regenerating CSS for font ${i + 1}/${fontRefs.length}: ${fontDoc.title}`);
|
|
1788
|
+
const updatedFileInput = await generateCssFile({
|
|
1789
|
+
woff2File,
|
|
1790
|
+
fileInput: fontDoc.fileInput,
|
|
1791
|
+
fileName: fontDoc._id,
|
|
1792
|
+
fontName: fontDoc.title,
|
|
1793
|
+
variableFont: fontDoc.variableFont || false,
|
|
1794
|
+
weight: fontDoc.weight || 400,
|
|
1795
|
+
client,
|
|
1796
|
+
style: fontDoc.style || "normal"
|
|
1797
|
+
});
|
|
1798
|
+
await client.patch(fontRefs[i]._ref).set({ fileInput: updatedFileInput }).commit();
|
|
1799
|
+
updatedCount++;
|
|
1800
|
+
setStatus(`Regenerated CSS for ${updatedCount}/${fontRefs.length} fonts...`);
|
|
1801
|
+
} catch (err) {
|
|
1802
|
+
console.error(`Error regenerating CSS for font ${fontRefs[i]._ref}:`, err);
|
|
1803
|
+
errorCount++;
|
|
1804
|
+
}
|
|
1805
|
+
}
|
|
1806
|
+
const successMessage = `Successfully regenerated CSS for ${updatedCount} fonts${errorCount > 0 ? ` (${errorCount} errors)` : ""}`;
|
|
1807
|
+
setStatus(successMessage);
|
|
1808
|
+
if (errorCount > 0) setError(true);
|
|
1809
|
+
} catch (err) {
|
|
1810
|
+
console.error("Error regenerating CSS files:", err);
|
|
1811
|
+
setError(true);
|
|
1812
|
+
setStatus(`Error: ${err.message}`);
|
|
1813
|
+
}
|
|
1814
|
+
setReady(true);
|
|
1815
|
+
}, [title, slug, client]);
|
|
1816
|
+
const handleInputChange = (e) => {
|
|
1817
|
+
setInputPrice(e.target.value);
|
|
1818
|
+
setError(false);
|
|
1819
|
+
setStatus("ready");
|
|
1820
|
+
};
|
|
1821
|
+
const renderTooltipLabel = (label, description) => /* @__PURE__ */ React4.createElement(
|
|
1822
|
+
Tooltip,
|
|
1823
|
+
{
|
|
1824
|
+
content: /* @__PURE__ */ React4.createElement(Box2, { padding: 2, style: { maxWidth: 260 } }, /* @__PURE__ */ React4.createElement(Text4, { size: 1, style: { lineHeight: 1.6 } }, description)),
|
|
1825
|
+
placement: "top",
|
|
1826
|
+
portal: true
|
|
1827
|
+
},
|
|
1828
|
+
/* @__PURE__ */ React4.createElement(Flex3, { align: "center", gap: 1, style: { cursor: "default" } }, /* @__PURE__ */ React4.createElement(Label, null, label), /* @__PURE__ */ React4.createElement(InfoOutlineIcon, { style: { opacity: 0.5, display: "block" } }))
|
|
1829
|
+
);
|
|
1830
|
+
const renderProcessing = () => /* @__PURE__ */ React4.createElement(Stack2, { space: 3, paddingY: 2 }, /* @__PURE__ */ React4.createElement(Flex3, { align: "center", gap: 3 }, /* @__PURE__ */ React4.createElement(Spinner, null), /* @__PURE__ */ React4.createElement(Text4, { size: 1, muted: true }, status)), /* @__PURE__ */ React4.createElement(Card, { tone: "caution", border: true, radius: 2, padding: 2 }, /* @__PURE__ */ React4.createElement(Flex3, { align: "center", justify: "space-between", gap: 2 }, /* @__PURE__ */ React4.createElement(Flex3, { align: "center", gap: 2 }, /* @__PURE__ */ React4.createElement(WarningOutlineIcon, { style: { flexShrink: 0 } }), /* @__PURE__ */ React4.createElement(Text4, { size: 1, weight: "semibold" }, "Do not close or reload this tab")), /* @__PURE__ */ React4.createElement(Text4, { size: 1, muted: true, style: { flexShrink: 0 } }, formatElapsed(elapsedSeconds)))));
|
|
1831
|
+
const renderDropZone = () => /* @__PURE__ */ React4.createElement(
|
|
1832
|
+
Box2,
|
|
1833
|
+
{
|
|
1834
|
+
onDragEnter: handleDragEnter,
|
|
1835
|
+
onDragOver: handleDragOver,
|
|
1836
|
+
onDragLeave: handleDragLeave,
|
|
1837
|
+
onDrop: handleDrop,
|
|
1838
|
+
style: {
|
|
1839
|
+
border: `2px dashed ${isDragging ? "var(--card-focus-ring-color)" : "var(--card-border-color)"}`,
|
|
1840
|
+
borderRadius: 4,
|
|
1841
|
+
padding: "28px 16px",
|
|
1842
|
+
textAlign: "center",
|
|
1843
|
+
background: isDragging ? "rgba(100, 153, 255, 0.06)" : "transparent",
|
|
1844
|
+
transition: "border-color 0.12s, background 0.12s",
|
|
1845
|
+
cursor: "default"
|
|
1846
|
+
}
|
|
1847
|
+
},
|
|
1848
|
+
/* @__PURE__ */ React4.createElement(
|
|
1849
|
+
"input",
|
|
1850
|
+
{
|
|
1851
|
+
ref: fileInputRef,
|
|
1852
|
+
type: "file",
|
|
1853
|
+
multiple: true,
|
|
1854
|
+
hidden: true,
|
|
1855
|
+
accept: ".ttf,.otf,.woff,.woff2,.eot,.svg",
|
|
1856
|
+
onChange: handleFileSelect
|
|
1857
|
+
}
|
|
1858
|
+
),
|
|
1859
|
+
/* @__PURE__ */ React4.createElement(Stack2, { space: 3 }, /* @__PURE__ */ React4.createElement(Text4, { size: 1, muted: true }, isDragging ? "Release to add files" : "Drop font files here"), /* @__PURE__ */ React4.createElement(Flex3, { justify: "center" }, /* @__PURE__ */ React4.createElement(
|
|
1860
|
+
Button2,
|
|
1861
|
+
{
|
|
1862
|
+
mode: "ghost",
|
|
1863
|
+
tone: "primary",
|
|
1864
|
+
fontSize: 1,
|
|
1865
|
+
padding: 2,
|
|
1866
|
+
text: "Browse files",
|
|
1867
|
+
onClick: () => {
|
|
1868
|
+
var _a;
|
|
1869
|
+
return (_a = fileInputRef.current) == null ? void 0 : _a.click();
|
|
1870
|
+
}
|
|
1871
|
+
}
|
|
1872
|
+
)))
|
|
1873
|
+
);
|
|
1874
|
+
const renderFileList = () => {
|
|
1875
|
+
const sorted = sortFilesByType(pendingFiles);
|
|
1876
|
+
return /* @__PURE__ */ React4.createElement(Stack2, { space: 2 }, /* @__PURE__ */ React4.createElement(Flex3, { align: "center", justify: "space-between" }, /* @__PURE__ */ React4.createElement(Text4, { size: 1, muted: true }, pendingFiles.length, " file", pendingFiles.length === 1 ? "" : "s", " selected"), /* @__PURE__ */ React4.createElement(
|
|
1877
|
+
Button2,
|
|
1878
|
+
{
|
|
1879
|
+
mode: "bleed",
|
|
1880
|
+
tone: "default",
|
|
1881
|
+
fontSize: 1,
|
|
1882
|
+
padding: 1,
|
|
1883
|
+
text: "Clear all",
|
|
1884
|
+
onClick: () => setPendingFiles([])
|
|
1885
|
+
}
|
|
1886
|
+
)), /* @__PURE__ */ React4.createElement(Box2, { style: { maxHeight: "260px", overflowY: "auto", display: "flex", flexDirection: "column", gap: "4px" } }, sorted.map((file, i) => {
|
|
1887
|
+
const ext = file.name.split(".").pop().toUpperCase();
|
|
1888
|
+
return /* @__PURE__ */ React4.createElement(Card, { key: `${file.name}-${file.size}-${i}`, border: true, radius: 1, paddingX: 2, paddingY: 2 }, /* @__PURE__ */ React4.createElement(Flex3, { justify: "space-between", align: "center", gap: 2 }, /* @__PURE__ */ React4.createElement(Flex3, { gap: 3, align: "center", style: { flex: 1, minWidth: 0 } }, /* @__PURE__ */ React4.createElement(
|
|
1889
|
+
Text4,
|
|
1890
|
+
{
|
|
1891
|
+
size: 0,
|
|
1892
|
+
style: { fontFamily: "monospace", minWidth: "2.5rem", flexShrink: 0 }
|
|
1893
|
+
},
|
|
1894
|
+
ext
|
|
1895
|
+
), /* @__PURE__ */ React4.createElement(Box2, { style: { flex: 1, minWidth: 0, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" } }, /* @__PURE__ */ React4.createElement(Text4, { size: 1 }, file.name))), /* @__PURE__ */ React4.createElement(
|
|
1896
|
+
Button2,
|
|
1897
|
+
{
|
|
1898
|
+
mode: "bleed",
|
|
1899
|
+
tone: "critical",
|
|
1900
|
+
icon: TrashIcon,
|
|
1901
|
+
padding: 2,
|
|
1902
|
+
onClick: () => handleRemoveFile(file)
|
|
1903
|
+
}
|
|
1904
|
+
)));
|
|
1905
|
+
})), /* @__PURE__ */ React4.createElement(
|
|
1906
|
+
Box2,
|
|
1907
|
+
{
|
|
1908
|
+
onDragEnter: handleDragEnter,
|
|
1909
|
+
onDragOver: handleDragOver,
|
|
1910
|
+
onDragLeave: handleDragLeave,
|
|
1911
|
+
onDrop: handleDrop,
|
|
1912
|
+
style: {
|
|
1913
|
+
border: `2px dashed ${isDragging ? "var(--card-focus-ring-color)" : "var(--card-border-color)"}`,
|
|
1914
|
+
borderRadius: 4,
|
|
1915
|
+
padding: "10px 16px",
|
|
1916
|
+
textAlign: "center",
|
|
1917
|
+
background: isDragging ? "rgba(100, 153, 255, 0.06)" : "transparent",
|
|
1918
|
+
transition: "border-color 0.12s, background 0.12s"
|
|
1919
|
+
}
|
|
1920
|
+
},
|
|
1921
|
+
/* @__PURE__ */ React4.createElement(
|
|
1922
|
+
"input",
|
|
1923
|
+
{
|
|
1924
|
+
ref: fileInputRef,
|
|
1925
|
+
type: "file",
|
|
1926
|
+
multiple: true,
|
|
1927
|
+
hidden: true,
|
|
1928
|
+
accept: ".ttf,.otf,.woff,.woff2,.eot,.svg",
|
|
1929
|
+
onChange: handleFileSelect
|
|
1930
|
+
}
|
|
1931
|
+
),
|
|
1932
|
+
/* @__PURE__ */ React4.createElement(Flex3, { align: "center", justify: "center", gap: 2 }, /* @__PURE__ */ React4.createElement(Text4, { size: 1, muted: true }, isDragging ? "Release to add" : "Drop more files or"), /* @__PURE__ */ React4.createElement(
|
|
1933
|
+
Button2,
|
|
1934
|
+
{
|
|
1935
|
+
mode: "bleed",
|
|
1936
|
+
tone: "primary",
|
|
1937
|
+
fontSize: 1,
|
|
1938
|
+
padding: 1,
|
|
1939
|
+
text: "browse",
|
|
1940
|
+
onClick: () => {
|
|
1941
|
+
var _a;
|
|
1942
|
+
return (_a = fileInputRef.current) == null ? void 0 : _a.click();
|
|
1943
|
+
}
|
|
1944
|
+
}
|
|
1945
|
+
))
|
|
1946
|
+
), /* @__PURE__ */ React4.createElement(
|
|
1947
|
+
Button2,
|
|
1948
|
+
{
|
|
1949
|
+
mode: "ghost",
|
|
1950
|
+
tone: "primary",
|
|
1951
|
+
icon: UploadIcon,
|
|
1952
|
+
text: `Upload ${pendingFiles.length} Font${pendingFiles.length === 1 ? "" : "s"}`,
|
|
1953
|
+
style: { width: "100%" },
|
|
1954
|
+
onClick: handleConfirmUpload
|
|
1955
|
+
}
|
|
1956
|
+
));
|
|
1957
|
+
};
|
|
1958
|
+
return /* @__PURE__ */ React4.createElement(React4.Fragment, null, title && title !== "" && slug && slug !== "" && /* @__PURE__ */ React4.createElement(React4.Fragment, null, /* @__PURE__ */ React4.createElement(
|
|
1959
|
+
StatusDisplay_default,
|
|
1960
|
+
{
|
|
1961
|
+
status,
|
|
1962
|
+
error,
|
|
1963
|
+
action: /* @__PURE__ */ React4.createElement(
|
|
1964
|
+
Button2,
|
|
1965
|
+
{
|
|
1966
|
+
mode: showUtilities ? "default" : "ghost",
|
|
1967
|
+
tone: "primary",
|
|
1968
|
+
icon: ControlsIcon,
|
|
1969
|
+
text: "Utilities",
|
|
1970
|
+
fontSize: 1,
|
|
1971
|
+
padding: 2,
|
|
1972
|
+
onClick: () => setShowUtilities((v) => !v)
|
|
1973
|
+
}
|
|
1974
|
+
)
|
|
1975
|
+
}
|
|
1976
|
+
), /* @__PURE__ */ React4.createElement(Card, { border: true, padding: 2, shadow: 1, radius: 2 }, showUtilities ? /* @__PURE__ */ React4.createElement(Stack2, { space: 4, marginTop: 2 }, /* @__PURE__ */ React4.createElement(Stack2, { space: 2 }, /* @__PURE__ */ React4.createElement(Text4, { size: 1, weight: "semibold", style: { lineHeight: 1.6 } }, "Regenerate Subfamilies"), /* @__PURE__ */ React4.createElement(RegenerateSubfamiliesComponent, null)), /* @__PURE__ */ React4.createElement(Stack2, { space: 3 }, /* @__PURE__ */ React4.createElement(Text4, { size: 1, weight: "semibold", style: { lineHeight: 1.6 } }, "Rename Fonts (name table, Full Name)"), /* @__PURE__ */ React4.createElement(Flex3, { align: "center", gap: 2 }, /* @__PURE__ */ React4.createElement(
|
|
1977
|
+
Switch,
|
|
1978
|
+
{
|
|
1979
|
+
checked: preserveShortenedNames,
|
|
1980
|
+
onChange: (e) => setPreserveShortenedNames(e.target.checked)
|
|
1981
|
+
}
|
|
1982
|
+
), renderTooltipLabel(
|
|
1983
|
+
"Preserve shortened names",
|
|
1984
|
+
'Abbreviations in font names are kept as-is (e.g. "XNarrow" stays "XNarrow", "Bd" stays "Bd").'
|
|
1985
|
+
)), ready === "rename" ? renderProcessing() : /* @__PURE__ */ React4.createElement(Button2, { mode: "ghost", tone: "primary", text: "Rename Existing Fonts", style: { width: "100%" }, onClick: handleRenameExistingFonts, disabled: ready !== true })), /* @__PURE__ */ React4.createElement(Stack2, { space: 3 }, /* @__PURE__ */ React4.createElement(Text4, { size: 1, weight: "semibold", style: { lineHeight: 1.6 } }, "Update Font Prices"), ready === "price" ? renderProcessing() : /* @__PURE__ */ React4.createElement(Stack2, { space: 2 }, /* @__PURE__ */ React4.createElement(PriceInput_default, { inputPrice, handleInputChange }), /* @__PURE__ */ React4.createElement(Button2, { mode: "ghost", tone: "primary", text: "Update All Font Prices", style: { width: "100%" }, onClick: handleChangeFontPrice, disabled: ready !== true }))), /* @__PURE__ */ React4.createElement(Stack2, { space: 3 }, /* @__PURE__ */ React4.createElement(Text4, { size: 1, weight: "semibold", style: { lineHeight: 1.6 } }, "Regenerate CSS"), /* @__PURE__ */ React4.createElement(Text4, { size: 1, muted: true, style: { lineHeight: 1.6 } }, "Rebuilds the CSS @font-face files for all fonts in the typeface fonts list."), ready === "css" ? renderProcessing() : /* @__PURE__ */ React4.createElement(Button2, { mode: "ghost", tone: "primary", text: "Regenerate CSS Files", style: { width: "100%" }, onClick: handleRegenerateCssFiles, disabled: ready !== true }))) : ready ? /* @__PURE__ */ React4.createElement(React4.Fragment, null, /* @__PURE__ */ React4.createElement(Grid, { columns: [2], gap: 4, marginTop: 1, marginBottom: 1 }, /* @__PURE__ */ React4.createElement(Box2, null, /* @__PURE__ */ React4.createElement(PriceInput_default, { inputPrice, handleInputChange })), /* @__PURE__ */ React4.createElement(Stack2, { space: 3 }, /* @__PURE__ */ React4.createElement(Flex3, { align: "center", gap: 2 }, /* @__PURE__ */ React4.createElement(
|
|
1986
|
+
Switch,
|
|
1987
|
+
{
|
|
1988
|
+
checked: preserveShortenedNames,
|
|
1989
|
+
onChange: (e) => setPreserveShortenedNames(e.target.checked)
|
|
1990
|
+
}
|
|
1991
|
+
), renderTooltipLabel(
|
|
1992
|
+
"Preserve shortened names",
|
|
1993
|
+
'Abbreviations in font names are kept as-is (e.g. "XNarrow" stays "XNarrow", "Bd" stays "Bd").'
|
|
1994
|
+
)), /* @__PURE__ */ React4.createElement(Flex3, { align: "center", gap: 2 }, /* @__PURE__ */ React4.createElement(
|
|
1995
|
+
Switch,
|
|
1996
|
+
{
|
|
1997
|
+
checked: preserveFileNames,
|
|
1998
|
+
onChange: (e) => setPreserveFileNames(e.target.checked)
|
|
1999
|
+
}
|
|
2000
|
+
), renderTooltipLabel(
|
|
2001
|
+
"Preserve file names",
|
|
2002
|
+
"Original filename capitalisation is used for asset naming instead of the normalised font title."
|
|
2003
|
+
)))), /* @__PURE__ */ React4.createElement(Box2, { marginTop: 3 }, pendingFiles.length === 0 ? renderDropZone() : renderFileList())) : renderProcessing())));
|
|
2004
|
+
};
|
|
2005
|
+
|
|
2006
|
+
// src/components/GenerateCollectionsPairsComponent.jsx
|
|
2007
|
+
import React5, { useCallback as useCallback2, useState as useState3 } from "react";
|
|
2008
|
+
import { Stack as Stack3, Grid as Grid2, Flex as Flex4, Text as Text5, Button as Button3, Card as Card2, Spinner as Spinner2 } from "@sanity/ui";
|
|
2009
|
+
import { useFormValue as useFormValue3 } from "sanity";
|
|
2010
|
+
import slugify2 from "slugify";
|
|
2011
|
+
import { nanoid as nanoid6 } from "nanoid";
|
|
2012
|
+
var GenerateCollectionsPairsComponent = () => {
|
|
2013
|
+
const [status, setStatus] = useState3("ready");
|
|
2014
|
+
const [ready, setReady] = useState3(true);
|
|
2015
|
+
const [collectionPrice, setCollectionPrice] = useState3(
|
|
2016
|
+
process.env.SANITY_STUDIO_DEFAULT_COLLECTION_PRICE || 20
|
|
2017
|
+
);
|
|
2018
|
+
const [pairPrice, setPairPrice] = useState3(
|
|
2019
|
+
process.env.SANITY_STUDIO_DEFAULT_PAIR_PRICE || 75
|
|
2020
|
+
);
|
|
2021
|
+
const client = useSanityClient();
|
|
2022
|
+
const doc_id = useFormValue3(["_id"]);
|
|
2023
|
+
const title = useFormValue3(["title"]);
|
|
2024
|
+
const slug = useFormValue3(["slug"]);
|
|
2025
|
+
const stylesObject = useFormValue3(["styles"]);
|
|
2026
|
+
const createSanityCollection = useCallback2(async (fontsList, collectionSlug, newTitle) => {
|
|
2027
|
+
const newSlug = collectionSlug.toLowerCase().trim();
|
|
2028
|
+
const fontRefs = fontsList.map((font) => ({
|
|
2029
|
+
_key: nanoid6(),
|
|
2030
|
+
_type: "reference",
|
|
2031
|
+
_ref: font._id ?? font._ref,
|
|
2032
|
+
_weak: true
|
|
2033
|
+
}));
|
|
2034
|
+
let preferredStyle = { weight: fontsList[0].weight, style: fontsList[0].style, _ref: fontsList[0]._ref };
|
|
2035
|
+
fontsList.forEach((font) => {
|
|
2036
|
+
if (Number(font.weight) < Number(preferredStyle.weight)) return;
|
|
2037
|
+
if (Number(font.weight) === Number(preferredStyle.weight) && preferredStyle.style === "Italic" && font.style === "Regular") {
|
|
2038
|
+
preferredStyle = { weight: font.weight, style: font.style, _ref: font._id };
|
|
2039
|
+
} else if (Number(font.weight) > Number(preferredStyle.weight)) {
|
|
2040
|
+
preferredStyle = { weight: font.weight, style: font.style, _ref: font._id };
|
|
2041
|
+
}
|
|
2042
|
+
});
|
|
2043
|
+
const price = (collectionPrice ? Number(collectionPrice) : 0) * fontRefs.length;
|
|
2044
|
+
await client.createOrReplace({
|
|
2045
|
+
_key: nanoid6(),
|
|
2046
|
+
_id: newSlug,
|
|
2047
|
+
_type: "collection",
|
|
2048
|
+
state: "active",
|
|
2049
|
+
type: "collection",
|
|
2050
|
+
preferredStyle: { _type: "reference", _ref: preferredStyle._ref, _weak: true },
|
|
2051
|
+
title: newTitle,
|
|
2052
|
+
slug: { _type: "slug", current: newSlug },
|
|
2053
|
+
fonts: fontRefs,
|
|
2054
|
+
price
|
|
2055
|
+
}).catch((err) => {
|
|
2056
|
+
console.error("Error creating collection:", err.message);
|
|
2057
|
+
});
|
|
2058
|
+
return { _ref: newSlug, _type: "reference", _weak: true, _key: nanoid6() };
|
|
2059
|
+
}, [collectionPrice, client]);
|
|
2060
|
+
const createSanityPair = useCallback2(async (pair, pairSlug, newTitle) => {
|
|
2061
|
+
const newSlug = pairSlug.toLowerCase().trim();
|
|
2062
|
+
const fontRefs = pair.map((font) => ({
|
|
2063
|
+
_key: nanoid6(),
|
|
2064
|
+
_type: "reference",
|
|
2065
|
+
_ref: font._id,
|
|
2066
|
+
_weak: true
|
|
2067
|
+
}));
|
|
2068
|
+
await client.createOrReplace({
|
|
2069
|
+
_key: nanoid6(),
|
|
2070
|
+
_id: newSlug,
|
|
2071
|
+
_type: "pair",
|
|
2072
|
+
preferredStyle: { _type: "reference", _ref: fontRefs[0]._ref, _weak: true },
|
|
2073
|
+
title: newTitle,
|
|
2074
|
+
slug: { _type: "slug", current: newSlug },
|
|
2075
|
+
fonts: fontRefs,
|
|
2076
|
+
price: pairPrice ? Number(pairPrice) : 0
|
|
2077
|
+
}).catch((err) => {
|
|
2078
|
+
console.error("Error creating pair:", err.message);
|
|
2079
|
+
});
|
|
2080
|
+
return { _ref: newSlug, _type: "reference", _weak: true, _key: nanoid6() };
|
|
2081
|
+
}, [pairPrice, client]);
|
|
2082
|
+
const handleGenerateCollections = useCallback2(async () => {
|
|
2083
|
+
setStatus("Generating collections...");
|
|
2084
|
+
setReady(false);
|
|
2085
|
+
try {
|
|
2086
|
+
const result = await client.fetch(
|
|
2087
|
+
`*[_type == "typeface" && _id == $id]{ "fonts": styles.fonts[] -> }[0]`,
|
|
2088
|
+
{ id: doc_id }
|
|
2089
|
+
);
|
|
2090
|
+
const sanityFonts = (result == null ? void 0 : result.fonts) ?? [];
|
|
2091
|
+
const subfamilies = (stylesObject == null ? void 0 : stylesObject.subfamilies) ?? [];
|
|
2092
|
+
const totalCollections = subfamilies.length + 3;
|
|
2093
|
+
const fullFamily = [], uprights = [], italics = [];
|
|
2094
|
+
for (const font of sanityFonts) {
|
|
2095
|
+
fullFamily.push(font);
|
|
2096
|
+
if (font.style === "Regular") uprights.push(font);
|
|
2097
|
+
else italics.push(font);
|
|
2098
|
+
}
|
|
2099
|
+
const typefacePatch = [];
|
|
2100
|
+
if (fullFamily.length > 1) {
|
|
2101
|
+
setStatus(`[1/${totalCollections}] Generating full family collection`);
|
|
2102
|
+
typefacePatch.push(await createSanityCollection(fullFamily, `${slug.current}-full-family`, `${title} Full Family`));
|
|
2103
|
+
}
|
|
2104
|
+
if (uprights.length > 1) {
|
|
2105
|
+
setStatus(`[2/${totalCollections}] Generating uprights collection`);
|
|
2106
|
+
const ref = await createSanityCollection(uprights, `${slug.current}-uprights`, `${title} Uprights`);
|
|
2107
|
+
if (ref) typefacePatch.push(ref);
|
|
2108
|
+
}
|
|
2109
|
+
if (italics.length > 1) {
|
|
2110
|
+
setStatus(`[3/${totalCollections}] Generating italics collection`);
|
|
2111
|
+
const ref = await createSanityCollection(italics, `${slug.current}-italics`, `${title} Italics`);
|
|
2112
|
+
if (ref) typefacePatch.push(ref);
|
|
2113
|
+
}
|
|
2114
|
+
for (let i = 0; i < subfamilies.length; i++) {
|
|
2115
|
+
setStatus(`[${i + 4}/${totalCollections}] Generating ${subfamilies[i].title} collection`);
|
|
2116
|
+
const ref = await createSanityCollection(
|
|
2117
|
+
subfamilies[i].fonts,
|
|
2118
|
+
`${slug.current}-${slugify2(subfamilies[i].title)}-family`,
|
|
2119
|
+
`${title} ${subfamilies[i].title} Family`
|
|
2120
|
+
);
|
|
2121
|
+
if (ref) typefacePatch.push(ref);
|
|
2122
|
+
}
|
|
2123
|
+
await client.patch(doc_id).set({ styles: { ...stylesObject, collections: typefacePatch } }).commit();
|
|
2124
|
+
setStatus("Collections generated");
|
|
2125
|
+
} catch (err) {
|
|
2126
|
+
console.error("Error generating collections:", err);
|
|
2127
|
+
setStatus("Error generating collections");
|
|
2128
|
+
}
|
|
2129
|
+
setReady(true);
|
|
2130
|
+
}, [doc_id, title, slug, stylesObject, collectionPrice, client, createSanityCollection]);
|
|
2131
|
+
const handleGeneratePairs = useCallback2(async () => {
|
|
2132
|
+
var _a;
|
|
2133
|
+
setStatus("Generating pairs...");
|
|
2134
|
+
setReady(false);
|
|
2135
|
+
try {
|
|
2136
|
+
const result = await client.fetch(
|
|
2137
|
+
`*[_type == "typeface" && _id == $id]{ "fonts": styles.fonts[] -> }[0]`,
|
|
2138
|
+
{ id: doc_id }
|
|
2139
|
+
);
|
|
2140
|
+
const sanityFonts = (result == null ? void 0 : result.fonts) ?? [];
|
|
2141
|
+
const regular = [], italic = [];
|
|
2142
|
+
for (const font of sanityFonts) {
|
|
2143
|
+
if (font.style === "Regular") regular.push(font);
|
|
2144
|
+
else italic.push(font);
|
|
2145
|
+
}
|
|
2146
|
+
const pairs = [];
|
|
2147
|
+
for (const reg of regular) {
|
|
2148
|
+
for (const ita of italic) {
|
|
2149
|
+
if (ita.subfamily === reg.subfamily && ita.weight === reg.weight && ita.weightName === reg.weightName) {
|
|
2150
|
+
pairs.push([reg, ita]);
|
|
2151
|
+
}
|
|
2152
|
+
}
|
|
2153
|
+
}
|
|
2154
|
+
const typefacePatch = [];
|
|
2155
|
+
for (let i = 0; i < pairs.length; i++) {
|
|
2156
|
+
const [reg] = pairs[i];
|
|
2157
|
+
let pairSlug, pairTitle;
|
|
2158
|
+
if (reg.subfamily && reg.subfamily !== "") {
|
|
2159
|
+
if (reg.subfamily === "Regular") {
|
|
2160
|
+
pairSlug = `${slug.current}-${slugify2(reg.weightName)}s`;
|
|
2161
|
+
pairTitle = `${title} ${reg.weightName}s`;
|
|
2162
|
+
} else {
|
|
2163
|
+
pairSlug = `${slug.current}-${slugify2(reg.subfamily)}-${slugify2(reg.weightName)}s`;
|
|
2164
|
+
pairTitle = `${title} ${reg.subfamily} ${reg.weightName}s`;
|
|
2165
|
+
}
|
|
2166
|
+
} else {
|
|
2167
|
+
pairSlug = `${slug.current}-${slugify2(reg.weightName)}s`;
|
|
2168
|
+
pairTitle = `${title} ${reg.weightName}s`;
|
|
2169
|
+
}
|
|
2170
|
+
setStatus(`[${i + 1}/${pairs.length}] Generating ${pairTitle}`);
|
|
2171
|
+
const ref = await createSanityPair(pairs[i], pairSlug, pairTitle);
|
|
2172
|
+
if (ref) typefacePatch.push(ref);
|
|
2173
|
+
}
|
|
2174
|
+
const preferredStyle = ((_a = regular[0]) == null ? void 0 : _a._id) ?? "";
|
|
2175
|
+
await client.patch(doc_id).set({
|
|
2176
|
+
preferredStyle: { _ref: preferredStyle, _type: "reference", _weak: true },
|
|
2177
|
+
styles: { ...stylesObject, pairs: typefacePatch }
|
|
2178
|
+
}).commit();
|
|
2179
|
+
setStatus("Pairs generated");
|
|
2180
|
+
} catch (err) {
|
|
2181
|
+
console.error("Error generating pairs:", err);
|
|
2182
|
+
setStatus("Error generating pairs");
|
|
2183
|
+
}
|
|
2184
|
+
setReady(true);
|
|
2185
|
+
}, [doc_id, title, slug, stylesObject, pairPrice, client, createSanityPair]);
|
|
2186
|
+
if (!title || !slug) return null;
|
|
2187
|
+
return /* @__PURE__ */ React5.createElement(Stack3, { space: 2 }, /* @__PURE__ */ React5.createElement(StatusDisplay_default, { status, error: false }), /* @__PURE__ */ React5.createElement(Card2, { border: true, padding: 2, shadow: 1, radius: 2 }, ready ? /* @__PURE__ */ React5.createElement(Stack3, { space: 3 }, /* @__PURE__ */ React5.createElement(Grid2, { columns: [2], gap: 4, marginTop: 1, marginBottom: 1 }, /* @__PURE__ */ React5.createElement(Stack3, { space: 2 }, /* @__PURE__ */ React5.createElement(Text5, { size: 1, muted: true }, "Collection price / font"), /* @__PURE__ */ React5.createElement(Flex4, { align: "center", gap: 2 }, /* @__PURE__ */ React5.createElement(Text5, { size: 1, muted: true }, "$"), /* @__PURE__ */ React5.createElement(
|
|
2188
|
+
"input",
|
|
2189
|
+
{
|
|
2190
|
+
value: collectionPrice,
|
|
2191
|
+
onChange: (e) => setCollectionPrice(e.target.value),
|
|
2192
|
+
type: "number",
|
|
2193
|
+
style: { textAlign: "end", padding: "5px", maxWidth: "75px" }
|
|
2194
|
+
}
|
|
2195
|
+
))), /* @__PURE__ */ React5.createElement(Stack3, { space: 2 }, /* @__PURE__ */ React5.createElement(Text5, { size: 1, muted: true }, "Pair price"), /* @__PURE__ */ React5.createElement(Flex4, { align: "center", gap: 2 }, /* @__PURE__ */ React5.createElement(Text5, { size: 1, muted: true }, "$"), /* @__PURE__ */ React5.createElement(
|
|
2196
|
+
"input",
|
|
2197
|
+
{
|
|
2198
|
+
value: pairPrice,
|
|
2199
|
+
onChange: (e) => setPairPrice(e.target.value),
|
|
2200
|
+
type: "number",
|
|
2201
|
+
style: { textAlign: "end", padding: "5px", maxWidth: "75px" }
|
|
2202
|
+
}
|
|
2203
|
+
)))), /* @__PURE__ */ React5.createElement(Button3, { mode: "ghost", tone: "primary", text: "Generate Collections", style: { width: "100%" }, onClick: handleGenerateCollections }), /* @__PURE__ */ React5.createElement(Button3, { mode: "ghost", tone: "primary", text: "Generate Pairs", style: { width: "100%" }, onClick: handleGeneratePairs })) : /* @__PURE__ */ React5.createElement(Flex4, { align: "center", justify: "center", gap: 3, padding: 4 }, /* @__PURE__ */ React5.createElement(Spinner2, null), /* @__PURE__ */ React5.createElement(Text5, { muted: true, size: 1 }, status))));
|
|
2204
|
+
};
|
|
2205
|
+
|
|
2206
|
+
// src/components/UpdateScriptsComponent.jsx
|
|
2207
|
+
import React6, { useState as useState4, useCallback as useCallback3, useRef as useRef2, useEffect as useEffect2 } from "react";
|
|
2208
|
+
import { Stack as Stack4, Text as Text6, Button as Button4 } from "@sanity/ui";
|
|
2209
|
+
import { useFormValue as useFormValue4, set } from "sanity";
|
|
2210
|
+
var UpdateScriptsComponent = (props) => {
|
|
2211
|
+
const { onChange } = props;
|
|
2212
|
+
const client = useSanityClient();
|
|
2213
|
+
const scripts = useFormValue4(["scripts"]) || [];
|
|
2214
|
+
const fonts = useFormValue4(["styles", "fonts"]);
|
|
2215
|
+
const isReadyRef = useRef2(false);
|
|
2216
|
+
const [message, setMessage] = useState4("");
|
|
2217
|
+
useEffect2(() => {
|
|
2218
|
+
const timer = setTimeout(() => {
|
|
2219
|
+
isReadyRef.current = true;
|
|
2220
|
+
}, 100);
|
|
2221
|
+
return () => clearTimeout(timer);
|
|
2222
|
+
}, []);
|
|
2223
|
+
const updateFromFonts = useCallback3(async () => {
|
|
2224
|
+
if (!fonts || fonts.length === 0) {
|
|
2225
|
+
setMessage("No fonts found to extract scripts from");
|
|
2226
|
+
return;
|
|
2227
|
+
}
|
|
2228
|
+
const fontRefs = fonts.map((font) => font._ref);
|
|
2229
|
+
let result;
|
|
2230
|
+
try {
|
|
2231
|
+
result = await client.fetch(
|
|
2232
|
+
`*[_type == "font" && _id in $fontRefs]{ _id, scriptFileInput }`,
|
|
2233
|
+
{ fontRefs }
|
|
2234
|
+
);
|
|
2235
|
+
} catch (err) {
|
|
2236
|
+
console.error("Failed to fetch font documents:", err);
|
|
2237
|
+
setMessage("Error updating scripts: " + err.message);
|
|
2238
|
+
return;
|
|
2239
|
+
}
|
|
2240
|
+
const newScripts = result.reduce((acc, font) => {
|
|
2241
|
+
if (!(font == null ? void 0 : font.scriptFileInput)) return acc;
|
|
2242
|
+
for (const language of Object.keys(font.scriptFileInput)) {
|
|
2243
|
+
if (!acc.includes(language)) acc.push(language);
|
|
2244
|
+
}
|
|
2245
|
+
return acc;
|
|
2246
|
+
}, []);
|
|
2247
|
+
if (isReadyRef.current) onChange(set(newScripts));
|
|
2248
|
+
setMessage("Scripts updated");
|
|
2249
|
+
}, [onChange, fonts, client]);
|
|
2250
|
+
return /* @__PURE__ */ React6.createElement(Stack4, { space: 3 }, /* @__PURE__ */ React6.createElement(
|
|
2251
|
+
Button4,
|
|
2252
|
+
{
|
|
2253
|
+
mode: "ghost",
|
|
2254
|
+
tone: "primary",
|
|
2255
|
+
width: "fill",
|
|
2256
|
+
text: "Update Scripts from Font Files",
|
|
2257
|
+
onClick: updateFromFonts
|
|
2258
|
+
}
|
|
2259
|
+
), message && /* @__PURE__ */ React6.createElement(Text6, { size: 1, style: { color: "green" } }, message), props.renderDefault(props));
|
|
2260
|
+
};
|
|
2261
|
+
|
|
2262
|
+
// src/components/SingleUploaderTool.jsx
|
|
2263
|
+
import React7, { useState as useState5, useEffect as useEffect3, useCallback as useCallback4, useMemo as useMemo3 } from "react";
|
|
2264
|
+
import { Button as Button5, Grid as Grid3, Stack as Stack5, Flex as Flex5, Box as Box3, Text as Text7, Card as Card3 } from "@sanity/ui";
|
|
2265
|
+
import { TrashIcon as TrashIcon2, ControlsIcon as ControlsIcon2 } from "@sanity/icons";
|
|
2266
|
+
import { useFormValue as useFormValue5, set as set2, unset } from "sanity";
|
|
2267
|
+
import { Buffer as Buffer3 } from "buffer";
|
|
2268
|
+
import * as fontkit5 from "fontkit";
|
|
2269
|
+
|
|
2270
|
+
// src/utils/generateFontFile.js
|
|
2271
|
+
async function generateFontFile({
|
|
2272
|
+
srcUrl,
|
|
2273
|
+
language = null,
|
|
2274
|
+
filename,
|
|
2275
|
+
codes,
|
|
2276
|
+
documentId,
|
|
2277
|
+
documentTitle,
|
|
2278
|
+
documentVariableFont,
|
|
2279
|
+
documentStyle,
|
|
2280
|
+
documentWeight,
|
|
2281
|
+
fileInput
|
|
2282
|
+
}) {
|
|
2283
|
+
await fetch(`${process.env.SANITY_STUDIO_SITE_URL}/api/sanity/fontWorker`, {
|
|
2284
|
+
method: "POST",
|
|
2285
|
+
mode: "no-cors",
|
|
2286
|
+
headers: { "Content-Type": "application/json" },
|
|
2287
|
+
body: JSON.stringify({
|
|
2288
|
+
code: "generate-fonts",
|
|
2289
|
+
language,
|
|
2290
|
+
srcUrl,
|
|
2291
|
+
filename,
|
|
2292
|
+
documentId,
|
|
2293
|
+
documentTitle,
|
|
2294
|
+
documentVariableFont,
|
|
2295
|
+
documentStyle,
|
|
2296
|
+
documentWeight,
|
|
2297
|
+
fileInput,
|
|
2298
|
+
codes
|
|
2299
|
+
})
|
|
2300
|
+
}).catch((e) => {
|
|
2301
|
+
console.error(e.message);
|
|
2302
|
+
return -1;
|
|
2303
|
+
});
|
|
2304
|
+
return 1;
|
|
2305
|
+
}
|
|
2306
|
+
|
|
2307
|
+
// src/utils/generateSubset.js
|
|
2308
|
+
async function generateSubset({
|
|
2309
|
+
woff2Url,
|
|
2310
|
+
filename,
|
|
2311
|
+
documentId,
|
|
2312
|
+
documentTitle,
|
|
2313
|
+
documentVariableFont,
|
|
2314
|
+
documentStyle,
|
|
2315
|
+
documentWeight
|
|
2316
|
+
}) {
|
|
2317
|
+
await fetch(`${process.env.SANITY_STUDIO_SITE_URL}/api/sanity/fontWorker`, {
|
|
2318
|
+
method: "POST",
|
|
2319
|
+
mode: "no-cors",
|
|
2320
|
+
headers: { "Content-Type": "application/json" },
|
|
2321
|
+
body: JSON.stringify({
|
|
2322
|
+
code: "generate-subset",
|
|
2323
|
+
woff2Url,
|
|
2324
|
+
filename,
|
|
2325
|
+
documentId,
|
|
2326
|
+
documentTitle,
|
|
2327
|
+
documentVariableFont,
|
|
2328
|
+
documentStyle,
|
|
2329
|
+
documentWeight
|
|
2330
|
+
})
|
|
2331
|
+
}).catch((e) => {
|
|
2332
|
+
console.warn("Subset generation call failed:", e.message);
|
|
2333
|
+
return -1;
|
|
2334
|
+
});
|
|
2335
|
+
return 1;
|
|
2336
|
+
}
|
|
2337
|
+
|
|
2338
|
+
// src/components/SingleUploaderTool.jsx
|
|
2339
|
+
var SingleUploaderTool = (props) => {
|
|
2340
|
+
var _a, _b, _c, _d;
|
|
2341
|
+
const client = useSanityClient();
|
|
2342
|
+
const { elementProps: { ref }, onChange } = props;
|
|
2343
|
+
const [message, setMessage] = useState5("");
|
|
2344
|
+
const [status, setStatus] = useState5("ready");
|
|
2345
|
+
const [error, setError] = useState5(false);
|
|
2346
|
+
const [filenames, setFilenames] = useState5({});
|
|
2347
|
+
const [showAdvanced, setShowAdvanced] = useState5(false);
|
|
2348
|
+
const fileInput = useFormValue5(["fileInput"]);
|
|
2349
|
+
const doc_id = useFormValue5(["_id"]);
|
|
2350
|
+
const doc_title = useFormValue5(["title"]);
|
|
2351
|
+
const doc_typefaceName = useFormValue5(["typefaceName"]);
|
|
2352
|
+
const doc_variableFont = useFormValue5(["variableFont"]);
|
|
2353
|
+
const doc_weight = useFormValue5(["weight"]);
|
|
2354
|
+
const doc_style = useFormValue5(["style"]);
|
|
2355
|
+
const doc_slug = useFormValue5(["slug"]);
|
|
2356
|
+
const doc_metaData = useFormValue5(["metaData"]);
|
|
2357
|
+
const { weightKeywordList, italicKeywordList } = useMemo3(() => generateStyleKeywords(), []);
|
|
2358
|
+
useEffect3(() => {
|
|
2359
|
+
handleSetFilenames();
|
|
2360
|
+
}, [fileInput]);
|
|
2361
|
+
const handleSetFilenames = useCallback4(async () => {
|
|
2362
|
+
var _a2, _b2, _c2, _d2, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r;
|
|
2363
|
+
const woff2WebRef = ((_b2 = (_a2 = fileInput == null ? void 0 : fileInput.woff2_web) == null ? void 0 : _a2.asset) == null ? void 0 : _b2._ref) ?? null;
|
|
2364
|
+
const woff2SubsetRef = ((_d2 = (_c2 = fileInput == null ? void 0 : fileInput.woff2_subset) == null ? void 0 : _c2.asset) == null ? void 0 : _d2._ref) ?? null;
|
|
2365
|
+
const assetIds = [
|
|
2366
|
+
(_f = (_e = fileInput == null ? void 0 : fileInput.ttf) == null ? void 0 : _e.asset) == null ? void 0 : _f._ref,
|
|
2367
|
+
(_h = (_g = fileInput == null ? void 0 : fileInput.otf) == null ? void 0 : _g.asset) == null ? void 0 : _h._ref,
|
|
2368
|
+
(_j = (_i = fileInput == null ? void 0 : fileInput.woff) == null ? void 0 : _i.asset) == null ? void 0 : _j._ref,
|
|
2369
|
+
(_l = (_k = fileInput == null ? void 0 : fileInput.woff2) == null ? void 0 : _k.asset) == null ? void 0 : _l._ref,
|
|
2370
|
+
(_n = (_m = fileInput == null ? void 0 : fileInput.eot) == null ? void 0 : _m.asset) == null ? void 0 : _n._ref,
|
|
2371
|
+
(_p = (_o = fileInput == null ? void 0 : fileInput.svg) == null ? void 0 : _o.asset) == null ? void 0 : _p._ref,
|
|
2372
|
+
(_r = (_q = fileInput == null ? void 0 : fileInput.css) == null ? void 0 : _q.asset) == null ? void 0 : _r._ref,
|
|
2373
|
+
woff2WebRef,
|
|
2374
|
+
woff2SubsetRef
|
|
2375
|
+
].filter(Boolean);
|
|
2376
|
+
if (assetIds.length === 0) {
|
|
2377
|
+
setFilenames({});
|
|
2378
|
+
return;
|
|
2379
|
+
}
|
|
2380
|
+
const assetData = await client.fetch(
|
|
2381
|
+
`*[_id in $assetIds]{ _id, originalFilename }`,
|
|
2382
|
+
{ assetIds }
|
|
2383
|
+
);
|
|
2384
|
+
const fontNames = assetData.reduce((acc, cur) => {
|
|
2385
|
+
if (cur.originalFilename.endsWith(".ttf")) acc.ttf = cur.originalFilename;
|
|
2386
|
+
else if (cur.originalFilename.endsWith(".otf")) acc.otf = cur.originalFilename;
|
|
2387
|
+
else if (cur.originalFilename.endsWith(".woff2") && cur._id === woff2WebRef) acc.woff2_web = cur.originalFilename;
|
|
2388
|
+
else if (cur.originalFilename.endsWith(".woff2") && cur._id === woff2SubsetRef) acc.woff2_subset = cur.originalFilename;
|
|
2389
|
+
else if (cur.originalFilename.endsWith(".woff2")) acc.woff2 = cur.originalFilename;
|
|
2390
|
+
else if (cur.originalFilename.endsWith(".woff")) acc.woff = cur.originalFilename;
|
|
2391
|
+
else if (cur.originalFilename.endsWith(".eot")) acc.eot = cur.originalFilename;
|
|
2392
|
+
else if (cur.originalFilename.endsWith(".svg")) acc.svg = cur.originalFilename;
|
|
2393
|
+
else if (cur.originalFilename.endsWith(".css")) acc.css = cur.originalFilename;
|
|
2394
|
+
return acc;
|
|
2395
|
+
}, {});
|
|
2396
|
+
setFilenames(fontNames);
|
|
2397
|
+
}, [fileInput, client]);
|
|
2398
|
+
const handleGenerateCssFile = useCallback4(async () => {
|
|
2399
|
+
var _a2, _b2;
|
|
2400
|
+
setMessage("Building CSS: " + doc_title + ".css");
|
|
2401
|
+
setStatus("Building CSS file");
|
|
2402
|
+
setError(false);
|
|
2403
|
+
try {
|
|
2404
|
+
const woff2AssetRef = (_b2 = (_a2 = fileInput == null ? void 0 : fileInput.woff2) == null ? void 0 : _a2.asset) == null ? void 0 : _b2._ref;
|
|
2405
|
+
if (!woff2AssetRef) throw new Error("No woff2 file available");
|
|
2406
|
+
const [woff2Asset] = await client.fetch(
|
|
2407
|
+
`*[_id == $id]{ originalFilename, url }`,
|
|
2408
|
+
{ id: woff2AssetRef }
|
|
2409
|
+
);
|
|
2410
|
+
const blob = await (await fetch(woff2Asset.url)).blob();
|
|
2411
|
+
const newFileInput = await generateCssFile({
|
|
2412
|
+
woff2File: blob,
|
|
2413
|
+
fileInput,
|
|
2414
|
+
fontName: doc_title,
|
|
2415
|
+
fileName: woff2Asset.originalFilename.replace(".woff2", ""),
|
|
2416
|
+
variableFont: doc_variableFont,
|
|
2417
|
+
weight: doc_weight,
|
|
2418
|
+
client
|
|
2419
|
+
});
|
|
2420
|
+
setMessage("CSS built");
|
|
2421
|
+
setStatus("CSS built successfully");
|
|
2422
|
+
setTimeout(() => {
|
|
2423
|
+
setMessage("");
|
|
2424
|
+
setStatus("ready");
|
|
2425
|
+
}, 2e3);
|
|
2426
|
+
onChange(set2(newFileInput));
|
|
2427
|
+
} catch (err) {
|
|
2428
|
+
console.error("Error building CSS file:", err);
|
|
2429
|
+
setMessage("Error building CSS file: " + err.message);
|
|
2430
|
+
setStatus("Error building CSS file");
|
|
2431
|
+
setError(true);
|
|
2432
|
+
setTimeout(() => {
|
|
2433
|
+
setMessage("");
|
|
2434
|
+
setStatus("ready");
|
|
2435
|
+
setError(false);
|
|
2436
|
+
}, 3e3);
|
|
2437
|
+
}
|
|
2438
|
+
}, [fileInput, onChange, doc_title, doc_variableFont, doc_weight, client]);
|
|
2439
|
+
const handleGenerateFontFile = useCallback4(async (code, sourceFile) => {
|
|
2440
|
+
var _a2;
|
|
2441
|
+
const isMissing = Array.isArray(code);
|
|
2442
|
+
const label = code === "all" ? "all font files" : isMissing ? "missing files" : code + " file";
|
|
2443
|
+
setMessage(`Building ${label}...`);
|
|
2444
|
+
setStatus(`Building ${label}`);
|
|
2445
|
+
setError(false);
|
|
2446
|
+
try {
|
|
2447
|
+
const url = `https://cdn.sanity.io/files/${process.env.SANITY_STUDIO_PROJECT_ID}/${process.env.SANITY_STUDIO_DATASET}/${(_a2 = sourceFile == null ? void 0 : sourceFile.asset) == null ? void 0 : _a2._ref.replace("file-", "").replace("-", ".")}`;
|
|
2448
|
+
const codes = code === "all" ? ["otf", "woff", "woff2", "eot", "svg", "data"] : isMissing ? code : [code];
|
|
2449
|
+
await generateFontFile({
|
|
2450
|
+
codes,
|
|
2451
|
+
srcUrl: url,
|
|
2452
|
+
filename: doc_slug.current,
|
|
2453
|
+
documentId: doc_id,
|
|
2454
|
+
documentTitle: doc_title,
|
|
2455
|
+
documentVariableFont: doc_variableFont,
|
|
2456
|
+
documentStyle: doc_style,
|
|
2457
|
+
documentWeight: doc_weight,
|
|
2458
|
+
fileInput,
|
|
2459
|
+
client
|
|
2460
|
+
});
|
|
2461
|
+
setMessage("Files built");
|
|
2462
|
+
setStatus("Files built successfully");
|
|
2463
|
+
setTimeout(() => {
|
|
2464
|
+
setMessage("");
|
|
2465
|
+
setStatus("ready");
|
|
2466
|
+
}, 2e3);
|
|
2467
|
+
} catch (err) {
|
|
2468
|
+
console.error("Error building font files:", err);
|
|
2469
|
+
setMessage("Error building font files: " + err.message);
|
|
2470
|
+
setStatus("Error building font files");
|
|
2471
|
+
setError(true);
|
|
2472
|
+
setTimeout(() => {
|
|
2473
|
+
setMessage("");
|
|
2474
|
+
setStatus("ready");
|
|
2475
|
+
setError(false);
|
|
2476
|
+
}, 3e3);
|
|
2477
|
+
}
|
|
2478
|
+
}, [doc_id, doc_title, doc_variableFont, doc_style, doc_weight, doc_slug, fileInput, client]);
|
|
2479
|
+
const handleGenerateFontData = useCallback4(async () => {
|
|
2480
|
+
var _a2, _b2;
|
|
2481
|
+
setMessage("Building font data...");
|
|
2482
|
+
setStatus("Building font data");
|
|
2483
|
+
setError(false);
|
|
2484
|
+
try {
|
|
2485
|
+
if (!((_b2 = (_a2 = fileInput == null ? void 0 : fileInput.ttf) == null ? void 0 : _a2.asset) == null ? void 0 : _b2._ref)) {
|
|
2486
|
+
setMessage("Error: TTF file is required for font data generation");
|
|
2487
|
+
setStatus("Error: TTF file is required");
|
|
2488
|
+
setError(true);
|
|
2489
|
+
setTimeout(() => {
|
|
2490
|
+
setMessage("");
|
|
2491
|
+
setStatus("ready");
|
|
2492
|
+
setError(false);
|
|
2493
|
+
}, 2e3);
|
|
2494
|
+
return;
|
|
2495
|
+
}
|
|
2496
|
+
const [ttfAsset] = await client.fetch(
|
|
2497
|
+
`*[_id == $id]{ url }`,
|
|
2498
|
+
{ id: fileInput.ttf.asset._ref }
|
|
2499
|
+
);
|
|
2500
|
+
if (!(ttfAsset == null ? void 0 : ttfAsset.url)) throw new Error("Could not fetch TTF file URL");
|
|
2501
|
+
const arrayBuffer = await (await fetch(ttfAsset.url)).arrayBuffer();
|
|
2502
|
+
const font = fontkit5.create(Buffer3.from(arrayBuffer));
|
|
2503
|
+
const { weightName, subfamilyName, style, variableFont } = extractFontMetadata(
|
|
2504
|
+
font,
|
|
2505
|
+
doc_typefaceName,
|
|
2506
|
+
weightKeywordList,
|
|
2507
|
+
italicKeywordList
|
|
2508
|
+
);
|
|
2509
|
+
const weight = determineWeight(font, weightName);
|
|
2510
|
+
await client.patch(doc_id).set({ weightName, subfamily: subfamilyName, style, variableFont, weight }).commit();
|
|
2511
|
+
const fontData = await generateFontData({
|
|
2512
|
+
url: ttfAsset.url,
|
|
2513
|
+
fontKit: font,
|
|
2514
|
+
fontId: doc_id,
|
|
2515
|
+
client,
|
|
2516
|
+
commit: true
|
|
2517
|
+
});
|
|
2518
|
+
if (variableFont && fontData.variableInstances) {
|
|
2519
|
+
const fontObj = {
|
|
2520
|
+
_id: doc_id,
|
|
2521
|
+
typefaceName: doc_typefaceName,
|
|
2522
|
+
variableFont,
|
|
2523
|
+
variableInstances: fontData.variableInstances
|
|
2524
|
+
};
|
|
2525
|
+
const instanceMappings = await parseVariableFontInstances(fontObj, client);
|
|
2526
|
+
if (instanceMappings.length > 0) {
|
|
2527
|
+
await client.patch(doc_id).set({ variableInstanceReferences: instanceMappings }).commit();
|
|
2528
|
+
}
|
|
2529
|
+
}
|
|
2530
|
+
setMessage("Font data built successfully");
|
|
2531
|
+
setStatus("Font data built successfully");
|
|
2532
|
+
setTimeout(() => {
|
|
2533
|
+
setMessage("");
|
|
2534
|
+
setStatus("ready");
|
|
2535
|
+
}, 2e3);
|
|
2536
|
+
} catch (err) {
|
|
2537
|
+
console.error("Error building font data:", err);
|
|
2538
|
+
setMessage("Error building font data: " + err.message);
|
|
2539
|
+
setStatus("Error building font data");
|
|
2540
|
+
setError(true);
|
|
2541
|
+
setTimeout(() => {
|
|
2542
|
+
setMessage("");
|
|
2543
|
+
setStatus("ready");
|
|
2544
|
+
setError(false);
|
|
2545
|
+
}, 3e3);
|
|
2546
|
+
}
|
|
2547
|
+
}, [fileInput, doc_id, doc_typefaceName, client, weightKeywordList, italicKeywordList]);
|
|
2548
|
+
const handleGenerateSubsetAndWeb = useCallback4(async () => {
|
|
2549
|
+
var _a2, _b2;
|
|
2550
|
+
try {
|
|
2551
|
+
const woff2AssetRef = (_b2 = (_a2 = fileInput == null ? void 0 : fileInput.woff2) == null ? void 0 : _a2.asset) == null ? void 0 : _b2._ref;
|
|
2552
|
+
if (!woff2AssetRef) throw new Error("No woff2 file available");
|
|
2553
|
+
setMessage("Building WEB + SUBSET files...");
|
|
2554
|
+
setStatus("Building WEB + SUBSET");
|
|
2555
|
+
setError(false);
|
|
2556
|
+
const [woff2Asset] = await client.fetch(
|
|
2557
|
+
`*[_id == $id]{ originalFilename, url }`,
|
|
2558
|
+
{ id: woff2AssetRef }
|
|
2559
|
+
);
|
|
2560
|
+
await generateSubset({
|
|
2561
|
+
woff2Url: woff2Asset.url,
|
|
2562
|
+
filename: doc_slug.current,
|
|
2563
|
+
documentId: doc_id,
|
|
2564
|
+
documentTitle: doc_title,
|
|
2565
|
+
documentVariableFont: doc_variableFont,
|
|
2566
|
+
documentStyle: doc_style,
|
|
2567
|
+
documentWeight: doc_weight
|
|
2568
|
+
});
|
|
2569
|
+
setMessage("WEB + SUBSET building in background");
|
|
2570
|
+
setStatus("Building in background");
|
|
2571
|
+
setTimeout(() => {
|
|
2572
|
+
setMessage("");
|
|
2573
|
+
setStatus("ready");
|
|
2574
|
+
}, 4e3);
|
|
2575
|
+
} catch (err) {
|
|
2576
|
+
console.error("Error building WEB + SUBSET:", err);
|
|
2577
|
+
setMessage("Error: " + err.message);
|
|
2578
|
+
setStatus("Error building WEB + SUBSET");
|
|
2579
|
+
setError(true);
|
|
2580
|
+
setTimeout(() => {
|
|
2581
|
+
setMessage("");
|
|
2582
|
+
setStatus("ready");
|
|
2583
|
+
setError(false);
|
|
2584
|
+
}, 3e3);
|
|
2585
|
+
}
|
|
2586
|
+
}, [fileInput, doc_id, doc_title, doc_variableFont, doc_style, doc_weight, doc_slug, client]);
|
|
2587
|
+
const handleUploadTopLevelFile = useCallback4(async (event, fieldName) => {
|
|
2588
|
+
try {
|
|
2589
|
+
const file = event.target.files[0];
|
|
2590
|
+
if (!file) return;
|
|
2591
|
+
const ext = file.name.split(".").pop();
|
|
2592
|
+
const filename = `${doc_slug.current}-${fieldName}.${ext}`;
|
|
2593
|
+
setMessage(`Uploading ${fieldName}...`);
|
|
2594
|
+
setStatus(`Uploading ${fieldName}`);
|
|
2595
|
+
setError(false);
|
|
2596
|
+
const asset = await client.assets.upload("file", file, { filename });
|
|
2597
|
+
const newFileInput = {
|
|
2598
|
+
...fileInput,
|
|
2599
|
+
[fieldName]: { _type: "file", asset: { _ref: asset._id, _type: "reference" } }
|
|
2600
|
+
};
|
|
2601
|
+
onChange(set2(newFileInput));
|
|
2602
|
+
setMessage(`${fieldName} uploaded`);
|
|
2603
|
+
setStatus(`${fieldName} uploaded successfully`);
|
|
2604
|
+
setTimeout(() => {
|
|
2605
|
+
setMessage("");
|
|
2606
|
+
setStatus("ready");
|
|
2607
|
+
}, 2e3);
|
|
2608
|
+
} catch (err) {
|
|
2609
|
+
console.error(`Error uploading ${fieldName}:`, err);
|
|
2610
|
+
setMessage("Error: " + err.message);
|
|
2611
|
+
setStatus(`Error uploading ${fieldName}`);
|
|
2612
|
+
setError(true);
|
|
2613
|
+
setTimeout(() => {
|
|
2614
|
+
setMessage("");
|
|
2615
|
+
setStatus("ready");
|
|
2616
|
+
setError(false);
|
|
2617
|
+
}, 3e3);
|
|
2618
|
+
}
|
|
2619
|
+
}, [fileInput, onChange, doc_slug, client]);
|
|
2620
|
+
const handleUpload = useCallback4(async (event, code) => {
|
|
2621
|
+
try {
|
|
2622
|
+
const file = event.target.files[0];
|
|
2623
|
+
if (!file) {
|
|
2624
|
+
setMessage("No file selected");
|
|
2625
|
+
setStatus("No file selected");
|
|
2626
|
+
setError(true);
|
|
2627
|
+
return;
|
|
2628
|
+
}
|
|
2629
|
+
const ext = file.name.split(".").pop();
|
|
2630
|
+
const filename = doc_slug.current + "." + ext;
|
|
2631
|
+
setMessage("Uploading: " + filename);
|
|
2632
|
+
setStatus("Uploading: " + filename);
|
|
2633
|
+
setError(false);
|
|
2634
|
+
const asset = await client.assets.upload("file", file, { filename });
|
|
2635
|
+
let newFileInput = {
|
|
2636
|
+
...fileInput,
|
|
2637
|
+
[code]: { _type: "file", asset: { _ref: asset._id, _type: "reference" } }
|
|
2638
|
+
};
|
|
2639
|
+
setMessage(filename + " uploaded");
|
|
2640
|
+
setStatus(filename + " uploaded successfully");
|
|
2641
|
+
if (code === "woff2") {
|
|
2642
|
+
setMessage("Building CSS: " + doc_title + ".css");
|
|
2643
|
+
setStatus("Building CSS file");
|
|
2644
|
+
newFileInput = await generateCssFile({
|
|
2645
|
+
woff2File: file,
|
|
2646
|
+
fileInput: newFileInput,
|
|
2647
|
+
fontName: doc_title,
|
|
2648
|
+
fileName: filename.replace(".woff2", ""),
|
|
2649
|
+
variableFont: doc_variableFont,
|
|
2650
|
+
weight: doc_weight,
|
|
2651
|
+
style: doc_style || "Normal",
|
|
2652
|
+
client
|
|
2653
|
+
});
|
|
2654
|
+
setMessage(doc_title + ".css built");
|
|
2655
|
+
setStatus("CSS file built successfully");
|
|
2656
|
+
}
|
|
2657
|
+
if (code === "ttf") {
|
|
2658
|
+
const fontBuffer = await readFontFile(file);
|
|
2659
|
+
const font = fontkit5.create(fontBuffer);
|
|
2660
|
+
const { weightName, subfamilyName, style, variableFont } = extractFontMetadata(
|
|
2661
|
+
font,
|
|
2662
|
+
doc_typefaceName,
|
|
2663
|
+
weightKeywordList,
|
|
2664
|
+
italicKeywordList
|
|
2665
|
+
);
|
|
2666
|
+
const weight = determineWeight(font, weightName);
|
|
2667
|
+
const normalizedId = doc_id.startsWith("drafts.") ? doc_id.replace("drafts.", "") : doc_id;
|
|
2668
|
+
await client.patch(normalizedId).set({ weightName, subfamily: subfamilyName, style, variableFont, weight }).commit();
|
|
2669
|
+
}
|
|
2670
|
+
onChange(set2(newFileInput));
|
|
2671
|
+
setTimeout(() => {
|
|
2672
|
+
setMessage("");
|
|
2673
|
+
setStatus("ready");
|
|
2674
|
+
}, 2e3);
|
|
2675
|
+
} catch (err) {
|
|
2676
|
+
console.error("Error uploading file:", err);
|
|
2677
|
+
setMessage("Error uploading file: " + err.message);
|
|
2678
|
+
setStatus("Error uploading file");
|
|
2679
|
+
setError(true);
|
|
2680
|
+
setTimeout(() => {
|
|
2681
|
+
setMessage("");
|
|
2682
|
+
setStatus("ready");
|
|
2683
|
+
setError(false);
|
|
2684
|
+
}, 3e3);
|
|
2685
|
+
}
|
|
2686
|
+
}, [fileInput, onChange, doc_title, doc_typefaceName, doc_variableFont, doc_weight, doc_slug, doc_id, client, weightKeywordList, italicKeywordList]);
|
|
2687
|
+
const handleDelete = useCallback4(async (code) => {
|
|
2688
|
+
var _a2, _b2;
|
|
2689
|
+
try {
|
|
2690
|
+
setMessage(`Deleting ${code} file...`);
|
|
2691
|
+
setStatus(`Deleting ${code} file`);
|
|
2692
|
+
setError(false);
|
|
2693
|
+
const asset = (_b2 = (_a2 = fileInput[code]) == null ? void 0 : _a2.asset) == null ? void 0 : _b2._ref;
|
|
2694
|
+
if (!asset) {
|
|
2695
|
+
setMessage(`No ${code} file to delete`);
|
|
2696
|
+
setStatus(`No ${code} file to delete`);
|
|
2697
|
+
setError(true);
|
|
2698
|
+
return;
|
|
2699
|
+
}
|
|
2700
|
+
onChange(unset([code]));
|
|
2701
|
+
await client.delete(asset);
|
|
2702
|
+
setMessage(`${code} file deleted`);
|
|
2703
|
+
setStatus(`${code} file deleted successfully`);
|
|
2704
|
+
setTimeout(() => {
|
|
2705
|
+
setMessage("");
|
|
2706
|
+
setStatus("ready");
|
|
2707
|
+
}, 2e3);
|
|
2708
|
+
} catch (err) {
|
|
2709
|
+
console.error("Error deleting asset:", err);
|
|
2710
|
+
setMessage("WARNING: " + err.message);
|
|
2711
|
+
setStatus("Error deleting asset");
|
|
2712
|
+
setError(true);
|
|
2713
|
+
setTimeout(() => {
|
|
2714
|
+
setMessage("");
|
|
2715
|
+
setStatus("ready");
|
|
2716
|
+
setError(false);
|
|
2717
|
+
}, 3e3);
|
|
2718
|
+
}
|
|
2719
|
+
}, [fileInput, onChange, client]);
|
|
2720
|
+
const handleDeleteTopLevel = useCallback4(async (fieldName) => {
|
|
2721
|
+
var _a2, _b2;
|
|
2722
|
+
try {
|
|
2723
|
+
setMessage(`Deleting ${fieldName}...`);
|
|
2724
|
+
setStatus(`Deleting ${fieldName}`);
|
|
2725
|
+
setError(false);
|
|
2726
|
+
const asset = (_b2 = (_a2 = fileInput == null ? void 0 : fileInput[fieldName]) == null ? void 0 : _a2.asset) == null ? void 0 : _b2._ref;
|
|
2727
|
+
if (!asset) {
|
|
2728
|
+
setMessage(`No ${fieldName} file to delete`);
|
|
2729
|
+
setStatus(`No ${fieldName} file to delete`);
|
|
2730
|
+
setError(true);
|
|
2731
|
+
return;
|
|
2732
|
+
}
|
|
2733
|
+
onChange(unset([fieldName]));
|
|
2734
|
+
await client.delete(asset);
|
|
2735
|
+
setMessage(`${fieldName} deleted`);
|
|
2736
|
+
setStatus(`${fieldName} deleted successfully`);
|
|
2737
|
+
setTimeout(() => {
|
|
2738
|
+
setMessage("");
|
|
2739
|
+
setStatus("ready");
|
|
2740
|
+
}, 2e3);
|
|
2741
|
+
} catch (err) {
|
|
2742
|
+
console.error(`Error deleting ${fieldName}:`, err);
|
|
2743
|
+
setMessage("Error: " + err.message);
|
|
2744
|
+
setStatus(`Error deleting ${fieldName}`);
|
|
2745
|
+
setError(true);
|
|
2746
|
+
setTimeout(() => {
|
|
2747
|
+
setMessage("");
|
|
2748
|
+
setStatus("ready");
|
|
2749
|
+
setError(false);
|
|
2750
|
+
}, 3e3);
|
|
2751
|
+
}
|
|
2752
|
+
}, [fileInput, onChange, client]);
|
|
2753
|
+
const handleDeleteAll = useCallback4(async () => {
|
|
2754
|
+
try {
|
|
2755
|
+
setMessage("Deleting all files and metadata...");
|
|
2756
|
+
setStatus("Deleting all files and metadata");
|
|
2757
|
+
setError(false);
|
|
2758
|
+
onChange(unset([]));
|
|
2759
|
+
await client.patch(doc_id).set({
|
|
2760
|
+
characterSet: { chars: [] },
|
|
2761
|
+
glyphCount: 0,
|
|
2762
|
+
metaData: void 0,
|
|
2763
|
+
metrics: void 0,
|
|
2764
|
+
normalWeight: void 0,
|
|
2765
|
+
price: 0,
|
|
2766
|
+
sell: false,
|
|
2767
|
+
style: "Normal",
|
|
2768
|
+
variableAxes: void 0,
|
|
2769
|
+
variableFont: false,
|
|
2770
|
+
weight: 400,
|
|
2771
|
+
variableInstances: void 0
|
|
2772
|
+
}).commit();
|
|
2773
|
+
const allAssets = Object.keys(fileInput).filter((k) => k !== "documentInfo").map((k) => {
|
|
2774
|
+
var _a2, _b2;
|
|
2775
|
+
return (_b2 = (_a2 = fileInput[k]) == null ? void 0 : _a2.asset) == null ? void 0 : _b2._ref;
|
|
2776
|
+
}).filter(Boolean);
|
|
2777
|
+
for (const assetRef of allAssets) {
|
|
2778
|
+
try {
|
|
2779
|
+
await client.delete(assetRef);
|
|
2780
|
+
} catch (e) {
|
|
2781
|
+
console.error("Error deleting asset:", e.message);
|
|
2782
|
+
}
|
|
2783
|
+
}
|
|
2784
|
+
setMessage("All files and metadata deleted");
|
|
2785
|
+
setStatus("All files and metadata deleted successfully");
|
|
2786
|
+
setTimeout(() => {
|
|
2787
|
+
setMessage("");
|
|
2788
|
+
setStatus("ready");
|
|
2789
|
+
}, 2e3);
|
|
2790
|
+
} catch (err) {
|
|
2791
|
+
console.error("Error deleting all files:", err);
|
|
2792
|
+
setMessage("Delete error: " + err.message);
|
|
2793
|
+
setStatus("Error deleting all files");
|
|
2794
|
+
setError(true);
|
|
2795
|
+
setTimeout(() => {
|
|
2796
|
+
setMessage("");
|
|
2797
|
+
setStatus("ready");
|
|
2798
|
+
setError(false);
|
|
2799
|
+
}, 3e3);
|
|
2800
|
+
}
|
|
2801
|
+
}, [fileInput, doc_id, onChange, client]);
|
|
2802
|
+
const renderFontSection = (format, buildSource = null) => {
|
|
2803
|
+
var _a2, _b2;
|
|
2804
|
+
const formatUpper = format.toUpperCase();
|
|
2805
|
+
const hasFile = !!((_b2 = (_a2 = fileInput == null ? void 0 : fileInput[format]) == null ? void 0 : _a2.asset) == null ? void 0 : _b2._ref);
|
|
2806
|
+
const fileUrl = hasFile ? `https://cdn.sanity.io/files/${process.env.SANITY_STUDIO_PROJECT_ID}/${process.env.SANITY_STUDIO_DATASET}/${fileInput[format].asset._ref.replace("file-", "").replace("-", ".")}` : null;
|
|
2807
|
+
return /* @__PURE__ */ React7.createElement(Card3, { border: true, radius: 1, paddingX: 2, paddingY: 3 }, /* @__PURE__ */ React7.createElement(Flex5, { justify: "space-between", align: "center", gap: 2 }, /* @__PURE__ */ React7.createElement(Flex5, { gap: 3, align: "center", style: { flex: 1, minWidth: 0 } }, /* @__PURE__ */ React7.createElement(Text7, { size: 0, style: { fontFamily: "monospace", minWidth: "2.5rem", flexShrink: 0, opacity: hasFile ? 1 : 0.5 } }, formatUpper), hasFile ? /* @__PURE__ */ React7.createElement(Box3, { style: { flex: 1, minWidth: 0, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" } }, /* @__PURE__ */ React7.createElement("a", { href: fileUrl, target: "_blank", rel: "noreferrer" }, (filenames == null ? void 0 : filenames[format]) || "File")) : /* @__PURE__ */ React7.createElement(Text7, { size: 1, muted: true }, "\u2014")), status === "ready" && /* @__PURE__ */ React7.createElement(Flex5, { gap: 1, align: "center", style: { flexShrink: 0 } }, buildSource && (fileInput == null ? void 0 : fileInput[buildSource]) && /* @__PURE__ */ React7.createElement(Button5, { mode: "ghost", tone: "primary", fontSize: 1, padding: 2, onClick: () => handleGenerateFontFile(format, fileInput[buildSource]), text: "Build" }), /* @__PURE__ */ React7.createElement(Button5, { as: "label", mode: "ghost", tone: "primary", fontSize: 1, padding: 2, style: { cursor: "pointer" } }, /* @__PURE__ */ React7.createElement(Text7, { size: 1 }, "Upload"), /* @__PURE__ */ React7.createElement("input", { ref, type: "file", hidden: true, onChange: (e) => handleUpload(e, format) })), hasFile && /* @__PURE__ */ React7.createElement(Button5, { mode: "bleed", tone: "critical", icon: TrashIcon2, padding: 2, onClick: () => handleDelete(format) }))));
|
|
2808
|
+
};
|
|
2809
|
+
const renderTopLevelAssetSection = (label, fieldName, assetRef, filename, onBuild) => {
|
|
2810
|
+
const hasFile = !!assetRef;
|
|
2811
|
+
const fileUrl = hasFile ? `https://cdn.sanity.io/files/${process.env.SANITY_STUDIO_PROJECT_ID}/${process.env.SANITY_STUDIO_DATASET}/${assetRef.replace("file-", "").replace("-", ".")}` : null;
|
|
2812
|
+
return /* @__PURE__ */ React7.createElement(Card3, { border: true, radius: 1, paddingX: 2, paddingY: 3 }, /* @__PURE__ */ React7.createElement(Flex5, { justify: "space-between", align: "center", gap: 2 }, /* @__PURE__ */ React7.createElement(Flex5, { gap: 3, align: "center", style: { flex: 1, minWidth: 0 } }, /* @__PURE__ */ React7.createElement(Text7, { size: 0, style: { fontFamily: "monospace", minWidth: "2.5rem", flexShrink: 0, opacity: hasFile ? 1 : 0.5 } }, label), hasFile ? /* @__PURE__ */ React7.createElement(Box3, { style: { flex: 1, minWidth: 0, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" } }, /* @__PURE__ */ React7.createElement("a", { href: fileUrl, target: "_blank", rel: "noreferrer" }, filename || "File")) : /* @__PURE__ */ React7.createElement(Text7, { size: 1, muted: true }, "\u2014")), status === "ready" && /* @__PURE__ */ React7.createElement(Flex5, { gap: 1, align: "center", style: { flexShrink: 0 } }, onBuild && (fileInput == null ? void 0 : fileInput.woff2) && /* @__PURE__ */ React7.createElement(Button5, { mode: "ghost", tone: "primary", fontSize: 1, padding: 2, onClick: onBuild, text: "Build" }), /* @__PURE__ */ React7.createElement(Button5, { as: "label", mode: "ghost", tone: "primary", fontSize: 1, padding: 2, style: { cursor: "pointer" } }, /* @__PURE__ */ React7.createElement(Text7, { size: 1 }, "Upload"), /* @__PURE__ */ React7.createElement("input", { type: "file", hidden: true, onChange: (e) => handleUploadTopLevelFile(e, fieldName) })), hasFile && /* @__PURE__ */ React7.createElement(Button5, { mode: "bleed", tone: "critical", icon: TrashIcon2, padding: 2, onClick: () => handleDeleteTopLevel(fieldName) }))));
|
|
2813
|
+
};
|
|
2814
|
+
const renderCssSection = () => {
|
|
2815
|
+
var _a2, _b2;
|
|
2816
|
+
const hasFile = !!((_b2 = (_a2 = fileInput == null ? void 0 : fileInput.css) == null ? void 0 : _a2.asset) == null ? void 0 : _b2._ref);
|
|
2817
|
+
const fileUrl = hasFile ? `https://cdn.sanity.io/files/${process.env.SANITY_STUDIO_PROJECT_ID}/${process.env.SANITY_STUDIO_DATASET}/${fileInput.css.asset._ref.replace("file-", "").replace("-", ".")}` : null;
|
|
2818
|
+
return /* @__PURE__ */ React7.createElement(Card3, { border: true, radius: 1, paddingX: 2, paddingY: 3 }, /* @__PURE__ */ React7.createElement(Flex5, { justify: "space-between", align: "center", gap: 2 }, /* @__PURE__ */ React7.createElement(Flex5, { gap: 3, align: "center", style: { flex: 1, minWidth: 0 } }, /* @__PURE__ */ React7.createElement(Text7, { size: 0, style: { fontFamily: "monospace", minWidth: "2.5rem", flexShrink: 0, opacity: hasFile ? 1 : 0.5 } }, "CSS"), hasFile ? /* @__PURE__ */ React7.createElement(Box3, { style: { flex: 1, minWidth: 0, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" } }, /* @__PURE__ */ React7.createElement("a", { href: fileUrl, target: "_blank", rel: "noreferrer" }, (filenames == null ? void 0 : filenames.css) || "File")) : /* @__PURE__ */ React7.createElement(Text7, { size: 1, muted: true }, "\u2014")), status === "ready" && /* @__PURE__ */ React7.createElement(Flex5, { gap: 1, align: "center", style: { flexShrink: 0 } }, (fileInput == null ? void 0 : fileInput.woff2) && /* @__PURE__ */ React7.createElement(Button5, { mode: "ghost", tone: "primary", fontSize: 1, padding: 2, onClick: () => handleGenerateCssFile(), text: "Build" }), hasFile && /* @__PURE__ */ React7.createElement(Button5, { mode: "bleed", tone: "critical", icon: TrashIcon2, padding: 2, onClick: () => handleDelete("css") }))));
|
|
2819
|
+
};
|
|
2820
|
+
const renderDataSection = () => /* @__PURE__ */ React7.createElement(Card3, { border: true, radius: 1, paddingX: 2, paddingY: 3 }, /* @__PURE__ */ React7.createElement(Flex5, { justify: "space-between", align: "center", gap: 2 }, /* @__PURE__ */ React7.createElement(Flex5, { gap: 3, align: "center", style: { flex: 1, minWidth: 0 } }, /* @__PURE__ */ React7.createElement(Text7, { size: 0, style: { fontFamily: "monospace", minWidth: "2.5rem", flexShrink: 0, opacity: (doc_metaData == null ? void 0 : doc_metaData.version) ? 1 : 0.5 } }, "DATA"), (doc_metaData == null ? void 0 : doc_metaData.version) ? /* @__PURE__ */ React7.createElement(Text7, { size: 1 }, "v", doc_metaData.version, " ", /* @__PURE__ */ React7.createElement(Text7, { as: "span", size: 1, muted: true }, "(", doc_metaData.genDate, ")")) : /* @__PURE__ */ React7.createElement(Text7, { size: 1, muted: true }, "\u2014")), status === "ready" && (fileInput == null ? void 0 : fileInput.ttf) && /* @__PURE__ */ React7.createElement(Flex5, { gap: 1, align: "center", style: { flexShrink: 0 } }, /* @__PURE__ */ React7.createElement(Button5, { mode: "ghost", tone: "primary", fontSize: 1, padding: 2, onClick: () => handleGenerateFontData(), text: "Build" }))));
|
|
2821
|
+
return /* @__PURE__ */ React7.createElement(Stack5, { space: 2 }, /* @__PURE__ */ React7.createElement(
|
|
2822
|
+
StatusDisplay_default,
|
|
2823
|
+
{
|
|
2824
|
+
status,
|
|
2825
|
+
error,
|
|
2826
|
+
action: /* @__PURE__ */ React7.createElement(
|
|
2827
|
+
Button5,
|
|
2828
|
+
{
|
|
2829
|
+
mode: "bleed",
|
|
2830
|
+
icon: ControlsIcon2,
|
|
2831
|
+
padding: 2,
|
|
2832
|
+
tone: showAdvanced ? "primary" : "default",
|
|
2833
|
+
title: "Show advanced file formats",
|
|
2834
|
+
onClick: () => setShowAdvanced((v) => !v)
|
|
2835
|
+
}
|
|
2836
|
+
)
|
|
2837
|
+
}
|
|
2838
|
+
), renderFontSection("ttf"), status === "ready" && (fileInput == null ? void 0 : fileInput.ttf) && /* @__PURE__ */ React7.createElement(Grid3, { columns: [2], gap: 2 }, /* @__PURE__ */ React7.createElement(
|
|
2839
|
+
Button5,
|
|
2840
|
+
{
|
|
2841
|
+
mode: "ghost",
|
|
2842
|
+
tone: "primary",
|
|
2843
|
+
onClick: () => handleGenerateFontFile("all", fileInput.ttf),
|
|
2844
|
+
text: "Rebuild All from TTF",
|
|
2845
|
+
style: { width: "100%" }
|
|
2846
|
+
}
|
|
2847
|
+
), /* @__PURE__ */ React7.createElement(
|
|
2848
|
+
Button5,
|
|
2849
|
+
{
|
|
2850
|
+
mode: "ghost",
|
|
2851
|
+
tone: "primary",
|
|
2852
|
+
onClick: () => {
|
|
2853
|
+
var _a2, _b2, _c2, _d2, _e, _f, _g, _h, _i, _j;
|
|
2854
|
+
const missing = [
|
|
2855
|
+
!((_b2 = (_a2 = fileInput == null ? void 0 : fileInput.otf) == null ? void 0 : _a2.asset) == null ? void 0 : _b2._ref) && "otf",
|
|
2856
|
+
!((_d2 = (_c2 = fileInput == null ? void 0 : fileInput.woff) == null ? void 0 : _c2.asset) == null ? void 0 : _d2._ref) && "woff",
|
|
2857
|
+
!((_f = (_e = fileInput == null ? void 0 : fileInput.woff2) == null ? void 0 : _e.asset) == null ? void 0 : _f._ref) && "woff2",
|
|
2858
|
+
!((_h = (_g = fileInput == null ? void 0 : fileInput.eot) == null ? void 0 : _g.asset) == null ? void 0 : _h._ref) && "eot",
|
|
2859
|
+
!((_j = (_i = fileInput == null ? void 0 : fileInput.svg) == null ? void 0 : _i.asset) == null ? void 0 : _j._ref) && "svg",
|
|
2860
|
+
!(doc_metaData == null ? void 0 : doc_metaData.version) && "data"
|
|
2861
|
+
].filter(Boolean);
|
|
2862
|
+
handleGenerateFontFile(missing, fileInput.ttf);
|
|
2863
|
+
},
|
|
2864
|
+
text: "Build Missing",
|
|
2865
|
+
style: { width: "100%" }
|
|
2866
|
+
}
|
|
2867
|
+
)), renderFontSection("otf", "woff"), renderFontSection("woff", "ttf"), renderFontSection("woff2", "ttf"), showAdvanced && renderTopLevelAssetSection("WEB", "woff2_web", (_b = (_a = fileInput == null ? void 0 : fileInput.woff2_web) == null ? void 0 : _a.asset) == null ? void 0 : _b._ref, filenames == null ? void 0 : filenames.woff2_web, handleGenerateSubsetAndWeb), showAdvanced && renderTopLevelAssetSection("SUBSET", "woff2_subset", (_d = (_c = fileInput == null ? void 0 : fileInput.woff2_subset) == null ? void 0 : _c.asset) == null ? void 0 : _d._ref, filenames == null ? void 0 : filenames.woff2_subset, handleGenerateSubsetAndWeb), showAdvanced && renderFontSection("eot", "ttf"), showAdvanced && renderFontSection("svg", "ttf"), renderCssSection(), showAdvanced && renderDataSection(), status === "ready" && ((fileInput == null ? void 0 : fileInput.ttf) || (fileInput == null ? void 0 : fileInput.otf) || (fileInput == null ? void 0 : fileInput.woff) || (fileInput == null ? void 0 : fileInput.woff2)) && /* @__PURE__ */ React7.createElement(Button5, { mode: "ghost", tone: "critical", onClick: () => handleDeleteAll(), text: "Delete All", style: { width: "100%" } }));
|
|
2868
|
+
};
|
|
2869
|
+
|
|
2870
|
+
// src/components/UploadScriptsComponent.jsx
|
|
2871
|
+
import React9, { useCallback as useCallback5, useEffect as useEffect4, useMemo as useMemo4, useState as useState6 } from "react";
|
|
2872
|
+
import { Button as Button6, Flex as Flex6, Grid as Grid4, Stack as Stack6, Text as Text8, TextInput, MenuButton, Menu, MenuItem, Select } from "@sanity/ui";
|
|
2873
|
+
import * as fontkit6 from "fontkit";
|
|
2874
|
+
import slugify3 from "slugify";
|
|
2875
|
+
import { useFormValue as useFormValue6 } from "sanity";
|
|
2876
|
+
import { nanoid as nanoid7 } from "nanoid";
|
|
2877
|
+
|
|
2878
|
+
// src/utils/utils.js
|
|
2879
|
+
import React8 from "react";
|
|
2880
|
+
var HtmlDescription = ({ children }) => {
|
|
2881
|
+
return children || "";
|
|
2882
|
+
};
|
|
2883
|
+
var SCRIPTS = (process.env.SANITY_STUDIO_SCRIPTS || "").split(",").map((script) => script.trim()).filter(Boolean);
|
|
2884
|
+
var SCRIPTS_OBJECT = SCRIPTS.map((script) => {
|
|
2885
|
+
return { title: script[0].toUpperCase() + script.slice(1), value: script };
|
|
2886
|
+
});
|
|
2887
|
+
|
|
2888
|
+
// src/components/UploadScriptsComponent.jsx
|
|
2889
|
+
var UploadScriptsComponent = (props) => {
|
|
2890
|
+
const { elementProps: { ref } } = props;
|
|
2891
|
+
const client = useSanityClient();
|
|
2892
|
+
const [selectedScript, setSelectedScript] = useState6("");
|
|
2893
|
+
const [status, setStatus] = React9.useState("");
|
|
2894
|
+
const [ready, setReady] = React9.useState(true);
|
|
2895
|
+
let doc_id = useFormValue6(["_id"]);
|
|
2896
|
+
const title = useFormValue6(["title"]);
|
|
2897
|
+
const slug = useFormValue6(["slug"]);
|
|
2898
|
+
const scripts = useFormValue6(["scripts"]) || [];
|
|
2899
|
+
const stylesObject = useFormValue6(["styles"]);
|
|
2900
|
+
let subfamiliesArray = (stylesObject == null ? void 0 : stylesObject.subfamilies) || [];
|
|
2901
|
+
const { weightKeywordList, italicKeywordList } = useMemo4(
|
|
2902
|
+
() => generateStyleKeywords(),
|
|
2903
|
+
[]
|
|
2904
|
+
);
|
|
2905
|
+
const readFontFile3 = (file) => {
|
|
2906
|
+
return new Promise((resolve, reject) => {
|
|
2907
|
+
const reader = new FileReader();
|
|
2908
|
+
reader.onload = (event) => {
|
|
2909
|
+
resolve(new Uint8Array(event.target.result));
|
|
2910
|
+
};
|
|
2911
|
+
reader.onerror = (error) => {
|
|
2912
|
+
reject(error);
|
|
2913
|
+
};
|
|
2914
|
+
reader.readAsArrayBuffer(file);
|
|
2915
|
+
});
|
|
2916
|
+
};
|
|
2917
|
+
const handleUpload = useCallback5(async (event, script) => {
|
|
2918
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
|
|
2919
|
+
setReady(false);
|
|
2920
|
+
try {
|
|
2921
|
+
let failedFiles = [];
|
|
2922
|
+
console.log("handle upload ", title, script);
|
|
2923
|
+
setStatus("uploading fonts files.. ");
|
|
2924
|
+
if (!title) {
|
|
2925
|
+
console.error("typeface needs title");
|
|
2926
|
+
return;
|
|
2927
|
+
}
|
|
2928
|
+
let fontRefs = [];
|
|
2929
|
+
let variableRefs = [];
|
|
2930
|
+
let subfamilies = {};
|
|
2931
|
+
let fontsObjects = {};
|
|
2932
|
+
for (var i = 0; i < event.target.files.length; i++) {
|
|
2933
|
+
const file = event.target.files[i];
|
|
2934
|
+
const fontBuffer = await readFontFile3(file);
|
|
2935
|
+
const font = fontkit6.create(fontBuffer);
|
|
2936
|
+
console.log("reading font : ", font.fullName + " " + file.name, font.name.records);
|
|
2937
|
+
let weightName = ((_b = (_a = font == null ? void 0 : font.name) == null ? void 0 : _a.records) == null ? void 0 : _b.preferredSubfamily) ? (_d = (_c = font == null ? void 0 : font.name) == null ? void 0 : _c.records) == null ? void 0 : _d.preferredSubfamily : (_f = (_e = font == null ? void 0 : font.name) == null ? void 0 : _e.records) == null ? void 0 : _f.fontSubfamily;
|
|
2938
|
+
weightName = (weightName == null ? void 0 : weightName.en) ? weightName.en : weightName.constructor == Object ? weightName[Object.keys(weightName)[0]] : weightName;
|
|
2939
|
+
weightName = weightName == null ? void 0 : weightName.replace("Italic", "").replace("It", "").trim();
|
|
2940
|
+
if ((weightName == "" || weightName.toLowerCase() == "roman") && ((_h = (_g = font == null ? void 0 : font.name) == null ? void 0 : _g.records) == null ? void 0 : _h.fullName)) {
|
|
2941
|
+
weightName = (_j = (_i = font == null ? void 0 : font.name) == null ? void 0 : _i.records) == null ? void 0 : _j.fullName;
|
|
2942
|
+
weightName = (weightName == null ? void 0 : weightName.en) ? weightName.en : weightName.constructor == Object ? weightName[Object.keys(weightName)[0]] : weightName;
|
|
2943
|
+
weightName = weightName == null ? void 0 : weightName.replace(title + " ", "").replace(title, "").trim();
|
|
2944
|
+
weightName = weightName == null ? void 0 : weightName.replace("Italic", "").replace("It", "").trim();
|
|
2945
|
+
}
|
|
2946
|
+
let variableFont = (font == null ? void 0 : font.variationAxes) && Object.keys(font.variationAxes).length > 0 ? true : false;
|
|
2947
|
+
let subfamilyName = font.familyName.toLowerCase().trim().replace(title.toLowerCase().trim(), "").trim();
|
|
2948
|
+
let fontTitle = font == null ? void 0 : font.fullName;
|
|
2949
|
+
let style = (font == null ? void 0 : font.italicAngle) !== 0 || (font == null ? void 0 : font.fullName.toLowerCase().includes("italic")) ? "Italic" : "Regular";
|
|
2950
|
+
if (fontTitle.toLowerCase().trim().includes(script)) {
|
|
2951
|
+
fontTitle = fontTitle.toLowerCase().trim().replace(script, "").trim();
|
|
2952
|
+
fontTitle = fontTitle.split(" ").map((word) => {
|
|
2953
|
+
if (word == "") return;
|
|
2954
|
+
return word;
|
|
2955
|
+
}).filter((word) => word != void 0).join(" ");
|
|
2956
|
+
}
|
|
2957
|
+
weightKeywordList.forEach((keyword) => {
|
|
2958
|
+
const kw = keyword.trim();
|
|
2959
|
+
if (subfamilyName.includes(kw)) subfamilyName = subfamilyName.replace(kw, "").trim();
|
|
2960
|
+
});
|
|
2961
|
+
let italicKW = [];
|
|
2962
|
+
italicKeywordList.forEach((keyword) => {
|
|
2963
|
+
const kw = keyword.toLowerCase().trim();
|
|
2964
|
+
if (subfamilyName.includes(kw)) {
|
|
2965
|
+
subfamilyName = subfamilyName.replace(kw, "");
|
|
2966
|
+
}
|
|
2967
|
+
if (fontTitle.includes(kw)) {
|
|
2968
|
+
fontTitle = fontTitle.replace(kw, "");
|
|
2969
|
+
italicKW.push(kw.charAt(0).toUpperCase() + kw.slice(1));
|
|
2970
|
+
}
|
|
2971
|
+
});
|
|
2972
|
+
fontTitle = fontTitle.replace(/-/g, " ");
|
|
2973
|
+
fontTitle = fontTitle.trim().split(" ").map((word) => word[0].toUpperCase() + word.slice(1)).join(" ");
|
|
2974
|
+
if (subfamilyName.trim().includes(script)) {
|
|
2975
|
+
subfamilyName = subfamilyName.trim().replace(script, "").trim();
|
|
2976
|
+
}
|
|
2977
|
+
subfamilyName = subfamilyName.trim();
|
|
2978
|
+
subfamilyName = subfamilyName == "" ? "Regular" : subfamilyName.split(" ").map((word) => word[0].toUpperCase() + word.slice(1)).join(" ");
|
|
2979
|
+
if (subfamilyName !== "") {
|
|
2980
|
+
weightName = weightName.replace(`${subfamilyName} `, "").replace(` ${subfamilyName}`, "").trim();
|
|
2981
|
+
}
|
|
2982
|
+
if (variableFont && !fontTitle.toLowerCase().trim().endsWith(" vf")) fontTitle = fontTitle + " VF";
|
|
2983
|
+
if (italicKW.length > 0) {
|
|
2984
|
+
italicKW = italicKW.map((item) => reverseSpellingLookup(item));
|
|
2985
|
+
fontTitle = fontTitle + italicKW.join(" ");
|
|
2986
|
+
style = "Italic";
|
|
2987
|
+
}
|
|
2988
|
+
let id = slugify3(fontTitle.toLowerCase().trim());
|
|
2989
|
+
console.log("=== Font Info ====");
|
|
2990
|
+
console.log(" ");
|
|
2991
|
+
console.log("font id : ", id);
|
|
2992
|
+
console.log("font title : ", fontTitle);
|
|
2993
|
+
console.log("fontkit fullName : ", font.fullName);
|
|
2994
|
+
console.log("fontkit family name: ", font.familyName);
|
|
2995
|
+
console.log("file name : ", file.name);
|
|
2996
|
+
console.log("subfamily : ", subfamilyName);
|
|
2997
|
+
console.log("style : ", style);
|
|
2998
|
+
console.log("weight : ", weightName);
|
|
2999
|
+
console.log("variable : ", variableFont);
|
|
3000
|
+
console.log("italicKW ", italicKW);
|
|
3001
|
+
console.log(" ");
|
|
3002
|
+
console.log("=======");
|
|
3003
|
+
subfamilies[id] = subfamilyName;
|
|
3004
|
+
if (fontsObjects[id]) {
|
|
3005
|
+
fontsObjects[id].files = [...fontsObjects[id].files, file];
|
|
3006
|
+
} else {
|
|
3007
|
+
let fontObject = {
|
|
3008
|
+
_key: nanoid7(),
|
|
3009
|
+
_id: id,
|
|
3010
|
+
title: fontTitle,
|
|
3011
|
+
slug: { _type: "slug", current: id },
|
|
3012
|
+
typefaceName: title,
|
|
3013
|
+
// Change to match Typeface Document
|
|
3014
|
+
style: (font == null ? void 0 : font.italicAngle) !== 0 || (font == null ? void 0 : font.fullName.toLowerCase().includes("italic")) ? "Italic" : "Regular",
|
|
3015
|
+
variableFont,
|
|
3016
|
+
weightName,
|
|
3017
|
+
normalWeight: true,
|
|
3018
|
+
// TODO : check if weight is normal ??
|
|
3019
|
+
weight: ((_k = font["OS/2"]) == null ? void 0 : _k.usWeightClass) ? Number((_l = font["OS/2"]) == null ? void 0 : _l.usWeightClass) : /hairline|extra thin|extrathin/.test(weightName == null ? void 0 : weightName.toLowerCase()) ? 100 : /thin|extra light|extralight/.test(weightName == null ? void 0 : weightName.toLowerCase()) ? 200 : /light|book/.test(weightName == null ? void 0 : weightName.toLowerCase()) ? 300 : /regular|normal/.test(weightName == null ? void 0 : weightName.toLowerCase()) ? 400 : /medium/.test(weightName == null ? void 0 : weightName.toLowerCase()) ? 500 : /semi bold|semibold/.test(weightName == null ? void 0 : weightName.toLowerCase()) ? 600 : /bold/.test(weightName == null ? void 0 : weightName.toLowerCase()) ? 700 : /extra bold|extrabold/.test(weightName == null ? void 0 : weightName.toLowerCase()) ? 800 : /black|ultra/.test(weightName == null ? void 0 : weightName.toLowerCase()) ? 900 : 400,
|
|
3020
|
+
files: [file],
|
|
3021
|
+
fontKit: font,
|
|
3022
|
+
scriptFileInput: { [script]: {} }
|
|
3023
|
+
};
|
|
3024
|
+
fontsObjects[id] = fontObject;
|
|
3025
|
+
}
|
|
3026
|
+
}
|
|
3027
|
+
let uniqueSubfamiles = [...new Set(Object.values(subfamilies))];
|
|
3028
|
+
console.log("Subfamilies : ", subfamilies, uniqueSubfamiles, uniqueSubfamiles.length);
|
|
3029
|
+
console.log("fontsObjects : ", fontsObjects);
|
|
3030
|
+
for (var i = 0; i < Object.keys(fontsObjects).length; i++) {
|
|
3031
|
+
let id = Object.keys(fontsObjects)[i];
|
|
3032
|
+
let fontObject = fontsObjects[id];
|
|
3033
|
+
let files = fontObject.files;
|
|
3034
|
+
let newFileInput = fontObject.scriptFileInput[script];
|
|
3035
|
+
console.log(fontObject.title, " : subfamily : ", subfamilies[id]);
|
|
3036
|
+
if (uniqueSubfamiles.length > 1) fontObject.subfamily = subfamilies[id];
|
|
3037
|
+
else fontObject.subfamily = "";
|
|
3038
|
+
fontObject.price = process.env.SANITY_STUDIO_DEFAULT_STYLE_PRICE || 40;
|
|
3039
|
+
if (fontObject.price > 0) fontObject.sell = true;
|
|
3040
|
+
for (var j = 0; j < files.length; j++) {
|
|
3041
|
+
let file = files[j];
|
|
3042
|
+
let fileType = "";
|
|
3043
|
+
if (file.name.endsWith(".otf")) fileType = "otf";
|
|
3044
|
+
else if (file.name.endsWith(".ttf")) fileType = "ttf";
|
|
3045
|
+
else if (file.name.endsWith(".woff")) fileType = "woff";
|
|
3046
|
+
else if (file.name.endsWith(".woff2")) fileType = "woff2";
|
|
3047
|
+
else if (file.name.endsWith(".eot")) fileType = "eot";
|
|
3048
|
+
else if (file.name.endsWith(".svg")) fileType = "svg";
|
|
3049
|
+
console.log("uploading font file : ", fontObject._id + "." + fileType);
|
|
3050
|
+
const filename = fontObject._id + "-" + script;
|
|
3051
|
+
let fontTitle = fontObject.title + " " + script;
|
|
3052
|
+
fontTitle = fontTitle.split(" ").map((word) => word[0].toUpperCase() + word.slice(1)).join(" ");
|
|
3053
|
+
let baseAsset = await client.assets.upload("file", file, { filename: filename + "." + fileType }).catch((err) => {
|
|
3054
|
+
console.error("error uploading font: ", fontObject.title);
|
|
3055
|
+
setStatus("error uploading font " + err.message);
|
|
3056
|
+
});
|
|
3057
|
+
newFileInput[fileType] = {
|
|
3058
|
+
_type: "file",
|
|
3059
|
+
asset: {
|
|
3060
|
+
_ref: baseAsset._id,
|
|
3061
|
+
_type: "reference"
|
|
3062
|
+
}
|
|
3063
|
+
};
|
|
3064
|
+
console.log("newFileInput", newFileInput);
|
|
3065
|
+
if (file.name.endsWith(".woff2")) {
|
|
3066
|
+
console.log("generating css file for: ", fontObject.title);
|
|
3067
|
+
setStatus("generating css file for: " + fontObject.title);
|
|
3068
|
+
newFileInput = await generateCssFile({
|
|
3069
|
+
woff2File: file,
|
|
3070
|
+
fileInput: newFileInput,
|
|
3071
|
+
// script: script,
|
|
3072
|
+
fontName: fontTitle,
|
|
3073
|
+
fileName: filename,
|
|
3074
|
+
variableFont: fontObject.variableFont,
|
|
3075
|
+
weight: fontObject.weight,
|
|
3076
|
+
client
|
|
3077
|
+
});
|
|
3078
|
+
}
|
|
3079
|
+
fontObject.scriptFileInput[script] = newFileInput;
|
|
3080
|
+
fontsObjects[id] = fontObject;
|
|
3081
|
+
}
|
|
3082
|
+
}
|
|
3083
|
+
console.log("creating sanity fonts", fontsObjects);
|
|
3084
|
+
for (var i = 0; i < Object.keys(fontsObjects).length; i++) {
|
|
3085
|
+
let fontId = Object.keys(fontsObjects)[i];
|
|
3086
|
+
let font = fontsObjects[fontId];
|
|
3087
|
+
let existingFont = await client.fetch(
|
|
3088
|
+
`*[_type == 'font' && _id == $fontId]{
|
|
3089
|
+
fileInput,
|
|
3090
|
+
description,
|
|
3091
|
+
metaData,
|
|
3092
|
+
metrics,
|
|
3093
|
+
opentypeFeatures,
|
|
3094
|
+
characterSet,
|
|
3095
|
+
subfamily,
|
|
3096
|
+
scriptFileInput,
|
|
3097
|
+
}`,
|
|
3098
|
+
{ fontId: font._id }
|
|
3099
|
+
);
|
|
3100
|
+
existingFont = existingFont[0];
|
|
3101
|
+
let fontResponse;
|
|
3102
|
+
let files = font.files;
|
|
3103
|
+
let fontKit = font.fontKit;
|
|
3104
|
+
delete font.files;
|
|
3105
|
+
delete font.fontKit;
|
|
3106
|
+
console.log("creating font : ", font);
|
|
3107
|
+
try {
|
|
3108
|
+
if (existingFont && existingFont != null) {
|
|
3109
|
+
if (existingFont.scriptFileInput && existingFont.scriptFileInput != null) {
|
|
3110
|
+
let newFileInput = { ...font.scriptFileInput };
|
|
3111
|
+
Object.keys(existingFont.scriptFileInput).forEach((key) => {
|
|
3112
|
+
if (!newFileInput[key]) {
|
|
3113
|
+
newFileInput[key] = existingFont.scriptFileInput[key];
|
|
3114
|
+
}
|
|
3115
|
+
});
|
|
3116
|
+
font.scriptFileInput = newFileInput;
|
|
3117
|
+
}
|
|
3118
|
+
fontResponse = await client.patch(font._id).set({ scriptFileInput: font.scriptFileInput }).commit();
|
|
3119
|
+
} else {
|
|
3120
|
+
fontResponse = await client.createOrReplace({
|
|
3121
|
+
_key: nanoid7(),
|
|
3122
|
+
_id: font._id,
|
|
3123
|
+
_type: "font",
|
|
3124
|
+
...font
|
|
3125
|
+
});
|
|
3126
|
+
}
|
|
3127
|
+
} catch (e) {
|
|
3128
|
+
console.error("error creating font: ", font.title, font.subfamily);
|
|
3129
|
+
failedFiles = [...failedFiles, ...files.map((file) => {
|
|
3130
|
+
return { name: file.name, fk: fontKit };
|
|
3131
|
+
})];
|
|
3132
|
+
continue;
|
|
3133
|
+
}
|
|
3134
|
+
const fontRef = { _key: nanoid7(), _type: "reference", _ref: fontResponse._id, _weak: true };
|
|
3135
|
+
console.log("font response : ", fontResponse);
|
|
3136
|
+
console.log("existing styles object : ", stylesObject);
|
|
3137
|
+
if (!font.variableFont) {
|
|
3138
|
+
if (stylesObject.fonts && stylesObject.fonts.length > 0) {
|
|
3139
|
+
let fontExists = stylesObject.fonts.findIndex((font2) => font2._ref == fontResponse._id);
|
|
3140
|
+
let inFontRefs = fontRefs.findIndex((font2) => font2._ref == fontResponse._id);
|
|
3141
|
+
if (fontExists == -1 && inFontRefs == -1) {
|
|
3142
|
+
fontRefs.push(fontRef);
|
|
3143
|
+
}
|
|
3144
|
+
} else {
|
|
3145
|
+
fontRefs.push(fontRef);
|
|
3146
|
+
}
|
|
3147
|
+
}
|
|
3148
|
+
if (font.variableFont) {
|
|
3149
|
+
if (stylesObject.variableFont && stylesObject.variableFont.length > 0) {
|
|
3150
|
+
let vfExists = stylesObject.variableFont.findIndex((font2) => font2._ref == fontResponse._id);
|
|
3151
|
+
let inVariableRefs = variableRefs.findIndex((font2) => font2._ref == fontResponse._id);
|
|
3152
|
+
if (vfExists == -1 && inVariableRefs == -1 && font.variableFont) {
|
|
3153
|
+
variableRefs.push(fontRef);
|
|
3154
|
+
}
|
|
3155
|
+
} else {
|
|
3156
|
+
variableRefs.push(fontRef);
|
|
3157
|
+
}
|
|
3158
|
+
}
|
|
3159
|
+
console.log(fontResponse._id, " created!");
|
|
3160
|
+
}
|
|
3161
|
+
console.log("updating styles refs (fonts, variable fonts, subfamilies) ", fontRefs, variableRefs, subfamilies, uniqueSubfamiles);
|
|
3162
|
+
setStatus("Updating font references...");
|
|
3163
|
+
let newStylesObject = stylesObject.fonts ? { ...stylesObject, fonts: [...stylesObject.fonts, ...fontRefs] } : { ...stylesObject, fonts: [...fontRefs] };
|
|
3164
|
+
if (uniqueSubfamiles.length > 1) {
|
|
3165
|
+
newStylesObject.subfamilies = uniqueSubfamiles;
|
|
3166
|
+
} else {
|
|
3167
|
+
newStylesObject.subfamilies = [];
|
|
3168
|
+
}
|
|
3169
|
+
newStylesObject.variableFont = (stylesObject == null ? void 0 : stylesObject.variableFont) ? [...stylesObject == null ? void 0 : stylesObject.variableFont, ...variableRefs] : [...variableRefs];
|
|
3170
|
+
let patch = { styles: newStylesObject };
|
|
3171
|
+
subfamiliesArray = subfamiliesArray ? subfamiliesArray : [];
|
|
3172
|
+
console.log("new styles obj : ", newStylesObject);
|
|
3173
|
+
console.log("existing subfamily list : ", subfamiliesArray);
|
|
3174
|
+
console.log("unique subfamilies ", uniqueSubfamiles);
|
|
3175
|
+
subfamiliesArray = [...subfamiliesArray, ...uniqueSubfamiles].filter((sf, index, self) => {
|
|
3176
|
+
return self.indexOf(sf) === index;
|
|
3177
|
+
});
|
|
3178
|
+
patch.styles.subfamilies = subfamiliesArray;
|
|
3179
|
+
console.log("doc_id : ", doc_id);
|
|
3180
|
+
console.log("typeface patch : ", patch);
|
|
3181
|
+
let includedScripts = [script, ...scripts].filter((lang, index, self) => {
|
|
3182
|
+
return self.indexOf(lang) === index;
|
|
3183
|
+
});
|
|
3184
|
+
patch.scripts = includedScripts;
|
|
3185
|
+
console.log("included scripts : ", includedScripts);
|
|
3186
|
+
if (doc_id.startsWith("drafts.")) {
|
|
3187
|
+
await client.patch(doc_id).set(patch).commit().catch((err) => {
|
|
3188
|
+
console.error("error patching styles: ", err.message);
|
|
3189
|
+
setStatus("error patching styles " + err.message);
|
|
3190
|
+
});
|
|
3191
|
+
doc_id = doc_id.replace("drafts.", "");
|
|
3192
|
+
}
|
|
3193
|
+
await client.patch(doc_id).set(patch).commit().catch((err) => {
|
|
3194
|
+
console.error("error patching styles: ", err.message);
|
|
3195
|
+
setStatus("error patching styles");
|
|
3196
|
+
});
|
|
3197
|
+
console.log("success");
|
|
3198
|
+
if (failedFiles.length > 0) {
|
|
3199
|
+
console.log("failed files : ", failedFiles);
|
|
3200
|
+
const names = failedFiles.map((file) => file.name);
|
|
3201
|
+
console.log("names : ", failedFiles.map((file) => {
|
|
3202
|
+
var _a2, _b2;
|
|
3203
|
+
return (_b2 = (_a2 = file == null ? void 0 : file.fk) == null ? void 0 : _a2.name) == null ? void 0 : _b2.records;
|
|
3204
|
+
}));
|
|
3205
|
+
setStatus("fonts uploaded with errors. Failed files : " + names.join(", "));
|
|
3206
|
+
} else {
|
|
3207
|
+
setStatus("fonts uploaded!");
|
|
3208
|
+
}
|
|
3209
|
+
setStatus("fonts uploaded!");
|
|
3210
|
+
} catch (e) {
|
|
3211
|
+
console.error(e);
|
|
3212
|
+
setStatus("error uploading font " + e.message);
|
|
3213
|
+
}
|
|
3214
|
+
setReady(true);
|
|
3215
|
+
}, [title, slug, doc_id]);
|
|
3216
|
+
return /* @__PURE__ */ React9.createElement(Stack6, null, !ready && /* @__PURE__ */ React9.createElement(Text8, null, /* @__PURE__ */ React9.createElement("br", null), status, /* @__PURE__ */ React9.createElement("br", null), /* @__PURE__ */ React9.createElement("br", null)), ready && /* @__PURE__ */ React9.createElement(Stack6, null, /* @__PURE__ */ React9.createElement(Grid4, { columns: !!(selectedScript && selectedScript !== "") ? 2 : 1, gap: 2 }, /* @__PURE__ */ React9.createElement(
|
|
3217
|
+
Select,
|
|
3218
|
+
{
|
|
3219
|
+
id: "menu-button-example",
|
|
3220
|
+
onChange: (e) => setSelectedScript(e.target.value)
|
|
3221
|
+
},
|
|
3222
|
+
/* @__PURE__ */ React9.createElement("option", { key: "script-none", value: "" }, " "),
|
|
3223
|
+
SCRIPTS.map(
|
|
3224
|
+
(script, i) => {
|
|
3225
|
+
var _a;
|
|
3226
|
+
return /* @__PURE__ */ React9.createElement("option", { key: "script-" + i, value: script }, ((_a = script[0]) == null ? void 0 : _a.toUpperCase()) + script.slice(1));
|
|
3227
|
+
}
|
|
3228
|
+
)
|
|
3229
|
+
), !!(selectedScript && selectedScript !== "") && /* @__PURE__ */ React9.createElement(React9.Fragment, null, /* @__PURE__ */ React9.createElement("label", { htmlFor: "upload-scripts-file" }, /* @__PURE__ */ React9.createElement(
|
|
3230
|
+
Button6,
|
|
3231
|
+
{
|
|
3232
|
+
style: { pointerEvents: "none" },
|
|
3233
|
+
text: "Upload (ttf/otf/woff/woff2/etc..)"
|
|
3234
|
+
}
|
|
3235
|
+
)), /* @__PURE__ */ React9.createElement(
|
|
3236
|
+
"input",
|
|
3237
|
+
{
|
|
3238
|
+
ref,
|
|
3239
|
+
name: "upload-scripts-file",
|
|
3240
|
+
id: "upload-scripts-file",
|
|
3241
|
+
type: "file",
|
|
3242
|
+
multiple: true,
|
|
3243
|
+
hidden: true,
|
|
3244
|
+
onChange: (event) => handleUpload(event, selectedScript)
|
|
3245
|
+
}
|
|
3246
|
+
)))));
|
|
3247
|
+
};
|
|
3248
|
+
|
|
3249
|
+
// src/components/FontScriptUploaderComponent.jsx
|
|
3250
|
+
import React10, { useState as useState7, useEffect as useEffect5, useCallback as useCallback6 } from "react";
|
|
3251
|
+
import { Stack as Stack7, Flex as Flex7, Text as Text9, Button as Button7 } from "@sanity/ui";
|
|
3252
|
+
import { useFormValue as useFormValue7, set as set3, unset as unset2 } from "sanity";
|
|
3253
|
+
var FontScriptUploaderComponent = (props) => {
|
|
3254
|
+
const client = useSanityClient();
|
|
3255
|
+
const {
|
|
3256
|
+
elementProps: { ref },
|
|
3257
|
+
onChange,
|
|
3258
|
+
value = ""
|
|
3259
|
+
} = props;
|
|
3260
|
+
const [expanded, setExpanded] = useState7(SCRIPTS.reduce((acc, language) => ({ ...acc, [language]: true }), {}));
|
|
3261
|
+
const [message, setMessage] = useState7({});
|
|
3262
|
+
const [status, setStatus] = useState7("ready");
|
|
3263
|
+
const [filenames, setFilenames] = useState7({});
|
|
3264
|
+
let scriptFileInput = useFormValue7(["scriptFileInput"]) || [];
|
|
3265
|
+
let fileInput = useFormValue7(["fileInput"]);
|
|
3266
|
+
let doc_id = useFormValue7(["_id"]);
|
|
3267
|
+
let doc_title = useFormValue7(["title"]);
|
|
3268
|
+
let doc_variableFont = useFormValue7(["variableFont"]);
|
|
3269
|
+
let doc_weight = useFormValue7(["weight"]);
|
|
3270
|
+
let doc_style = useFormValue7(["style"]);
|
|
3271
|
+
let doc_slug = useFormValue7(["slug"]);
|
|
3272
|
+
useEffect5(() => {
|
|
3273
|
+
if (!scriptFileInput || Object.keys(scriptFileInput).length === 0) return;
|
|
3274
|
+
handleSetFilenames();
|
|
3275
|
+
}, [scriptFileInput]);
|
|
3276
|
+
const handleSetFilenames = useCallback6(async () => {
|
|
3277
|
+
console.log("Set font names ", scriptFileInput);
|
|
3278
|
+
let allIds = [];
|
|
3279
|
+
const assetIds = SCRIPTS.reduce((acc, language) => {
|
|
3280
|
+
if (scriptFileInput[language]) {
|
|
3281
|
+
let newFileInput = Object.keys(scriptFileInput[language]).reduce((ftacc, filetype) => {
|
|
3282
|
+
var _a, _b, _c, _d, _e, _f;
|
|
3283
|
+
if (!((_b = (_a = scriptFileInput[language][filetype]) == null ? void 0 : _a.asset) == null ? void 0 : _b._ref)) return ftacc;
|
|
3284
|
+
allIds.push((_d = (_c = scriptFileInput[language][filetype]) == null ? void 0 : _c.asset) == null ? void 0 : _d._ref);
|
|
3285
|
+
return { ...ftacc, [filetype]: (_f = (_e = scriptFileInput[language][filetype]) == null ? void 0 : _e.asset) == null ? void 0 : _f._ref };
|
|
3286
|
+
}, {});
|
|
3287
|
+
acc[language] = newFileInput;
|
|
3288
|
+
}
|
|
3289
|
+
return acc;
|
|
3290
|
+
}, {});
|
|
3291
|
+
let assetData = await client.fetch(`*[_id in $allIds] {
|
|
3292
|
+
_id,
|
|
3293
|
+
originalFilename
|
|
3294
|
+
}`, { allIds });
|
|
3295
|
+
assetData = assetData.reduce((acc, asset) => {
|
|
3296
|
+
let ref2 = asset._id;
|
|
3297
|
+
return { ...acc, [ref2]: asset.originalFilename };
|
|
3298
|
+
}, {});
|
|
3299
|
+
let fontNames = {};
|
|
3300
|
+
SCRIPTS.forEach((language) => {
|
|
3301
|
+
if (assetIds[language]) {
|
|
3302
|
+
Object.keys(assetIds[language]).forEach((filetype) => {
|
|
3303
|
+
let ref2 = assetIds[language][filetype];
|
|
3304
|
+
fontNames[language] = { ...fontNames[language], [filetype]: assetData[ref2] };
|
|
3305
|
+
});
|
|
3306
|
+
}
|
|
3307
|
+
});
|
|
3308
|
+
setFilenames(fontNames);
|
|
3309
|
+
}, [scriptFileInput]);
|
|
3310
|
+
const handleGenerateCssFile = useCallback6(async (language) => {
|
|
3311
|
+
var _a, _b, _c;
|
|
3312
|
+
setMessage({ ...message, [language]: "Generating css: " + doc_title + ".css" });
|
|
3313
|
+
const woff2AssetRef = (_c = (_b = (_a = scriptFileInput[language]) == null ? void 0 : _a.woff2) == null ? void 0 : _b.asset) == null ? void 0 : _c._ref;
|
|
3314
|
+
let [woff2Buffer] = await client.fetch(
|
|
3315
|
+
`*[_id == $id]{ originalFilename, url }`,
|
|
3316
|
+
{ id: woff2AssetRef }
|
|
3317
|
+
);
|
|
3318
|
+
let blob = await fetch(woff2Buffer.url);
|
|
3319
|
+
blob = await blob.blob();
|
|
3320
|
+
let newFileInput = await generateCssFile({
|
|
3321
|
+
woff2File: blob,
|
|
3322
|
+
fileInput: scriptFileInput,
|
|
3323
|
+
language,
|
|
3324
|
+
fontName: doc_title,
|
|
3325
|
+
fileName: woff2Buffer.originalFilename.replace(".woff2", ""),
|
|
3326
|
+
variableFont: doc_variableFont,
|
|
3327
|
+
weight: doc_weight,
|
|
3328
|
+
style: doc_style,
|
|
3329
|
+
client
|
|
3330
|
+
});
|
|
3331
|
+
setMessage({ ...message, [language]: "CSS generated!" });
|
|
3332
|
+
setTimeout(() => {
|
|
3333
|
+
setMessage({});
|
|
3334
|
+
}, 2e3);
|
|
3335
|
+
onChange(set3(newFileInput));
|
|
3336
|
+
}, [scriptFileInput, onChange, doc_title, doc_variableFont]);
|
|
3337
|
+
const handleGenerateFontFile = useCallback6(async (code, sourceFile, language) => {
|
|
3338
|
+
var _a;
|
|
3339
|
+
setMessage({ ...message, [language]: "Generating files: ", code });
|
|
3340
|
+
let url = `https://cdn.sanity.io/files/${process.env.SANITY_STUDIO_PROJECT_ID}/${process.env.SANITY_STUDIO_DATASET}/${(_a = sourceFile == null ? void 0 : sourceFile.asset) == null ? void 0 : _a._ref.replace("file-", "").replace("-", ".")}`;
|
|
3341
|
+
console.log("Handle generate font file ", code, sourceFile, url);
|
|
3342
|
+
if (code === "all") {
|
|
3343
|
+
await generateFontFile({
|
|
3344
|
+
codes: ["otf", "woff", "woff2", "eot", "svg"],
|
|
3345
|
+
language,
|
|
3346
|
+
srcUrl: url,
|
|
3347
|
+
filename: doc_slug.current + "-" + language,
|
|
3348
|
+
documentId: doc_id,
|
|
3349
|
+
documentTitle: doc_title,
|
|
3350
|
+
documentVariableFont: doc_variableFont,
|
|
3351
|
+
documentStyle: doc_style,
|
|
3352
|
+
documentWeight: doc_weight,
|
|
3353
|
+
fileInput: scriptFileInput
|
|
3354
|
+
});
|
|
3355
|
+
} else {
|
|
3356
|
+
await generateFontFile({
|
|
3357
|
+
codes: [code],
|
|
3358
|
+
language,
|
|
3359
|
+
srcUrl: url,
|
|
3360
|
+
filename: doc_slug.current + "-" + language,
|
|
3361
|
+
documentId: doc_id,
|
|
3362
|
+
documentTitle: doc_title,
|
|
3363
|
+
documentVariableFont: doc_variableFont,
|
|
3364
|
+
documentStyle: doc_style,
|
|
3365
|
+
documentWeight: doc_weight,
|
|
3366
|
+
fileInput: scriptFileInput
|
|
3367
|
+
});
|
|
3368
|
+
}
|
|
3369
|
+
setMessage({ ...message, [language]: "Files generated!" });
|
|
3370
|
+
setTimeout(() => {
|
|
3371
|
+
setMessage({});
|
|
3372
|
+
}, 2e3);
|
|
3373
|
+
}, []);
|
|
3374
|
+
const handleUpload = useCallback6(async (event, language, code) => {
|
|
3375
|
+
console.log("Handle upload ", scriptFileInput, language, code);
|
|
3376
|
+
let file = event.target.files[0];
|
|
3377
|
+
let filename = doc_slug.current + "-" + language + "." + file.name.split(".").pop();
|
|
3378
|
+
setMessage({ ...message, [language]: "Uploading: " + filename });
|
|
3379
|
+
var asset = await client.assets.upload("file", file, { filename });
|
|
3380
|
+
let langObj = scriptFileInput[language] ? { ...scriptFileInput[language] } : {};
|
|
3381
|
+
let newFileInput = {
|
|
3382
|
+
...scriptFileInput,
|
|
3383
|
+
[language]: {
|
|
3384
|
+
...langObj,
|
|
3385
|
+
[code]: {
|
|
3386
|
+
_type: "file",
|
|
3387
|
+
asset: {
|
|
3388
|
+
_ref: asset._id,
|
|
3389
|
+
_type: "reference"
|
|
3390
|
+
}
|
|
3391
|
+
}
|
|
3392
|
+
}
|
|
3393
|
+
};
|
|
3394
|
+
let id = doc_id;
|
|
3395
|
+
if (id.startsWith("drafts.")) {
|
|
3396
|
+
id = id.replace("drafts.", "");
|
|
3397
|
+
}
|
|
3398
|
+
setMessage({ ...message, [language]: filename + " uploaded!" });
|
|
3399
|
+
setTimeout(() => {
|
|
3400
|
+
setMessage({});
|
|
3401
|
+
}, 2e3);
|
|
3402
|
+
if (code === "woff2") {
|
|
3403
|
+
console.log("woff2");
|
|
3404
|
+
setMessage({ ...message, [language]: "Generating Css: " + doc_title + ".css" });
|
|
3405
|
+
newFileInput = await generateCssFile({
|
|
3406
|
+
woff2File: file,
|
|
3407
|
+
fileInput: newFileInput,
|
|
3408
|
+
language,
|
|
3409
|
+
fontName: doc_title + "-" + language,
|
|
3410
|
+
fileName: filename.replace(".woff2", ""),
|
|
3411
|
+
variableFont: doc_variableFont,
|
|
3412
|
+
weight: doc_weight,
|
|
3413
|
+
style: doc_style,
|
|
3414
|
+
client
|
|
3415
|
+
});
|
|
3416
|
+
setMessage({ ...message, [language]: "" + doc_title + ".css generated!" });
|
|
3417
|
+
}
|
|
3418
|
+
onChange(set3(newFileInput));
|
|
3419
|
+
}, [scriptFileInput, onChange, doc_title, doc_variableFont, doc_slug]);
|
|
3420
|
+
const handleDelete = useCallback6(async (code, language) => {
|
|
3421
|
+
var _a, _b;
|
|
3422
|
+
console.log("Delete : ", code, language);
|
|
3423
|
+
setMessage({ ...message, [language]: `deleting ${language} ${code}` });
|
|
3424
|
+
const asset = (_b = (_a = scriptFileInput[language][code]) == null ? void 0 : _a.asset) == null ? void 0 : _b._ref;
|
|
3425
|
+
let newFileInput = { ...scriptFileInput };
|
|
3426
|
+
delete newFileInput[language][code];
|
|
3427
|
+
onChange(unset2([language, code]));
|
|
3428
|
+
await client.delete(asset).then((result) => {
|
|
3429
|
+
setMessage({ ...message, [language]: "deleted asset: ", result });
|
|
3430
|
+
setTimeout(() => {
|
|
3431
|
+
setMessage({});
|
|
3432
|
+
}, 2e3);
|
|
3433
|
+
}).catch((e) => {
|
|
3434
|
+
console.error("Error deleting asset: ", e.message);
|
|
3435
|
+
setMessage({ ...message, [language]: "WARNING: " + e.message });
|
|
3436
|
+
});
|
|
3437
|
+
}, [doc_id, scriptFileInput, onChange]);
|
|
3438
|
+
const handleDeleteAll = useCallback6(async (language) => {
|
|
3439
|
+
var _a, _b;
|
|
3440
|
+
setMessage({ ...message, [language]: "deleting..." });
|
|
3441
|
+
onChange(unset2([language]));
|
|
3442
|
+
console.log("Delete all : ", scriptFileInput[language]);
|
|
3443
|
+
for (var i = 0; i < Object.keys(scriptFileInput[language]).length; i++) {
|
|
3444
|
+
let refKey = Object.keys(scriptFileInput[language])[i];
|
|
3445
|
+
if (refKey == "documentInfo") return;
|
|
3446
|
+
const asset = (_b = (_a = scriptFileInput[language][refKey]) == null ? void 0 : _a.asset) == null ? void 0 : _b._ref;
|
|
3447
|
+
try {
|
|
3448
|
+
await client.delete(asset).then((result) => {
|
|
3449
|
+
setMessage({ ...message, [language]: "deleted asset: ", result });
|
|
3450
|
+
setTimeout(() => {
|
|
3451
|
+
setMessage({});
|
|
3452
|
+
}, 2e3);
|
|
3453
|
+
});
|
|
3454
|
+
} catch (e) {
|
|
3455
|
+
console.error("Error deleting asset: ", e.message);
|
|
3456
|
+
}
|
|
3457
|
+
}
|
|
3458
|
+
}, [scriptFileInput]);
|
|
3459
|
+
return /* @__PURE__ */ React10.createElement(Stack7, { space: 4 }, SCRIPTS && scriptFileInput && SCRIPTS.map((language, i) => {
|
|
3460
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C, _D, _E, _F, _G, _H, _I, _J, _K, _L, _M, _N, _O, _P, _Q, _R, _S, _T, _U, _V, _W, _X, _Y, _Z, __, _$, _aa, _ba, _ca, _da, _ea, _fa, _ga, _ha, _ia, _ja, _ka, _la, _ma, _na, _oa, _pa, _qa, _ra, _sa, _ta, _ua, _va, _wa, _xa, _ya, _za, _Aa, _Ba, _Ca, _Da, _Ea, _Fa, _Ga;
|
|
3461
|
+
return /* @__PURE__ */ React10.createElement(Stack7, { space: 2, key: "language-" + i, style: { borderBottom: "1px solid var(--card-border-color)", paddingBottom: 8 } }, /* @__PURE__ */ React10.createElement(Flex7, { gap: 2 }, /* @__PURE__ */ React10.createElement(Text9, { weight: "semibold" }, ((_a = language[0]) == null ? void 0 : _a.toUpperCase()) + language.slice(1)), message && message[language] && message[language] !== "" && /* @__PURE__ */ React10.createElement(Text9, { style: { color: "green" } }, message[language])), expanded[language] && /* @__PURE__ */ React10.createElement(Stack7, { space: 2 }, /* @__PURE__ */ React10.createElement(Flex7, { justify: "space-between", align: "center" }, /* @__PURE__ */ React10.createElement(Text9, null, "TTF:\xA0", !((_d = (_c = (_b = scriptFileInput[language]) == null ? void 0 : _b.ttf) == null ? void 0 : _c.asset) == null ? void 0 : _d._ref) ? ((_e = filenames[language]) == null ? void 0 : _e.ttf) ? /* @__PURE__ */ React10.createElement("b", null, filenames[language].ttf) : /* @__PURE__ */ React10.createElement("b", null, "Empty") : /* @__PURE__ */ React10.createElement("a", { href: `https://cdn.sanity.io/files/${process.env.SANITY_STUDIO_PROJECT_ID}/${process.env.SANITY_STUDIO_DATASET}/${(_h = (_g = (_f = scriptFileInput[language]) == null ? void 0 : _f.ttf) == null ? void 0 : _g.asset) == null ? void 0 : _h._ref.replace("file-", "").replace("-", ".")}`, target: "_blank" }, ((_i = filenames[language]) == null ? void 0 : _i.ttf) ? /* @__PURE__ */ React10.createElement("b", null, filenames[language].ttf) : /* @__PURE__ */ React10.createElement("b", null, "File"))), status === "ready" && /* @__PURE__ */ React10.createElement(Flex7, { gap: 1 }, /* @__PURE__ */ React10.createElement("label", null, /* @__PURE__ */ React10.createElement(Button7, { as: "span", mode: "ghost" }, "Upload"), /* @__PURE__ */ React10.createElement("input", { ref, type: "file", style: { display: "none" }, onChange: (event) => handleUpload(event, language, "ttf") })), ((_j = value[language]) == null ? void 0 : _j.ttf) && /* @__PURE__ */ React10.createElement(Button7, { mode: "ghost", tone: "critical", onClick: () => handleDelete("ttf", language) }, "\xD7"))), status === "ready" && ((_k = value[language]) == null ? void 0 : _k.ttf) && /* @__PURE__ */ React10.createElement(Button7, { mode: "default", onClick: () => handleGenerateFontFile("all", value[language].ttf, language) }, "Regenerate Files from TTF"), /* @__PURE__ */ React10.createElement(Flex7, { justify: "space-between", align: "center" }, /* @__PURE__ */ React10.createElement(Text9, null, "OTF:\xA0", !((_n = (_m = (_l = scriptFileInput[language]) == null ? void 0 : _l.otf) == null ? void 0 : _m.asset) == null ? void 0 : _n._ref) ? ((_o = filenames[language]) == null ? void 0 : _o.otf) ? /* @__PURE__ */ React10.createElement("b", null, (_p = filenames[language]) == null ? void 0 : _p.otf) : /* @__PURE__ */ React10.createElement("b", null, "Empty") : /* @__PURE__ */ React10.createElement("a", { href: `https://cdn.sanity.io/files/${process.env.SANITY_STUDIO_PROJECT_ID}/${process.env.SANITY_STUDIO_DATASET}/${(_s = (_r = (_q = scriptFileInput[language]) == null ? void 0 : _q.otf) == null ? void 0 : _r.asset) == null ? void 0 : _s._ref.replace("file-", "").replace("-", ".")}`, target: "_blank" }, ((_t = filenames[language]) == null ? void 0 : _t.otf) ? /* @__PURE__ */ React10.createElement("b", null, (_u = filenames[language]) == null ? void 0 : _u.otf) : /* @__PURE__ */ React10.createElement("b", null, "File"))), status === "ready" && /* @__PURE__ */ React10.createElement(Flex7, { gap: 1 }, ((_v = value[language]) == null ? void 0 : _v.woff) && /* @__PURE__ */ React10.createElement(Button7, { mode: "default", onClick: () => handleGenerateFontFile("otf", value[language].woff, language) }, "Build"), /* @__PURE__ */ React10.createElement("label", null, /* @__PURE__ */ React10.createElement(Button7, { as: "span", mode: "ghost" }, "Upload"), /* @__PURE__ */ React10.createElement("input", { ref, type: "file", style: { display: "none" }, onChange: async (event) => handleUpload(event, language, "otf") })), ((_w = value[language]) == null ? void 0 : _w.otf) && /* @__PURE__ */ React10.createElement(Button7, { mode: "ghost", tone: "critical", onClick: () => handleDelete("otf", language) }, "\xD7"))), /* @__PURE__ */ React10.createElement(Flex7, { justify: "space-between", align: "center" }, /* @__PURE__ */ React10.createElement(Text9, null, "WOFF:\xA0", !((_z = (_y = (_x = scriptFileInput[language]) == null ? void 0 : _x.woff) == null ? void 0 : _y.asset) == null ? void 0 : _z._ref) ? ((_A = filenames[language]) == null ? void 0 : _A.woff) ? /* @__PURE__ */ React10.createElement("b", null, (_B = filenames[language]) == null ? void 0 : _B.woff) : /* @__PURE__ */ React10.createElement("b", null, "Empty") : /* @__PURE__ */ React10.createElement("a", { href: `https://cdn.sanity.io/files/${process.env.SANITY_STUDIO_PROJECT_ID}/${process.env.SANITY_STUDIO_DATASET}/${(_E = (_D = (_C = scriptFileInput[language]) == null ? void 0 : _C.woff) == null ? void 0 : _D.asset) == null ? void 0 : _E._ref.replace("file-", "").replace("-", ".")}`, target: "_blank" }, ((_F = filenames[language]) == null ? void 0 : _F.woff) ? /* @__PURE__ */ React10.createElement("b", null, (_G = filenames[language]) == null ? void 0 : _G.woff) : /* @__PURE__ */ React10.createElement("b", null, "File"))), status === "ready" && /* @__PURE__ */ React10.createElement(Flex7, { gap: 1 }, ((_H = value[language]) == null ? void 0 : _H.ttf) && /* @__PURE__ */ React10.createElement(Button7, { mode: "default", onClick: () => handleGenerateFontFile("woff", value[language].ttf, language) }, "Build"), /* @__PURE__ */ React10.createElement("label", null, /* @__PURE__ */ React10.createElement(Button7, { as: "span", mode: "ghost" }, "Upload"), /* @__PURE__ */ React10.createElement("input", { ref, type: "file", style: { display: "none" }, onChange: async (event) => handleUpload(event, language, "woff") })), ((_I = value[language]) == null ? void 0 : _I.woff) && /* @__PURE__ */ React10.createElement(Button7, { mode: "ghost", tone: "critical", onClick: () => handleDelete("woff", language) }, "\xD7"))), /* @__PURE__ */ React10.createElement(Flex7, { justify: "space-between", align: "center" }, /* @__PURE__ */ React10.createElement(Text9, null, "WOFF2:\xA0", !((_L = (_K = (_J = scriptFileInput[language]) == null ? void 0 : _J.woff2) == null ? void 0 : _K.asset) == null ? void 0 : _L._ref) ? ((_M = filenames[language]) == null ? void 0 : _M.woff2) ? /* @__PURE__ */ React10.createElement("b", null, (_N = filenames[language]) == null ? void 0 : _N.woff2) : /* @__PURE__ */ React10.createElement("b", null, "Empty") : /* @__PURE__ */ React10.createElement("a", { href: `https://cdn.sanity.io/files/${process.env.SANITY_STUDIO_PROJECT_ID}/${process.env.SANITY_STUDIO_DATASET}/${(_Q = (_P = (_O = scriptFileInput[language]) == null ? void 0 : _O.woff2) == null ? void 0 : _P.asset) == null ? void 0 : _Q._ref.replace("file-", "").replace("-", ".")}`, target: "_blank" }, ((_R = filenames[language]) == null ? void 0 : _R.woff2) ? /* @__PURE__ */ React10.createElement("b", null, (_S = filenames[language]) == null ? void 0 : _S.woff2) : /* @__PURE__ */ React10.createElement("b", null, "File"))), status === "ready" && /* @__PURE__ */ React10.createElement(Flex7, { gap: 1 }, ((_T = value[language]) == null ? void 0 : _T.ttf) && /* @__PURE__ */ React10.createElement(Button7, { mode: "default", onClick: () => handleGenerateFontFile("woff2", value[language].ttf, language) }, "Build"), /* @__PURE__ */ React10.createElement("label", null, /* @__PURE__ */ React10.createElement(Button7, { as: "span", mode: "ghost" }, "Upload"), /* @__PURE__ */ React10.createElement("input", { ref, type: "file", style: { display: "none" }, onChange: async (event) => handleUpload(event, language, "woff2") })), ((_U = value[language]) == null ? void 0 : _U.woff2) && /* @__PURE__ */ React10.createElement(Button7, { mode: "ghost", tone: "critical", onClick: () => handleDelete("woff2", language) }, "\xD7"))), /* @__PURE__ */ React10.createElement(Flex7, { justify: "space-between", align: "center" }, /* @__PURE__ */ React10.createElement(Text9, null, "EOT:\xA0", !((_X = (_W = (_V = scriptFileInput[language]) == null ? void 0 : _V.eot) == null ? void 0 : _W.asset) == null ? void 0 : _X._ref) ? ((_Y = filenames[language]) == null ? void 0 : _Y.eot) ? /* @__PURE__ */ React10.createElement("b", null, (_Z = filenames[language]) == null ? void 0 : _Z.eot) : /* @__PURE__ */ React10.createElement("b", null, "Empty") : /* @__PURE__ */ React10.createElement("a", { href: `https://cdn.sanity.io/files/${process.env.SANITY_STUDIO_PROJECT_ID}/${process.env.SANITY_STUDIO_DATASET}/${(_aa = (_$ = (__ = scriptFileInput[language]) == null ? void 0 : __.eot) == null ? void 0 : _$.asset) == null ? void 0 : _aa._ref.replace("file-", "").replace("-", ".")}`, target: "_blank" }, ((_ba = filenames[language]) == null ? void 0 : _ba.eot) ? /* @__PURE__ */ React10.createElement("b", null, (_ca = filenames[language]) == null ? void 0 : _ca.eot) : /* @__PURE__ */ React10.createElement("b", null, "File"))), status === "ready" && /* @__PURE__ */ React10.createElement(Flex7, { gap: 1 }, ((_da = value[language]) == null ? void 0 : _da.ttf) && /* @__PURE__ */ React10.createElement(Button7, { mode: "default", onClick: () => handleGenerateFontFile("eot", value[language].ttf, language) }, "Build"), /* @__PURE__ */ React10.createElement("label", null, /* @__PURE__ */ React10.createElement(Button7, { as: "span", mode: "ghost" }, "Upload"), /* @__PURE__ */ React10.createElement("input", { ref, type: "file", style: { display: "none" }, onChange: async (event) => handleUpload(event, language, "eot") })), ((_ea = value[language]) == null ? void 0 : _ea.eot) && /* @__PURE__ */ React10.createElement(Button7, { mode: "ghost", tone: "critical", onClick: () => handleDelete("eot", language) }, "\xD7"))), /* @__PURE__ */ React10.createElement(Flex7, { justify: "space-between", align: "center" }, /* @__PURE__ */ React10.createElement(Text9, null, "SVG:\xA0", !((_ha = (_ga = (_fa = scriptFileInput[language]) == null ? void 0 : _fa.svg) == null ? void 0 : _ga.asset) == null ? void 0 : _ha._ref) ? ((_ia = filenames[language]) == null ? void 0 : _ia.svg) ? /* @__PURE__ */ React10.createElement("b", null, (_ja = filenames[language]) == null ? void 0 : _ja.svg) : /* @__PURE__ */ React10.createElement("b", null, "Empty") : /* @__PURE__ */ React10.createElement("a", { href: `https://cdn.sanity.io/files/${process.env.SANITY_STUDIO_PROJECT_ID}/${process.env.SANITY_STUDIO_DATASET}/${(_ma = (_la = (_ka = scriptFileInput[language]) == null ? void 0 : _ka.svg) == null ? void 0 : _la.asset) == null ? void 0 : _ma._ref.replace("file-", "").replace("-", ".")}`, target: "_blank" }, ((_na = filenames[language]) == null ? void 0 : _na.svg) ? /* @__PURE__ */ React10.createElement("b", null, (_oa = filenames[language]) == null ? void 0 : _oa.svg) : /* @__PURE__ */ React10.createElement("b", null, "File"))), status === "ready" && /* @__PURE__ */ React10.createElement(Flex7, { gap: 1 }, ((_pa = value[language]) == null ? void 0 : _pa.ttf) && /* @__PURE__ */ React10.createElement(Button7, { mode: "default", onClick: () => handleGenerateFontFile("svg", value[language].ttf, language) }, "Build"), /* @__PURE__ */ React10.createElement("label", null, /* @__PURE__ */ React10.createElement(Button7, { as: "span", mode: "ghost" }, "Upload"), /* @__PURE__ */ React10.createElement("input", { ref, type: "file", style: { display: "none" }, onChange: async (event) => handleUpload(event, language, "svg") })), ((_qa = value[language]) == null ? void 0 : _qa.svg) && /* @__PURE__ */ React10.createElement(Button7, { mode: "ghost", tone: "critical", onClick: () => handleDelete("svg", language) }, "\xD7"))), /* @__PURE__ */ React10.createElement(Flex7, { justify: "space-between", align: "center" }, /* @__PURE__ */ React10.createElement(Text9, null, "CSS:\xA0", !((_ta = (_sa = (_ra = scriptFileInput[language]) == null ? void 0 : _ra.css) == null ? void 0 : _sa.asset) == null ? void 0 : _ta._ref) ? ((_ua = filenames[language]) == null ? void 0 : _ua.css) ? /* @__PURE__ */ React10.createElement("b", null, (_va = filenames[language]) == null ? void 0 : _va.css) : /* @__PURE__ */ React10.createElement("b", null, "Empty") : /* @__PURE__ */ React10.createElement("a", { href: `https://cdn.sanity.io/files/${process.env.SANITY_STUDIO_PROJECT_ID}/${process.env.SANITY_STUDIO_DATASET}/${(_ya = (_xa = (_wa = scriptFileInput[language]) == null ? void 0 : _wa.css) == null ? void 0 : _xa.asset) == null ? void 0 : _ya._ref.replace("file-", "").replace("-", ".")}`, target: "_blank" }, ((_za = filenames[language]) == null ? void 0 : _za.css) ? /* @__PURE__ */ React10.createElement("b", null, (_Aa = filenames[language]) == null ? void 0 : _Aa.css) : /* @__PURE__ */ React10.createElement("b", null, "File"))), status === "ready" && /* @__PURE__ */ React10.createElement(Flex7, { gap: 1 }, ((_Ba = value[language]) == null ? void 0 : _Ba.woff2) && /* @__PURE__ */ React10.createElement(Button7, { mode: "default", onClick: () => handleGenerateCssFile(language) }, "Build"), ((_Ca = value[language]) == null ? void 0 : _Ca.css) && /* @__PURE__ */ React10.createElement(Button7, { mode: "ghost", tone: "critical", onClick: () => handleDelete("css", language) }, "\xD7"))), status === "ready" && (((_Da = value[language]) == null ? void 0 : _Da.ttf) || ((_Ea = value[language]) == null ? void 0 : _Ea.otf) || ((_Fa = value[language]) == null ? void 0 : _Fa.woff) || ((_Ga = value[language]) == null ? void 0 : _Ga.woff2)) && /* @__PURE__ */ React10.createElement(Button7, { mode: "ghost", tone: "critical", onClick: () => handleDeleteAll(language), style: { width: "100%" } }, "Delete All")));
|
|
3462
|
+
}));
|
|
3463
|
+
};
|
|
3464
|
+
|
|
3465
|
+
// src/components/UploadButton.jsx
|
|
3466
|
+
import React11, { forwardRef } from "react";
|
|
3467
|
+
import { Button as Button8, Text as Text10 } from "@sanity/ui";
|
|
3468
|
+
var UploadButton = forwardRef(({ handleUpload }, ref) => {
|
|
3469
|
+
return /* @__PURE__ */ React11.createElement(
|
|
3470
|
+
Button8,
|
|
3471
|
+
{
|
|
3472
|
+
mode: "ghost",
|
|
3473
|
+
tone: "primary",
|
|
3474
|
+
width: "fill",
|
|
3475
|
+
padding: 3,
|
|
3476
|
+
style: { position: "relative" }
|
|
3477
|
+
},
|
|
3478
|
+
/* @__PURE__ */ React11.createElement(Text10, { align: "center" }, "Upload (ttf/otf/woff/woff2/etc...)"),
|
|
3479
|
+
/* @__PURE__ */ React11.createElement(
|
|
3480
|
+
"input",
|
|
3481
|
+
{
|
|
3482
|
+
ref,
|
|
3483
|
+
type: "file",
|
|
3484
|
+
multiple: true,
|
|
3485
|
+
style: {
|
|
3486
|
+
position: "absolute",
|
|
3487
|
+
top: 0,
|
|
3488
|
+
left: 0,
|
|
3489
|
+
width: "100%",
|
|
3490
|
+
height: "100%",
|
|
3491
|
+
opacity: 0,
|
|
3492
|
+
cursor: "pointer"
|
|
3493
|
+
},
|
|
3494
|
+
onChange: handleUpload
|
|
3495
|
+
}
|
|
3496
|
+
)
|
|
3497
|
+
);
|
|
3498
|
+
});
|
|
3499
|
+
UploadButton.displayName = "UploadButton";
|
|
3500
|
+
var UploadButton_default = UploadButton;
|
|
3501
|
+
|
|
3502
|
+
// src/utils/getEmptyFontKit.js
|
|
3503
|
+
import * as fontkit7 from "fontkit";
|
|
3504
|
+
import slugify4 from "slugify";
|
|
3505
|
+
async function getEmptyFontKit({ title, files, weightKeywordList, italicKeywordList }) {
|
|
3506
|
+
var _a, _b, _c, _d, _e, _f;
|
|
3507
|
+
let fontNames = {};
|
|
3508
|
+
let subfamilies = {};
|
|
3509
|
+
for (var i = 0; i < files.length; i++) {
|
|
3510
|
+
const file = files[i];
|
|
3511
|
+
const fontBuffer = await readFontFile2(file);
|
|
3512
|
+
const font = fontkit7.create(fontBuffer);
|
|
3513
|
+
let weightName = ((_b = (_a = font == null ? void 0 : font.name) == null ? void 0 : _a.records) == null ? void 0 : _b.preferredSubfamily) ? (_d = (_c = font == null ? void 0 : font.name) == null ? void 0 : _c.records) == null ? void 0 : _d.preferredSubfamily : (_f = (_e = font == null ? void 0 : font.name) == null ? void 0 : _e.records) == null ? void 0 : _f.fontSubfamily;
|
|
3514
|
+
weightName = (weightName == null ? void 0 : weightName.en) ? weightName.en : weightName.constructor == Object ? weightName[Object.keys(weightName)[0]] : weightName;
|
|
3515
|
+
let variableFont = (font == null ? void 0 : font.variationAxes) && Object.keys(font.variationAxes).length > 0 ? true : false;
|
|
3516
|
+
let subfamilyName = font.familyName.toLowerCase().trim().replace(title.toLowerCase().trim(), "").trim();
|
|
3517
|
+
let fontTitle = font == null ? void 0 : font.fullName.toLowerCase().trim();
|
|
3518
|
+
weightKeywordList.forEach((keyword) => {
|
|
3519
|
+
const kw = keyword.toLowerCase().trim();
|
|
3520
|
+
if (fontTitle.includes(kw)) {
|
|
3521
|
+
fontTitle = fontTitle.replace(kw, "");
|
|
3522
|
+
}
|
|
3523
|
+
if (subfamilyName.includes(kw)) {
|
|
3524
|
+
subfamilyName = subfamilyName.replace(kw, "");
|
|
3525
|
+
}
|
|
3526
|
+
});
|
|
3527
|
+
italicKeywordList.forEach((keyword) => {
|
|
3528
|
+
const kw = keyword.toLowerCase().trim();
|
|
3529
|
+
if (subfamilyName.includes(kw)) {
|
|
3530
|
+
subfamilyName = subfamilyName.replace(kw, "");
|
|
3531
|
+
}
|
|
3532
|
+
if (fontTitle.includes(kw)) {
|
|
3533
|
+
fontTitle = fontTitle.replace(kw, "");
|
|
3534
|
+
}
|
|
3535
|
+
});
|
|
3536
|
+
fontTitle = fontTitle.trim().split(" ").map((word) => word[0].toUpperCase() + word.slice(1)).join(" ");
|
|
3537
|
+
subfamilyName = subfamilyName.trim();
|
|
3538
|
+
subfamilyName = subfamilyName == "" ? "Regular" : subfamilyName.split(" ").map((word) => word[0].toUpperCase() + word.slice(1)).join(" ");
|
|
3539
|
+
let id = slugify4(fontTitle.toLowerCase().trim());
|
|
3540
|
+
if (variableFont && !id.endsWith("-vf")) {
|
|
3541
|
+
id = id + "-vf";
|
|
3542
|
+
fontTitle = fontTitle + " VF";
|
|
3543
|
+
}
|
|
3544
|
+
if (!subfamilies[id]) {
|
|
3545
|
+
subfamilies[id] = [subfamilyName];
|
|
3546
|
+
} else if (subfamilies[id].indexOf(subfamilyName) == -1) {
|
|
3547
|
+
subfamilies[id] = [...subfamilies[id], subfamilyName];
|
|
3548
|
+
}
|
|
3549
|
+
if (!fontNames[id]) {
|
|
3550
|
+
fontNames[id] = [{
|
|
3551
|
+
file: file.name,
|
|
3552
|
+
fullName: font.fullName,
|
|
3553
|
+
familyName: font.familyName,
|
|
3554
|
+
subFamily: subfamilyName
|
|
3555
|
+
}];
|
|
3556
|
+
} else if (fontNames[id].indexOf(file.name) == -1) {
|
|
3557
|
+
fontNames[id].push({
|
|
3558
|
+
file: file.name,
|
|
3559
|
+
fullName: font.fullName,
|
|
3560
|
+
familyName: font.familyName,
|
|
3561
|
+
subFamily: subfamilyName
|
|
3562
|
+
});
|
|
3563
|
+
}
|
|
3564
|
+
}
|
|
3565
|
+
console.log("font names : ", fontNames);
|
|
3566
|
+
}
|
|
3567
|
+
var readFontFile2 = (file) => {
|
|
3568
|
+
return new Promise((resolve, reject) => {
|
|
3569
|
+
const reader = new FileReader();
|
|
3570
|
+
reader.onload = (event) => {
|
|
3571
|
+
resolve(new Uint8Array(event.target.result));
|
|
3572
|
+
};
|
|
3573
|
+
reader.onerror = (error) => {
|
|
3574
|
+
reject(error);
|
|
3575
|
+
};
|
|
3576
|
+
reader.readAsArrayBuffer(file);
|
|
3577
|
+
});
|
|
3578
|
+
};
|
|
3579
|
+
export {
|
|
3580
|
+
BatchUploadFonts,
|
|
3581
|
+
FontScriptUploaderComponent,
|
|
3582
|
+
GenerateCollectionsPairsComponent,
|
|
3583
|
+
HtmlDescription,
|
|
3584
|
+
PriceInput_default as PriceInput,
|
|
3585
|
+
RegenerateSubfamiliesComponent,
|
|
3586
|
+
SCRIPTS,
|
|
3587
|
+
SCRIPTS_OBJECT,
|
|
3588
|
+
SingleUploaderTool,
|
|
3589
|
+
StatusDisplay_default as StatusDisplay,
|
|
3590
|
+
UpdateScriptsComponent,
|
|
3591
|
+
UploadButton_default as UploadButton,
|
|
3592
|
+
UploadScriptsComponent,
|
|
3593
|
+
addItalicToFontTitle,
|
|
3594
|
+
createFontObject,
|
|
3595
|
+
determineWeight,
|
|
3596
|
+
expandAbbreviations,
|
|
3597
|
+
extractFontMetadata,
|
|
3598
|
+
extractWeightFromFullName,
|
|
3599
|
+
extractWeightName,
|
|
3600
|
+
formatFontTitle,
|
|
3601
|
+
generateCssFile,
|
|
3602
|
+
generateFontData,
|
|
3603
|
+
generateFontFile,
|
|
3604
|
+
generateStyleKeywords,
|
|
3605
|
+
generateSubset,
|
|
3606
|
+
getEmptyFontKit,
|
|
3607
|
+
logFontInfo,
|
|
3608
|
+
parseVariableFontInstances_default as parseVariableFontInstances,
|
|
3609
|
+
processFontFiles,
|
|
3610
|
+
processItalicKeywords,
|
|
3611
|
+
processSubfamilyName,
|
|
3612
|
+
readFontFile,
|
|
3613
|
+
removeWeightNames,
|
|
3614
|
+
renameFontDocuments,
|
|
3615
|
+
reverseSpellingLookup,
|
|
3616
|
+
sanitizeForSanityId,
|
|
3617
|
+
sortFontObjects,
|
|
3618
|
+
updateFontPrices,
|
|
3619
|
+
updateTypefaceDocument,
|
|
3620
|
+
uploadFontFiles,
|
|
3621
|
+
useSanityClient
|
|
3622
|
+
};
|