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