@liiift-studio/sanity-font-manager 2.5.3 → 2.5.5
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 +17679 -793
- package/dist/index.mjs +17404 -621
- package/package.json +1 -3
- package/src/index.js +4 -0
- package/src/utils/parseFont.js +33 -27
- package/src/utils/setupDecompressors.js +10 -0
- package/dist/UploadModal-NQZGBJAH.js +0 -7
- package/dist/UploadModal-YJCVHM63.mjs +0 -7
- package/dist/chunk-EBO3CZXG.mjs +0 -15
- package/dist/chunk-MCKGQKYU.js +0 -15
- package/dist/chunk-PGSHWNGW.js +0 -4250
- package/dist/chunk-WSQYNIIZ.mjs +0 -4250
- package/dist/unbrotli-CTU3FKHW.mjs +0 -3434
- package/dist/unbrotli-NSXTNE2T.js +0 -3434
package/dist/chunk-WSQYNIIZ.mjs
DELETED
|
@@ -1,4250 +0,0 @@
|
|
|
1
|
-
// src/components/UploadModal.jsx
|
|
2
|
-
import React10, { useReducer as useReducer2, useCallback as useCallback6, useState as useState7, useMemo as useMemo7, useRef as useRef3, useEffect as useEffect5 } from "react";
|
|
3
|
-
import { Dialog, Box as Box9, Flex as Flex10, Text as Text10, Badge as Badge8, Button as Button8 } from "@sanity/ui";
|
|
4
|
-
|
|
5
|
-
// src/utils/planTypes.js
|
|
6
|
-
var FONT_STATUS = {
|
|
7
|
-
PENDING: "pending",
|
|
8
|
-
PROCESSING: "processing",
|
|
9
|
-
PROCESSED: "processed",
|
|
10
|
-
ERROR: "error"
|
|
11
|
-
};
|
|
12
|
-
var PLAN_PHASE = {
|
|
13
|
-
IDLE: "idle",
|
|
14
|
-
PROCESSING: "processing",
|
|
15
|
-
REVIEWING: "reviewing",
|
|
16
|
-
READY: "ready",
|
|
17
|
-
EXECUTING: "executing",
|
|
18
|
-
COMPLETE: "complete",
|
|
19
|
-
ERROR: "error"
|
|
20
|
-
};
|
|
21
|
-
var RECOMMENDATION = {
|
|
22
|
-
USE_EXACT: "use-exact",
|
|
23
|
-
USE_CANDIDATE: "use-candidate",
|
|
24
|
-
AMBIGUOUS: "ambiguous",
|
|
25
|
-
CREATE: "create"
|
|
26
|
-
};
|
|
27
|
-
var EXECUTION_STATUS = {
|
|
28
|
-
QUEUED: "queued",
|
|
29
|
-
UPLOADING_ASSETS: "uploading-assets",
|
|
30
|
-
GENERATING_CSS: "generating-css",
|
|
31
|
-
GENERATING_METADATA: "generating-metadata",
|
|
32
|
-
CREATING_DOCUMENT: "creating-document",
|
|
33
|
-
PATCHING_TYPEFACE: "patching-typeface",
|
|
34
|
-
COMPLETE: "complete",
|
|
35
|
-
ERROR: "error",
|
|
36
|
-
SKIPPED: "skipped"
|
|
37
|
-
};
|
|
38
|
-
var PROCESSING_OWNED_FIELDS = ["parsedMetadata", "glyphCount", "opentypeFeatures", "variationAxes", "status"];
|
|
39
|
-
var PLAN_VERSION = 1;
|
|
40
|
-
var CONCURRENCY_LIMIT = 3;
|
|
41
|
-
var MAX_RETRIES = 3;
|
|
42
|
-
var BASE_BACKOFF_MS = 1e3;
|
|
43
|
-
var JITTER_FACTOR = 0.25;
|
|
44
|
-
function backoffWithJitter(attempt) {
|
|
45
|
-
const base = BASE_BACKOFF_MS * Math.pow(2, attempt);
|
|
46
|
-
const jitter = base * JITTER_FACTOR * (Math.random() * 2 - 1);
|
|
47
|
-
return Math.round(base + jitter);
|
|
48
|
-
}
|
|
49
|
-
function createFontDecisions({
|
|
50
|
-
titleSource = "fontkit-fullName",
|
|
51
|
-
title = "",
|
|
52
|
-
titleOriginal = "",
|
|
53
|
-
documentId = "",
|
|
54
|
-
weight = 400,
|
|
55
|
-
weightSource = "default-400",
|
|
56
|
-
matchedKeyword = null,
|
|
57
|
-
weightName = "",
|
|
58
|
-
weightNameSource = "nameId17-preferredSubfamily",
|
|
59
|
-
style = "Regular",
|
|
60
|
-
styleSource = "default-regular",
|
|
61
|
-
styleReason = "",
|
|
62
|
-
subfamily = "",
|
|
63
|
-
subfamilySource = "default-empty",
|
|
64
|
-
titleAlternatives = []
|
|
65
|
-
}) {
|
|
66
|
-
return {
|
|
67
|
-
title: {
|
|
68
|
-
source: titleSource,
|
|
69
|
-
original: titleOriginal,
|
|
70
|
-
processed: title,
|
|
71
|
-
alternatives: titleAlternatives,
|
|
72
|
-
userOverride: null
|
|
73
|
-
},
|
|
74
|
-
documentId: {
|
|
75
|
-
source: "derived-from-title",
|
|
76
|
-
generated: documentId,
|
|
77
|
-
userOverride: null
|
|
78
|
-
},
|
|
79
|
-
weight: {
|
|
80
|
-
source: weightSource,
|
|
81
|
-
detected: weight,
|
|
82
|
-
matchedKeyword,
|
|
83
|
-
rawWeightName: weightName,
|
|
84
|
-
userOverride: null
|
|
85
|
-
},
|
|
86
|
-
weightName: {
|
|
87
|
-
source: weightNameSource,
|
|
88
|
-
detected: weightName,
|
|
89
|
-
userOverride: null
|
|
90
|
-
},
|
|
91
|
-
style: {
|
|
92
|
-
source: styleSource,
|
|
93
|
-
detected: style,
|
|
94
|
-
reason: styleReason,
|
|
95
|
-
userOverride: null
|
|
96
|
-
},
|
|
97
|
-
subfamily: {
|
|
98
|
-
source: subfamilySource,
|
|
99
|
-
detected: subfamily,
|
|
100
|
-
userOverride: null
|
|
101
|
-
},
|
|
102
|
-
existingDocument: {
|
|
103
|
-
recommendation: RECOMMENDATION.CREATE,
|
|
104
|
-
exact: null,
|
|
105
|
-
candidates: [],
|
|
106
|
-
userChoice: null,
|
|
107
|
-
selectedCandidate: null,
|
|
108
|
-
lookupFailed: false
|
|
109
|
-
}
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
function createEmptyPlan(settings = {}) {
|
|
113
|
-
return {
|
|
114
|
-
version: PLAN_VERSION,
|
|
115
|
-
settings: {
|
|
116
|
-
price: 0,
|
|
117
|
-
preserveShortenedNames: false,
|
|
118
|
-
preserveFileNames: false,
|
|
119
|
-
...settings
|
|
120
|
-
},
|
|
121
|
-
fonts: {},
|
|
122
|
-
subfamilyGroups: {},
|
|
123
|
-
phase: PLAN_PHASE.IDLE,
|
|
124
|
-
processingProgress: {
|
|
125
|
-
total: 0,
|
|
126
|
-
completed: 0,
|
|
127
|
-
failed: 0,
|
|
128
|
-
currentFile: null
|
|
129
|
-
}
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// src/utils/sanitizeForSanityId.js
|
|
134
|
-
import slugify from "slugify";
|
|
135
|
-
function sanitizeForSanityId(str) {
|
|
136
|
-
if (!str || typeof str !== "string") {
|
|
137
|
-
return "font-" + Date.now();
|
|
138
|
-
}
|
|
139
|
-
let sanitized = str.toLowerCase().trim();
|
|
140
|
-
sanitized = sanitized.replace(/\+/g, "plus");
|
|
141
|
-
sanitized = sanitized.replace(/&/g, "and");
|
|
142
|
-
sanitized = sanitized.replace(/@/g, "at");
|
|
143
|
-
sanitized = slugify(sanitized, {
|
|
144
|
-
replacement: "-",
|
|
145
|
-
remove: /[^\w\s-]/g,
|
|
146
|
-
lower: true,
|
|
147
|
-
strict: true,
|
|
148
|
-
locale: "en",
|
|
149
|
-
trim: true
|
|
150
|
-
});
|
|
151
|
-
sanitized = sanitized.replace(/[^a-z0-9\-_]/g, "-");
|
|
152
|
-
sanitized = sanitized.replace(/-+/g, "-");
|
|
153
|
-
sanitized = sanitized.replace(/^[-_]+|[-_]+$/g, "");
|
|
154
|
-
if (sanitized && !/^[a-z_]/.test(sanitized)) {
|
|
155
|
-
sanitized = "font_" + sanitized;
|
|
156
|
-
}
|
|
157
|
-
if (!sanitized) {
|
|
158
|
-
sanitized = "font_" + Date.now();
|
|
159
|
-
}
|
|
160
|
-
if (sanitized.length > 128) {
|
|
161
|
-
const hash = Math.random().toString(36).substring(2, 8);
|
|
162
|
-
sanitized = sanitized.substring(0, 120) + "_" + hash;
|
|
163
|
-
}
|
|
164
|
-
if (!/^[a-z_][a-z0-9\-_]*$/.test(sanitized)) {
|
|
165
|
-
console.warn(`ID sanitization produced invalid result: "${sanitized}", using fallback`);
|
|
166
|
-
sanitized = "font_" + Date.now();
|
|
167
|
-
}
|
|
168
|
-
return sanitized;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
// src/utils/planReducer.js
|
|
172
|
-
var VALID_TRANSITIONS = {
|
|
173
|
-
[PLAN_PHASE.IDLE]: [PLAN_PHASE.PROCESSING],
|
|
174
|
-
[PLAN_PHASE.PROCESSING]: [PLAN_PHASE.REVIEWING],
|
|
175
|
-
[PLAN_PHASE.REVIEWING]: [PLAN_PHASE.READY, PLAN_PHASE.EXECUTING],
|
|
176
|
-
[PLAN_PHASE.READY]: [PLAN_PHASE.EXECUTING],
|
|
177
|
-
[PLAN_PHASE.EXECUTING]: [PLAN_PHASE.COMPLETE, PLAN_PHASE.ERROR],
|
|
178
|
-
[PLAN_PHASE.COMPLETE]: [PLAN_PHASE.EXECUTING],
|
|
179
|
-
[PLAN_PHASE.ERROR]: [PLAN_PHASE.EXECUTING]
|
|
180
|
-
};
|
|
181
|
-
function planReducer(state, action) {
|
|
182
|
-
switch (action.type) {
|
|
183
|
-
// ---------------------------------------------------------------
|
|
184
|
-
// Phase / Settings
|
|
185
|
-
// ---------------------------------------------------------------
|
|
186
|
-
case "SET_PHASE": {
|
|
187
|
-
if (action.phase === PLAN_PHASE.IDLE) {
|
|
188
|
-
return { ...state, phase: PLAN_PHASE.IDLE };
|
|
189
|
-
}
|
|
190
|
-
const validNext = VALID_TRANSITIONS[state.phase] || [];
|
|
191
|
-
if (!validNext.includes(action.phase)) {
|
|
192
|
-
console.warn(`Invalid phase transition: ${state.phase} \u2192 ${action.phase}`);
|
|
193
|
-
return state;
|
|
194
|
-
}
|
|
195
|
-
const nextState = { ...state, phase: action.phase };
|
|
196
|
-
if (typeof action.totalFiles === "number") {
|
|
197
|
-
nextState.processingProgress = {
|
|
198
|
-
...state.processingProgress,
|
|
199
|
-
total: action.totalFiles,
|
|
200
|
-
completed: 0,
|
|
201
|
-
failed: 0,
|
|
202
|
-
currentFile: null
|
|
203
|
-
};
|
|
204
|
-
}
|
|
205
|
-
return nextState;
|
|
206
|
-
}
|
|
207
|
-
case "SET_SETTINGS": {
|
|
208
|
-
if (state.phase !== PLAN_PHASE.IDLE && state.phase !== PLAN_PHASE.REVIEWING && state.phase !== PLAN_PHASE.READY) {
|
|
209
|
-
console.warn("SET_SETTINGS blocked \u2014 settings locked during processing/execution");
|
|
210
|
-
return state;
|
|
211
|
-
}
|
|
212
|
-
return { ...state, settings: { ...state.settings, ...action.settings } };
|
|
213
|
-
}
|
|
214
|
-
// ---------------------------------------------------------------
|
|
215
|
-
// Processing (Phase 1) — dispatched by buildUploadPlan callbacks
|
|
216
|
-
// ---------------------------------------------------------------
|
|
217
|
-
case "UPDATE_PROCESSING_PROGRESS": {
|
|
218
|
-
return {
|
|
219
|
-
...state,
|
|
220
|
-
processingProgress: { ...state.processingProgress, ...action.progress }
|
|
221
|
-
};
|
|
222
|
-
}
|
|
223
|
-
case "ADD_PROCESSED_FONT": {
|
|
224
|
-
const { tempId, fontEntry } = action;
|
|
225
|
-
const fonts = { ...state.fonts };
|
|
226
|
-
if (fonts[tempId]) {
|
|
227
|
-
const existing = fonts[tempId];
|
|
228
|
-
const merged = { ...existing };
|
|
229
|
-
for (const field of PROCESSING_OWNED_FIELDS) {
|
|
230
|
-
merged[field] = fontEntry[field];
|
|
231
|
-
}
|
|
232
|
-
fonts[tempId] = merged;
|
|
233
|
-
} else {
|
|
234
|
-
fonts[tempId] = fontEntry;
|
|
235
|
-
}
|
|
236
|
-
const subfamilyGroups = { ...state.subfamilyGroups };
|
|
237
|
-
const sfName = fontEntry.subfamily || "default";
|
|
238
|
-
if (!fontEntry.variableFont || fontEntry.subfamily) {
|
|
239
|
-
if (!subfamilyGroups[sfName]) {
|
|
240
|
-
subfamilyGroups[sfName] = { title: sfName, fontIds: [] };
|
|
241
|
-
}
|
|
242
|
-
if (!subfamilyGroups[sfName].fontIds.includes(tempId)) {
|
|
243
|
-
subfamilyGroups[sfName] = {
|
|
244
|
-
...subfamilyGroups[sfName],
|
|
245
|
-
fontIds: [...subfamilyGroups[sfName].fontIds, tempId]
|
|
246
|
-
};
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
return {
|
|
250
|
-
...state,
|
|
251
|
-
fonts,
|
|
252
|
-
subfamilyGroups,
|
|
253
|
-
processingProgress: {
|
|
254
|
-
...state.processingProgress,
|
|
255
|
-
completed: state.processingProgress.completed + 1
|
|
256
|
-
}
|
|
257
|
-
};
|
|
258
|
-
}
|
|
259
|
-
case "SET_PROCESSING_ERROR": {
|
|
260
|
-
const { tempId, error } = action;
|
|
261
|
-
if (!state.fonts[tempId]) return state;
|
|
262
|
-
return {
|
|
263
|
-
...state,
|
|
264
|
-
fonts: {
|
|
265
|
-
...state.fonts,
|
|
266
|
-
[tempId]: { ...state.fonts[tempId], status: FONT_STATUS.ERROR, error }
|
|
267
|
-
},
|
|
268
|
-
processingProgress: {
|
|
269
|
-
...state.processingProgress,
|
|
270
|
-
failed: state.processingProgress.failed + 1
|
|
271
|
-
}
|
|
272
|
-
};
|
|
273
|
-
}
|
|
274
|
-
// ---------------------------------------------------------------
|
|
275
|
-
// User Edits (Review Step)
|
|
276
|
-
// ---------------------------------------------------------------
|
|
277
|
-
case "SET_FONT_TITLE": {
|
|
278
|
-
const { tempId, title, source: titleSource } = action;
|
|
279
|
-
const font = state.fonts[tempId];
|
|
280
|
-
if (!font) return state;
|
|
281
|
-
const newSource = titleSource || "user-override";
|
|
282
|
-
const updated = {
|
|
283
|
-
...font,
|
|
284
|
-
title,
|
|
285
|
-
decisions: {
|
|
286
|
-
...font.decisions,
|
|
287
|
-
title: { ...font.decisions.title, userOverride: title, source: newSource }
|
|
288
|
-
}
|
|
289
|
-
};
|
|
290
|
-
if (!font.decisions.documentId.userOverride) {
|
|
291
|
-
const newDocId = sanitizeForSanityId(title);
|
|
292
|
-
updated.documentId = newDocId;
|
|
293
|
-
updated.decisions = {
|
|
294
|
-
...updated.decisions,
|
|
295
|
-
documentId: { ...updated.decisions.documentId, generated: newDocId }
|
|
296
|
-
};
|
|
297
|
-
}
|
|
298
|
-
return updateFontAndCheckConflicts(state, tempId, updated);
|
|
299
|
-
}
|
|
300
|
-
case "SET_FONT_DOCUMENT_ID": {
|
|
301
|
-
const { tempId, documentId } = action;
|
|
302
|
-
const font = state.fonts[tempId];
|
|
303
|
-
if (!font) return state;
|
|
304
|
-
const sanitized = sanitizeForSanityId(documentId);
|
|
305
|
-
const updated = {
|
|
306
|
-
...font,
|
|
307
|
-
documentId: sanitized,
|
|
308
|
-
decisions: {
|
|
309
|
-
...font.decisions,
|
|
310
|
-
documentId: { ...font.decisions.documentId, userOverride: documentId }
|
|
311
|
-
}
|
|
312
|
-
};
|
|
313
|
-
return updateFontAndCheckConflicts(state, tempId, updated);
|
|
314
|
-
}
|
|
315
|
-
case "SET_FONT_WEIGHT": {
|
|
316
|
-
const { tempId, weight } = action;
|
|
317
|
-
const font = state.fonts[tempId];
|
|
318
|
-
if (!font) return state;
|
|
319
|
-
const clamped = Math.max(1, Math.min(1e3, weight));
|
|
320
|
-
return {
|
|
321
|
-
...state,
|
|
322
|
-
fonts: {
|
|
323
|
-
...state.fonts,
|
|
324
|
-
[tempId]: {
|
|
325
|
-
...font,
|
|
326
|
-
weight: clamped,
|
|
327
|
-
decisions: {
|
|
328
|
-
...font.decisions,
|
|
329
|
-
weight: { ...font.decisions.weight, userOverride: clamped }
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
};
|
|
334
|
-
}
|
|
335
|
-
case "SET_FONT_WEIGHT_NAME": {
|
|
336
|
-
const { tempId, weightName } = action;
|
|
337
|
-
const font = state.fonts[tempId];
|
|
338
|
-
if (!font) return state;
|
|
339
|
-
return {
|
|
340
|
-
...state,
|
|
341
|
-
fonts: {
|
|
342
|
-
...state.fonts,
|
|
343
|
-
[tempId]: {
|
|
344
|
-
...font,
|
|
345
|
-
weightName,
|
|
346
|
-
decisions: {
|
|
347
|
-
...font.decisions,
|
|
348
|
-
weightName: { ...font.decisions.weightName, userOverride: weightName }
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
};
|
|
353
|
-
}
|
|
354
|
-
case "SET_FONT_STYLE": {
|
|
355
|
-
const { tempId, style } = action;
|
|
356
|
-
const font = state.fonts[tempId];
|
|
357
|
-
if (!font) return state;
|
|
358
|
-
return {
|
|
359
|
-
...state,
|
|
360
|
-
fonts: {
|
|
361
|
-
...state.fonts,
|
|
362
|
-
[tempId]: {
|
|
363
|
-
...font,
|
|
364
|
-
style,
|
|
365
|
-
decisions: {
|
|
366
|
-
...font.decisions,
|
|
367
|
-
style: { ...font.decisions.style, userOverride: style }
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
};
|
|
372
|
-
}
|
|
373
|
-
case "SET_FONT_SUBFAMILY": {
|
|
374
|
-
const { tempId, subfamily } = action;
|
|
375
|
-
const font = state.fonts[tempId];
|
|
376
|
-
if (!font) return state;
|
|
377
|
-
const oldSubfamily = font.subfamily || "default";
|
|
378
|
-
const newSubfamily = subfamily || "default";
|
|
379
|
-
const updated = {
|
|
380
|
-
...font,
|
|
381
|
-
subfamily,
|
|
382
|
-
decisions: {
|
|
383
|
-
...font.decisions,
|
|
384
|
-
subfamily: { ...font.decisions.subfamily, userOverride: subfamily }
|
|
385
|
-
}
|
|
386
|
-
};
|
|
387
|
-
let newState = { ...state, fonts: { ...state.fonts, [tempId]: updated } };
|
|
388
|
-
if (oldSubfamily !== newSubfamily) {
|
|
389
|
-
newState = moveFontBetweenGroups(newState, tempId, oldSubfamily, newSubfamily);
|
|
390
|
-
}
|
|
391
|
-
return newState;
|
|
392
|
-
}
|
|
393
|
-
case "SET_FONT_ACTION": {
|
|
394
|
-
const { tempId, decision } = action;
|
|
395
|
-
const font = state.fonts[tempId];
|
|
396
|
-
if (!font) return state;
|
|
397
|
-
const existingDoc = { ...font.decisions.existingDocument, userChoice: decision };
|
|
398
|
-
if (decision === "create") {
|
|
399
|
-
existingDoc.selectedCandidate = null;
|
|
400
|
-
}
|
|
401
|
-
return {
|
|
402
|
-
...state,
|
|
403
|
-
fonts: {
|
|
404
|
-
...state.fonts,
|
|
405
|
-
[tempId]: {
|
|
406
|
-
...font,
|
|
407
|
-
decisions: { ...font.decisions, existingDocument: existingDoc }
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
};
|
|
411
|
-
}
|
|
412
|
-
case "SET_FONT_CANDIDATE": {
|
|
413
|
-
const { tempId, candidate } = action;
|
|
414
|
-
const font = state.fonts[tempId];
|
|
415
|
-
if (!font) return state;
|
|
416
|
-
return {
|
|
417
|
-
...state,
|
|
418
|
-
fonts: {
|
|
419
|
-
...state.fonts,
|
|
420
|
-
[tempId]: {
|
|
421
|
-
...font,
|
|
422
|
-
documentId: candidate._id,
|
|
423
|
-
decisions: {
|
|
424
|
-
...font.decisions,
|
|
425
|
-
existingDocument: {
|
|
426
|
-
...font.decisions.existingDocument,
|
|
427
|
-
selectedCandidate: candidate,
|
|
428
|
-
userChoice: "update"
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
};
|
|
434
|
-
}
|
|
435
|
-
// ---------------------------------------------------------------
|
|
436
|
-
// Subfamily Organization
|
|
437
|
-
// ---------------------------------------------------------------
|
|
438
|
-
case "MOVE_FONT_TO_SUBFAMILY": {
|
|
439
|
-
const { tempId, fromSubfamily, toSubfamily } = action;
|
|
440
|
-
const font = state.fonts[tempId];
|
|
441
|
-
if (!font) return state;
|
|
442
|
-
let newState = {
|
|
443
|
-
...state,
|
|
444
|
-
fonts: {
|
|
445
|
-
...state.fonts,
|
|
446
|
-
[tempId]: { ...font, subfamily: toSubfamily }
|
|
447
|
-
}
|
|
448
|
-
};
|
|
449
|
-
return moveFontBetweenGroups(newState, tempId, fromSubfamily, toSubfamily);
|
|
450
|
-
}
|
|
451
|
-
case "CREATE_SUBFAMILY_GROUP": {
|
|
452
|
-
const { title } = action;
|
|
453
|
-
if (state.subfamilyGroups[title]) return state;
|
|
454
|
-
return {
|
|
455
|
-
...state,
|
|
456
|
-
subfamilyGroups: {
|
|
457
|
-
...state.subfamilyGroups,
|
|
458
|
-
[title]: { title, fontIds: [] }
|
|
459
|
-
}
|
|
460
|
-
};
|
|
461
|
-
}
|
|
462
|
-
case "REMOVE_SUBFAMILY_GROUP": {
|
|
463
|
-
const { title } = action;
|
|
464
|
-
const group = state.subfamilyGroups[title];
|
|
465
|
-
if (!group) return state;
|
|
466
|
-
if (group.fontIds.length > 0) {
|
|
467
|
-
console.warn("Cannot remove subfamily group with fonts \u2014 reassign fonts first");
|
|
468
|
-
return state;
|
|
469
|
-
}
|
|
470
|
-
const { [title]: _, ...remaining } = state.subfamilyGroups;
|
|
471
|
-
return { ...state, subfamilyGroups: remaining };
|
|
472
|
-
}
|
|
473
|
-
// ---------------------------------------------------------------
|
|
474
|
-
// Bulk Actions
|
|
475
|
-
// ---------------------------------------------------------------
|
|
476
|
-
case "ACCEPT_ALL_SUGGESTIONS": {
|
|
477
|
-
const scope = action.scope || Object.keys(state.fonts);
|
|
478
|
-
const fonts = { ...state.fonts };
|
|
479
|
-
for (const tempId of scope) {
|
|
480
|
-
if (!fonts[tempId]) continue;
|
|
481
|
-
fonts[tempId] = resetFontToSuggestions(fonts[tempId]);
|
|
482
|
-
}
|
|
483
|
-
const subfamilyGroups = rebuildSubfamilyGroups(fonts);
|
|
484
|
-
return { ...state, fonts, subfamilyGroups };
|
|
485
|
-
}
|
|
486
|
-
case "RESET_FONT_TO_SUGGESTIONS": {
|
|
487
|
-
const { tempId } = action;
|
|
488
|
-
const font = state.fonts[tempId];
|
|
489
|
-
if (!font) return state;
|
|
490
|
-
const reset = resetFontToSuggestions(font);
|
|
491
|
-
const fonts = { ...state.fonts, [tempId]: reset };
|
|
492
|
-
const subfamilyGroups = rebuildSubfamilyGroups(fonts);
|
|
493
|
-
return { ...state, fonts, subfamilyGroups };
|
|
494
|
-
}
|
|
495
|
-
case "REMOVE_FONT": {
|
|
496
|
-
const { tempId } = action;
|
|
497
|
-
if (!state.fonts[tempId]) return state;
|
|
498
|
-
const { [tempId]: removed, ...remainingFonts } = state.fonts;
|
|
499
|
-
const subfamilyGroups = {};
|
|
500
|
-
for (const [key, group] of Object.entries(state.subfamilyGroups)) {
|
|
501
|
-
const filtered = group.fontIds.filter((id) => id !== tempId);
|
|
502
|
-
if (filtered.length > 0) {
|
|
503
|
-
subfamilyGroups[key] = { ...group, fontIds: filtered };
|
|
504
|
-
}
|
|
505
|
-
}
|
|
506
|
-
return { ...state, fonts: remainingFonts, subfamilyGroups };
|
|
507
|
-
}
|
|
508
|
-
default:
|
|
509
|
-
return state;
|
|
510
|
-
}
|
|
511
|
-
}
|
|
512
|
-
function resetFontToSuggestions(font) {
|
|
513
|
-
const d = font.decisions;
|
|
514
|
-
return {
|
|
515
|
-
...font,
|
|
516
|
-
title: d.title.processed,
|
|
517
|
-
documentId: d.documentId.generated,
|
|
518
|
-
weight: d.weight.detected,
|
|
519
|
-
weightName: d.weightName.detected,
|
|
520
|
-
style: d.style.detected,
|
|
521
|
-
subfamily: d.subfamily.detected,
|
|
522
|
-
_idConflict: false,
|
|
523
|
-
decisions: {
|
|
524
|
-
...d,
|
|
525
|
-
title: { ...d.title, userOverride: null, source: d.title.original ? d.title.source : d.title.source },
|
|
526
|
-
documentId: { ...d.documentId, userOverride: null },
|
|
527
|
-
weight: { ...d.weight, userOverride: null },
|
|
528
|
-
weightName: { ...d.weightName, userOverride: null },
|
|
529
|
-
style: { ...d.style, userOverride: null },
|
|
530
|
-
subfamily: { ...d.subfamily, userOverride: null },
|
|
531
|
-
existingDocument: { ...d.existingDocument, userChoice: null, selectedCandidate: null }
|
|
532
|
-
}
|
|
533
|
-
};
|
|
534
|
-
}
|
|
535
|
-
function moveFontBetweenGroups(state, tempId, fromKey, toKey) {
|
|
536
|
-
const groups = { ...state.subfamilyGroups };
|
|
537
|
-
if (groups[fromKey]) {
|
|
538
|
-
const filtered = groups[fromKey].fontIds.filter((id) => id !== tempId);
|
|
539
|
-
if (filtered.length === 0) {
|
|
540
|
-
delete groups[fromKey];
|
|
541
|
-
} else {
|
|
542
|
-
groups[fromKey] = { ...groups[fromKey], fontIds: filtered };
|
|
543
|
-
}
|
|
544
|
-
}
|
|
545
|
-
if (!groups[toKey]) {
|
|
546
|
-
groups[toKey] = { title: toKey, fontIds: [] };
|
|
547
|
-
}
|
|
548
|
-
if (!groups[toKey].fontIds.includes(tempId)) {
|
|
549
|
-
groups[toKey] = { ...groups[toKey], fontIds: [...groups[toKey].fontIds, tempId] };
|
|
550
|
-
}
|
|
551
|
-
return { ...state, subfamilyGroups: groups };
|
|
552
|
-
}
|
|
553
|
-
function updateFontAndCheckConflicts(state, tempId, updatedFont) {
|
|
554
|
-
const fonts = { ...state.fonts, [tempId]: updatedFont };
|
|
555
|
-
const idMap = {};
|
|
556
|
-
for (const [id, font] of Object.entries(fonts)) {
|
|
557
|
-
fonts[id] = { ...font, _idConflict: false };
|
|
558
|
-
const docId = font.documentId;
|
|
559
|
-
if (!idMap[docId]) {
|
|
560
|
-
idMap[docId] = [id];
|
|
561
|
-
} else {
|
|
562
|
-
idMap[docId].push(id);
|
|
563
|
-
}
|
|
564
|
-
}
|
|
565
|
-
for (const ids of Object.values(idMap)) {
|
|
566
|
-
if (ids.length > 1) {
|
|
567
|
-
for (const id of ids) {
|
|
568
|
-
fonts[id] = { ...fonts[id], _idConflict: true };
|
|
569
|
-
}
|
|
570
|
-
}
|
|
571
|
-
}
|
|
572
|
-
return { ...state, fonts };
|
|
573
|
-
}
|
|
574
|
-
function rebuildSubfamilyGroups(fonts) {
|
|
575
|
-
const groups = {};
|
|
576
|
-
for (const [tempId, font] of Object.entries(fonts)) {
|
|
577
|
-
if (font.status === FONT_STATUS.ERROR) continue;
|
|
578
|
-
const sfName = font.subfamily || "default";
|
|
579
|
-
if (!font.variableFont || font.subfamily) {
|
|
580
|
-
if (!groups[sfName]) {
|
|
581
|
-
groups[sfName] = { title: sfName, fontIds: [] };
|
|
582
|
-
}
|
|
583
|
-
if (!groups[sfName].fontIds.includes(tempId)) {
|
|
584
|
-
groups[sfName].fontIds.push(tempId);
|
|
585
|
-
}
|
|
586
|
-
}
|
|
587
|
-
}
|
|
588
|
-
return groups;
|
|
589
|
-
}
|
|
590
|
-
|
|
591
|
-
// src/utils/buildUploadPlan.js
|
|
592
|
-
import { nanoid as nanoid2 } from "nanoid";
|
|
593
|
-
|
|
594
|
-
// src/utils/parseFont.js
|
|
595
|
-
var _Font = null;
|
|
596
|
-
async function getFont() {
|
|
597
|
-
if (!_Font) {
|
|
598
|
-
const pako = await import("pako");
|
|
599
|
-
globalThis.pako = pako.default || pako;
|
|
600
|
-
await import("./unbrotli-CTU3FKHW.mjs");
|
|
601
|
-
const mod = await import("lib-font");
|
|
602
|
-
_Font = mod.Font;
|
|
603
|
-
}
|
|
604
|
-
return _Font;
|
|
605
|
-
}
|
|
606
|
-
var MAX_FONT_FILE_SIZE = 50 * 1024 * 1024;
|
|
607
|
-
async function parseFont(buffer, filename) {
|
|
608
|
-
if (buffer.byteLength > MAX_FONT_FILE_SIZE) {
|
|
609
|
-
throw new Error(`Font file exceeds ${MAX_FONT_FILE_SIZE / 1024 / 1024}MB limit: ${filename} (${(buffer.byteLength / 1024 / 1024).toFixed(1)}MB)`);
|
|
610
|
-
}
|
|
611
|
-
const Font = await getFont();
|
|
612
|
-
return new Promise((resolve, reject) => {
|
|
613
|
-
const font = new Font("font", { skipStyleSheet: true });
|
|
614
|
-
font.onload = (evt) => resolve(evt.detail.font);
|
|
615
|
-
font.onerror = (evt) => {
|
|
616
|
-
var _a;
|
|
617
|
-
return reject(new Error(((_a = evt.detail) == null ? void 0 : _a.message) || `Failed to parse ${filename}`));
|
|
618
|
-
};
|
|
619
|
-
font.fromDataBuffer(buffer, filename);
|
|
620
|
-
});
|
|
621
|
-
}
|
|
622
|
-
|
|
623
|
-
// src/utils/fontHelpers.js
|
|
624
|
-
var nameCache = /* @__PURE__ */ new WeakMap();
|
|
625
|
-
function getNameString(font, nameID) {
|
|
626
|
-
var _a, _b, _c;
|
|
627
|
-
if (!nameCache.has(font)) nameCache.set(font, {});
|
|
628
|
-
const cache = nameCache.get(font);
|
|
629
|
-
if (nameID in cache) return cache[nameID];
|
|
630
|
-
const records = ((_c = (_b = (_a = font.opentype) == null ? void 0 : _a.tables) == null ? void 0 : _b.name) == null ? void 0 : _c.nameRecords) || [];
|
|
631
|
-
const win = records.find((r) => r.nameID === nameID && r.platformID === 3 && r.languageID === 1033);
|
|
632
|
-
if (win == null ? void 0 : win.string) {
|
|
633
|
-
cache[nameID] = win.string;
|
|
634
|
-
return win.string;
|
|
635
|
-
}
|
|
636
|
-
const mac = records.find((r) => r.nameID === nameID && r.platformID === 1 && r.languageID === 0);
|
|
637
|
-
if (mac == null ? void 0 : mac.string) {
|
|
638
|
-
cache[nameID] = mac.string;
|
|
639
|
-
return mac.string;
|
|
640
|
-
}
|
|
641
|
-
const any = records.find((r) => r.nameID === nameID);
|
|
642
|
-
const result = (any == null ? void 0 : any.string) || "";
|
|
643
|
-
cache[nameID] = result;
|
|
644
|
-
return result;
|
|
645
|
-
}
|
|
646
|
-
function getAllFeatureTags(font) {
|
|
647
|
-
var _a;
|
|
648
|
-
const tags = /* @__PURE__ */ new Set();
|
|
649
|
-
const tables = (_a = font.opentype) == null ? void 0 : _a.tables;
|
|
650
|
-
for (const layoutTable of [tables == null ? void 0 : tables.GSUB, tables == null ? void 0 : tables.GPOS]) {
|
|
651
|
-
if (!layoutTable) continue;
|
|
652
|
-
try {
|
|
653
|
-
for (const scriptTag of layoutTable.getSupportedScripts()) {
|
|
654
|
-
const script = layoutTable.getScriptTable(scriptTag);
|
|
655
|
-
for (const langTag of layoutTable.getSupportedLangSys(script)) {
|
|
656
|
-
const langsys = layoutTable.getLangSysTable(script, langTag);
|
|
657
|
-
for (const feature of layoutTable.getFeatures(langsys)) {
|
|
658
|
-
tags.add(feature.featureTag.trim());
|
|
659
|
-
}
|
|
660
|
-
}
|
|
661
|
-
}
|
|
662
|
-
} catch (err) {
|
|
663
|
-
console.warn(`Error reading ${layoutTable === tables.GSUB ? "GSUB" : "GPOS"} features:`, err.message);
|
|
664
|
-
}
|
|
665
|
-
}
|
|
666
|
-
return [...tags];
|
|
667
|
-
}
|
|
668
|
-
function getCharacterSet(font) {
|
|
669
|
-
var _a, _b;
|
|
670
|
-
const cmap = (_b = (_a = font.opentype) == null ? void 0 : _a.tables) == null ? void 0 : _b.cmap;
|
|
671
|
-
if (!cmap) return [];
|
|
672
|
-
try {
|
|
673
|
-
return cmap.getSupportedCharCodes(3, 1);
|
|
674
|
-
} catch {
|
|
675
|
-
return [];
|
|
676
|
-
}
|
|
677
|
-
}
|
|
678
|
-
function getVariationAxes(font) {
|
|
679
|
-
var _a, _b;
|
|
680
|
-
const fvar = (_b = (_a = font.opentype) == null ? void 0 : _a.tables) == null ? void 0 : _b.fvar;
|
|
681
|
-
if (!(fvar == null ? void 0 : fvar.axes)) return null;
|
|
682
|
-
const axes = {};
|
|
683
|
-
for (const axis of fvar.axes) {
|
|
684
|
-
if (axis.minValue === axis.maxValue) continue;
|
|
685
|
-
axes[axis.tag] = {
|
|
686
|
-
min: axis.minValue,
|
|
687
|
-
max: axis.maxValue,
|
|
688
|
-
default: axis.defaultValue,
|
|
689
|
-
name: getNameString(font, axis.axisNameID) || axis.tag
|
|
690
|
-
};
|
|
691
|
-
}
|
|
692
|
-
return Object.keys(axes).length > 0 ? axes : null;
|
|
693
|
-
}
|
|
694
|
-
function getNamedInstances(font) {
|
|
695
|
-
var _a, _b;
|
|
696
|
-
const fvar = (_b = (_a = font.opentype) == null ? void 0 : _a.tables) == null ? void 0 : _b.fvar;
|
|
697
|
-
if (!(fvar == null ? void 0 : fvar.instances)) return [];
|
|
698
|
-
return fvar.instances.map((inst) => ({
|
|
699
|
-
name: getNameString(font, inst.subfamilyNameID),
|
|
700
|
-
coordinates: inst.coordinates,
|
|
701
|
-
postScriptName: getNameString(font, inst.postScriptNameID || 0)
|
|
702
|
-
}));
|
|
703
|
-
}
|
|
704
|
-
function getFontMetrics(font) {
|
|
705
|
-
var _a;
|
|
706
|
-
const tables = (_a = font.opentype) == null ? void 0 : _a.tables;
|
|
707
|
-
const os2 = tables == null ? void 0 : tables["OS/2"];
|
|
708
|
-
const head = tables == null ? void 0 : tables.head;
|
|
709
|
-
const post = tables == null ? void 0 : tables.post;
|
|
710
|
-
const hhea = tables == null ? void 0 : tables.hhea;
|
|
711
|
-
const useTypo = os2 ? (os2.fsSelection & 128) !== 0 : false;
|
|
712
|
-
return {
|
|
713
|
-
unitsPerEm: (head == null ? void 0 : head.unitsPerEm) || 1e3,
|
|
714
|
-
ascender: useTypo ? (os2 == null ? void 0 : os2.sTypoAscender) || 0 : (hhea == null ? void 0 : hhea.ascender) ?? (os2 == null ? void 0 : os2.sTypoAscender) ?? 0,
|
|
715
|
-
descender: useTypo ? (os2 == null ? void 0 : os2.sTypoDescender) || 0 : (hhea == null ? void 0 : hhea.descender) ?? (os2 == null ? void 0 : os2.sTypoDescender) ?? 0,
|
|
716
|
-
lineGap: useTypo ? (os2 == null ? void 0 : os2.sTypoLineGap) || 0 : (hhea == null ? void 0 : hhea.lineGap) ?? (os2 == null ? void 0 : os2.sTypoLineGap) ?? 0,
|
|
717
|
-
underlinePosition: (post == null ? void 0 : post.underlinePosition) || 0,
|
|
718
|
-
underlineThickness: (post == null ? void 0 : post.underlineThickness) || 0,
|
|
719
|
-
italicAngle: (post == null ? void 0 : post.italicAngle) || 0,
|
|
720
|
-
capHeight: (os2 == null ? void 0 : os2.version) >= 2 ? (os2 == null ? void 0 : os2.sCapHeight) || 0 : 0,
|
|
721
|
-
xHeight: (os2 == null ? void 0 : os2.version) >= 2 ? (os2 == null ? void 0 : os2.sxHeight) || 0 : 0,
|
|
722
|
-
boundingBox: {
|
|
723
|
-
xMin: (head == null ? void 0 : head.xMin) || 0,
|
|
724
|
-
yMin: (head == null ? void 0 : head.yMin) || 0,
|
|
725
|
-
xMax: (head == null ? void 0 : head.xMax) || 0,
|
|
726
|
-
yMax: (head == null ? void 0 : head.yMax) || 0
|
|
727
|
-
}
|
|
728
|
-
};
|
|
729
|
-
}
|
|
730
|
-
function getFontMetadata(font) {
|
|
731
|
-
return {
|
|
732
|
-
postscriptName: getNameString(font, 6),
|
|
733
|
-
fullName: getNameString(font, 4),
|
|
734
|
-
familyName: getNameString(font, 1),
|
|
735
|
-
subfamilyName: getNameString(font, 2),
|
|
736
|
-
copyright: getNameString(font, 0),
|
|
737
|
-
version: getNameString(font, 5),
|
|
738
|
-
genDate: (/* @__PURE__ */ new Date()).toISOString()
|
|
739
|
-
};
|
|
740
|
-
}
|
|
741
|
-
function getWeightClass(font) {
|
|
742
|
-
var _a, _b, _c;
|
|
743
|
-
return ((_c = (_b = (_a = font.opentype) == null ? void 0 : _a.tables) == null ? void 0 : _b["OS/2"]) == null ? void 0 : _c.usWeightClass) || null;
|
|
744
|
-
}
|
|
745
|
-
function getFsSelection(font) {
|
|
746
|
-
var _a, _b, _c;
|
|
747
|
-
return ((_c = (_b = (_a = font.opentype) == null ? void 0 : _a.tables) == null ? void 0 : _b["OS/2"]) == null ? void 0 : _c.fsSelection) || 0;
|
|
748
|
-
}
|
|
749
|
-
function getMacStyle(font) {
|
|
750
|
-
var _a, _b, _c;
|
|
751
|
-
const macStyle = (_c = (_b = (_a = font.opentype) == null ? void 0 : _a.tables) == null ? void 0 : _b.head) == null ? void 0 : _c.macStyle;
|
|
752
|
-
if (!macStyle) return 0;
|
|
753
|
-
if (typeof macStyle === "number") return macStyle;
|
|
754
|
-
if (typeof macStyle === "object") {
|
|
755
|
-
let value = 0;
|
|
756
|
-
for (let i = 0; i < 16; i++) {
|
|
757
|
-
if (macStyle[i]) value |= 1 << 15 - i;
|
|
758
|
-
}
|
|
759
|
-
return value;
|
|
760
|
-
}
|
|
761
|
-
return 0;
|
|
762
|
-
}
|
|
763
|
-
function getItalicAngle(font) {
|
|
764
|
-
var _a, _b, _c;
|
|
765
|
-
return ((_c = (_b = (_a = font.opentype) == null ? void 0 : _a.tables) == null ? void 0 : _b.post) == null ? void 0 : _c.italicAngle) || 0;
|
|
766
|
-
}
|
|
767
|
-
function getGlyphCount(font) {
|
|
768
|
-
var _a, _b, _c;
|
|
769
|
-
return ((_c = (_b = (_a = font.opentype) == null ? void 0 : _a.tables) == null ? void 0 : _b.maxp) == null ? void 0 : _c.numGlyphs) || 0;
|
|
770
|
-
}
|
|
771
|
-
function getFamilyClass(font) {
|
|
772
|
-
var _a, _b, _c;
|
|
773
|
-
return ((_c = (_b = (_a = font.opentype) == null ? void 0 : _a.tables) == null ? void 0 : _b["OS/2"]) == null ? void 0 : _c.sFamilyClass) || 0;
|
|
774
|
-
}
|
|
775
|
-
function escapeCssFontName(name) {
|
|
776
|
-
return name.replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/"/g, '\\"').replace(/;/g, "");
|
|
777
|
-
}
|
|
778
|
-
|
|
779
|
-
// src/utils/processFontFiles.js
|
|
780
|
-
import { nanoid } from "nanoid";
|
|
781
|
-
|
|
782
|
-
// src/utils/generateKeywords.js
|
|
783
|
-
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"];
|
|
784
|
-
var modifiers = ["Demi", "Semi", "Extra", "Ultra", "Super", "Plus"];
|
|
785
|
-
var coreItalics = ["Italic", "Slant", "Oblique", "Cursive", "Rotalic", "Reverse", "Crab Claw", "Crabclaw", "South Paw", "Southpaw", "Backwards", "Backslant", "Backslanted", "Back Slant"];
|
|
786
|
-
var alternativeSpelling = {
|
|
787
|
-
Backslant: ["Bsl"],
|
|
788
|
-
Backwards: ["Bck"],
|
|
789
|
-
Black: ["Blak", "Blk"],
|
|
790
|
-
Bold: ["Bd", "Bld"],
|
|
791
|
-
// B omitted — too ambiguous
|
|
792
|
-
Condensed: ["Cond", "Cnd"],
|
|
793
|
-
Crabclaw: ["Crab", "Claw"],
|
|
794
|
-
Cursive: ["Cur"],
|
|
795
|
-
Dark: ["Drk"],
|
|
796
|
-
Expanded: ["Exp"],
|
|
797
|
-
Extra: ["Xt", "Xtra", "Xtr", "X"],
|
|
798
|
-
// X omitted as standalone — too ambiguous
|
|
799
|
-
ExtraBlack: ["Xblk", "XBlk", "Xblck", "XBlck"],
|
|
800
|
-
ExtraBold: ["Xbd", "XBd", "Xbld", "XBld", "Xbold", "XBold", "ExBold", "Exbold", "Exbd", "ExBd", "Exbld", "ExBld"],
|
|
801
|
-
ExtraCondensed: ["XCond", "Xcnd"],
|
|
802
|
-
ExtraExpanded: ["XExp"],
|
|
803
|
-
ExtraLight: ["Xlight", "XLight", "Xlt", "XLt", "Xlgt", "XLgt", "Xl", "XL", "Xlght", "XLght"],
|
|
804
|
-
ExtraThin: ["Xthin", "Xthn", "Xth", "XThin", "XThn", "XTh", "XT"],
|
|
805
|
-
Extended: ["Ext"],
|
|
806
|
-
Hairline: ["Hl", "Hln", "Hlnn", "Hlnne", "Hlnnne"],
|
|
807
|
-
Italic: ["Ital", "It"],
|
|
808
|
-
Light: ["Lt", "Lght"],
|
|
809
|
-
Medium: ["Med", "Md", "md", "med"],
|
|
810
|
-
Oblique: ["Obl"],
|
|
811
|
-
Plus: ["Pls"],
|
|
812
|
-
Regular: ["Reg", "Rg"],
|
|
813
|
-
Reverse: ["Rev"],
|
|
814
|
-
Rotalic: ["Rot"],
|
|
815
|
-
SemiBold: ["SmBd", "Sb", "Sbd", "Sbld", "Sbold", "Semibd", "SemiBd", "Semibld", "SemiBld", "semiBd", "semiBld"],
|
|
816
|
-
Slant: ["Sl"],
|
|
817
|
-
Southpaw: ["South", "Paw"],
|
|
818
|
-
Super: ["Supr"],
|
|
819
|
-
Thin: ["Thn"],
|
|
820
|
-
Ultra: ["Ult", "Ultre", "Ul", "Ulta"],
|
|
821
|
-
XX: ["XXt", "XXtra", "XXtr", "XX"],
|
|
822
|
-
XXBlack: ["XXblk", "XXBlk", "XXblck", "XXBlck"],
|
|
823
|
-
XXLight: ["XXlight", "XXLight", "XXlt", "XXLt", "XXlgt", "XXLgt", "XXl", "XXL", "XXlght", "XXLght"],
|
|
824
|
-
XXX: ["XXXt", "XXXtra", "XXXtr", "XXX"],
|
|
825
|
-
XXXLight: ["XXXlight", "XXXLight", "XXXlt", "XXXLt", "XXXlgt", "XXXLgt", "XXXl", "XXXL", "XXXlght", "XXXLght"]
|
|
826
|
-
};
|
|
827
|
-
function reverseSpellingLookup(str) {
|
|
828
|
-
let exactMatch = "";
|
|
829
|
-
Object.keys(alternativeSpelling).forEach(function(key) {
|
|
830
|
-
alternativeSpelling[key].forEach(function(alternative) {
|
|
831
|
-
if (str === alternative) {
|
|
832
|
-
exactMatch = key;
|
|
833
|
-
}
|
|
834
|
-
});
|
|
835
|
-
});
|
|
836
|
-
if (exactMatch) return exactMatch;
|
|
837
|
-
let result = "";
|
|
838
|
-
let longestMatch = 0;
|
|
839
|
-
Object.keys(alternativeSpelling).forEach(function(key) {
|
|
840
|
-
alternativeSpelling[key].forEach(function(alternative) {
|
|
841
|
-
const regex = new RegExp(`\\b${alternative}\\b`);
|
|
842
|
-
if (regex.test(str) && alternative.length > longestMatch) {
|
|
843
|
-
result = key;
|
|
844
|
-
longestMatch = alternative.length;
|
|
845
|
-
}
|
|
846
|
-
});
|
|
847
|
-
});
|
|
848
|
-
return result;
|
|
849
|
-
}
|
|
850
|
-
function expandAbbreviations(str) {
|
|
851
|
-
if (!str) return str;
|
|
852
|
-
return str.split(" ").map((word) => {
|
|
853
|
-
const expanded = reverseSpellingLookup(word);
|
|
854
|
-
return expanded || word;
|
|
855
|
-
}).join(" ");
|
|
856
|
-
}
|
|
857
|
-
function removeWeightNames(str) {
|
|
858
|
-
if (!str) return str;
|
|
859
|
-
return str.split(" ").map((word) => {
|
|
860
|
-
coreWeights.forEach((weight) => {
|
|
861
|
-
if (word === weight) word = "";
|
|
862
|
-
modifiers.forEach((modifier) => {
|
|
863
|
-
if (word === modifier || modifier + weight === word) word = "";
|
|
864
|
-
});
|
|
865
|
-
});
|
|
866
|
-
const expanded = reverseSpellingLookup(word);
|
|
867
|
-
if (expanded) return "";
|
|
868
|
-
return word;
|
|
869
|
-
}).join(" ").trim();
|
|
870
|
-
}
|
|
871
|
-
function generateStyleKeywords() {
|
|
872
|
-
let weightKeywordList = [];
|
|
873
|
-
let italicKeywordList = [];
|
|
874
|
-
weightKeywordList = [...coreWeights];
|
|
875
|
-
modifiers.forEach((modifier) => {
|
|
876
|
-
coreWeights.forEach((weight) => {
|
|
877
|
-
weightKeywordList.push(modifier + weight);
|
|
878
|
-
});
|
|
879
|
-
});
|
|
880
|
-
weightKeywordList = [...weightKeywordList, ...modifiers];
|
|
881
|
-
italicKeywordList = [...coreItalics];
|
|
882
|
-
weightKeywordList = weightKeywordList.map(function(el) {
|
|
883
|
-
var newEls = [];
|
|
884
|
-
Object.keys(alternativeSpelling).forEach(function(key) {
|
|
885
|
-
if (el.indexOf(key) !== -1) {
|
|
886
|
-
alternativeSpelling[key].forEach(function(alternative) {
|
|
887
|
-
let newSpelling = el.replace(key, alternative);
|
|
888
|
-
newEls.push(newSpelling);
|
|
889
|
-
Object.keys(alternativeSpelling).forEach(function(key2) {
|
|
890
|
-
if (newSpelling.indexOf(key2) !== -1) {
|
|
891
|
-
alternativeSpelling[key2].forEach(function(alternative2) {
|
|
892
|
-
let newSpelling2 = newSpelling.replace(key2, alternative2);
|
|
893
|
-
newEls.push(newSpelling2);
|
|
894
|
-
Object.keys(alternativeSpelling).forEach(function(key3) {
|
|
895
|
-
if (newSpelling2.indexOf(key3) !== -1) {
|
|
896
|
-
alternativeSpelling[key3].forEach(function(alternative3) {
|
|
897
|
-
newEls.push(newSpelling2.replace(key3, alternative3));
|
|
898
|
-
});
|
|
899
|
-
}
|
|
900
|
-
});
|
|
901
|
-
});
|
|
902
|
-
}
|
|
903
|
-
});
|
|
904
|
-
});
|
|
905
|
-
}
|
|
906
|
-
});
|
|
907
|
-
newEls.push(el);
|
|
908
|
-
return newEls;
|
|
909
|
-
}).reduce(function(a, b) {
|
|
910
|
-
return a.concat(b);
|
|
911
|
-
});
|
|
912
|
-
italicKeywordList = italicKeywordList.map(function(el) {
|
|
913
|
-
var newEls = [];
|
|
914
|
-
Object.keys(alternativeSpelling).forEach(function(key) {
|
|
915
|
-
if (el.indexOf(key) !== -1) {
|
|
916
|
-
alternativeSpelling[key].forEach(function(alternative) {
|
|
917
|
-
newEls.push(el.replace(key, alternative));
|
|
918
|
-
});
|
|
919
|
-
}
|
|
920
|
-
});
|
|
921
|
-
newEls.push(el);
|
|
922
|
-
return newEls;
|
|
923
|
-
}).reduce(function(a, b) {
|
|
924
|
-
return a.concat(b);
|
|
925
|
-
});
|
|
926
|
-
weightKeywordList = weightKeywordList.sort((a, b) => b.length - a.length);
|
|
927
|
-
italicKeywordList = italicKeywordList.sort((a, b) => b.length - a.length);
|
|
928
|
-
weightKeywordList = weightKeywordList.filter((item, pos) => weightKeywordList.indexOf(item) === pos);
|
|
929
|
-
italicKeywordList = italicKeywordList.filter((item, pos) => italicKeywordList.indexOf(item) === pos);
|
|
930
|
-
return { weightKeywordList, italicKeywordList };
|
|
931
|
-
}
|
|
932
|
-
|
|
933
|
-
// src/utils/processFontFiles.js
|
|
934
|
-
var readFontFile = (file) => {
|
|
935
|
-
return new Promise((resolve, reject) => {
|
|
936
|
-
const reader = new FileReader();
|
|
937
|
-
reader.onload = (event) => {
|
|
938
|
-
resolve(event.target.result);
|
|
939
|
-
};
|
|
940
|
-
reader.onerror = (error) => {
|
|
941
|
-
reject(error);
|
|
942
|
-
};
|
|
943
|
-
reader.readAsArrayBuffer(file);
|
|
944
|
-
});
|
|
945
|
-
};
|
|
946
|
-
var processFontFiles = async (files, title, weightKeywordList, italicKeywordList, setStatus, preserveShortenedNames = false, preserveFileNames = false) => {
|
|
947
|
-
let failedFiles = [];
|
|
948
|
-
let subfamilies = {};
|
|
949
|
-
let fontsObjects = {};
|
|
950
|
-
let newPreferredStyle = { weight: -100, style: "Italic", _ref: "" };
|
|
951
|
-
for (let i = 0; i < files.length; i++) {
|
|
952
|
-
const file = files[i];
|
|
953
|
-
const fontBuffer = await readFontFile(file);
|
|
954
|
-
const font = await parseFont(fontBuffer, file.name);
|
|
955
|
-
console.log("File name:", file.name);
|
|
956
|
-
const ttfFallbackMeta = await getWebfontFallbackMetadata(file, font, files);
|
|
957
|
-
let { weightName, subfamilyName, fontTitle, style, italicKW, variableFont } = extractFontMetadata(
|
|
958
|
-
font,
|
|
959
|
-
title,
|
|
960
|
-
weightKeywordList,
|
|
961
|
-
italicKeywordList,
|
|
962
|
-
preserveShortenedNames,
|
|
963
|
-
ttfFallbackMeta
|
|
964
|
-
);
|
|
965
|
-
let id;
|
|
966
|
-
let originalFilename = null;
|
|
967
|
-
if (preserveFileNames) {
|
|
968
|
-
originalFilename = file.name.replace(/\.(ttf|otf|woff2?|eot|svg)$/i, "");
|
|
969
|
-
const normalizedName = originalFilename.replace(/-/g, " ").replace(/([a-z])([A-Z])/g, "$1 $2").replace(/\s+/g, " ").trim();
|
|
970
|
-
fontTitle = normalizedName;
|
|
971
|
-
id = sanitizeForSanityId(normalizedName);
|
|
972
|
-
} else {
|
|
973
|
-
id = sanitizeForSanityId(fontTitle);
|
|
974
|
-
}
|
|
975
|
-
logFontInfo(id, fontTitle, font, file.name, subfamilyName, style, weightName, variableFont, italicKW);
|
|
976
|
-
subfamilies[id] = subfamilyName;
|
|
977
|
-
if (fontsObjects[id]) {
|
|
978
|
-
fontsObjects[id].files = [...fontsObjects[id].files, file];
|
|
979
|
-
if (preserveFileNames && originalFilename) {
|
|
980
|
-
fontsObjects[id].originalFilename = originalFilename;
|
|
981
|
-
}
|
|
982
|
-
} else {
|
|
983
|
-
fontsObjects[id] = createFontObject(
|
|
984
|
-
id,
|
|
985
|
-
fontTitle,
|
|
986
|
-
title,
|
|
987
|
-
font,
|
|
988
|
-
variableFont,
|
|
989
|
-
weightName,
|
|
990
|
-
subfamilyName,
|
|
991
|
-
file,
|
|
992
|
-
preserveFileNames ? originalFilename : null
|
|
993
|
-
);
|
|
994
|
-
}
|
|
995
|
-
}
|
|
996
|
-
fontsObjects = sortFontObjects(fontsObjects);
|
|
997
|
-
const uniqueSubfamilies = [...new Set(Object.values(subfamilies))];
|
|
998
|
-
console.log("Subfamilies:", subfamilies);
|
|
999
|
-
console.log("Unique subfamilies:", uniqueSubfamilies, uniqueSubfamilies.length);
|
|
1000
|
-
console.log("Font objects:", Object.keys(fontsObjects));
|
|
1001
|
-
return { fontsObjects, subfamilies, uniqueSubfamilies, newPreferredStyle, failedFiles };
|
|
1002
|
-
};
|
|
1003
|
-
var getWebfontFallbackMetadata = async (file, font, files) => {
|
|
1004
|
-
if (!file.name.endsWith(".woff2") && !file.name.endsWith(".woff")) return null;
|
|
1005
|
-
const fullName = getNameString(font, 4);
|
|
1006
|
-
if (fullName && fullName !== "" && !/^[A-Z0-9]+$/.test(fullName)) return null;
|
|
1007
|
-
const ttfFile = files.find((f) => f.name === file.name.replace(".woff2", ".ttf").replace(".woff", ".ttf"));
|
|
1008
|
-
if (!ttfFile) return null;
|
|
1009
|
-
try {
|
|
1010
|
-
const ttfBuffer = await readFontFile(ttfFile);
|
|
1011
|
-
const ttfFont = await parseFont(ttfBuffer, ttfFile.name);
|
|
1012
|
-
return {
|
|
1013
|
-
fullName: getNameString(ttfFont, 4),
|
|
1014
|
-
familyName: getNameString(ttfFont, 1),
|
|
1015
|
-
subfamilyName: getNameString(ttfFont, 2),
|
|
1016
|
-
preferredSubfamily: getNameString(ttfFont, 17),
|
|
1017
|
-
preferredFamily: getNameString(ttfFont, 16)
|
|
1018
|
-
};
|
|
1019
|
-
} catch (err) {
|
|
1020
|
-
console.warn("Could not parse TTF companion for webfont fallback:", err.message);
|
|
1021
|
-
return null;
|
|
1022
|
-
}
|
|
1023
|
-
};
|
|
1024
|
-
var extractFontMetadata = (font, title, weightKeywordList, italicKeywordList, preserveShortenedNames = false, ttfFallbackMeta = null) => {
|
|
1025
|
-
let weightName = extractWeightName(font, italicKeywordList, ttfFallbackMeta);
|
|
1026
|
-
if (!preserveShortenedNames) {
|
|
1027
|
-
weightName = expandAbbreviations(weightName);
|
|
1028
|
-
}
|
|
1029
|
-
const fullName = getNameString(font, 4) || (ttfFallbackMeta == null ? void 0 : ttfFallbackMeta.fullName) || "";
|
|
1030
|
-
const axes = getVariationAxes(font);
|
|
1031
|
-
const variableFont = axes !== null;
|
|
1032
|
-
if (!variableFont && (weightName === "" || weightName.toLowerCase() === "roman") && fullName) {
|
|
1033
|
-
weightName = extractWeightFromFullName(font, title, ttfFallbackMeta);
|
|
1034
|
-
if (!preserveShortenedNames) {
|
|
1035
|
-
weightName = expandAbbreviations(weightName);
|
|
1036
|
-
}
|
|
1037
|
-
}
|
|
1038
|
-
const trimmedTitle = title.trim();
|
|
1039
|
-
const nameId4Remainder = fullName ? fullName.replace(trimmedTitle, "").trim() : "";
|
|
1040
|
-
const nameId1 = getNameString(font, 1) || (ttfFallbackMeta == null ? void 0 : ttfFallbackMeta.familyName) || "";
|
|
1041
|
-
const nameId1Remainder = nameId1 ? nameId1.replace(trimmedTitle, "").trim() : "";
|
|
1042
|
-
let subfamilyName = nameId4Remainder || nameId1Remainder;
|
|
1043
|
-
if (!preserveShortenedNames) {
|
|
1044
|
-
subfamilyName = expandAbbreviations(subfamilyName);
|
|
1045
|
-
}
|
|
1046
|
-
let fontTitle = fullName.trim() || "";
|
|
1047
|
-
const italicAngle = getItalicAngle(font);
|
|
1048
|
-
let style = italicAngle !== 0 || fullName.toLowerCase().includes("italic") ? "Italic" : "Regular";
|
|
1049
|
-
const italicKW = processItalicKeywords(font, fontTitle, italicKeywordList);
|
|
1050
|
-
subfamilyName = processSubfamilyName(subfamilyName, weightKeywordList, italicKW, preserveShortenedNames);
|
|
1051
|
-
fontTitle = formatFontTitle(fontTitle, preserveShortenedNames);
|
|
1052
|
-
subfamilyName = subfamilyName.replace(/\b(Italic|Slant|Slanted|Oblique|Backslant|Roman|Upright)\b/gi, "").replace(/\s+/g, " ").trim();
|
|
1053
|
-
if (subfamilyName !== "") {
|
|
1054
|
-
weightName = weightName.replace(`${subfamilyName} `, "").replace(` ${subfamilyName}`, "").trim();
|
|
1055
|
-
}
|
|
1056
|
-
if (variableFont) {
|
|
1057
|
-
if (!fontTitle.toLowerCase().includes("vf")) {
|
|
1058
|
-
fontTitle = fontTitle + " VF";
|
|
1059
|
-
}
|
|
1060
|
-
subfamilyName = "";
|
|
1061
|
-
}
|
|
1062
|
-
if (!(variableFont && fontTitle.toLowerCase().includes("italic"))) {
|
|
1063
|
-
fontTitle = addItalicToFontTitle(font, fontTitle, italicKW, style, preserveShortenedNames);
|
|
1064
|
-
}
|
|
1065
|
-
return { weightName, subfamilyName, fontTitle, style, italicKW, variableFont };
|
|
1066
|
-
};
|
|
1067
|
-
var extractWeightName = (font, italicKW, ttfFallbackMeta = null) => {
|
|
1068
|
-
let weightName = getNameString(font, 17) || getNameString(font, 2) || (ttfFallbackMeta == null ? void 0 : ttfFallbackMeta.preferredSubfamily) || (ttfFallbackMeta == null ? void 0 : ttfFallbackMeta.subfamilyName) || "";
|
|
1069
|
-
const axes = getVariationAxes(font);
|
|
1070
|
-
if (axes !== null) {
|
|
1071
|
-
return "";
|
|
1072
|
-
}
|
|
1073
|
-
if (italicKW) {
|
|
1074
|
-
italicKW.forEach((keyword) => {
|
|
1075
|
-
const kwRegex = new RegExp(`\\b${keyword.trim()}\\b`, "i");
|
|
1076
|
-
if (kwRegex.test(weightName)) {
|
|
1077
|
-
weightName = weightName.replace(kwRegex, "").trim();
|
|
1078
|
-
}
|
|
1079
|
-
});
|
|
1080
|
-
}
|
|
1081
|
-
return weightName == null ? void 0 : weightName.toString().replace("Italic", "").replace("It", "").replace("Slanted", "").replace("Slant", "").replace("Backslant", "").trim();
|
|
1082
|
-
};
|
|
1083
|
-
var extractWeightFromFullName = (font, title, ttfFallbackMeta = null) => {
|
|
1084
|
-
let weightName = getNameString(font, 4) || (ttfFallbackMeta == null ? void 0 : ttfFallbackMeta.fullName) || "";
|
|
1085
|
-
weightName = weightName.replace(title + " ", "").replace(title, "").trim();
|
|
1086
|
-
weightName = weightName.replace("Italic", "").replace("It", "").replace("Slanted", "").replace("Slant", "").trim();
|
|
1087
|
-
return weightName;
|
|
1088
|
-
};
|
|
1089
|
-
var processSubfamilyName = (subfamilyName, weightKeywordList, italicKeywordList, preserveShortenedNames = false) => {
|
|
1090
|
-
weightKeywordList.forEach((keyword) => {
|
|
1091
|
-
const kwRegex = new RegExp(`\\b${keyword.trim()}\\b`, "i");
|
|
1092
|
-
if (kwRegex.test(subfamilyName)) {
|
|
1093
|
-
subfamilyName = subfamilyName.replace(kwRegex, "").trim();
|
|
1094
|
-
}
|
|
1095
|
-
subfamilyName = removeWeightNames(subfamilyName) || subfamilyName;
|
|
1096
|
-
if (!preserveShortenedNames) {
|
|
1097
|
-
subfamilyName = expandAbbreviations(subfamilyName);
|
|
1098
|
-
}
|
|
1099
|
-
});
|
|
1100
|
-
italicKeywordList.forEach((keyword) => {
|
|
1101
|
-
const kwRegex = new RegExp(`\\b${keyword.trim()}\\b`, "i");
|
|
1102
|
-
if (kwRegex.test(subfamilyName)) {
|
|
1103
|
-
subfamilyName = subfamilyName.replace(kwRegex, "").trim();
|
|
1104
|
-
}
|
|
1105
|
-
});
|
|
1106
|
-
return subfamilyName;
|
|
1107
|
-
};
|
|
1108
|
-
var processItalicKeywords = (font, fontTitle, italicKeywordList) => {
|
|
1109
|
-
let italicKW = [];
|
|
1110
|
-
const fullName = getNameString(font, 4);
|
|
1111
|
-
italicKeywordList.forEach((keyword) => {
|
|
1112
|
-
const kw = keyword.trim();
|
|
1113
|
-
const kwRegex = new RegExp(`\\b${kw}\\b`, "i");
|
|
1114
|
-
if (kwRegex.test(fontTitle)) {
|
|
1115
|
-
fontTitle = fontTitle.replace(kwRegex, "").trim();
|
|
1116
|
-
italicKW.push(kw);
|
|
1117
|
-
}
|
|
1118
|
-
if (fullName && fullName.toLowerCase().includes(kw.toLowerCase())) {
|
|
1119
|
-
if (!italicKW.includes(kw)) italicKW.push(kw);
|
|
1120
|
-
}
|
|
1121
|
-
});
|
|
1122
|
-
return italicKW;
|
|
1123
|
-
};
|
|
1124
|
-
var formatFontTitle = (fontTitle, preserveShortenedNames = false) => {
|
|
1125
|
-
const hasItalic = fontTitle.toLowerCase().includes("italic");
|
|
1126
|
-
fontTitle = fontTitle.replace(/-/g, " ");
|
|
1127
|
-
return fontTitle.replace(/\s+/g, " ").trim().split(" ").map((word) => {
|
|
1128
|
-
if (hasItalic && word.toLowerCase() === "italic") return "Italic";
|
|
1129
|
-
let fullWord = word;
|
|
1130
|
-
if (!preserveShortenedNames) {
|
|
1131
|
-
fullWord = reverseSpellingLookup(word) || word;
|
|
1132
|
-
}
|
|
1133
|
-
return fullWord[0].toUpperCase() + fullWord.slice(1);
|
|
1134
|
-
}).join(" ");
|
|
1135
|
-
};
|
|
1136
|
-
var addItalicToFontTitle = (font, fontTitle, italicKW, style, preserveShortenedNames = false) => {
|
|
1137
|
-
const hasItalicAngle = getItalicAngle(font) !== 0;
|
|
1138
|
-
const fullName = getNameString(font, 4);
|
|
1139
|
-
const hasItalicInName = fullName.toLowerCase().includes("italic");
|
|
1140
|
-
if (italicKW.length > 0 || hasItalicAngle || hasItalicInName) {
|
|
1141
|
-
italicKW = [...new Set(italicKW)];
|
|
1142
|
-
if (italicKW.length === 0 && (hasItalicAngle || hasItalicInName)) {
|
|
1143
|
-
italicKW = ["Italic"];
|
|
1144
|
-
}
|
|
1145
|
-
if (!preserveShortenedNames) {
|
|
1146
|
-
italicKW = italicKW.map((item) => reverseSpellingLookup(item) || item);
|
|
1147
|
-
}
|
|
1148
|
-
italicKW = [...new Set(italicKW)];
|
|
1149
|
-
if (italicKW.length > 1 && italicKW.includes("Italic")) {
|
|
1150
|
-
italicKW = ["Italic"];
|
|
1151
|
-
}
|
|
1152
|
-
const fontTitleLower = fontTitle.toLowerCase();
|
|
1153
|
-
italicKW = italicKW.filter((keyword) => {
|
|
1154
|
-
const keywordLower = keyword.toLowerCase();
|
|
1155
|
-
const kwRegex = new RegExp(`\\b${keywordLower}\\b`);
|
|
1156
|
-
const isSubstring = fontTitleLower.split(" ").some(
|
|
1157
|
-
(word) => word.includes(keywordLower) || keywordLower.includes(word)
|
|
1158
|
-
);
|
|
1159
|
-
return !kwRegex.test(fontTitleLower) && !isSubstring;
|
|
1160
|
-
});
|
|
1161
|
-
if (italicKW.length > 0) {
|
|
1162
|
-
fontTitle = fontTitle.trim() + " " + italicKW.join(" ");
|
|
1163
|
-
}
|
|
1164
|
-
}
|
|
1165
|
-
return fontTitle;
|
|
1166
|
-
};
|
|
1167
|
-
var createFontObject = (id, fontTitle, title, font, variableFont, weightName, subfamilyName, file, originalFilename = null) => {
|
|
1168
|
-
const italicAngle = getItalicAngle(font);
|
|
1169
|
-
const fullName = getNameString(font, 4);
|
|
1170
|
-
const fontObject = {
|
|
1171
|
-
_key: nanoid(),
|
|
1172
|
-
_id: id,
|
|
1173
|
-
title: fontTitle,
|
|
1174
|
-
slug: { _type: "slug", current: id },
|
|
1175
|
-
typefaceName: title,
|
|
1176
|
-
style: italicAngle !== 0 || fullName.toLowerCase().includes("italic") ? "Italic" : "Regular",
|
|
1177
|
-
variableFont,
|
|
1178
|
-
weightName,
|
|
1179
|
-
subfamily: subfamilyName,
|
|
1180
|
-
normalWeight: true,
|
|
1181
|
-
weight: Number(determineWeight(font, weightName)),
|
|
1182
|
-
fileInput: {},
|
|
1183
|
-
files: [file],
|
|
1184
|
-
fontKit: font
|
|
1185
|
-
};
|
|
1186
|
-
if (originalFilename) {
|
|
1187
|
-
fontObject.originalFilename = originalFilename;
|
|
1188
|
-
}
|
|
1189
|
-
return fontObject;
|
|
1190
|
-
};
|
|
1191
|
-
var determineWeight = (font, weightName) => {
|
|
1192
|
-
const usWeightClass = getWeightClass(font);
|
|
1193
|
-
if (usWeightClass) {
|
|
1194
|
-
return Number(usWeightClass);
|
|
1195
|
-
}
|
|
1196
|
-
const wn = (weightName == null ? void 0 : weightName.toLowerCase()) || "";
|
|
1197
|
-
if (/hairline|extra thin|extrathin/.test(wn)) return 100;
|
|
1198
|
-
if (/thin|extra light|extralight/.test(wn)) return 200;
|
|
1199
|
-
if (/light|book/.test(wn)) return 300;
|
|
1200
|
-
if (/regular|normal/.test(wn)) return 400;
|
|
1201
|
-
if (/medium/.test(wn)) return 500;
|
|
1202
|
-
if (/semi bold|semibold/.test(wn)) return 600;
|
|
1203
|
-
if (/extra bold|extrabold/.test(wn)) return 800;
|
|
1204
|
-
if (/bold/.test(wn)) return 700;
|
|
1205
|
-
if (/black|ultra/.test(wn)) return 900;
|
|
1206
|
-
return 400;
|
|
1207
|
-
};
|
|
1208
|
-
var sortFontObjects = (fontsObjects) => {
|
|
1209
|
-
return Object.fromEntries(
|
|
1210
|
-
Object.entries(fontsObjects).sort((a, b) => {
|
|
1211
|
-
const weightA = Number(a[1].weight);
|
|
1212
|
-
const weightB = Number(b[1].weight);
|
|
1213
|
-
if (weightA === weightB) {
|
|
1214
|
-
if (a[1].style === "Regular" && b[1].style === "Italic") return -1;
|
|
1215
|
-
if (a[1].style === "Italic" && b[1].style === "Regular") return 1;
|
|
1216
|
-
return 0;
|
|
1217
|
-
}
|
|
1218
|
-
return weightA - weightB;
|
|
1219
|
-
})
|
|
1220
|
-
);
|
|
1221
|
-
};
|
|
1222
|
-
var logFontInfo = (id, fontTitle, font, fileName, subfamilyName, style, weightName, variableFont, italicKW) => {
|
|
1223
|
-
const fullName = getNameString(font, 4);
|
|
1224
|
-
const familyName = getNameString(font, 1);
|
|
1225
|
-
const italicAngle = getItalicAngle(font);
|
|
1226
|
-
console.log("=== Font Info ====");
|
|
1227
|
-
console.log("Font id:", id);
|
|
1228
|
-
console.log("Font title:", fontTitle);
|
|
1229
|
-
console.log("Full name:", fullName);
|
|
1230
|
-
console.log("Family name:", familyName);
|
|
1231
|
-
console.log("File name:", fileName);
|
|
1232
|
-
console.log("Subfamily:", subfamilyName);
|
|
1233
|
-
console.log("Style:", style);
|
|
1234
|
-
console.log("Weight:", weightName);
|
|
1235
|
-
console.log("Variable:", variableFont);
|
|
1236
|
-
console.log("ItalicKW:", italicKW);
|
|
1237
|
-
console.log("Italic detection:", italicAngle !== 0 || fullName.toLowerCase().includes("italic") ? "Italic" : "Regular");
|
|
1238
|
-
console.log("=======");
|
|
1239
|
-
};
|
|
1240
|
-
|
|
1241
|
-
// src/utils/resolveExistingFont.js
|
|
1242
|
-
var resolveExistingFont = async (font, client) => {
|
|
1243
|
-
const result = {
|
|
1244
|
-
exact: null,
|
|
1245
|
-
candidates: [],
|
|
1246
|
-
recommendation: RECOMMENDATION.CREATE,
|
|
1247
|
-
lookupFailed: false
|
|
1248
|
-
};
|
|
1249
|
-
try {
|
|
1250
|
-
const idMatches = await client.fetch(
|
|
1251
|
-
`*[_type == 'font' && (_id == $id || _id == $draftId || slug.current == $id)]{
|
|
1252
|
-
_id, title, weight, style, weightName, typefaceName, subfamily, variableFont,
|
|
1253
|
-
fileInput, description, metaData, metrics, opentypeFeatures, characterSet,
|
|
1254
|
-
scriptFileInput, variableInstanceReferences
|
|
1255
|
-
}`,
|
|
1256
|
-
{ id: font._id, draftId: `drafts.${font._id}` }
|
|
1257
|
-
);
|
|
1258
|
-
if (idMatches.length > 0) {
|
|
1259
|
-
result.exact = idMatches[0];
|
|
1260
|
-
result.recommendation = RECOMMENDATION.USE_EXACT;
|
|
1261
|
-
return result;
|
|
1262
|
-
}
|
|
1263
|
-
const subfamily = font.subfamily || "";
|
|
1264
|
-
const contentMatches = await client.fetch(
|
|
1265
|
-
`*[_type == 'font'
|
|
1266
|
-
&& lower(typefaceName) == lower($typefaceName)
|
|
1267
|
-
&& lower(weightName) == lower($weightName)
|
|
1268
|
-
&& lower(style) == lower($style)
|
|
1269
|
-
&& (variableFont == $variableFont || (!defined(variableFont) && $variableFont == false))
|
|
1270
|
-
&& (
|
|
1271
|
-
lower(coalesce(subfamily, '')) == lower($subfamily)
|
|
1272
|
-
|| (lower(coalesce(subfamily, '')) in ['', 'regular'] && lower($subfamily) in ['', 'regular'])
|
|
1273
|
-
)
|
|
1274
|
-
]{
|
|
1275
|
-
_id, title, weight, style, weightName, typefaceName, subfamily, variableFont,
|
|
1276
|
-
fileInput, description, metaData, metrics, opentypeFeatures, characterSet,
|
|
1277
|
-
scriptFileInput, variableInstanceReferences
|
|
1278
|
-
}`,
|
|
1279
|
-
{
|
|
1280
|
-
typefaceName: font.typefaceName,
|
|
1281
|
-
weightName: font.weightName || "",
|
|
1282
|
-
style: font.style || "Regular",
|
|
1283
|
-
variableFont: font.variableFont || false,
|
|
1284
|
-
subfamily: subfamily === "" ? "regular" : subfamily
|
|
1285
|
-
}
|
|
1286
|
-
);
|
|
1287
|
-
if (contentMatches.length === 1) {
|
|
1288
|
-
result.candidates = contentMatches;
|
|
1289
|
-
result.recommendation = RECOMMENDATION.USE_CANDIDATE;
|
|
1290
|
-
return result;
|
|
1291
|
-
}
|
|
1292
|
-
if (contentMatches.length > 1) {
|
|
1293
|
-
result.candidates = contentMatches;
|
|
1294
|
-
result.recommendation = RECOMMENDATION.AMBIGUOUS;
|
|
1295
|
-
console.warn(
|
|
1296
|
-
`Ambiguous font match for "${font.title}" \u2014 ${contentMatches.length} candidates found:`,
|
|
1297
|
-
contentMatches.map((c) => c._id)
|
|
1298
|
-
);
|
|
1299
|
-
return result;
|
|
1300
|
-
}
|
|
1301
|
-
} catch (err) {
|
|
1302
|
-
console.error("Error resolving existing font:", font._id, err.message);
|
|
1303
|
-
result.lookupFailed = true;
|
|
1304
|
-
}
|
|
1305
|
-
return result;
|
|
1306
|
-
};
|
|
1307
|
-
|
|
1308
|
-
// src/utils/buildUploadPlan.js
|
|
1309
|
-
async function buildUploadPlan({
|
|
1310
|
-
files,
|
|
1311
|
-
typefaceTitle,
|
|
1312
|
-
docId,
|
|
1313
|
-
settings = {},
|
|
1314
|
-
client,
|
|
1315
|
-
stylesObject = {},
|
|
1316
|
-
weightKeywordList = [],
|
|
1317
|
-
italicKeywordList = [],
|
|
1318
|
-
onProgress
|
|
1319
|
-
}) {
|
|
1320
|
-
const plan = createEmptyPlan(settings);
|
|
1321
|
-
plan.settings.typefaceTitle = typefaceTitle;
|
|
1322
|
-
plan.phase = PLAN_PHASE.PROCESSING;
|
|
1323
|
-
plan.processingProgress.total = files.length;
|
|
1324
|
-
for (let i = 0; i < files.length; i++) {
|
|
1325
|
-
const file = files[i];
|
|
1326
|
-
plan.processingProgress.currentFile = file.name;
|
|
1327
|
-
try {
|
|
1328
|
-
const fontBuffer = await readFontFile(file);
|
|
1329
|
-
const font = await parseFont(fontBuffer, file.name);
|
|
1330
|
-
const entry = await buildFontPlanEntry({
|
|
1331
|
-
file,
|
|
1332
|
-
font,
|
|
1333
|
-
typefaceTitle,
|
|
1334
|
-
settings: plan.settings,
|
|
1335
|
-
weightKeywordList,
|
|
1336
|
-
italicKeywordList,
|
|
1337
|
-
client
|
|
1338
|
-
});
|
|
1339
|
-
const existingKey = Object.keys(plan.fonts).find((k) => plan.fonts[k].documentId === entry.documentId);
|
|
1340
|
-
if (existingKey) {
|
|
1341
|
-
plan.fonts[existingKey].files = [...plan.fonts[existingKey].files, ...entry.files];
|
|
1342
|
-
} else {
|
|
1343
|
-
plan.fonts[entry.tempId] = entry;
|
|
1344
|
-
if (!entry.variableFont || entry.subfamily) {
|
|
1345
|
-
const sfName = entry.subfamily;
|
|
1346
|
-
if (!plan.subfamilyGroups[sfName]) {
|
|
1347
|
-
plan.subfamilyGroups[sfName] = { title: sfName, fontIds: [] };
|
|
1348
|
-
}
|
|
1349
|
-
plan.subfamilyGroups[sfName].fontIds.push(entry.tempId);
|
|
1350
|
-
}
|
|
1351
|
-
}
|
|
1352
|
-
plan.processingProgress.completed++;
|
|
1353
|
-
if (onProgress) {
|
|
1354
|
-
onProgress({
|
|
1355
|
-
type: "font-processed",
|
|
1356
|
-
tempId: entry.tempId,
|
|
1357
|
-
fontEntry: entry,
|
|
1358
|
-
progress: { ...plan.processingProgress }
|
|
1359
|
-
});
|
|
1360
|
-
}
|
|
1361
|
-
} catch (err) {
|
|
1362
|
-
console.error(`Error processing ${file.name}:`, err.message);
|
|
1363
|
-
plan.processingProgress.failed++;
|
|
1364
|
-
const errorTempId = sanitizeForSanityId(file.name) + "-" + nanoid2(6);
|
|
1365
|
-
plan.fonts[errorTempId] = {
|
|
1366
|
-
tempId: errorTempId,
|
|
1367
|
-
files: [file],
|
|
1368
|
-
sourceFileName: file.name,
|
|
1369
|
-
title: file.name,
|
|
1370
|
-
documentId: "",
|
|
1371
|
-
weight: 400,
|
|
1372
|
-
weightName: "",
|
|
1373
|
-
style: "Regular",
|
|
1374
|
-
subfamily: "",
|
|
1375
|
-
variableFont: false,
|
|
1376
|
-
originalFilename: null,
|
|
1377
|
-
decisions: createFontDecisions({}),
|
|
1378
|
-
status: FONT_STATUS.ERROR,
|
|
1379
|
-
error: err.message,
|
|
1380
|
-
parsedMetadata: null,
|
|
1381
|
-
glyphCount: 0,
|
|
1382
|
-
opentypeFeatures: [],
|
|
1383
|
-
variationAxes: null
|
|
1384
|
-
};
|
|
1385
|
-
if (onProgress) {
|
|
1386
|
-
onProgress({
|
|
1387
|
-
type: "font-error",
|
|
1388
|
-
tempId: errorTempId,
|
|
1389
|
-
error: err.message,
|
|
1390
|
-
progress: { ...plan.processingProgress }
|
|
1391
|
-
});
|
|
1392
|
-
}
|
|
1393
|
-
}
|
|
1394
|
-
}
|
|
1395
|
-
plan.processingProgress.currentFile = null;
|
|
1396
|
-
plan.phase = PLAN_PHASE.REVIEWING;
|
|
1397
|
-
if (onProgress) {
|
|
1398
|
-
onProgress({
|
|
1399
|
-
type: "processing-complete",
|
|
1400
|
-
progress: { ...plan.processingProgress }
|
|
1401
|
-
});
|
|
1402
|
-
}
|
|
1403
|
-
return plan;
|
|
1404
|
-
}
|
|
1405
|
-
async function buildFontPlanEntry({
|
|
1406
|
-
file,
|
|
1407
|
-
font,
|
|
1408
|
-
typefaceTitle,
|
|
1409
|
-
settings,
|
|
1410
|
-
weightKeywordList,
|
|
1411
|
-
italicKeywordList,
|
|
1412
|
-
client
|
|
1413
|
-
}) {
|
|
1414
|
-
const { weightName, subfamilyName, fontTitle, style, italicKW, variableFont } = extractFontMetadata(
|
|
1415
|
-
font,
|
|
1416
|
-
typefaceTitle,
|
|
1417
|
-
weightKeywordList,
|
|
1418
|
-
italicKeywordList,
|
|
1419
|
-
settings.preserveShortenedNames
|
|
1420
|
-
);
|
|
1421
|
-
let finalTitle = fontTitle;
|
|
1422
|
-
let originalFilename = null;
|
|
1423
|
-
if (settings.preserveFileNames) {
|
|
1424
|
-
originalFilename = file.name.replace(/\.(ttf|otf|woff2?|eot|svg)$/i, "");
|
|
1425
|
-
const normalizedName = originalFilename.replace(/-/g, " ").replace(/([a-z])([A-Z])/g, "$1 $2").replace(/\s+/g, " ").trim();
|
|
1426
|
-
finalTitle = normalizedName;
|
|
1427
|
-
}
|
|
1428
|
-
const documentId = sanitizeForSanityId(finalTitle);
|
|
1429
|
-
const tempId = documentId + "-" + nanoid2(6);
|
|
1430
|
-
const weight = Number(determineWeight(font, weightName));
|
|
1431
|
-
const titleAlternatives = [
|
|
1432
|
-
{ value: getNameString(font, 1), source: "nameId1-familyName" },
|
|
1433
|
-
{ value: getNameString(font, 4), source: "nameId4-fullName" },
|
|
1434
|
-
{ value: getNameString(font, 6), source: "nameId6-postscriptName" },
|
|
1435
|
-
{ value: getNameString(font, 16), source: "nameId16-preferredFamily" },
|
|
1436
|
-
{ value: getNameString(font, 17), source: "nameId17-preferredSubfamily" },
|
|
1437
|
-
{ value: file.name.replace(/\.(ttf|otf|woff2?|eot|svg)$/i, ""), source: "filename" }
|
|
1438
|
-
].filter((alt) => alt.value);
|
|
1439
|
-
const titleSource = settings.preserveFileNames ? "filename" : "fontkit-fullName";
|
|
1440
|
-
const usWeightClass = getWeightClass(font);
|
|
1441
|
-
const weightSource = usWeightClass ? "os2-usWeightClass" : "keyword-match";
|
|
1442
|
-
const matchedKeyword = usWeightClass ? null : weightName;
|
|
1443
|
-
const nameId17 = getNameString(font, 17);
|
|
1444
|
-
const weightNameSource = variableFont ? "variable-font-empty" : nameId17 ? "nameId17-preferredSubfamily" : "nameId2-fontSubfamily";
|
|
1445
|
-
const italicAngle = getItalicAngle(font);
|
|
1446
|
-
const fullName = getNameString(font, 4);
|
|
1447
|
-
let styleSource = "default-regular";
|
|
1448
|
-
let styleReason = "";
|
|
1449
|
-
if (style === "Italic") {
|
|
1450
|
-
if (italicAngle !== 0) {
|
|
1451
|
-
styleSource = "italic-angle";
|
|
1452
|
-
styleReason = `italicAngle = ${italicAngle}`;
|
|
1453
|
-
} else if (fullName.toLowerCase().includes("italic")) {
|
|
1454
|
-
styleSource = "name-contains-italic";
|
|
1455
|
-
styleReason = 'fullName contains "italic"';
|
|
1456
|
-
}
|
|
1457
|
-
}
|
|
1458
|
-
const familyNameRaw = getNameString(font, 1);
|
|
1459
|
-
const nameId4Remainder = fullName ? fullName.replace(typefaceTitle.trim(), "").trim() : "";
|
|
1460
|
-
const nameId1Remainder = familyNameRaw ? familyNameRaw.replace(typefaceTitle.trim(), "").trim() : "";
|
|
1461
|
-
let subfamilySource = "default-empty";
|
|
1462
|
-
if (nameId4Remainder && subfamilyName) {
|
|
1463
|
-
subfamilySource = "nameId4-subtraction";
|
|
1464
|
-
} else if (nameId1Remainder && subfamilyName) {
|
|
1465
|
-
subfamilySource = "nameId1-subtraction";
|
|
1466
|
-
}
|
|
1467
|
-
const decisions = createFontDecisions({
|
|
1468
|
-
titleSource,
|
|
1469
|
-
title: finalTitle,
|
|
1470
|
-
titleOriginal: getNameString(font, 4),
|
|
1471
|
-
documentId,
|
|
1472
|
-
weight,
|
|
1473
|
-
weightSource,
|
|
1474
|
-
matchedKeyword,
|
|
1475
|
-
weightName,
|
|
1476
|
-
weightNameSource,
|
|
1477
|
-
style,
|
|
1478
|
-
styleSource,
|
|
1479
|
-
styleReason,
|
|
1480
|
-
subfamily: subfamilyName,
|
|
1481
|
-
subfamilySource,
|
|
1482
|
-
titleAlternatives
|
|
1483
|
-
});
|
|
1484
|
-
const fontForResolution = {
|
|
1485
|
-
_id: documentId,
|
|
1486
|
-
typefaceName: typefaceTitle,
|
|
1487
|
-
weightName,
|
|
1488
|
-
style,
|
|
1489
|
-
subfamily: subfamilyName,
|
|
1490
|
-
variableFont,
|
|
1491
|
-
title: finalTitle
|
|
1492
|
-
};
|
|
1493
|
-
try {
|
|
1494
|
-
const resolution = await resolveExistingFont(fontForResolution, client);
|
|
1495
|
-
decisions.existingDocument = {
|
|
1496
|
-
recommendation: resolution.recommendation,
|
|
1497
|
-
exact: resolution.exact,
|
|
1498
|
-
candidates: resolution.candidates,
|
|
1499
|
-
userChoice: null,
|
|
1500
|
-
selectedCandidate: null,
|
|
1501
|
-
lookupFailed: resolution.lookupFailed || false
|
|
1502
|
-
};
|
|
1503
|
-
} catch (err) {
|
|
1504
|
-
console.warn("Document resolution failed for", documentId, err.message);
|
|
1505
|
-
decisions.existingDocument.lookupFailed = true;
|
|
1506
|
-
}
|
|
1507
|
-
const metadata = getFontMetadata(font);
|
|
1508
|
-
const metrics = getFontMetrics(font);
|
|
1509
|
-
const axes = getVariationAxes(font);
|
|
1510
|
-
const finalSubfamily = variableFont ? "" : subfamilyName || "Regular";
|
|
1511
|
-
console.log(`[buildFontPlanEntry] "${finalTitle}" \u2192 subfamily: "${subfamilyName}" \u2192 final: "${finalSubfamily}" (variableFont: ${variableFont})`);
|
|
1512
|
-
return {
|
|
1513
|
-
tempId,
|
|
1514
|
-
files: [file],
|
|
1515
|
-
sourceFileName: file.name,
|
|
1516
|
-
title: finalTitle,
|
|
1517
|
-
documentId,
|
|
1518
|
-
weight,
|
|
1519
|
-
weightName,
|
|
1520
|
-
style,
|
|
1521
|
-
subfamily: finalSubfamily,
|
|
1522
|
-
variableFont,
|
|
1523
|
-
originalFilename,
|
|
1524
|
-
decisions,
|
|
1525
|
-
status: FONT_STATUS.PROCESSED,
|
|
1526
|
-
error: null,
|
|
1527
|
-
parsedMetadata: { ...metadata, ...metrics },
|
|
1528
|
-
glyphCount: getGlyphCount(font),
|
|
1529
|
-
opentypeFeatures: getAllFeatureTags(font),
|
|
1530
|
-
variationAxes: axes
|
|
1531
|
-
};
|
|
1532
|
-
}
|
|
1533
|
-
|
|
1534
|
-
// src/components/UploadStep1Settings.jsx
|
|
1535
|
-
import React, { useState, useCallback, useRef, useMemo } from "react";
|
|
1536
|
-
import { Box, Flex, Stack, Text, Button, Card, Badge } from "@sanity/ui";
|
|
1537
|
-
import { UploadIcon, TrashIcon } from "@sanity/icons";
|
|
1538
|
-
var ACCEPTED_EXTENSIONS = ["ttf", "otf", "woff", "woff2", "eot", "svg"];
|
|
1539
|
-
var TYPE_ORDER = ["ttf", "otf", "eot", "svg", "woff", "woff2"];
|
|
1540
|
-
var filterFontFiles = (files) => Array.from(files).filter((f) => ACCEPTED_EXTENSIONS.includes(f.name.split(".").pop().toLowerCase()));
|
|
1541
|
-
var sortFilesByType = (files) => Array.from(files).sort((a, b) => {
|
|
1542
|
-
const aIdx = TYPE_ORDER.indexOf(a.name.split(".").pop().toLowerCase());
|
|
1543
|
-
const bIdx = TYPE_ORDER.indexOf(b.name.split(".").pop().toLowerCase());
|
|
1544
|
-
if (aIdx === bIdx) return a.name.localeCompare(b.name);
|
|
1545
|
-
return aIdx - bIdx;
|
|
1546
|
-
});
|
|
1547
|
-
function UploadStep1Settings({ settings, onStartProcessing }) {
|
|
1548
|
-
const [pendingFiles, setPendingFiles] = useState([]);
|
|
1549
|
-
const [isDragging, setIsDragging] = useState(false);
|
|
1550
|
-
const [filterType, setFilterType] = useState(null);
|
|
1551
|
-
const fileInputRef = useRef(null);
|
|
1552
|
-
const handleFileSelect = useCallback((e) => {
|
|
1553
|
-
const files = filterFontFiles(e.target.files);
|
|
1554
|
-
if (files.length > 0) setPendingFiles((prev) => [...prev, ...files]);
|
|
1555
|
-
e.target.value = "";
|
|
1556
|
-
}, []);
|
|
1557
|
-
const handleRemoveFile = useCallback((file) => {
|
|
1558
|
-
setPendingFiles((prev) => prev.filter((f) => f !== file));
|
|
1559
|
-
}, []);
|
|
1560
|
-
const handleDragEnter = useCallback((e) => {
|
|
1561
|
-
e.preventDefault();
|
|
1562
|
-
setIsDragging(true);
|
|
1563
|
-
}, []);
|
|
1564
|
-
const handleDragOver = useCallback((e) => {
|
|
1565
|
-
e.preventDefault();
|
|
1566
|
-
}, []);
|
|
1567
|
-
const handleDragLeave = useCallback((e) => {
|
|
1568
|
-
e.preventDefault();
|
|
1569
|
-
setIsDragging(false);
|
|
1570
|
-
}, []);
|
|
1571
|
-
const handleDrop = useCallback((e) => {
|
|
1572
|
-
e.preventDefault();
|
|
1573
|
-
setIsDragging(false);
|
|
1574
|
-
const files = filterFontFiles(e.dataTransfer.files);
|
|
1575
|
-
if (files.length > 0) setPendingFiles((prev) => [...prev, ...files]);
|
|
1576
|
-
}, []);
|
|
1577
|
-
const handleProcess = useCallback(() => {
|
|
1578
|
-
const sorted = sortFilesByType(pendingFiles);
|
|
1579
|
-
onStartProcessing(sorted, settings);
|
|
1580
|
-
}, [pendingFiles, settings, onStartProcessing]);
|
|
1581
|
-
const typeBreakdown = useMemo(() => {
|
|
1582
|
-
const counts = {};
|
|
1583
|
-
pendingFiles.forEach((f) => {
|
|
1584
|
-
const ext = f.name.split(".").pop().toLowerCase();
|
|
1585
|
-
counts[ext] = (counts[ext] || 0) + 1;
|
|
1586
|
-
});
|
|
1587
|
-
const values = Object.values(counts);
|
|
1588
|
-
if (values.length <= 1) return { counts, modeCount: 0, outlierExts: /* @__PURE__ */ new Set() };
|
|
1589
|
-
const freq = {};
|
|
1590
|
-
values.forEach((v) => {
|
|
1591
|
-
freq[v] = (freq[v] || 0) + 1;
|
|
1592
|
-
});
|
|
1593
|
-
const modeCount = Number(Object.entries(freq).sort((a, b) => b[1] - a[1])[0][0]);
|
|
1594
|
-
const outlierExts = /* @__PURE__ */ new Set();
|
|
1595
|
-
Object.entries(counts).forEach(([ext, count]) => {
|
|
1596
|
-
if (count !== modeCount) outlierExts.add(ext);
|
|
1597
|
-
});
|
|
1598
|
-
return { counts, modeCount, outlierExts };
|
|
1599
|
-
}, [pendingFiles]);
|
|
1600
|
-
const displayedFiles = useMemo(() => {
|
|
1601
|
-
if (!filterType) return pendingFiles;
|
|
1602
|
-
return pendingFiles.filter((f) => f.name.split(".").pop().toLowerCase() === filterType);
|
|
1603
|
-
}, [pendingFiles, filterType]);
|
|
1604
|
-
const dropZoneStyle = {
|
|
1605
|
-
border: `2px dashed ${isDragging ? "var(--card-focus-ring-color)" : "var(--card-border-color)"}`,
|
|
1606
|
-
borderRadius: 4,
|
|
1607
|
-
padding: pendingFiles.length > 0 ? "10px 16px" : "28px 16px",
|
|
1608
|
-
textAlign: "center",
|
|
1609
|
-
background: isDragging ? "rgba(100, 153, 255, 0.06)" : "transparent",
|
|
1610
|
-
transition: "border-color 0.12s, background 0.12s"
|
|
1611
|
-
};
|
|
1612
|
-
return /* @__PURE__ */ React.createElement(Stack, { space: 4 }, /* @__PURE__ */ React.createElement(
|
|
1613
|
-
Box,
|
|
1614
|
-
{
|
|
1615
|
-
onDragEnter: handleDragEnter,
|
|
1616
|
-
onDragOver: handleDragOver,
|
|
1617
|
-
onDragLeave: handleDragLeave,
|
|
1618
|
-
onDrop: handleDrop,
|
|
1619
|
-
style: dropZoneStyle
|
|
1620
|
-
},
|
|
1621
|
-
/* @__PURE__ */ React.createElement(
|
|
1622
|
-
"input",
|
|
1623
|
-
{
|
|
1624
|
-
ref: fileInputRef,
|
|
1625
|
-
type: "file",
|
|
1626
|
-
multiple: true,
|
|
1627
|
-
hidden: true,
|
|
1628
|
-
accept: ".ttf,.otf,.woff,.woff2,.eot,.svg",
|
|
1629
|
-
onChange: handleFileSelect
|
|
1630
|
-
}
|
|
1631
|
-
),
|
|
1632
|
-
pendingFiles.length === 0 ? /* @__PURE__ */ React.createElement(Stack, { space: 3 }, /* @__PURE__ */ React.createElement(Text, { size: 1, muted: true }, isDragging ? "Release to add files" : "Drop font files here"), /* @__PURE__ */ React.createElement(Flex, { justify: "center" }, /* @__PURE__ */ React.createElement(
|
|
1633
|
-
Button,
|
|
1634
|
-
{
|
|
1635
|
-
mode: "ghost",
|
|
1636
|
-
tone: "primary",
|
|
1637
|
-
fontSize: 1,
|
|
1638
|
-
padding: 2,
|
|
1639
|
-
text: "Browse files",
|
|
1640
|
-
onClick: () => {
|
|
1641
|
-
var _a;
|
|
1642
|
-
return (_a = fileInputRef.current) == null ? void 0 : _a.click();
|
|
1643
|
-
}
|
|
1644
|
-
}
|
|
1645
|
-
))) : /* @__PURE__ */ React.createElement(Flex, { align: "center", justify: "center", gap: 2 }, /* @__PURE__ */ React.createElement(Text, { size: 1, muted: true }, isDragging ? "Release to add" : "Drop more files or"), /* @__PURE__ */ React.createElement(
|
|
1646
|
-
Button,
|
|
1647
|
-
{
|
|
1648
|
-
mode: "bleed",
|
|
1649
|
-
tone: "primary",
|
|
1650
|
-
fontSize: 1,
|
|
1651
|
-
padding: 1,
|
|
1652
|
-
text: "browse",
|
|
1653
|
-
onClick: () => {
|
|
1654
|
-
var _a;
|
|
1655
|
-
return (_a = fileInputRef.current) == null ? void 0 : _a.click();
|
|
1656
|
-
}
|
|
1657
|
-
}
|
|
1658
|
-
))
|
|
1659
|
-
), pendingFiles.length > 0 && /* @__PURE__ */ React.createElement(Stack, { space: 3 }, /* @__PURE__ */ React.createElement(Flex, { align: "center", justify: "space-between" }, /* @__PURE__ */ React.createElement(Flex, { align: "center", gap: 2 }, /* @__PURE__ */ React.createElement(Text, { size: 1, weight: "semibold" }, filterType ? `${displayedFiles.length} of ${pendingFiles.length} files (${filterType.toUpperCase()})` : `${pendingFiles.length} file${pendingFiles.length === 1 ? "" : "s"}`), /* @__PURE__ */ React.createElement(Flex, { gap: 1 }, TYPE_ORDER.filter((ext) => typeBreakdown.counts[ext]).map((ext) => {
|
|
1660
|
-
const count = typeBreakdown.counts[ext];
|
|
1661
|
-
const isOutlier = typeBreakdown.outlierExts.has(ext);
|
|
1662
|
-
const isActive = filterType === ext;
|
|
1663
|
-
return /* @__PURE__ */ React.createElement(
|
|
1664
|
-
Badge,
|
|
1665
|
-
{
|
|
1666
|
-
key: ext,
|
|
1667
|
-
tone: isOutlier ? "critical" : isActive ? "primary" : "default",
|
|
1668
|
-
mode: isActive ? "default" : isOutlier ? "default" : "outline",
|
|
1669
|
-
fontSize: 0,
|
|
1670
|
-
style: { cursor: "pointer" },
|
|
1671
|
-
onClick: () => setFilterType(isActive ? null : ext)
|
|
1672
|
-
},
|
|
1673
|
-
count,
|
|
1674
|
-
" ",
|
|
1675
|
-
ext.toUpperCase()
|
|
1676
|
-
);
|
|
1677
|
-
}), filterType && /* @__PURE__ */ React.createElement(
|
|
1678
|
-
Badge,
|
|
1679
|
-
{
|
|
1680
|
-
mode: "outline",
|
|
1681
|
-
fontSize: 0,
|
|
1682
|
-
style: { cursor: "pointer" },
|
|
1683
|
-
onClick: () => setFilterType(null)
|
|
1684
|
-
},
|
|
1685
|
-
"Clear filter"
|
|
1686
|
-
))), /* @__PURE__ */ React.createElement(
|
|
1687
|
-
Button,
|
|
1688
|
-
{
|
|
1689
|
-
mode: "bleed",
|
|
1690
|
-
tone: "default",
|
|
1691
|
-
fontSize: 1,
|
|
1692
|
-
padding: 1,
|
|
1693
|
-
text: "Clear all",
|
|
1694
|
-
onClick: () => setPendingFiles([])
|
|
1695
|
-
}
|
|
1696
|
-
)), /* @__PURE__ */ React.createElement(Box, { style: { maxHeight: 350, overflowY: "auto" } }, /* @__PURE__ */ React.createElement(
|
|
1697
|
-
Flex,
|
|
1698
|
-
{
|
|
1699
|
-
align: "center",
|
|
1700
|
-
gap: 2,
|
|
1701
|
-
paddingX: 2,
|
|
1702
|
-
paddingY: 1,
|
|
1703
|
-
style: { borderBottom: "1px solid var(--card-border-color)" }
|
|
1704
|
-
},
|
|
1705
|
-
/* @__PURE__ */ React.createElement(Text, { size: 0, weight: "semibold", muted: true, style: { width: 56, flexShrink: 0 } }, "Type"),
|
|
1706
|
-
/* @__PURE__ */ React.createElement(Text, { size: 0, weight: "semibold", muted: true, style: { flex: 1 } }, "File Name"),
|
|
1707
|
-
/* @__PURE__ */ React.createElement(Box, { style: { width: 32 } })
|
|
1708
|
-
), /* @__PURE__ */ React.createElement(Stack, { space: 0 }, displayedFiles.map((file, i) => {
|
|
1709
|
-
const ext = file.name.split(".").pop().toLowerCase();
|
|
1710
|
-
return /* @__PURE__ */ React.createElement(
|
|
1711
|
-
Flex,
|
|
1712
|
-
{
|
|
1713
|
-
key: `${file.name}-${file.size}-${i}`,
|
|
1714
|
-
align: "center",
|
|
1715
|
-
gap: 2,
|
|
1716
|
-
paddingX: 2,
|
|
1717
|
-
paddingY: 2,
|
|
1718
|
-
style: {
|
|
1719
|
-
borderBottom: "1px solid var(--card-border-color)"
|
|
1720
|
-
}
|
|
1721
|
-
},
|
|
1722
|
-
/* @__PURE__ */ React.createElement(
|
|
1723
|
-
Badge,
|
|
1724
|
-
{
|
|
1725
|
-
tone: "primary",
|
|
1726
|
-
mode: "outline",
|
|
1727
|
-
fontSize: 0,
|
|
1728
|
-
style: { width: 56, flexShrink: 0, textAlign: "center" }
|
|
1729
|
-
},
|
|
1730
|
-
ext.toUpperCase()
|
|
1731
|
-
),
|
|
1732
|
-
/* @__PURE__ */ React.createElement(Text, { size: 1, style: { flex: 1 } }, file.name),
|
|
1733
|
-
/* @__PURE__ */ React.createElement(
|
|
1734
|
-
Button,
|
|
1735
|
-
{
|
|
1736
|
-
mode: "bleed",
|
|
1737
|
-
tone: "critical",
|
|
1738
|
-
icon: TrashIcon,
|
|
1739
|
-
padding: 1,
|
|
1740
|
-
onClick: () => handleRemoveFile(file),
|
|
1741
|
-
style: { flexShrink: 0 }
|
|
1742
|
-
}
|
|
1743
|
-
)
|
|
1744
|
-
);
|
|
1745
|
-
})))), pendingFiles.length > 0 && /* @__PURE__ */ React.createElement(
|
|
1746
|
-
Button,
|
|
1747
|
-
{
|
|
1748
|
-
mode: "default",
|
|
1749
|
-
tone: "primary",
|
|
1750
|
-
icon: UploadIcon,
|
|
1751
|
-
text: `Process ${pendingFiles.length} File${pendingFiles.length === 1 ? "" : "s"}`,
|
|
1752
|
-
style: { width: "100%" },
|
|
1753
|
-
fontSize: 2,
|
|
1754
|
-
padding: 4,
|
|
1755
|
-
onClick: handleProcess
|
|
1756
|
-
}
|
|
1757
|
-
));
|
|
1758
|
-
}
|
|
1759
|
-
|
|
1760
|
-
// src/components/UploadStep2Review.jsx
|
|
1761
|
-
import React6, { useMemo as useMemo4, useState as useState3, useCallback as useCallback3, useEffect as useEffect2 } from "react";
|
|
1762
|
-
import { Box as Box5, Grid, Stack as Stack4, Flex as Flex6, Text as Text6, Label as Label4, Switch as Switch2, Button as Button5, Card as Card4, Spinner, Badge as Badge4, Tooltip as Tooltip3 } from "@sanity/ui";
|
|
1763
|
-
import { InfoOutlineIcon as InfoOutlineIcon3 } from "@sanity/icons";
|
|
1764
|
-
|
|
1765
|
-
// src/components/FontReviewCard.jsx
|
|
1766
|
-
import React3, { useState as useState2, useCallback as useCallback2, useEffect, useMemo as useMemo2, memo } from "react";
|
|
1767
|
-
import { Card as Card3, Stack as Stack3, Flex as Flex3, Box as Box3, Text as Text3, TextInput, Badge as Badge3, Button as Button3, Select, Tooltip as Tooltip2, Label as Label2 } from "@sanity/ui";
|
|
1768
|
-
import { ChevronDownIcon, ChevronRightIcon, TrashIcon as TrashIcon2, ResetIcon, InfoOutlineIcon as InfoOutlineIcon2 } from "@sanity/icons";
|
|
1769
|
-
|
|
1770
|
-
// src/components/ExistingDocumentResolver.jsx
|
|
1771
|
-
import React2 from "react";
|
|
1772
|
-
import { Card as Card2, Stack as Stack2, Flex as Flex2, Text as Text2, Badge as Badge2, Button as Button2, Label, Switch, Tooltip, Box as Box2 } from "@sanity/ui";
|
|
1773
|
-
import { InfoOutlineIcon } from "@sanity/icons";
|
|
1774
|
-
function ExistingDocumentResolver({ decision, tempId, dispatch }) {
|
|
1775
|
-
if (!decision) return null;
|
|
1776
|
-
const { recommendation, exact, candidates, userChoice, selectedCandidate, lookupFailed } = decision;
|
|
1777
|
-
const effectiveAction = userChoice || (recommendation === RECOMMENDATION.USE_EXACT || recommendation === RECOMMENDATION.USE_CANDIDATE ? "update" : "create");
|
|
1778
|
-
const isUpdating = effectiveAction === "update";
|
|
1779
|
-
const hasMatch = exact || (candidates == null ? void 0 : candidates.length) > 0;
|
|
1780
|
-
const handleToggle = () => {
|
|
1781
|
-
dispatch({ type: "SET_FONT_ACTION", tempId, decision: isUpdating ? "create" : "update" });
|
|
1782
|
-
};
|
|
1783
|
-
const handleSelectCandidate = (candidate) => {
|
|
1784
|
-
dispatch({ type: "SET_FONT_CANDIDATE", tempId, candidate });
|
|
1785
|
-
};
|
|
1786
|
-
if (lookupFailed) {
|
|
1787
|
-
return /* @__PURE__ */ React2.createElement(Card2, { tone: "caution", border: true, padding: 2, radius: 1 }, /* @__PURE__ */ React2.createElement(Text2, { size: 0 }, "Could not check for existing documents \u2014 will create new."));
|
|
1788
|
-
}
|
|
1789
|
-
if (recommendation === RECOMMENDATION.CREATE && !userChoice) {
|
|
1790
|
-
return /* @__PURE__ */ React2.createElement(Stack2, { space: 2 }, /* @__PURE__ */ React2.createElement(Label, { size: 0 }, "Existing Document"), /* @__PURE__ */ React2.createElement(Card2, { tone: "default", border: true, padding: 2, radius: 1 }, /* @__PURE__ */ React2.createElement(Text2, { size: 1 }, "No existing document found \u2014 will create new.")));
|
|
1791
|
-
}
|
|
1792
|
-
if (hasMatch) {
|
|
1793
|
-
const matchDoc = exact || selectedCandidate || (candidates == null ? void 0 : candidates[0]);
|
|
1794
|
-
const matchType = exact ? "Exact Match" : (candidates == null ? void 0 : candidates.length) > 1 ? "Multiple Matches" : "Likely Match";
|
|
1795
|
-
const matchTone = exact ? "positive" : "caution";
|
|
1796
|
-
return /* @__PURE__ */ React2.createElement(Stack2, { space: 2 }, /* @__PURE__ */ React2.createElement(Label, { size: 0 }, "Existing Document"), /* @__PURE__ */ React2.createElement(Flex2, { align: "center", gap: 2 }, /* @__PURE__ */ React2.createElement(
|
|
1797
|
-
Switch,
|
|
1798
|
-
{
|
|
1799
|
-
checked: isUpdating,
|
|
1800
|
-
onChange: handleToggle,
|
|
1801
|
-
style: { cursor: "pointer" }
|
|
1802
|
-
}
|
|
1803
|
-
), /* @__PURE__ */ React2.createElement(Stack2, { space: 1 }, /* @__PURE__ */ React2.createElement(Text2, { size: 1, weight: "semibold" }, isUpdating ? "Update existing document" : "Create new document"), /* @__PURE__ */ React2.createElement(Text2, { size: 0, muted: true }, isUpdating ? "Files will be uploaded to the matched document below." : "A new document will be created. You may need to update the Document ID above to avoid conflicts."))), /* @__PURE__ */ React2.createElement(
|
|
1804
|
-
Card2,
|
|
1805
|
-
{
|
|
1806
|
-
tone: isUpdating ? matchTone : "default",
|
|
1807
|
-
border: true,
|
|
1808
|
-
padding: 2,
|
|
1809
|
-
radius: 1,
|
|
1810
|
-
style: { opacity: isUpdating ? 1 : 0.5, transition: "opacity 0.15s ease" }
|
|
1811
|
-
},
|
|
1812
|
-
/* @__PURE__ */ React2.createElement(Stack2, { space: 2 }, /* @__PURE__ */ React2.createElement(Flex2, { align: "center", gap: 2 }, /* @__PURE__ */ React2.createElement(Badge2, { tone: isUpdating ? matchTone : "default", fontSize: 0 }, matchType), /* @__PURE__ */ React2.createElement(Text2, { size: 1, style: { fontFamily: "monospace" } }, matchDoc == null ? void 0 : matchDoc._id)), /* @__PURE__ */ React2.createElement(Text2, { size: 0, muted: true }, matchDoc == null ? void 0 : matchDoc.title, " \xB7 ", matchDoc == null ? void 0 : matchDoc.weightName, " \xB7 ", matchDoc == null ? void 0 : matchDoc.style, (matchDoc == null ? void 0 : matchDoc.subfamily) ? ` \xB7 ${matchDoc.subfamily}` : ""))
|
|
1813
|
-
), isUpdating && (candidates == null ? void 0 : candidates.length) > 1 && /* @__PURE__ */ React2.createElement(Stack2, { space: 1 }, /* @__PURE__ */ React2.createElement(Text2, { size: 0, muted: true }, "Select which document to update:"), candidates.map((candidate) => /* @__PURE__ */ React2.createElement(
|
|
1814
|
-
Card2,
|
|
1815
|
-
{
|
|
1816
|
-
key: candidate._id,
|
|
1817
|
-
border: true,
|
|
1818
|
-
radius: 1,
|
|
1819
|
-
padding: 2,
|
|
1820
|
-
tone: (selectedCandidate == null ? void 0 : selectedCandidate._id) === candidate._id ? "positive" : "default",
|
|
1821
|
-
style: { cursor: "pointer" },
|
|
1822
|
-
onClick: () => handleSelectCandidate(candidate)
|
|
1823
|
-
},
|
|
1824
|
-
/* @__PURE__ */ React2.createElement(Flex2, { align: "center", gap: 2 }, /* @__PURE__ */ React2.createElement(
|
|
1825
|
-
"input",
|
|
1826
|
-
{
|
|
1827
|
-
type: "radio",
|
|
1828
|
-
name: `candidate-${tempId}`,
|
|
1829
|
-
checked: (selectedCandidate == null ? void 0 : selectedCandidate._id) === candidate._id,
|
|
1830
|
-
onChange: () => handleSelectCandidate(candidate),
|
|
1831
|
-
style: { cursor: "pointer" }
|
|
1832
|
-
}
|
|
1833
|
-
), /* @__PURE__ */ React2.createElement(Stack2, { space: 1, style: { flex: 1 } }, /* @__PURE__ */ React2.createElement(Text2, { size: 1, style: { fontFamily: "monospace" } }, candidate._id), /* @__PURE__ */ React2.createElement(Text2, { size: 0, muted: true }, candidate.title, " \xB7 ", candidate.weightName, " \xB7 ", candidate.style, candidate.subfamily ? ` \xB7 ${candidate.subfamily}` : "")))
|
|
1834
|
-
))));
|
|
1835
|
-
}
|
|
1836
|
-
if (userChoice === "create") {
|
|
1837
|
-
return /* @__PURE__ */ React2.createElement(Stack2, { space: 2 }, /* @__PURE__ */ React2.createElement(Label, { size: 0 }, "Existing Document"), /* @__PURE__ */ React2.createElement(Card2, { border: true, padding: 2, radius: 1 }, /* @__PURE__ */ React2.createElement(Text2, { size: 1 }, "Will create new document")));
|
|
1838
|
-
}
|
|
1839
|
-
return null;
|
|
1840
|
-
}
|
|
1841
|
-
|
|
1842
|
-
// src/components/FontReviewCard.jsx
|
|
1843
|
-
var STANDARD_TYPES = ["ttf", "otf", "woff", "woff2"];
|
|
1844
|
-
var EXTENDED_TYPES = ["eot", "svg", "css", "woff2_subset", "woff2_web"];
|
|
1845
|
-
var FontReviewCard = memo(function FontReviewCard2({ entry, dispatch, allExpanded, typefaceTitle, price }) {
|
|
1846
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o;
|
|
1847
|
-
const [expanded, setExpanded] = useState2(false);
|
|
1848
|
-
const [showAllFileTypes, setShowAllFileTypes] = useState2(false);
|
|
1849
|
-
const [showDocPreview, setShowDocPreview] = useState2(false);
|
|
1850
|
-
useEffect(() => {
|
|
1851
|
-
setExpanded(allExpanded);
|
|
1852
|
-
}, [allExpanded]);
|
|
1853
|
-
const [localTitle, setLocalTitle] = useState2(entry.title);
|
|
1854
|
-
const [localWeight, setLocalWeight] = useState2(String(entry.weight));
|
|
1855
|
-
const [localWeightName, setLocalWeightName] = useState2(entry.weightName);
|
|
1856
|
-
const [localSubfamily, setLocalSubfamily] = useState2(entry.subfamily);
|
|
1857
|
-
const [localDocId, setLocalDocId] = useState2(entry.documentId);
|
|
1858
|
-
const isError = entry.status === FONT_STATUS.ERROR;
|
|
1859
|
-
const hasConflict = entry._idConflict;
|
|
1860
|
-
const resolution = (_a = entry.decisions) == null ? void 0 : _a.existingDocument;
|
|
1861
|
-
const isUpdate = (resolution == null ? void 0 : resolution.userChoice) === "update" || !(resolution == null ? void 0 : resolution.userChoice) && ((resolution == null ? void 0 : resolution.recommendation) === RECOMMENDATION.USE_EXACT || (resolution == null ? void 0 : resolution.recommendation) === RECOMMENDATION.USE_CANDIDATE);
|
|
1862
|
-
const isCreateNewOverride = (resolution == null ? void 0 : resolution.userChoice) === "create" && ((resolution == null ? void 0 : resolution.exact) || ((_b = resolution == null ? void 0 : resolution.candidates) == null ? void 0 : _b.length) > 0);
|
|
1863
|
-
const docIdEditable = isCreateNewOverride || hasConflict;
|
|
1864
|
-
const hasUserOverrides = useMemo2(() => {
|
|
1865
|
-
var _a2, _b2, _c2, _d2, _e2, _f2;
|
|
1866
|
-
const d = entry.decisions;
|
|
1867
|
-
if (!d) return false;
|
|
1868
|
-
return ((_a2 = d.title) == null ? void 0 : _a2.userOverride) != null || ((_b2 = d.weight) == null ? void 0 : _b2.userOverride) != null || ((_c2 = d.weightName) == null ? void 0 : _c2.userOverride) != null || ((_d2 = d.style) == null ? void 0 : _d2.userOverride) != null || ((_e2 = d.subfamily) == null ? void 0 : _e2.userOverride) != null || ((_f2 = d.documentId) == null ? void 0 : _f2.userOverride) != null;
|
|
1869
|
-
}, [entry.decisions]);
|
|
1870
|
-
const fileExtMap = useMemo2(() => {
|
|
1871
|
-
const map = {};
|
|
1872
|
-
(entry.files || []).forEach((f) => {
|
|
1873
|
-
var _a2, _b2;
|
|
1874
|
-
const ext = (_b2 = (_a2 = f.name) == null ? void 0 : _a2.split(".").pop()) == null ? void 0 : _b2.toLowerCase();
|
|
1875
|
-
if (ext) map[ext] = f.name;
|
|
1876
|
-
});
|
|
1877
|
-
return map;
|
|
1878
|
-
}, [entry.files]);
|
|
1879
|
-
const fileCount = ((_c = entry.files) == null ? void 0 : _c.length) || 0;
|
|
1880
|
-
const cardTone = isError ? "critical" : hasConflict ? "caution" : "default";
|
|
1881
|
-
const handleTitleBlur = useCallback2(() => {
|
|
1882
|
-
if (localTitle !== entry.title) {
|
|
1883
|
-
dispatch({ type: "SET_FONT_TITLE", tempId: entry.tempId, title: localTitle });
|
|
1884
|
-
}
|
|
1885
|
-
}, [localTitle, entry.title, entry.tempId, dispatch]);
|
|
1886
|
-
const handleDocIdBlur = useCallback2(() => {
|
|
1887
|
-
if (localDocId !== entry.documentId) {
|
|
1888
|
-
dispatch({ type: "SET_FONT_DOCUMENT_ID", tempId: entry.tempId, documentId: localDocId });
|
|
1889
|
-
}
|
|
1890
|
-
}, [localDocId, entry.documentId, entry.tempId, dispatch]);
|
|
1891
|
-
const handleWeightBlur = useCallback2(() => {
|
|
1892
|
-
const num = Number(localWeight);
|
|
1893
|
-
if (!isNaN(num) && num !== entry.weight) {
|
|
1894
|
-
dispatch({ type: "SET_FONT_WEIGHT", tempId: entry.tempId, weight: num });
|
|
1895
|
-
}
|
|
1896
|
-
}, [localWeight, entry.weight, entry.tempId, dispatch]);
|
|
1897
|
-
const handleWeightNameBlur = useCallback2(() => {
|
|
1898
|
-
if (localWeightName !== entry.weightName) {
|
|
1899
|
-
dispatch({ type: "SET_FONT_WEIGHT_NAME", tempId: entry.tempId, weightName: localWeightName });
|
|
1900
|
-
}
|
|
1901
|
-
}, [localWeightName, entry.weightName, entry.tempId, dispatch]);
|
|
1902
|
-
const handleStyleChange = useCallback2((e) => {
|
|
1903
|
-
dispatch({ type: "SET_FONT_STYLE", tempId: entry.tempId, style: e.target.value });
|
|
1904
|
-
}, [entry.tempId, dispatch]);
|
|
1905
|
-
const handleSubfamilyBlur = useCallback2(() => {
|
|
1906
|
-
if (localSubfamily !== entry.subfamily) {
|
|
1907
|
-
dispatch({ type: "SET_FONT_SUBFAMILY", tempId: entry.tempId, subfamily: localSubfamily });
|
|
1908
|
-
}
|
|
1909
|
-
}, [localSubfamily, entry.subfamily, entry.tempId, dispatch]);
|
|
1910
|
-
const handleReset = useCallback2(() => {
|
|
1911
|
-
dispatch({ type: "RESET_FONT_TO_SUGGESTIONS", tempId: entry.tempId });
|
|
1912
|
-
setLocalTitle(entry.decisions.title.processed);
|
|
1913
|
-
setLocalDocId(entry.decisions.documentId.generated);
|
|
1914
|
-
setLocalWeight(String(entry.decisions.weight.detected));
|
|
1915
|
-
setLocalWeightName(entry.decisions.weightName.detected);
|
|
1916
|
-
setLocalSubfamily(entry.decisions.subfamily.detected || "Regular");
|
|
1917
|
-
}, [entry, dispatch]);
|
|
1918
|
-
const handleRemove = useCallback2(() => {
|
|
1919
|
-
dispatch({ type: "REMOVE_FONT", tempId: entry.tempId });
|
|
1920
|
-
}, [entry.tempId, dispatch]);
|
|
1921
|
-
const formatSource = (decision) => {
|
|
1922
|
-
if (!decision) return null;
|
|
1923
|
-
let src = decision.source || "";
|
|
1924
|
-
src = src.replace(/nameId(\d+)-(\w+)/g, "nameId$1 ($2)");
|
|
1925
|
-
src = src.replace(/-/g, " ").replace("fontkit ", "");
|
|
1926
|
-
if (src === "default empty" && decision.detected === "") {
|
|
1927
|
-
src = 'empty \u2014 defaults to "Regular"';
|
|
1928
|
-
}
|
|
1929
|
-
const reason = decision.reason ? ` (${decision.reason})` : "";
|
|
1930
|
-
const override = decision.userOverride != null ? " (user override)" : "";
|
|
1931
|
-
return `Source: ${src}${reason}${override}`;
|
|
1932
|
-
};
|
|
1933
|
-
return /* @__PURE__ */ React3.createElement(Card3, { border: true, radius: 2, tone: cardTone, style: { marginBottom: -1 } }, /* @__PURE__ */ React3.createElement(
|
|
1934
|
-
Box3,
|
|
1935
|
-
{
|
|
1936
|
-
as: "button",
|
|
1937
|
-
onClick: () => !isError && setExpanded((v) => !v),
|
|
1938
|
-
style: {
|
|
1939
|
-
width: "100%",
|
|
1940
|
-
background: expanded ? "var(--card-muted-bg-color)" : "none",
|
|
1941
|
-
border: "none",
|
|
1942
|
-
cursor: isError ? "default" : "pointer",
|
|
1943
|
-
textAlign: "left",
|
|
1944
|
-
padding: 0,
|
|
1945
|
-
transition: "background 0.1s ease"
|
|
1946
|
-
},
|
|
1947
|
-
onMouseEnter: (e) => {
|
|
1948
|
-
if (!isError && !expanded) e.currentTarget.style.background = "var(--card-muted-bg-color)";
|
|
1949
|
-
},
|
|
1950
|
-
onMouseLeave: (e) => {
|
|
1951
|
-
if (!expanded) e.currentTarget.style.background = "none";
|
|
1952
|
-
}
|
|
1953
|
-
},
|
|
1954
|
-
/* @__PURE__ */ React3.createElement(Flex3, { align: "center", gap: 2, paddingX: 2, paddingY: 2 }, /* @__PURE__ */ React3.createElement(Box3, { style: { width: 20, flexShrink: 0 } }, !isError && (expanded ? /* @__PURE__ */ React3.createElement(ChevronDownIcon, null) : /* @__PURE__ */ React3.createElement(ChevronRightIcon, null))), /* @__PURE__ */ React3.createElement(Box3, { style: { flex: 1, whiteSpace: "nowrap" } }, /* @__PURE__ */ React3.createElement(Text3, { size: 1, weight: "semibold", style: { whiteSpace: "nowrap" } }, entry.title || entry.sourceFileName, entry.variableFont && /* @__PURE__ */ React3.createElement(Badge3, { tone: "primary", fontSize: 0, style: { marginLeft: 6 } }, "VF"), hasConflict && /* @__PURE__ */ React3.createElement(Badge3, { tone: "caution", fontSize: 0, style: { marginLeft: 6 } }, "ID Conflict"))), /* @__PURE__ */ React3.createElement(Text3, { size: 0, style: { width: 50, textAlign: "center", flexShrink: 0 } }, entry.weight), /* @__PURE__ */ React3.createElement(Text3, { size: 0, style: { width: 50, textAlign: "center", flexShrink: 0 } }, entry.style), /* @__PURE__ */ React3.createElement(Text3, { size: 0, style: { width: 40, textAlign: "center", flexShrink: 0 } }, fileCount), /* @__PURE__ */ React3.createElement(Box3, { style: { width: 55, textAlign: "center", flexShrink: 0 } }, /* @__PURE__ */ React3.createElement(Badge3, { tone: isError ? "critical" : isUpdate ? "caution" : "positive", fontSize: 0 }, isError ? "Error" : isUpdate ? "Update" : "Create")))
|
|
1955
|
-
), isError && /* @__PURE__ */ React3.createElement(Box3, { paddingX: 2, paddingBottom: 2 }, /* @__PURE__ */ React3.createElement(Flex3, { justify: "space-between", align: "center" }, /* @__PURE__ */ React3.createElement(Text3, { size: 0, muted: true }, entry.error), /* @__PURE__ */ React3.createElement(Button3, { mode: "bleed", tone: "critical", icon: TrashIcon2, padding: 1, onClick: handleRemove }))), expanded && !isError && /* @__PURE__ */ React3.createElement(Box3, { padding: 3, style: { borderTop: "1px solid var(--card-border-color)", background: "var(--card-muted-bg-color)" } }, /* @__PURE__ */ React3.createElement(Stack3, { space: 4 }, /* @__PURE__ */ React3.createElement(Stack3, { space: 2 }, /* @__PURE__ */ React3.createElement(Flex3, { align: "center", gap: 2 }, /* @__PURE__ */ React3.createElement(Label2, { size: 0 }, "Files (", fileCount, ")"), /* @__PURE__ */ React3.createElement(
|
|
1956
|
-
Button3,
|
|
1957
|
-
{
|
|
1958
|
-
mode: "bleed",
|
|
1959
|
-
fontSize: 0,
|
|
1960
|
-
padding: 1,
|
|
1961
|
-
text: showAllFileTypes ? "Hide extended types" : "Show all types",
|
|
1962
|
-
onClick: () => setShowAllFileTypes((v) => !v),
|
|
1963
|
-
style: { cursor: "pointer" }
|
|
1964
|
-
}
|
|
1965
|
-
)), /* @__PURE__ */ React3.createElement(Flex3, { gap: 1, wrap: "wrap" }, STANDARD_TYPES.map((ext) => /* @__PURE__ */ React3.createElement(
|
|
1966
|
-
Badge3,
|
|
1967
|
-
{
|
|
1968
|
-
key: ext,
|
|
1969
|
-
fontSize: 0,
|
|
1970
|
-
tone: fileExtMap[ext] ? "primary" : "default",
|
|
1971
|
-
mode: fileExtMap[ext] ? "default" : "outline",
|
|
1972
|
-
style: { opacity: fileExtMap[ext] ? 1 : 0.35 }
|
|
1973
|
-
},
|
|
1974
|
-
ext.toUpperCase(),
|
|
1975
|
-
fileExtMap[ext] ? `: ${fileExtMap[ext]}` : ""
|
|
1976
|
-
)), showAllFileTypes && EXTENDED_TYPES.map((ext) => /* @__PURE__ */ React3.createElement(
|
|
1977
|
-
Badge3,
|
|
1978
|
-
{
|
|
1979
|
-
key: ext,
|
|
1980
|
-
fontSize: 0,
|
|
1981
|
-
tone: fileExtMap[ext] ? "primary" : "default",
|
|
1982
|
-
mode: fileExtMap[ext] ? "default" : "outline",
|
|
1983
|
-
style: { opacity: fileExtMap[ext] ? 1 : 0.35 }
|
|
1984
|
-
},
|
|
1985
|
-
ext.toUpperCase(),
|
|
1986
|
-
fileExtMap[ext] ? `: ${fileExtMap[ext]}` : ""
|
|
1987
|
-
)))), /* @__PURE__ */ React3.createElement(Stack3, { space: 2 }, /* @__PURE__ */ React3.createElement(Label2, { size: 0 }, "Font Title"), /* @__PURE__ */ React3.createElement(
|
|
1988
|
-
TextInput,
|
|
1989
|
-
{
|
|
1990
|
-
value: localTitle,
|
|
1991
|
-
onChange: (e) => setLocalTitle(e.target.value),
|
|
1992
|
-
onBlur: handleTitleBlur,
|
|
1993
|
-
fontSize: 1
|
|
1994
|
-
}
|
|
1995
|
-
), /* @__PURE__ */ React3.createElement(Text3, { size: 0, muted: true }, formatSource((_d = entry.decisions) == null ? void 0 : _d.title)), ((_g = (_f = (_e = entry.decisions) == null ? void 0 : _e.title) == null ? void 0 : _f.alternatives) == null ? void 0 : _g.length) > 0 && /* @__PURE__ */ React3.createElement(Flex3, { gap: 1, wrap: "wrap" }, entry.decisions.title.alternatives.filter((a) => a.value).map((alt, i) => /* @__PURE__ */ React3.createElement(Tooltip2, { key: i, content: /* @__PURE__ */ React3.createElement(Box3, { padding: 1 }, /* @__PURE__ */ React3.createElement(Text3, { size: 0 }, alt.source.replace(/nameId(\d+)-(\w+)/g, "nameId$1 ($2)"))), portal: true }, /* @__PURE__ */ React3.createElement(
|
|
1996
|
-
Badge3,
|
|
1997
|
-
{
|
|
1998
|
-
mode: "outline",
|
|
1999
|
-
fontSize: 0,
|
|
2000
|
-
style: { cursor: "pointer" },
|
|
2001
|
-
onClick: () => {
|
|
2002
|
-
setLocalTitle(alt.value);
|
|
2003
|
-
dispatch({ type: "SET_FONT_TITLE", tempId: entry.tempId, title: alt.value, source: alt.source });
|
|
2004
|
-
}
|
|
2005
|
-
},
|
|
2006
|
-
alt.value.length > 30 ? alt.value.slice(0, 30) + "..." : alt.value
|
|
2007
|
-
))))), /* @__PURE__ */ React3.createElement(Stack3, { space: 2 }, /* @__PURE__ */ React3.createElement(Flex3, { align: "center", gap: 1 }, /* @__PURE__ */ React3.createElement(Label2, { size: 0 }, "Document ID"), !docIdEditable && /* @__PURE__ */ React3.createElement(
|
|
2008
|
-
Tooltip2,
|
|
2009
|
-
{
|
|
2010
|
-
content: /* @__PURE__ */ React3.createElement(Box3, { padding: 2, style: { maxWidth: 260 } }, /* @__PURE__ */ React3.createElement(Text3, { size: 1, style: { lineHeight: 1.6 } }, 'Document IDs must be unique. This field is auto-derived from the font title. It becomes editable when you choose "Create new instead" on a font with an existing match, or when there is a duplicate ID conflict.')),
|
|
2011
|
-
placement: "top",
|
|
2012
|
-
portal: true
|
|
2013
|
-
},
|
|
2014
|
-
/* @__PURE__ */ React3.createElement(InfoOutlineIcon2, { style: { opacity: 0.4, fontSize: 12 } })
|
|
2015
|
-
)), /* @__PURE__ */ React3.createElement(
|
|
2016
|
-
TextInput,
|
|
2017
|
-
{
|
|
2018
|
-
value: docIdEditable ? localDocId : entry.documentId,
|
|
2019
|
-
onChange: docIdEditable ? (e) => setLocalDocId(e.target.value) : void 0,
|
|
2020
|
-
onBlur: docIdEditable ? handleDocIdBlur : void 0,
|
|
2021
|
-
readOnly: !docIdEditable,
|
|
2022
|
-
fontSize: 1,
|
|
2023
|
-
style: { fontFamily: "monospace", opacity: docIdEditable ? 1 : 0.7 }
|
|
2024
|
-
}
|
|
2025
|
-
), hasConflict && /* @__PURE__ */ React3.createElement(Text3, { size: 0, tone: "caution" }, "This ID conflicts with another font in this batch \u2014 edit to make unique"), isCreateNewOverride && !hasConflict && /* @__PURE__ */ React3.createElement(Text3, { size: 0, tone: "caution" }, "Creating new document \u2014 edit the ID to avoid overwriting the existing document"), !docIdEditable && /* @__PURE__ */ React3.createElement(Text3, { size: 0, muted: true }, "Auto-derived from font title")), /* @__PURE__ */ React3.createElement(Flex3, { gap: 3 }, /* @__PURE__ */ React3.createElement(Box3, { style: { flex: 1 } }, /* @__PURE__ */ React3.createElement(Stack3, { space: 2 }, /* @__PURE__ */ React3.createElement(Label2, { size: 0 }, "Weight"), /* @__PURE__ */ React3.createElement(
|
|
2026
|
-
TextInput,
|
|
2027
|
-
{
|
|
2028
|
-
type: "number",
|
|
2029
|
-
value: localWeight,
|
|
2030
|
-
onChange: (e) => setLocalWeight(e.target.value),
|
|
2031
|
-
onBlur: handleWeightBlur,
|
|
2032
|
-
fontSize: 1
|
|
2033
|
-
}
|
|
2034
|
-
), ((_h = entry.decisions) == null ? void 0 : _h.weight) && /* @__PURE__ */ React3.createElement(Text3, { size: 0, muted: true }, formatSource(entry.decisions.weight)))), /* @__PURE__ */ React3.createElement(Box3, { style: { flex: 1 } }, /* @__PURE__ */ React3.createElement(Stack3, { space: 2 }, /* @__PURE__ */ React3.createElement(Label2, { size: 0 }, "Weight Name"), /* @__PURE__ */ React3.createElement(
|
|
2035
|
-
TextInput,
|
|
2036
|
-
{
|
|
2037
|
-
value: localWeightName,
|
|
2038
|
-
onChange: (e) => setLocalWeightName(e.target.value),
|
|
2039
|
-
onBlur: handleWeightNameBlur,
|
|
2040
|
-
fontSize: 1
|
|
2041
|
-
}
|
|
2042
|
-
), ((_i = entry.decisions) == null ? void 0 : _i.weightName) && /* @__PURE__ */ React3.createElement(Text3, { size: 0, muted: true }, formatSource(entry.decisions.weightName))))), /* @__PURE__ */ React3.createElement(Flex3, { gap: 3 }, /* @__PURE__ */ React3.createElement(Box3, { style: { flex: 1 } }, /* @__PURE__ */ React3.createElement(Stack3, { space: 2 }, /* @__PURE__ */ React3.createElement(Label2, { size: 0 }, "Style"), /* @__PURE__ */ React3.createElement(Select, { value: entry.style, onChange: handleStyleChange, fontSize: 1 }, /* @__PURE__ */ React3.createElement("option", { value: "Regular" }, "Regular"), /* @__PURE__ */ React3.createElement("option", { value: "Italic" }, "Italic")), ((_j = entry.decisions) == null ? void 0 : _j.style) && /* @__PURE__ */ React3.createElement(Text3, { size: 0, muted: true }, formatSource(entry.decisions.style)))), /* @__PURE__ */ React3.createElement(Box3, { style: { flex: 1 } }, /* @__PURE__ */ React3.createElement(Stack3, { space: 2 }, /* @__PURE__ */ React3.createElement(Label2, { size: 0 }, "Subfamily"), /* @__PURE__ */ React3.createElement(
|
|
2043
|
-
TextInput,
|
|
2044
|
-
{
|
|
2045
|
-
value: localSubfamily,
|
|
2046
|
-
onChange: (e) => setLocalSubfamily(e.target.value),
|
|
2047
|
-
onBlur: handleSubfamilyBlur,
|
|
2048
|
-
fontSize: 1
|
|
2049
|
-
}
|
|
2050
|
-
), ((_k = entry.decisions) == null ? void 0 : _k.subfamily) && /* @__PURE__ */ React3.createElement(Text3, { size: 0, muted: true }, formatSource(entry.decisions.subfamily))))), entry.variableFont && entry.variationAxes && /* @__PURE__ */ React3.createElement(Stack3, { space: 2 }, /* @__PURE__ */ React3.createElement(Label2, { size: 0 }, "Variable Font Axes"), /* @__PURE__ */ React3.createElement(Flex3, { gap: 1, wrap: "wrap" }, Object.entries(entry.variationAxes).map(([tag, axis]) => /* @__PURE__ */ React3.createElement(Badge3, { key: tag, mode: "outline", fontSize: 0 }, tag, " ", axis.min, "\u2013", axis.max))), ((_m = (_l = entry.decisions) == null ? void 0 : _l.weight) == null ? void 0 : _m.userOverride) != null && ((_n = entry.variationAxes) == null ? void 0 : _n.wght) && ((entry.weight < entry.variationAxes.wght.min || entry.weight > entry.variationAxes.wght.max) && /* @__PURE__ */ React3.createElement(Text3, { size: 0, tone: "caution" }, "Weight ", entry.weight, " is outside the wght axis range (", entry.variationAxes.wght.min, "\u2013", entry.variationAxes.wght.max, ")"))), /* @__PURE__ */ React3.createElement(
|
|
2051
|
-
ExistingDocumentResolver,
|
|
2052
|
-
{
|
|
2053
|
-
decision: (_o = entry.decisions) == null ? void 0 : _o.existingDocument,
|
|
2054
|
-
tempId: entry.tempId,
|
|
2055
|
-
dispatch
|
|
2056
|
-
}
|
|
2057
|
-
), /* @__PURE__ */ React3.createElement(Stack3, { space: 2 }, /* @__PURE__ */ React3.createElement(
|
|
2058
|
-
Button3,
|
|
2059
|
-
{
|
|
2060
|
-
mode: "bleed",
|
|
2061
|
-
fontSize: 0,
|
|
2062
|
-
padding: 1,
|
|
2063
|
-
text: showDocPreview ? "Hide document preview" : "Show document preview",
|
|
2064
|
-
onClick: () => setShowDocPreview((v) => !v),
|
|
2065
|
-
style: { cursor: "pointer", alignSelf: "flex-start" }
|
|
2066
|
-
}
|
|
2067
|
-
), showDocPreview && /* @__PURE__ */ React3.createElement(Card3, { border: true, padding: 3, radius: 1, style: { fontFamily: "monospace", fontSize: 12 } }, /* @__PURE__ */ React3.createElement(Stack3, { space: 2 }, [
|
|
2068
|
-
["_id", entry.documentId],
|
|
2069
|
-
["_type", "font"],
|
|
2070
|
-
["title", entry.title],
|
|
2071
|
-
["slug", entry.documentId],
|
|
2072
|
-
["typefaceName", typefaceTitle || "\u2014"],
|
|
2073
|
-
["weightName", entry.weightName || "\u2014"],
|
|
2074
|
-
["weight", entry.weight],
|
|
2075
|
-
["style", entry.style],
|
|
2076
|
-
["subfamily", entry.subfamily || "\u2014"],
|
|
2077
|
-
["variableFont", String(entry.variableFont)],
|
|
2078
|
-
["price", price ?? "\u2014"],
|
|
2079
|
-
["sell", price > 0 ? "true" : "false"],
|
|
2080
|
-
["normalWeight", "true"],
|
|
2081
|
-
["files", (entry.files || []).map((f) => f.name).join(", ") || "\u2014"]
|
|
2082
|
-
].map(([key, value]) => /* @__PURE__ */ React3.createElement(Flex3, { key, gap: 2 }, /* @__PURE__ */ React3.createElement(Text3, { size: 0, muted: true, style: { width: 120, flexShrink: 0 } }, key), /* @__PURE__ */ React3.createElement(Text3, { size: 0, style: { wordBreak: "break-all" } }, String(value))))))), /* @__PURE__ */ React3.createElement(Flex3, { justify: "flex-end", gap: 2 }, hasUserOverrides && /* @__PURE__ */ React3.createElement(
|
|
2083
|
-
Button3,
|
|
2084
|
-
{
|
|
2085
|
-
mode: "ghost",
|
|
2086
|
-
tone: "default",
|
|
2087
|
-
icon: ResetIcon,
|
|
2088
|
-
text: "Reset to Suggestions",
|
|
2089
|
-
fontSize: 1,
|
|
2090
|
-
padding: 2,
|
|
2091
|
-
onClick: handleReset
|
|
2092
|
-
}
|
|
2093
|
-
), /* @__PURE__ */ React3.createElement(
|
|
2094
|
-
Button3,
|
|
2095
|
-
{
|
|
2096
|
-
mode: "ghost",
|
|
2097
|
-
tone: "critical",
|
|
2098
|
-
icon: TrashIcon2,
|
|
2099
|
-
text: "Remove",
|
|
2100
|
-
fontSize: 1,
|
|
2101
|
-
padding: 2,
|
|
2102
|
-
onClick: handleRemove
|
|
2103
|
-
}
|
|
2104
|
-
)))));
|
|
2105
|
-
});
|
|
2106
|
-
var FontReviewCard_default = FontReviewCard;
|
|
2107
|
-
|
|
2108
|
-
// src/components/BulkActions.jsx
|
|
2109
|
-
import React4, { useMemo as useMemo3 } from "react";
|
|
2110
|
-
import { Flex as Flex4, Box as Box4, Button as Button4, TextInput as TextInput2, Select as Select2, Text as Text4, Label as Label3 } from "@sanity/ui";
|
|
2111
|
-
import { SearchIcon } from "@sanity/icons";
|
|
2112
|
-
function isUpdateEntry(entry) {
|
|
2113
|
-
var _a;
|
|
2114
|
-
const d = (_a = entry.decisions) == null ? void 0 : _a.existingDocument;
|
|
2115
|
-
const choice = d == null ? void 0 : d.userChoice;
|
|
2116
|
-
const rec = d == null ? void 0 : d.recommendation;
|
|
2117
|
-
return choice === "update" || !choice && (rec === RECOMMENDATION.USE_EXACT || rec === RECOMMENDATION.USE_CANDIDATE);
|
|
2118
|
-
}
|
|
2119
|
-
function BulkActions({
|
|
2120
|
-
fonts,
|
|
2121
|
-
dispatch,
|
|
2122
|
-
searchQuery,
|
|
2123
|
-
onSearchChange,
|
|
2124
|
-
filterBy,
|
|
2125
|
-
onFilterChange,
|
|
2126
|
-
allExpanded,
|
|
2127
|
-
onToggleExpandAll,
|
|
2128
|
-
visibleTempIds
|
|
2129
|
-
}) {
|
|
2130
|
-
const fontEntries = useMemo3(() => Object.values(fonts), [fonts]);
|
|
2131
|
-
const fontCount = fontEntries.length;
|
|
2132
|
-
const visibleCount = visibleTempIds.length;
|
|
2133
|
-
const filterCounts = useMemo3(() => {
|
|
2134
|
-
const createCount = fontEntries.filter((f) => f.status !== FONT_STATUS.ERROR && !isUpdateEntry(f)).length;
|
|
2135
|
-
const updateCount = fontEntries.filter((f) => f.status !== FONT_STATUS.ERROR && isUpdateEntry(f)).length;
|
|
2136
|
-
const errorCount = fontEntries.filter((f) => f.status === FONT_STATUS.ERROR).length;
|
|
2137
|
-
const conflictCount = fontEntries.filter((f) => f._idConflict).length;
|
|
2138
|
-
const italicCount = fontEntries.filter((f) => f.style === "Italic" && f.status !== FONT_STATUS.ERROR).length;
|
|
2139
|
-
const regularCount = fontEntries.filter((f) => f.style === "Regular" && f.status !== FONT_STATUS.ERROR).length;
|
|
2140
|
-
const subfamilyCounts = {};
|
|
2141
|
-
fontEntries.forEach((f) => {
|
|
2142
|
-
if (f.status === FONT_STATUS.ERROR) return;
|
|
2143
|
-
const sf = f.subfamily || "Regular";
|
|
2144
|
-
subfamilyCounts[sf] = (subfamilyCounts[sf] || 0) + 1;
|
|
2145
|
-
});
|
|
2146
|
-
return { createCount, updateCount, errorCount, conflictCount, italicCount, regularCount, subfamilyCounts };
|
|
2147
|
-
}, [fontEntries]);
|
|
2148
|
-
const subfamilies = useMemo3(
|
|
2149
|
-
() => Object.keys(filterCounts.subfamilyCounts).sort((a, b) => {
|
|
2150
|
-
if (a === "Regular") return -1;
|
|
2151
|
-
if (b === "Regular") return 1;
|
|
2152
|
-
return a.localeCompare(b);
|
|
2153
|
-
}),
|
|
2154
|
-
[filterCounts]
|
|
2155
|
-
);
|
|
2156
|
-
return /* @__PURE__ */ React4.createElement(Flex4, { gap: 2, align: "center", wrap: "wrap", style: { position: "sticky", top: 0, zIndex: 10, background: "var(--card-bg-color)", paddingBottom: 8, paddingTop: 4 } }, /* @__PURE__ */ React4.createElement(Box4, { style: { flex: 1, minWidth: 150 } }, /* @__PURE__ */ React4.createElement(
|
|
2157
|
-
TextInput2,
|
|
2158
|
-
{
|
|
2159
|
-
icon: SearchIcon,
|
|
2160
|
-
placeholder: "Search fonts...",
|
|
2161
|
-
value: searchQuery,
|
|
2162
|
-
onChange: (e) => onSearchChange(e.target.value),
|
|
2163
|
-
fontSize: 1
|
|
2164
|
-
}
|
|
2165
|
-
)), /* @__PURE__ */ React4.createElement(Flex4, { align: "center", gap: 1 }, /* @__PURE__ */ React4.createElement(Label3, { size: 0, style: { whiteSpace: "nowrap" } }, "Filter"), /* @__PURE__ */ React4.createElement(Select2, { value: filterBy, onChange: (e) => onFilterChange(e.target.value), fontSize: 1, style: { minWidth: 140 } }, /* @__PURE__ */ React4.createElement("option", { value: "all" }, "All (", fontCount, ")"), filterCounts.createCount > 0 && /* @__PURE__ */ React4.createElement("option", { value: "create" }, "Create (", filterCounts.createCount, ")"), filterCounts.updateCount > 0 && /* @__PURE__ */ React4.createElement("option", { value: "update" }, "Update (", filterCounts.updateCount, ")"), filterCounts.regularCount > 0 && /* @__PURE__ */ React4.createElement("option", { value: "style:regular" }, "Regular (", filterCounts.regularCount, ")"), filterCounts.italicCount > 0 && /* @__PURE__ */ React4.createElement("option", { value: "style:italic" }, "Italic (", filterCounts.italicCount, ")"), filterCounts.errorCount > 0 && /* @__PURE__ */ React4.createElement("option", { value: "error" }, "Errors (", filterCounts.errorCount, ")"), filterCounts.conflictCount > 0 && /* @__PURE__ */ React4.createElement("option", { value: "conflict" }, "Conflicts (", filterCounts.conflictCount, ")"), subfamilies.length > 1 && subfamilies.map((sf) => /* @__PURE__ */ React4.createElement("option", { key: sf, value: `sf:${sf}` }, sf, " (", filterCounts.subfamilyCounts[sf], ")")))), visibleCount !== fontCount && /* @__PURE__ */ React4.createElement(Text4, { size: 0, muted: true }, visibleCount, " of ", fontCount));
|
|
2166
|
-
}
|
|
2167
|
-
|
|
2168
|
-
// src/components/PriceInput.jsx
|
|
2169
|
-
import React5 from "react";
|
|
2170
|
-
import { Flex as Flex5, Text as Text5 } from "@sanity/ui";
|
|
2171
|
-
var PriceInput = ({ inputPrice, handleInputChange }) => /* @__PURE__ */ React5.createElement(Flex5, { align: "center", gap: 2 }, /* @__PURE__ */ React5.createElement(Text5, { size: 1, muted: true }, "Price:"), /* @__PURE__ */ React5.createElement(Text5, { size: 1, muted: true }, "$"), /* @__PURE__ */ React5.createElement(
|
|
2172
|
-
"input",
|
|
2173
|
-
{
|
|
2174
|
-
value: inputPrice,
|
|
2175
|
-
onChange: handleInputChange,
|
|
2176
|
-
type: "number",
|
|
2177
|
-
style: { textAlign: "end", padding: "5px", maxWidth: "75px" }
|
|
2178
|
-
}
|
|
2179
|
-
), /* @__PURE__ */ React5.createElement(Text5, { size: 1, muted: true }, "per style"));
|
|
2180
|
-
var PriceInput_default = PriceInput;
|
|
2181
|
-
|
|
2182
|
-
// src/components/UploadStep2Review.jsx
|
|
2183
|
-
function isUpdateEntry2(entry) {
|
|
2184
|
-
var _a;
|
|
2185
|
-
const d = (_a = entry.decisions) == null ? void 0 : _a.existingDocument;
|
|
2186
|
-
const choice = d == null ? void 0 : d.userChoice;
|
|
2187
|
-
const rec = d == null ? void 0 : d.recommendation;
|
|
2188
|
-
return choice === "update" || !choice && (rec === RECOMMENDATION.USE_EXACT || rec === RECOMMENDATION.USE_CANDIDATE);
|
|
2189
|
-
}
|
|
2190
|
-
function UploadStep2Review({
|
|
2191
|
-
plan,
|
|
2192
|
-
dispatch,
|
|
2193
|
-
onCancelProcessing,
|
|
2194
|
-
onReadyToUpload,
|
|
2195
|
-
onStartExecution,
|
|
2196
|
-
processingCancelled
|
|
2197
|
-
}) {
|
|
2198
|
-
var _a, _b, _c;
|
|
2199
|
-
const isProcessing = plan.phase === PLAN_PHASE.PROCESSING;
|
|
2200
|
-
const isReviewing = plan.phase === PLAN_PHASE.REVIEWING || plan.phase === PLAN_PHASE.READY;
|
|
2201
|
-
const [localPrice, setLocalPrice] = useState3(String(((_a = plan.settings) == null ? void 0 : _a.price) || 0));
|
|
2202
|
-
const [localPreserveShortenedNames, setLocalPreserveShortenedNames] = useState3(((_b = plan.settings) == null ? void 0 : _b.preserveShortenedNames) ?? true);
|
|
2203
|
-
const [localPreserveFileNames, setLocalPreserveFileNames] = useState3(((_c = plan.settings) == null ? void 0 : _c.preserveFileNames) ?? false);
|
|
2204
|
-
const [searchQuery, setSearchQuery] = useState3("");
|
|
2205
|
-
const [filterBy, setFilterBy] = useState3("all");
|
|
2206
|
-
const [allExpanded, setAllExpanded] = useState3(false);
|
|
2207
|
-
const [sortBy, setSortBy] = useState3("weight");
|
|
2208
|
-
const [sortDir, setSortDir] = useState3("asc");
|
|
2209
|
-
const fontEntries = useMemo4(() => Object.values(plan.fonts), [plan.fonts]);
|
|
2210
|
-
const processedCount = fontEntries.filter((f) => f.status === FONT_STATUS.PROCESSED).length;
|
|
2211
|
-
const errorCount = fontEntries.filter((f) => f.status === FONT_STATUS.ERROR).length;
|
|
2212
|
-
const totalCount = plan.processingProgress.total;
|
|
2213
|
-
useEffect2(() => {
|
|
2214
|
-
if (!isReviewing) return;
|
|
2215
|
-
const processed = fontEntries.filter((f) => f.status !== FONT_STATUS.ERROR);
|
|
2216
|
-
if (processed.length === 0) return;
|
|
2217
|
-
const bySubfamily = {};
|
|
2218
|
-
processed.forEach((f) => {
|
|
2219
|
-
const sf = f.subfamily || "(none)";
|
|
2220
|
-
if (!bySubfamily[sf]) bySubfamily[sf] = [];
|
|
2221
|
-
bySubfamily[sf].push(f);
|
|
2222
|
-
});
|
|
2223
|
-
console.group("[UploadStep2Review] Subfamily assignments");
|
|
2224
|
-
Object.entries(bySubfamily).forEach(([sf, fonts]) => {
|
|
2225
|
-
console.group(`Subfamily: "${sf}" (${fonts.length} font${fonts.length === 1 ? "" : "s"})`);
|
|
2226
|
-
fonts.forEach((f) => {
|
|
2227
|
-
var _a2, _b2, _c2, _d, _e;
|
|
2228
|
-
console.log(` "${f.title}" (id: ${f.documentId})`, {
|
|
2229
|
-
sourceFile: f.sourceFileName,
|
|
2230
|
-
weightName: f.weightName,
|
|
2231
|
-
style: f.style,
|
|
2232
|
-
subfamily: f.subfamily,
|
|
2233
|
-
subfamilyDecision: (_a2 = f.decisions) == null ? void 0 : _a2.subfamily,
|
|
2234
|
-
parsedMetadata: {
|
|
2235
|
-
familyName: (_b2 = f.parsedMetadata) == null ? void 0 : _b2.familyName,
|
|
2236
|
-
fullName: (_c2 = f.parsedMetadata) == null ? void 0 : _c2.fullName,
|
|
2237
|
-
preferredFamily: (_d = f.parsedMetadata) == null ? void 0 : _d.preferredFamily,
|
|
2238
|
-
preferredSubfamily: (_e = f.parsedMetadata) == null ? void 0 : _e.preferredSubfamily
|
|
2239
|
-
}
|
|
2240
|
-
});
|
|
2241
|
-
});
|
|
2242
|
-
console.groupEnd();
|
|
2243
|
-
});
|
|
2244
|
-
console.groupEnd();
|
|
2245
|
-
}, [isReviewing, fontEntries]);
|
|
2246
|
-
const createCount = useMemo4(
|
|
2247
|
-
() => fontEntries.filter((f) => f.status !== FONT_STATUS.ERROR && !isUpdateEntry2(f)).length,
|
|
2248
|
-
[fontEntries]
|
|
2249
|
-
);
|
|
2250
|
-
const updateCount = useMemo4(
|
|
2251
|
-
() => fontEntries.filter((f) => f.status !== FONT_STATUS.ERROR && isUpdateEntry2(f)).length,
|
|
2252
|
-
[fontEntries]
|
|
2253
|
-
);
|
|
2254
|
-
const visibleEntries = useMemo4(() => {
|
|
2255
|
-
let entries = fontEntries;
|
|
2256
|
-
if (filterBy === "create") {
|
|
2257
|
-
entries = entries.filter((f) => !isUpdateEntry2(f) && f.status !== FONT_STATUS.ERROR);
|
|
2258
|
-
} else if (filterBy === "update") {
|
|
2259
|
-
entries = entries.filter((f) => isUpdateEntry2(f));
|
|
2260
|
-
} else if (filterBy === "error") {
|
|
2261
|
-
entries = entries.filter((f) => f.status === FONT_STATUS.ERROR);
|
|
2262
|
-
} else if (filterBy === "conflict") {
|
|
2263
|
-
entries = entries.filter((f) => f._idConflict);
|
|
2264
|
-
} else if (filterBy === "style:italic") {
|
|
2265
|
-
entries = entries.filter((f) => f.style === "Italic" && f.status !== FONT_STATUS.ERROR);
|
|
2266
|
-
} else if (filterBy === "style:regular") {
|
|
2267
|
-
entries = entries.filter((f) => f.style === "Regular" && f.status !== FONT_STATUS.ERROR);
|
|
2268
|
-
} else if (filterBy.startsWith("sf:")) {
|
|
2269
|
-
const sf = filterBy.slice(3);
|
|
2270
|
-
entries = entries.filter((f) => f.subfamily === sf);
|
|
2271
|
-
}
|
|
2272
|
-
if (searchQuery.trim()) {
|
|
2273
|
-
const q = searchQuery.toLowerCase().trim();
|
|
2274
|
-
entries = entries.filter(
|
|
2275
|
-
(f) => {
|
|
2276
|
-
var _a2, _b2, _c2, _d;
|
|
2277
|
-
return ((_a2 = f.title) == null ? void 0 : _a2.toLowerCase().includes(q)) || ((_b2 = f.documentId) == null ? void 0 : _b2.toLowerCase().includes(q)) || ((_c2 = f.sourceFileName) == null ? void 0 : _c2.toLowerCase().includes(q)) || ((_d = f.weightName) == null ? void 0 : _d.toLowerCase().includes(q));
|
|
2278
|
-
}
|
|
2279
|
-
);
|
|
2280
|
-
}
|
|
2281
|
-
return entries;
|
|
2282
|
-
}, [fontEntries, filterBy, searchQuery]);
|
|
2283
|
-
const visibleTempIds = useMemo4(() => visibleEntries.map((e) => e.tempId), [visibleEntries]);
|
|
2284
|
-
const hasConflicts = fontEntries.some((f) => f._idConflict);
|
|
2285
|
-
const sortEntries = useCallback3((a, b) => {
|
|
2286
|
-
var _a2, _b2;
|
|
2287
|
-
const dir = sortDir === "asc" ? 1 : -1;
|
|
2288
|
-
let cmp = 0;
|
|
2289
|
-
switch (sortBy) {
|
|
2290
|
-
case "title":
|
|
2291
|
-
cmp = (a.title || "").localeCompare(b.title || "");
|
|
2292
|
-
break;
|
|
2293
|
-
case "weight":
|
|
2294
|
-
cmp = a.weight - b.weight;
|
|
2295
|
-
if (cmp === 0) {
|
|
2296
|
-
if (a.style === "Italic" && b.style !== "Italic") cmp = 1;
|
|
2297
|
-
else if (a.style !== "Italic" && b.style === "Italic") cmp = -1;
|
|
2298
|
-
}
|
|
2299
|
-
break;
|
|
2300
|
-
case "style":
|
|
2301
|
-
cmp = (a.style || "").localeCompare(b.style || "");
|
|
2302
|
-
break;
|
|
2303
|
-
case "files":
|
|
2304
|
-
cmp = (((_a2 = a.files) == null ? void 0 : _a2.length) || 0) - (((_b2 = b.files) == null ? void 0 : _b2.length) || 0);
|
|
2305
|
-
break;
|
|
2306
|
-
case "action": {
|
|
2307
|
-
const aUpdate = isUpdateEntry2(a) ? 1 : 0;
|
|
2308
|
-
const bUpdate = isUpdateEntry2(b) ? 1 : 0;
|
|
2309
|
-
cmp = aUpdate - bUpdate;
|
|
2310
|
-
break;
|
|
2311
|
-
}
|
|
2312
|
-
default:
|
|
2313
|
-
cmp = a.weight - b.weight;
|
|
2314
|
-
}
|
|
2315
|
-
return cmp * dir;
|
|
2316
|
-
}, [sortBy, sortDir]);
|
|
2317
|
-
const handleSort = useCallback3((column) => {
|
|
2318
|
-
if (sortBy === column) {
|
|
2319
|
-
setSortDir((d) => d === "asc" ? "desc" : "asc");
|
|
2320
|
-
} else {
|
|
2321
|
-
setSortBy(column);
|
|
2322
|
-
setSortDir("asc");
|
|
2323
|
-
}
|
|
2324
|
-
}, [sortBy]);
|
|
2325
|
-
const groupedEntries = useMemo4(() => {
|
|
2326
|
-
const groups = {};
|
|
2327
|
-
for (const entry of visibleEntries) {
|
|
2328
|
-
const sf = entry.subfamily || "Regular";
|
|
2329
|
-
if (!groups[sf]) groups[sf] = [];
|
|
2330
|
-
groups[sf].push(entry);
|
|
2331
|
-
}
|
|
2332
|
-
Object.values(groups).forEach((g) => g.sort(sortEntries));
|
|
2333
|
-
const sorted = {};
|
|
2334
|
-
const keys = Object.keys(groups).sort((a, b) => {
|
|
2335
|
-
if (a === "Regular") return -1;
|
|
2336
|
-
if (b === "Regular") return 1;
|
|
2337
|
-
return a.localeCompare(b);
|
|
2338
|
-
});
|
|
2339
|
-
keys.forEach((k) => {
|
|
2340
|
-
sorted[k] = groups[k];
|
|
2341
|
-
});
|
|
2342
|
-
return sorted;
|
|
2343
|
-
}, [visibleEntries]);
|
|
2344
|
-
const validationErrors = useMemo4(() => {
|
|
2345
|
-
const errors = [];
|
|
2346
|
-
const uploadable = fontEntries.filter((f) => f.status !== FONT_STATUS.ERROR);
|
|
2347
|
-
const missingTitles = uploadable.filter((f) => !f.title || f.title.trim() === "");
|
|
2348
|
-
if (missingTitles.length > 0) {
|
|
2349
|
-
errors.push(`${missingTitles.length} font${missingTitles.length === 1 ? "" : "s"} missing a title`);
|
|
2350
|
-
}
|
|
2351
|
-
const missingIds = uploadable.filter((f) => !f.documentId || f.documentId.trim() === "");
|
|
2352
|
-
if (missingIds.length > 0) {
|
|
2353
|
-
errors.push(`${missingIds.length} font${missingIds.length === 1 ? "" : "s"} missing a document ID`);
|
|
2354
|
-
}
|
|
2355
|
-
if (hasConflicts) {
|
|
2356
|
-
const conflictCount = uploadable.filter((f) => f._idConflict).length;
|
|
2357
|
-
errors.push(`${conflictCount} font${conflictCount === 1 ? "" : "s"} with duplicate document IDs`);
|
|
2358
|
-
}
|
|
2359
|
-
return errors;
|
|
2360
|
-
}, [fontEntries, hasConflicts]);
|
|
2361
|
-
const canUploadValidation = isReviewing && processedCount > 0 && validationErrors.length === 0;
|
|
2362
|
-
const handleUpload = useCallback3(() => {
|
|
2363
|
-
if (validationErrors.length > 0) {
|
|
2364
|
-
window.alert("Please fix the following before uploading:\n\n\u2022 " + validationErrors.join("\n\u2022 "));
|
|
2365
|
-
return;
|
|
2366
|
-
}
|
|
2367
|
-
onStartExecution();
|
|
2368
|
-
}, [validationErrors, onStartExecution]);
|
|
2369
|
-
const handleToggleExpandAll = useCallback3(() => {
|
|
2370
|
-
setAllExpanded((v) => !v);
|
|
2371
|
-
}, []);
|
|
2372
|
-
return /* @__PURE__ */ React6.createElement(Stack4, { space: 3 }, isProcessing && /* @__PURE__ */ React6.createElement(Card4, { border: true, padding: 3, radius: 2 }, /* @__PURE__ */ React6.createElement(Stack4, { space: 3 }, /* @__PURE__ */ React6.createElement(Flex6, { align: "center", gap: 3 }, /* @__PURE__ */ React6.createElement(Spinner, null), /* @__PURE__ */ React6.createElement(Text6, { size: 1 }, "Processing ", plan.processingProgress.completed, " of ", totalCount, " files...")), /* @__PURE__ */ React6.createElement(Box5, { style: { height: 4, background: "var(--card-border-color)", borderRadius: 2, overflow: "hidden" } }, /* @__PURE__ */ React6.createElement(
|
|
2373
|
-
Box5,
|
|
2374
|
-
{
|
|
2375
|
-
style: {
|
|
2376
|
-
height: "100%",
|
|
2377
|
-
width: "100%",
|
|
2378
|
-
transformOrigin: "left",
|
|
2379
|
-
transform: `scaleX(${totalCount > 0 ? plan.processingProgress.completed / totalCount : 0})`,
|
|
2380
|
-
background: "#43b649",
|
|
2381
|
-
transition: "transform 0.3s ease-out",
|
|
2382
|
-
borderRadius: 2
|
|
2383
|
-
}
|
|
2384
|
-
}
|
|
2385
|
-
)), plan.processingProgress.currentFile && /* @__PURE__ */ React6.createElement(Text6, { size: 0, muted: true, style: { fontFamily: "monospace" } }, plan.processingProgress.currentFile), /* @__PURE__ */ React6.createElement(Flex6, { justify: "flex-end" }, /* @__PURE__ */ React6.createElement(
|
|
2386
|
-
Button5,
|
|
2387
|
-
{
|
|
2388
|
-
mode: "ghost",
|
|
2389
|
-
tone: "caution",
|
|
2390
|
-
text: "Cancel Processing",
|
|
2391
|
-
fontSize: 1,
|
|
2392
|
-
padding: 2,
|
|
2393
|
-
onClick: onCancelProcessing
|
|
2394
|
-
}
|
|
2395
|
-
)))), isReviewing && /* @__PURE__ */ React6.createElement(Card4, { tone: errorCount > 0 ? "caution" : "positive", border: true, padding: 3, radius: 2 }, /* @__PURE__ */ React6.createElement(Flex6, { align: "center", gap: 3 }, /* @__PURE__ */ React6.createElement(Text6, { size: 1, weight: "semibold" }, processedCount, " document", processedCount === 1 ? "" : "s"), /* @__PURE__ */ React6.createElement(Flex6, { gap: 1 }, createCount > 0 && /* @__PURE__ */ React6.createElement(Badge4, { tone: "positive", fontSize: 0 }, createCount, " create"), updateCount > 0 && /* @__PURE__ */ React6.createElement(Badge4, { tone: "caution", fontSize: 0 }, updateCount, " update"), errorCount > 0 && /* @__PURE__ */ React6.createElement(Badge4, { tone: "critical", fontSize: 0 }, errorCount, " error", errorCount === 1 ? "" : "s")))), isReviewing && /* @__PURE__ */ React6.createElement(React6.Fragment, null, /* @__PURE__ */ React6.createElement(Text6, { size: 1, weight: "semibold" }, "Settings"), /* @__PURE__ */ React6.createElement(Card4, { border: true, padding: 3, radius: 2 }, /* @__PURE__ */ React6.createElement(Stack4, { space: 3 }, /* @__PURE__ */ React6.createElement(Grid, { columns: [2], gap: 4 }, /* @__PURE__ */ React6.createElement(Box5, null, /* @__PURE__ */ React6.createElement(
|
|
2396
|
-
PriceInput_default,
|
|
2397
|
-
{
|
|
2398
|
-
inputPrice: localPrice,
|
|
2399
|
-
handleInputChange: (e) => {
|
|
2400
|
-
setLocalPrice(e.target.value);
|
|
2401
|
-
dispatch({ type: "SET_SETTINGS", settings: { price: Number(e.target.value) || 0 } });
|
|
2402
|
-
}
|
|
2403
|
-
}
|
|
2404
|
-
)), /* @__PURE__ */ React6.createElement(Stack4, { space: 3 }, /* @__PURE__ */ React6.createElement(Flex6, { align: "center", gap: 2 }, /* @__PURE__ */ React6.createElement(
|
|
2405
|
-
Switch2,
|
|
2406
|
-
{
|
|
2407
|
-
checked: localPreserveShortenedNames,
|
|
2408
|
-
onChange: (e) => {
|
|
2409
|
-
setLocalPreserveShortenedNames(e.target.checked);
|
|
2410
|
-
dispatch({ type: "SET_SETTINGS", settings: { preserveShortenedNames: e.target.checked } });
|
|
2411
|
-
}
|
|
2412
|
-
}
|
|
2413
|
-
), /* @__PURE__ */ React6.createElement(
|
|
2414
|
-
Tooltip3,
|
|
2415
|
-
{
|
|
2416
|
-
content: /* @__PURE__ */ React6.createElement(Box5, { padding: 2, style: { maxWidth: 260 } }, /* @__PURE__ */ React6.createElement(Text6, { size: 1, style: { lineHeight: 1.6 } }, 'Abbreviations in font names are kept as-is (e.g. "XNarrow" stays "XNarrow").')),
|
|
2417
|
-
placement: "top",
|
|
2418
|
-
portal: true
|
|
2419
|
-
},
|
|
2420
|
-
/* @__PURE__ */ React6.createElement(Flex6, { align: "center", gap: 1, style: { cursor: "default" } }, /* @__PURE__ */ React6.createElement(Label4, null, "Preserve shortened names"), /* @__PURE__ */ React6.createElement(InfoOutlineIcon3, { style: { opacity: 0.5, display: "block" } }))
|
|
2421
|
-
)), /* @__PURE__ */ React6.createElement(Flex6, { align: "center", gap: 2 }, /* @__PURE__ */ React6.createElement(
|
|
2422
|
-
Switch2,
|
|
2423
|
-
{
|
|
2424
|
-
checked: localPreserveFileNames,
|
|
2425
|
-
onChange: (e) => {
|
|
2426
|
-
setLocalPreserveFileNames(e.target.checked);
|
|
2427
|
-
dispatch({ type: "SET_SETTINGS", settings: { preserveFileNames: e.target.checked } });
|
|
2428
|
-
}
|
|
2429
|
-
}
|
|
2430
|
-
), /* @__PURE__ */ React6.createElement(
|
|
2431
|
-
Tooltip3,
|
|
2432
|
-
{
|
|
2433
|
-
content: /* @__PURE__ */ React6.createElement(Box5, { padding: 2, style: { maxWidth: 260 } }, /* @__PURE__ */ React6.createElement(Text6, { size: 1, style: { lineHeight: 1.6 } }, "Original filename is used for the font title and document ID instead of embedded font metadata.")),
|
|
2434
|
-
placement: "top",
|
|
2435
|
-
portal: true
|
|
2436
|
-
},
|
|
2437
|
-
/* @__PURE__ */ React6.createElement(Flex6, { align: "center", gap: 1, style: { cursor: "default" } }, /* @__PURE__ */ React6.createElement(Label4, null, "Preserve file names"), /* @__PURE__ */ React6.createElement(InfoOutlineIcon3, { style: { opacity: 0.5, display: "block" } }))
|
|
2438
|
-
))))))), fontEntries.length > 0 && /* @__PURE__ */ React6.createElement(
|
|
2439
|
-
BulkActions,
|
|
2440
|
-
{
|
|
2441
|
-
fonts: plan.fonts,
|
|
2442
|
-
dispatch,
|
|
2443
|
-
searchQuery,
|
|
2444
|
-
onSearchChange: setSearchQuery,
|
|
2445
|
-
filterBy,
|
|
2446
|
-
onFilterChange: setFilterBy,
|
|
2447
|
-
allExpanded,
|
|
2448
|
-
onToggleExpandAll: handleToggleExpandAll,
|
|
2449
|
-
visibleTempIds
|
|
2450
|
-
}
|
|
2451
|
-
), fontEntries.length > 0 && isReviewing && /* @__PURE__ */ React6.createElement(Flex6, { justify: "flex-start", paddingY: 1 }, /* @__PURE__ */ React6.createElement(
|
|
2452
|
-
Button5,
|
|
2453
|
-
{
|
|
2454
|
-
mode: "ghost",
|
|
2455
|
-
fontSize: 0,
|
|
2456
|
-
padding: 2,
|
|
2457
|
-
text: allExpanded ? "Collapse All" : "Expand All",
|
|
2458
|
-
onClick: handleToggleExpandAll,
|
|
2459
|
-
style: { cursor: "pointer" }
|
|
2460
|
-
}
|
|
2461
|
-
)), fontEntries.length > 0 && isReviewing && /* @__PURE__ */ React6.createElement(
|
|
2462
|
-
Flex6,
|
|
2463
|
-
{
|
|
2464
|
-
align: "center",
|
|
2465
|
-
gap: 2,
|
|
2466
|
-
paddingX: 2,
|
|
2467
|
-
paddingY: 1,
|
|
2468
|
-
style: { borderBottom: "1px solid var(--card-border-color)", userSelect: "none" }
|
|
2469
|
-
},
|
|
2470
|
-
/* @__PURE__ */ React6.createElement(Box5, { style: { width: 20 } }),
|
|
2471
|
-
[
|
|
2472
|
-
{ key: "title", label: "Font Title", style: { flex: 1, cursor: "pointer" } },
|
|
2473
|
-
{ key: "weight", label: "Weight", style: { width: 50, textAlign: "center", cursor: "pointer" } },
|
|
2474
|
-
{ key: "style", label: "Style", style: { width: 50, textAlign: "center", cursor: "pointer" } },
|
|
2475
|
-
{ key: "files", label: "Files", style: { width: 40, textAlign: "center", cursor: "pointer" } },
|
|
2476
|
-
{ key: "action", label: "Action", style: { width: 55, textAlign: "center", cursor: "pointer" } }
|
|
2477
|
-
].map((col) => /* @__PURE__ */ React6.createElement(
|
|
2478
|
-
Text6,
|
|
2479
|
-
{
|
|
2480
|
-
key: col.key,
|
|
2481
|
-
size: 0,
|
|
2482
|
-
weight: "semibold",
|
|
2483
|
-
muted: sortBy !== col.key,
|
|
2484
|
-
style: col.style,
|
|
2485
|
-
onClick: () => handleSort(col.key)
|
|
2486
|
-
},
|
|
2487
|
-
col.label,
|
|
2488
|
-
" ",
|
|
2489
|
-
sortBy === col.key ? sortDir === "asc" ? "\u2191" : "\u2193" : ""
|
|
2490
|
-
))
|
|
2491
|
-
), Object.entries(groupedEntries).map(([subfamily, entries]) => /* @__PURE__ */ React6.createElement(Stack4, { key: subfamily, space: 1 }, /* @__PURE__ */ React6.createElement(Card4, { padding: 2, radius: 1, style: { background: "var(--card-muted-bg-color)" } }, /* @__PURE__ */ React6.createElement(Flex6, { align: "center", gap: 2 }, /* @__PURE__ */ React6.createElement(Text6, { size: 1, weight: "semibold" }, subfamily), /* @__PURE__ */ React6.createElement(Badge4, { mode: "outline", fontSize: 0 }, entries.length))), /* @__PURE__ */ React6.createElement(Stack4, { space: 0 }, entries.map((entry) => {
|
|
2492
|
-
var _a2, _b2;
|
|
2493
|
-
return /* @__PURE__ */ React6.createElement(
|
|
2494
|
-
FontReviewCard_default,
|
|
2495
|
-
{
|
|
2496
|
-
key: entry.tempId,
|
|
2497
|
-
entry,
|
|
2498
|
-
dispatch,
|
|
2499
|
-
allExpanded,
|
|
2500
|
-
typefaceTitle: (_a2 = plan.settings) == null ? void 0 : _a2.typefaceTitle,
|
|
2501
|
-
price: (_b2 = plan.settings) == null ? void 0 : _b2.price
|
|
2502
|
-
}
|
|
2503
|
-
);
|
|
2504
|
-
})))), visibleEntries.length === 0 && fontEntries.length > 0 && /* @__PURE__ */ React6.createElement(Card4, { border: true, padding: 4, radius: 2 }, /* @__PURE__ */ React6.createElement(Text6, { size: 1, muted: true, align: "center" }, "No fonts match the current filter")), isReviewing && validationErrors.length > 0 && /* @__PURE__ */ React6.createElement(Card4, { tone: "caution", border: true, padding: 2, radius: 2 }, /* @__PURE__ */ React6.createElement(Stack4, { space: 1 }, validationErrors.map((err, i) => /* @__PURE__ */ React6.createElement(Text6, { key: i, size: 0, tone: "caution" }, "\u2022 ", err)))), isReviewing && processedCount > 0 && /* @__PURE__ */ React6.createElement(Flex6, { justify: "flex-end", gap: 2, style: { position: "sticky", bottom: 0, background: "var(--card-bg-color)", paddingTop: 8, paddingBottom: 4 } }, /* @__PURE__ */ React6.createElement(
|
|
2505
|
-
Button5,
|
|
2506
|
-
{
|
|
2507
|
-
mode: "default",
|
|
2508
|
-
tone: "primary",
|
|
2509
|
-
text: `Upload ${processedCount} Font${processedCount === 1 ? "" : "s"} to Sanity`,
|
|
2510
|
-
disabled: !canUploadValidation,
|
|
2511
|
-
onClick: handleUpload
|
|
2512
|
-
}
|
|
2513
|
-
)));
|
|
2514
|
-
}
|
|
2515
|
-
|
|
2516
|
-
// src/components/UploadStep3Execute.jsx
|
|
2517
|
-
import React7, { useEffect as useEffect3, useReducer, useRef as useRef2, useState as useState4, useMemo as useMemo5 } from "react";
|
|
2518
|
-
import { Box as Box6, Stack as Stack5, Flex as Flex7, Text as Text7, Card as Card5, Spinner as Spinner2, Badge as Badge5 } from "@sanity/ui";
|
|
2519
|
-
import { WarningOutlineIcon, CheckmarkCircleIcon } from "@sanity/icons";
|
|
2520
|
-
|
|
2521
|
-
// src/utils/executeUploadPlan.js
|
|
2522
|
-
import { nanoid as nanoid5 } from "nanoid";
|
|
2523
|
-
|
|
2524
|
-
// src/utils/generateCssFile.js
|
|
2525
|
-
import base64 from "base-64";
|
|
2526
|
-
function _arrayBufferToBase64(buffer) {
|
|
2527
|
-
var binary = "";
|
|
2528
|
-
var bytes = new Uint8Array(buffer);
|
|
2529
|
-
var len = bytes.byteLength;
|
|
2530
|
-
for (var i = 0; i < len; i++) {
|
|
2531
|
-
binary += String.fromCharCode(bytes[i]);
|
|
2532
|
-
}
|
|
2533
|
-
return base64.encode(binary);
|
|
2534
|
-
}
|
|
2535
|
-
function buildVFDescriptors(axisMap) {
|
|
2536
|
-
const cssAxes = {};
|
|
2537
|
-
const skipped = [];
|
|
2538
|
-
if (!axisMap) return { descriptors: "", skipped: [] };
|
|
2539
|
-
try {
|
|
2540
|
-
for (const [tag, axis] of Object.entries(axisMap)) {
|
|
2541
|
-
const lo = Math.min(axis.min, axis.max);
|
|
2542
|
-
const hi = Math.max(axis.min, axis.max);
|
|
2543
|
-
if (lo === hi) {
|
|
2544
|
-
skipped.push(tag);
|
|
2545
|
-
continue;
|
|
2546
|
-
}
|
|
2547
|
-
if (tag === "wght") {
|
|
2548
|
-
cssAxes["font-weight"] = `${lo} ${hi}`;
|
|
2549
|
-
} else if (tag === "wdth") {
|
|
2550
|
-
cssAxes["font-stretch"] = `${Math.max(50, lo)}% ${Math.min(200, hi)}%`;
|
|
2551
|
-
} else if (tag === "slnt") {
|
|
2552
|
-
cssAxes["font-style"] = `oblique ${-hi}deg ${-lo}deg`;
|
|
2553
|
-
} else if (tag === "ital" && !cssAxes["font-style"]) {
|
|
2554
|
-
if (hi > 0) cssAxes["font-style"] = "italic";
|
|
2555
|
-
else skipped.push(tag);
|
|
2556
|
-
} else {
|
|
2557
|
-
skipped.push(tag);
|
|
2558
|
-
}
|
|
2559
|
-
}
|
|
2560
|
-
} catch (_) {
|
|
2561
|
-
}
|
|
2562
|
-
const descriptors = Object.entries(cssAxes).map(([k, v]) => `${k}:${v}`).join(";") + (Object.keys(cssAxes).length ? ";" : "");
|
|
2563
|
-
return { descriptors, skipped };
|
|
2564
|
-
}
|
|
2565
|
-
var FALLBACK_STACKS = {
|
|
2566
|
-
"sans-serif": "local('Arial'), local('Helvetica Neue'), local('Roboto'), local('Liberation Sans')",
|
|
2567
|
-
"serif": "local('Georgia'), local('Times New Roman'), local('Times')",
|
|
2568
|
-
"monospace": "local('Courier New'), local('Courier'), local('Menlo'), local('Monaco')",
|
|
2569
|
-
"default": "local('Arial'), local('Helvetica Neue'), local('Roboto'), local('Liberation Sans')"
|
|
2570
|
-
};
|
|
2571
|
-
var FAMILY_CLASS_MAP = {
|
|
2572
|
-
1: "serif",
|
|
2573
|
-
2: "serif",
|
|
2574
|
-
3: "serif",
|
|
2575
|
-
4: "serif",
|
|
2576
|
-
5: "serif",
|
|
2577
|
-
7: "serif",
|
|
2578
|
-
8: "sans-serif"
|
|
2579
|
-
};
|
|
2580
|
-
var SERIF_NAMES = /jubilat|corundum|dapifer|birra|daith/i;
|
|
2581
|
-
var SANS_NAMES = /halyard|gamay|omnes|kit/i;
|
|
2582
|
-
function detectFontCategory(font, fontName) {
|
|
2583
|
-
if (fontName && SERIF_NAMES.test(fontName)) return "serif";
|
|
2584
|
-
if (fontName && SANS_NAMES.test(fontName)) return "sans-serif";
|
|
2585
|
-
try {
|
|
2586
|
-
const familyClass = getFamilyClass(font);
|
|
2587
|
-
const highByte = familyClass >> 8 & 255;
|
|
2588
|
-
return FAMILY_CLASS_MAP[highByte] ?? "default";
|
|
2589
|
-
} catch {
|
|
2590
|
-
return "default";
|
|
2591
|
-
}
|
|
2592
|
-
}
|
|
2593
|
-
function calcFallbackData(font, fontName) {
|
|
2594
|
-
try {
|
|
2595
|
-
const metrics = getFontMetrics(font);
|
|
2596
|
-
const upm = metrics.unitsPerEm;
|
|
2597
|
-
const category = detectFontCategory(font, fontName);
|
|
2598
|
-
return {
|
|
2599
|
-
fallbackSrc: FALLBACK_STACKS[category],
|
|
2600
|
-
ascentOverride: `${(metrics.ascender / upm * 100).toFixed(2)}%`,
|
|
2601
|
-
descentOverride: `${(Math.abs(metrics.descender) / upm * 100).toFixed(2)}%`,
|
|
2602
|
-
lineGapOverride: `${(metrics.lineGap / upm * 100).toFixed(2)}%`
|
|
2603
|
-
};
|
|
2604
|
-
} catch (err) {
|
|
2605
|
-
console.error("Failed to extract fallback font data:", err);
|
|
2606
|
-
return {
|
|
2607
|
-
fallbackSrc: FALLBACK_STACKS["default"],
|
|
2608
|
-
ascentOverride: "100%",
|
|
2609
|
-
descentOverride: "0%",
|
|
2610
|
-
lineGapOverride: "0%"
|
|
2611
|
-
};
|
|
2612
|
-
}
|
|
2613
|
-
}
|
|
2614
|
-
async function generateCssFile({
|
|
2615
|
-
woff2File,
|
|
2616
|
-
fileInput,
|
|
2617
|
-
language = null,
|
|
2618
|
-
fileName,
|
|
2619
|
-
fontName,
|
|
2620
|
-
variableFont,
|
|
2621
|
-
weight,
|
|
2622
|
-
style = "Normal",
|
|
2623
|
-
client
|
|
2624
|
-
}) {
|
|
2625
|
-
try {
|
|
2626
|
-
let arrayBuffer = await woff2File.arrayBuffer();
|
|
2627
|
-
let b64 = _arrayBufferToBase64(arrayBuffer);
|
|
2628
|
-
let font = await parseFont(arrayBuffer, fileName + ".woff2");
|
|
2629
|
-
let { fallbackSrc, ascentOverride, descentOverride, lineGapOverride } = calcFallbackData(font, fontName);
|
|
2630
|
-
const safeFontName = escapeCssFontName(fontName);
|
|
2631
|
-
let cssString;
|
|
2632
|
-
if (variableFont) {
|
|
2633
|
-
const axisMap = getVariationAxes(font);
|
|
2634
|
-
let { descriptors, skipped } = buildVFDescriptors(axisMap);
|
|
2635
|
-
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" : "") + " */" : "";
|
|
2636
|
-
cssString = `${skipComment}@font-face{font-family:'${safeFontName}';src:url(data:application/font-woff2;charset=utf-8;base64,${b64})format('woff2');${descriptors}font-display:swap;}`;
|
|
2637
|
-
} else {
|
|
2638
|
-
let fontStyle = style === "Italic" ? "italic" : "normal";
|
|
2639
|
-
cssString = `@font-face{font-family:'${safeFontName}';src:url(data:application/font-woff2;charset=utf-8;base64,${b64})format('woff2');font-weight:${weight};font-style:${fontStyle};font-display:swap;}`;
|
|
2640
|
-
}
|
|
2641
|
-
let fallbackCssString = `@font-face{font-family:'${safeFontName} Fallback';src:${fallbackSrc};ascent-override:${ascentOverride};descent-override:${descentOverride};line-gap-override:${lineGapOverride};}`;
|
|
2642
|
-
const cssBytes = new TextEncoder().encode(cssString + fallbackCssString);
|
|
2643
|
-
let doc = await client.assets.upload("file", new Blob([cssBytes]), { filename: fileName + ".css" });
|
|
2644
|
-
let newFileInput = language == null ? {
|
|
2645
|
-
...fileInput,
|
|
2646
|
-
css: {
|
|
2647
|
-
_type: "file",
|
|
2648
|
-
asset: {
|
|
2649
|
-
_type: "reference",
|
|
2650
|
-
_ref: doc._id
|
|
2651
|
-
}
|
|
2652
|
-
}
|
|
2653
|
-
} : {
|
|
2654
|
-
...fileInput,
|
|
2655
|
-
[language]: {
|
|
2656
|
-
...fileInput[language],
|
|
2657
|
-
css: {
|
|
2658
|
-
_type: "file",
|
|
2659
|
-
asset: {
|
|
2660
|
-
_type: "reference",
|
|
2661
|
-
_ref: doc._id
|
|
2662
|
-
}
|
|
2663
|
-
}
|
|
2664
|
-
}
|
|
2665
|
-
};
|
|
2666
|
-
return newFileInput;
|
|
2667
|
-
} catch (err) {
|
|
2668
|
-
console.error(err);
|
|
2669
|
-
throw err;
|
|
2670
|
-
}
|
|
2671
|
-
}
|
|
2672
|
-
|
|
2673
|
-
// src/utils/generateFontData.js
|
|
2674
|
-
function buildFontMetadata(font) {
|
|
2675
|
-
const metaData = getFontMetadata(font);
|
|
2676
|
-
if (metaData.version) {
|
|
2677
|
-
metaData.version = String(metaData.version).replaceAll("Version ", "");
|
|
2678
|
-
}
|
|
2679
|
-
const metrics = getFontMetrics(font);
|
|
2680
|
-
return { metaData, metrics };
|
|
2681
|
-
}
|
|
2682
|
-
async function generateFontData({ fileInput, url, fontKit, fontId, client, commit = true }) {
|
|
2683
|
-
if (fontId.startsWith("drafts.")) {
|
|
2684
|
-
fontId = fontId.replace("drafts.", "");
|
|
2685
|
-
}
|
|
2686
|
-
console.log("Generate font data:", fontId, commit);
|
|
2687
|
-
let srcUrl;
|
|
2688
|
-
if (!url || url == null) {
|
|
2689
|
-
srcUrl = await client.fetch(`*[_id == $id]{url}`, { id: fileInput.ttf.asset._ref });
|
|
2690
|
-
srcUrl = srcUrl[0].url;
|
|
2691
|
-
} else {
|
|
2692
|
-
srcUrl = url;
|
|
2693
|
-
}
|
|
2694
|
-
let font = fontKit;
|
|
2695
|
-
if (!fontKit || fontKit == null) {
|
|
2696
|
-
let buffer = await fetch(srcUrl);
|
|
2697
|
-
buffer = await buffer.arrayBuffer();
|
|
2698
|
-
font = await parseFont(buffer, `${fontId}.ttf`);
|
|
2699
|
-
}
|
|
2700
|
-
const variableAxes = getVariationAxes(font);
|
|
2701
|
-
const namedInstances = getNamedInstances(font);
|
|
2702
|
-
let variableInstances = null;
|
|
2703
|
-
if (namedInstances.length > 0 && variableAxes) {
|
|
2704
|
-
variableInstances = {};
|
|
2705
|
-
const axisTags = Object.keys(variableAxes);
|
|
2706
|
-
for (const inst of namedInstances) {
|
|
2707
|
-
const key = inst.name || inst.postScriptName || "Unknown";
|
|
2708
|
-
const coord = {};
|
|
2709
|
-
axisTags.forEach((tag, index) => {
|
|
2710
|
-
coord[tag] = inst.coordinates[index];
|
|
2711
|
-
});
|
|
2712
|
-
variableInstances[key] = coord;
|
|
2713
|
-
}
|
|
2714
|
-
}
|
|
2715
|
-
console.log("Variable instances:", variableInstances);
|
|
2716
|
-
console.log("Variable axes:", variableAxes);
|
|
2717
|
-
const opentypeFeatures = getAllFeatureTags(font);
|
|
2718
|
-
const glyphCount = getGlyphCount(font);
|
|
2719
|
-
const characterSet = getCharacterSet(font);
|
|
2720
|
-
const { metaData, metrics } = buildFontMetadata(font);
|
|
2721
|
-
let variableFont = false;
|
|
2722
|
-
if (variableAxes && variableInstances && Object.keys(variableInstances).length > 0) {
|
|
2723
|
-
variableFont = true;
|
|
2724
|
-
}
|
|
2725
|
-
let patch = {
|
|
2726
|
-
metrics,
|
|
2727
|
-
metaData,
|
|
2728
|
-
variableFont,
|
|
2729
|
-
variableAxes: JSON.stringify(variableAxes),
|
|
2730
|
-
variableInstances: JSON.stringify(variableInstances),
|
|
2731
|
-
glyphCount,
|
|
2732
|
-
opentypeFeatures: { chars: opentypeFeatures },
|
|
2733
|
-
characterSet: { chars: characterSet }
|
|
2734
|
-
};
|
|
2735
|
-
console.log("Font data patch:", Object.keys(patch));
|
|
2736
|
-
if (commit) patch = await client.patch(fontId).set(patch).commit({ autoGenerateArrayKeys: true });
|
|
2737
|
-
return patch;
|
|
2738
|
-
}
|
|
2739
|
-
|
|
2740
|
-
// src/utils/parseVariableFontInstances.js
|
|
2741
|
-
import { nanoid as nanoid3 } from "nanoid";
|
|
2742
|
-
var WIDTH_PREFIXES = [
|
|
2743
|
-
"XXXWide",
|
|
2744
|
-
"XXWide",
|
|
2745
|
-
"XWide",
|
|
2746
|
-
"Wide",
|
|
2747
|
-
"XXXNarrow",
|
|
2748
|
-
"XXNarrow",
|
|
2749
|
-
"XNarrow",
|
|
2750
|
-
"Narrow"
|
|
2751
|
-
];
|
|
2752
|
-
function parseInstanceName(instanceName) {
|
|
2753
|
-
let subfamily = "";
|
|
2754
|
-
let remaining = instanceName.trim();
|
|
2755
|
-
for (const prefix of WIDTH_PREFIXES) {
|
|
2756
|
-
if (remaining.toLowerCase().startsWith(prefix.toLowerCase() + " ") || remaining.toLowerCase() === prefix.toLowerCase()) {
|
|
2757
|
-
subfamily = prefix;
|
|
2758
|
-
remaining = remaining.substring(prefix.length).trim();
|
|
2759
|
-
break;
|
|
2760
|
-
}
|
|
2761
|
-
}
|
|
2762
|
-
let style = "";
|
|
2763
|
-
for (const suffix of ["Backslant", "Slant", "Italic", "Oblique"]) {
|
|
2764
|
-
if (remaining.toLowerCase().endsWith(" " + suffix.toLowerCase()) || remaining.toLowerCase() === suffix.toLowerCase()) {
|
|
2765
|
-
style = suffix;
|
|
2766
|
-
remaining = remaining.substring(0, remaining.length - suffix.length).trim();
|
|
2767
|
-
break;
|
|
2768
|
-
}
|
|
2769
|
-
}
|
|
2770
|
-
return { subfamily, weight: remaining || "Regular", style };
|
|
2771
|
-
}
|
|
2772
|
-
function filterBySubfamily(staticFonts, instanceSubfamily, typefaceName) {
|
|
2773
|
-
if (!instanceSubfamily) {
|
|
2774
|
-
return staticFonts.filter((sf) => {
|
|
2775
|
-
const sub = (sf.subfamily || "").toLowerCase();
|
|
2776
|
-
if (sub === "" || sub === "regular") return true;
|
|
2777
|
-
const afterTypeface = (sf.title || "").replace(typefaceName, "").trim();
|
|
2778
|
-
return !WIDTH_PREFIXES.some((p) => afterTypeface.toLowerCase().startsWith(p.toLowerCase()));
|
|
2779
|
-
});
|
|
2780
|
-
}
|
|
2781
|
-
const lowerSf = instanceSubfamily.toLowerCase();
|
|
2782
|
-
const expanded = (expandAbbreviations(instanceSubfamily) || "").toLowerCase();
|
|
2783
|
-
return staticFonts.filter((sf) => {
|
|
2784
|
-
const sub = (sf.subfamily || "").toLowerCase();
|
|
2785
|
-
if (sub === lowerSf || expanded && sub === expanded) return true;
|
|
2786
|
-
const afterTypeface = (sf.title || "").replace(typefaceName, "").trim().toLowerCase();
|
|
2787
|
-
if (afterTypeface.startsWith(lowerSf)) return true;
|
|
2788
|
-
if (expanded && afterTypeface.startsWith(expanded)) return true;
|
|
2789
|
-
return false;
|
|
2790
|
-
});
|
|
2791
|
-
}
|
|
2792
|
-
var WEIGHT_MAP = [
|
|
2793
|
-
{ term: "ultra", weight: 950 },
|
|
2794
|
-
{ term: "xxlight", weight: 200 },
|
|
2795
|
-
{ term: "xlight", weight: 250 },
|
|
2796
|
-
{ term: "extralight", weight: 200 },
|
|
2797
|
-
{ term: "extra light", weight: 200 },
|
|
2798
|
-
{ term: "thin", weight: 100 },
|
|
2799
|
-
{ term: "hairline", weight: 100 },
|
|
2800
|
-
{ term: "light", weight: 300 },
|
|
2801
|
-
{ term: "regular", weight: 400 },
|
|
2802
|
-
{ term: "normal", weight: 400 },
|
|
2803
|
-
{ term: "medium", weight: 500 },
|
|
2804
|
-
{ term: "semibold", weight: 600 },
|
|
2805
|
-
{ term: "semi bold", weight: 600 },
|
|
2806
|
-
{ term: "extrabold", weight: 800 },
|
|
2807
|
-
{ term: "extra bold", weight: 800 },
|
|
2808
|
-
{ term: "xbold", weight: 800 },
|
|
2809
|
-
{ term: "bold", weight: 700 },
|
|
2810
|
-
{ term: "black", weight: 900 },
|
|
2811
|
-
{ term: "heavy", weight: 900 }
|
|
2812
|
-
];
|
|
2813
|
-
function weightFromName(name) {
|
|
2814
|
-
const lower = name.toLowerCase();
|
|
2815
|
-
for (const { term, weight } of WEIGHT_MAP) {
|
|
2816
|
-
if (lower === term || lower.includes(term)) return weight;
|
|
2817
|
-
}
|
|
2818
|
-
return 400;
|
|
2819
|
-
}
|
|
2820
|
-
var STRATEGIES = [
|
|
2821
|
-
// Pass 1: Exact title match (with typeface prefix)
|
|
2822
|
-
{
|
|
2823
|
-
name: "exact-title",
|
|
2824
|
-
match: (instanceName, parsed, candidates, typefaceName) => {
|
|
2825
|
-
const withPrefix = `${typefaceName} ${instanceName}`;
|
|
2826
|
-
return candidates.find((sf) => sf.title === instanceName || sf.title === withPrefix) || null;
|
|
2827
|
-
}
|
|
2828
|
-
},
|
|
2829
|
-
// Pass 2: Title normalisation — strip typeface name and compare remainder
|
|
2830
|
-
{
|
|
2831
|
-
name: "title-normalised",
|
|
2832
|
-
match: (instanceName, parsed, candidates, typefaceName) => {
|
|
2833
|
-
return candidates.find((sf) => {
|
|
2834
|
-
const sfName = (sf.title || "").replace(typefaceName, "").trim();
|
|
2835
|
-
if (sfName.toLowerCase() === instanceName.toLowerCase()) return true;
|
|
2836
|
-
if (parsed.weight === "Regular" && !parsed.style) {
|
|
2837
|
-
if (sfName.toLowerCase() === parsed.subfamily.toLowerCase()) return true;
|
|
2838
|
-
}
|
|
2839
|
-
return false;
|
|
2840
|
-
}) || null;
|
|
2841
|
-
}
|
|
2842
|
-
},
|
|
2843
|
-
// Pass 3: Abbreviation expansion (XLight → ExtraLight, XBold → ExtraBold)
|
|
2844
|
-
{
|
|
2845
|
-
name: "abbreviation",
|
|
2846
|
-
match: (instanceName, parsed, candidates, typefaceName) => {
|
|
2847
|
-
const expandedFull = instanceName.split(" ").map((w) => expandAbbreviations(w) || w).join(" ");
|
|
2848
|
-
let found = candidates.find((sf) => {
|
|
2849
|
-
const sfName = (sf.title || "").replace(typefaceName, "").trim();
|
|
2850
|
-
return sfName.toLowerCase() === expandedFull.toLowerCase();
|
|
2851
|
-
});
|
|
2852
|
-
if (found) return found;
|
|
2853
|
-
const expandedWeight = expandAbbreviations(parsed.weight) || parsed.weight;
|
|
2854
|
-
const target = [parsed.subfamily, expandedWeight, parsed.style].filter(Boolean).join(" ");
|
|
2855
|
-
return candidates.find((sf) => {
|
|
2856
|
-
const sfName = (sf.title || "").replace(typefaceName, "").trim();
|
|
2857
|
-
return sfName.toLowerCase() === target.toLowerCase();
|
|
2858
|
-
}) || null;
|
|
2859
|
-
}
|
|
2860
|
-
},
|
|
2861
|
-
// Pass 4: fullName metadata comparison
|
|
2862
|
-
{
|
|
2863
|
-
name: "metadata-fullName",
|
|
2864
|
-
match: (instanceName, parsed, candidates, typefaceName) => {
|
|
2865
|
-
return candidates.find((sf) => {
|
|
2866
|
-
var _a;
|
|
2867
|
-
if (!((_a = sf.metaData) == null ? void 0 : _a.fullName)) return false;
|
|
2868
|
-
const typefacePattern = new RegExp(`^${typefaceName}\\s+`, "i");
|
|
2869
|
-
const stylePart = sf.metaData.fullName.replace(typefacePattern, "").trim();
|
|
2870
|
-
return instanceName.toLowerCase() === stylePart.toLowerCase();
|
|
2871
|
-
}) || null;
|
|
2872
|
-
}
|
|
2873
|
-
},
|
|
2874
|
-
// Pass 5: Weight + style matching (numeric, within subfamily)
|
|
2875
|
-
{
|
|
2876
|
-
name: "weight-style",
|
|
2877
|
-
match: (instanceName, parsed, candidates) => {
|
|
2878
|
-
const instanceWeight = weightFromName(parsed.weight);
|
|
2879
|
-
const isBackslant = parsed.style.toLowerCase() === "backslant";
|
|
2880
|
-
const isSlant = parsed.style.toLowerCase() === "slant";
|
|
2881
|
-
const isItalic = parsed.style.toLowerCase() === "italic";
|
|
2882
|
-
return candidates.find((sf) => {
|
|
2883
|
-
var _a, _b;
|
|
2884
|
-
if (Number(sf.weight) !== instanceWeight) return false;
|
|
2885
|
-
if (isBackslant) return sf.style === "Italic" && ((_a = sf.title) == null ? void 0 : _a.toLowerCase().includes("backslant"));
|
|
2886
|
-
if (isSlant) return sf.style === "Italic" && !((_b = sf.title) == null ? void 0 : _b.toLowerCase().includes("backslant"));
|
|
2887
|
-
if (isItalic) return sf.style === "Italic";
|
|
2888
|
-
return sf.style === "Regular";
|
|
2889
|
-
}) || null;
|
|
2890
|
-
}
|
|
2891
|
-
},
|
|
2892
|
-
// Pass 6: weightName string comparison
|
|
2893
|
-
{
|
|
2894
|
-
name: "weightName",
|
|
2895
|
-
match: (instanceName, parsed, candidates) => {
|
|
2896
|
-
const cleanInstance = parsed.weight.toLowerCase().trim();
|
|
2897
|
-
return candidates.find((sf) => {
|
|
2898
|
-
if (!sf.weightName) return false;
|
|
2899
|
-
const cleanWeight = sf.weightName.toLowerCase().replace(/italic|slant|backslant/gi, "").trim();
|
|
2900
|
-
return cleanInstance === cleanWeight;
|
|
2901
|
-
}) || null;
|
|
2902
|
-
}
|
|
2903
|
-
}
|
|
2904
|
-
];
|
|
2905
|
-
var parseVariableFontInstances = async (font, client) => {
|
|
2906
|
-
if (!font.variableFont || !font.variableInstances) return [];
|
|
2907
|
-
let variableInstances;
|
|
2908
|
-
try {
|
|
2909
|
-
variableInstances = JSON.parse(font.variableInstances);
|
|
2910
|
-
} catch (err) {
|
|
2911
|
-
console.error("Error parsing variable instances:", err);
|
|
2912
|
-
variableInstances = {};
|
|
2913
|
-
}
|
|
2914
|
-
if (Object.keys(variableInstances).length === 0) return [];
|
|
2915
|
-
let staticFonts;
|
|
2916
|
-
const typeface = await client.fetch(
|
|
2917
|
-
`*[_type == 'typeface' && title == $typefaceName][0]{
|
|
2918
|
-
'fonts': styles.fonts[]-> {
|
|
2919
|
-
_id, title, subfamily, style, weight, weightName, metaData, variableFont
|
|
2920
|
-
}
|
|
2921
|
-
}`,
|
|
2922
|
-
{ typefaceName: font.typefaceName }
|
|
2923
|
-
);
|
|
2924
|
-
if ((typeface == null ? void 0 : typeface.fonts) && typeface.fonts.length > 0) {
|
|
2925
|
-
staticFonts = typeface.fonts.filter((f) => !f.variableFont);
|
|
2926
|
-
console.log("Using curated typeface fonts list:", staticFonts.length, "fonts");
|
|
2927
|
-
} else {
|
|
2928
|
-
console.warn("Typeface not found or no fonts in curated list, falling back to all fonts query");
|
|
2929
|
-
staticFonts = await client.fetch(
|
|
2930
|
-
`*[_type == 'font' && typefaceName == $typefaceName && variableFont != true]{
|
|
2931
|
-
_id, title, subfamily, style, weight, weightName, metaData
|
|
2932
|
-
}`,
|
|
2933
|
-
{ typefaceName: font.typefaceName }
|
|
2934
|
-
);
|
|
2935
|
-
}
|
|
2936
|
-
const instanceNames = Object.keys(variableInstances);
|
|
2937
|
-
console.log("Variable font instances:", instanceNames.length);
|
|
2938
|
-
console.log("Available static fonts:", staticFonts.length);
|
|
2939
|
-
const parsedInstances = instanceNames.map((name) => ({
|
|
2940
|
-
name,
|
|
2941
|
-
parsed: parseInstanceName(name)
|
|
2942
|
-
}));
|
|
2943
|
-
const results = /* @__PURE__ */ new Map();
|
|
2944
|
-
const claimedFontIds = /* @__PURE__ */ new Set();
|
|
2945
|
-
for (const strategy of STRATEGIES) {
|
|
2946
|
-
const unmatched = parsedInstances.filter((inst) => !results.has(inst.name));
|
|
2947
|
-
if (unmatched.length === 0) break;
|
|
2948
|
-
const passMatches = [];
|
|
2949
|
-
for (const inst of unmatched) {
|
|
2950
|
-
const subfamilyCandidates = filterBySubfamily(staticFonts, inst.parsed.subfamily, font.typefaceName).filter((sf) => !claimedFontIds.has(sf._id));
|
|
2951
|
-
const match = strategy.match(inst.name, inst.parsed, subfamilyCandidates, font.typefaceName, font);
|
|
2952
|
-
if (match) {
|
|
2953
|
-
passMatches.push({ instanceName: inst.name, font: match, strategy: strategy.name });
|
|
2954
|
-
}
|
|
2955
|
-
}
|
|
2956
|
-
for (const m of passMatches) {
|
|
2957
|
-
if (!claimedFontIds.has(m.font._id) && !results.has(m.instanceName)) {
|
|
2958
|
-
results.set(m.instanceName, { fontId: m.font._id, strategy: m.strategy });
|
|
2959
|
-
claimedFontIds.add(m.font._id);
|
|
2960
|
-
}
|
|
2961
|
-
}
|
|
2962
|
-
}
|
|
2963
|
-
const matched = [...results.values()].length;
|
|
2964
|
-
console.log(`[parseVariableFontInstances] Matched ${matched}/${instanceNames.length} instances across ${STRATEGIES.length} passes`);
|
|
2965
|
-
const instanceMappings = instanceNames.map((instanceName) => {
|
|
2966
|
-
const result = results.get(instanceName);
|
|
2967
|
-
const matchedFont = result ? staticFonts.find((sf) => sf._id === result.fontId) : null;
|
|
2968
|
-
console.log(`Instance "${instanceName}" \u2192 ${matchedFont ? `${matchedFont.title} (${result.strategy})` : "No match"}`);
|
|
2969
|
-
return {
|
|
2970
|
-
key: instanceName,
|
|
2971
|
-
value: matchedFont ? { _type: "reference", _ref: matchedFont._id, _weak: true } : null,
|
|
2972
|
-
_key: nanoid3()
|
|
2973
|
-
};
|
|
2974
|
-
});
|
|
2975
|
-
return instanceMappings;
|
|
2976
|
-
};
|
|
2977
|
-
var parseVariableFontInstances_default = parseVariableFontInstances;
|
|
2978
|
-
|
|
2979
|
-
// src/utils/updateTypefaceDocument.js
|
|
2980
|
-
import { nanoid as nanoid4 } from "nanoid";
|
|
2981
|
-
var updateTypefaceDocument = async (doc_id, fontRefs, variableRefs, subfamilies, uniqueSubfamilies, subfamiliesArray, preferredStyleRef, newPreferredStyle, stylesObject, client, setStatus, setError) => {
|
|
2982
|
-
console.log("Updating typeface document with new fonts:", { fontRefs, variableRefs, subfamilies, uniqueSubfamilies });
|
|
2983
|
-
setStatus("Updating typeface references...");
|
|
2984
|
-
const dedupeRefs = (existing, incoming) => {
|
|
2985
|
-
const merged = [...existing || []];
|
|
2986
|
-
const existingRefs = new Set(merged.map((r) => r._ref).filter(Boolean));
|
|
2987
|
-
incoming.forEach((ref) => {
|
|
2988
|
-
if (ref._ref && !existingRefs.has(ref._ref)) {
|
|
2989
|
-
merged.push(ref);
|
|
2990
|
-
existingRefs.add(ref._ref);
|
|
2991
|
-
}
|
|
2992
|
-
});
|
|
2993
|
-
return merged;
|
|
2994
|
-
};
|
|
2995
|
-
let patch = {
|
|
2996
|
-
"styles.fonts": dedupeRefs(stylesObject.fonts, fontRefs),
|
|
2997
|
-
"styles.variableFont": dedupeRefs(stylesObject == null ? void 0 : stylesObject.variableFont, variableRefs)
|
|
2998
|
-
};
|
|
2999
|
-
setStatus("Organising font subfamilies...");
|
|
3000
|
-
subfamiliesArray = subfamiliesArray || [];
|
|
3001
|
-
uniqueSubfamilies.forEach((subfamilyName) => {
|
|
3002
|
-
if (!subfamiliesArray.find((sf) => sf.title === subfamilyName)) {
|
|
3003
|
-
subfamiliesArray.push({
|
|
3004
|
-
title: subfamilyName,
|
|
3005
|
-
_key: nanoid4(),
|
|
3006
|
-
_type: "object",
|
|
3007
|
-
fonts: []
|
|
3008
|
-
});
|
|
3009
|
-
}
|
|
3010
|
-
});
|
|
3011
|
-
if (subfamiliesArray.length > 0) {
|
|
3012
|
-
Object.entries(subfamilies).forEach(([id, subfamilyName]) => {
|
|
3013
|
-
if (id.toLowerCase().includes("vf")) return;
|
|
3014
|
-
const subfamilyIndex = subfamiliesArray.findIndex((sf) => sf.title === subfamilyName);
|
|
3015
|
-
if (subfamilyIndex !== -1) {
|
|
3016
|
-
subfamiliesArray[subfamilyIndex].fonts.push({
|
|
3017
|
-
_ref: id,
|
|
3018
|
-
_key: nanoid4(),
|
|
3019
|
-
_type: "reference",
|
|
3020
|
-
_weak: true
|
|
3021
|
-
});
|
|
3022
|
-
}
|
|
3023
|
-
});
|
|
3024
|
-
subfamiliesArray = subfamiliesArray.map((subfamily) => ({
|
|
3025
|
-
...subfamily,
|
|
3026
|
-
fonts: subfamily.fonts.filter(
|
|
3027
|
-
(font, index, self) => index === self.findIndex((f) => f._ref === font._ref)
|
|
3028
|
-
)
|
|
3029
|
-
}));
|
|
3030
|
-
}
|
|
3031
|
-
patch["styles.subfamilies"] = subfamiliesArray;
|
|
3032
|
-
await updatePreferredStyle(doc_id, preferredStyleRef, newPreferredStyle, patch, client);
|
|
3033
|
-
console.log("doc_id: ", doc_id);
|
|
3034
|
-
console.log("Typeface patch: ", patch);
|
|
3035
|
-
console.log("New preferred style: ", newPreferredStyle);
|
|
3036
|
-
console.log("SubfamiliesArray:", subfamiliesArray);
|
|
3037
|
-
try {
|
|
3038
|
-
await client.patch(doc_id).set(patch).commit();
|
|
3039
|
-
console.log(`Updated document: ${doc_id}`);
|
|
3040
|
-
if (doc_id.startsWith("drafts.")) {
|
|
3041
|
-
await updatePublishedDocument(doc_id, patch, client);
|
|
3042
|
-
}
|
|
3043
|
-
} catch (err) {
|
|
3044
|
-
console.error("Error updating document:", err.message);
|
|
3045
|
-
setStatus("Error updating typeface");
|
|
3046
|
-
setError(true);
|
|
3047
|
-
}
|
|
3048
|
-
};
|
|
3049
|
-
var updatePreferredStyle = async (doc_id, preferredStyleRef, newPreferredStyle, patch, client) => {
|
|
3050
|
-
const isCurrentlyEmpty = !(preferredStyleRef == null ? void 0 : preferredStyleRef._ref) || preferredStyleRef._ref === "" || preferredStyleRef._ref === null;
|
|
3051
|
-
const hasCandidate = (newPreferredStyle == null ? void 0 : newPreferredStyle._ref) && newPreferredStyle._ref !== "";
|
|
3052
|
-
if (isCurrentlyEmpty && hasCandidate) {
|
|
3053
|
-
patch.preferredStyle = {
|
|
3054
|
-
_type: "reference",
|
|
3055
|
-
_ref: newPreferredStyle._ref,
|
|
3056
|
-
_weak: true
|
|
3057
|
-
};
|
|
3058
|
-
}
|
|
3059
|
-
};
|
|
3060
|
-
var updatePublishedDocument = async (doc_id, patch, client) => {
|
|
3061
|
-
const publishedId = doc_id.replace("drafts.", "");
|
|
3062
|
-
const publishedDoc = await client.fetch(`*[_id == $publishedId]`, { publishedId }).then((res) => res[0]);
|
|
3063
|
-
if (publishedDoc) {
|
|
3064
|
-
await client.patch(publishedId).set(patch).commit();
|
|
3065
|
-
console.log(`Updated published document: ${publishedId}`);
|
|
3066
|
-
} else {
|
|
3067
|
-
console.log(`No published document found for ${publishedId}, skipping`);
|
|
3068
|
-
}
|
|
3069
|
-
};
|
|
3070
|
-
|
|
3071
|
-
// src/utils/executeUploadPlan.js
|
|
3072
|
-
async function executeUploadPlan({
|
|
3073
|
-
plan,
|
|
3074
|
-
client,
|
|
3075
|
-
docId,
|
|
3076
|
-
stylesObject = {},
|
|
3077
|
-
preferredStyleRef = {},
|
|
3078
|
-
onProgress
|
|
3079
|
-
}) {
|
|
3080
|
-
var _a, _b, _c;
|
|
3081
|
-
const result = {
|
|
3082
|
-
success: true,
|
|
3083
|
-
created: 0,
|
|
3084
|
-
updated: 0,
|
|
3085
|
-
failed: 0,
|
|
3086
|
-
skipped: 0,
|
|
3087
|
-
failedFonts: [],
|
|
3088
|
-
fontRefs: [],
|
|
3089
|
-
variableRefs: [],
|
|
3090
|
-
typefacePatchError: null
|
|
3091
|
-
};
|
|
3092
|
-
const fontEntries = Object.values(plan.fonts);
|
|
3093
|
-
const queue = fontEntries.filter((entry) => entry.status !== FONT_STATUS.ERROR);
|
|
3094
|
-
const skipped = fontEntries.filter((entry) => entry.status === FONT_STATUS.ERROR);
|
|
3095
|
-
result.skipped = skipped.length;
|
|
3096
|
-
const progress = {};
|
|
3097
|
-
for (const entry of queue) {
|
|
3098
|
-
progress[entry.tempId] = {
|
|
3099
|
-
status: EXECUTION_STATUS.QUEUED,
|
|
3100
|
-
currentFile: null,
|
|
3101
|
-
filesComplete: 0,
|
|
3102
|
-
filesTotal: entry.files.length,
|
|
3103
|
-
assetRefs: {},
|
|
3104
|
-
error: null
|
|
3105
|
-
};
|
|
3106
|
-
}
|
|
3107
|
-
if (onProgress) {
|
|
3108
|
-
onProgress({ type: "execution-start", totalFonts: queue.length, skippedFonts: result.skipped });
|
|
3109
|
-
}
|
|
3110
|
-
const chunks = [];
|
|
3111
|
-
for (let i = 0; i < queue.length; i += CONCURRENCY_LIMIT) {
|
|
3112
|
-
chunks.push(queue.slice(i, i + CONCURRENCY_LIMIT));
|
|
3113
|
-
}
|
|
3114
|
-
let newPreferredStyle = { weight: -100, style: "Italic", _ref: "" };
|
|
3115
|
-
const subfamilies = {};
|
|
3116
|
-
const uniqueSubfamilies = /* @__PURE__ */ new Set();
|
|
3117
|
-
for (const chunk of chunks) {
|
|
3118
|
-
const chunkResults = await Promise.allSettled(
|
|
3119
|
-
chunk.map((entry) => executeSingleFont({
|
|
3120
|
-
entry,
|
|
3121
|
-
plan,
|
|
3122
|
-
client,
|
|
3123
|
-
progress,
|
|
3124
|
-
onProgress
|
|
3125
|
-
}))
|
|
3126
|
-
);
|
|
3127
|
-
for (let i = 0; i < chunkResults.length; i++) {
|
|
3128
|
-
const chunkResult = chunkResults[i];
|
|
3129
|
-
const entry = chunk[i];
|
|
3130
|
-
if (chunkResult.status === "fulfilled" && chunkResult.value) {
|
|
3131
|
-
const fontResult = chunkResult.value;
|
|
3132
|
-
if (fontResult.isNew) result.created++;
|
|
3133
|
-
else result.updated++;
|
|
3134
|
-
if (entry.variableFont) {
|
|
3135
|
-
result.variableRefs.push(fontResult.ref);
|
|
3136
|
-
} else {
|
|
3137
|
-
result.fontRefs.push(fontResult.ref);
|
|
3138
|
-
}
|
|
3139
|
-
subfamilies[entry.documentId] = entry.subfamily;
|
|
3140
|
-
if (entry.subfamily) uniqueSubfamilies.add(entry.subfamily);
|
|
3141
|
-
if (entry.weight > newPreferredStyle.weight) {
|
|
3142
|
-
newPreferredStyle = {
|
|
3143
|
-
weight: entry.weight,
|
|
3144
|
-
style: entry.style,
|
|
3145
|
-
_ref: fontResult.ref._ref
|
|
3146
|
-
};
|
|
3147
|
-
}
|
|
3148
|
-
} else {
|
|
3149
|
-
result.failed++;
|
|
3150
|
-
result.success = false;
|
|
3151
|
-
const errorMsg = ((_a = chunkResult.reason) == null ? void 0 : _a.message) || ((_b = chunkResult.value) == null ? void 0 : _b.error) || "Unknown error";
|
|
3152
|
-
result.failedFonts.push({
|
|
3153
|
-
tempId: entry.tempId,
|
|
3154
|
-
title: entry.title,
|
|
3155
|
-
error: errorMsg,
|
|
3156
|
-
failedAt: ((_c = progress[entry.tempId]) == null ? void 0 : _c.status) || "unknown"
|
|
3157
|
-
});
|
|
3158
|
-
}
|
|
3159
|
-
}
|
|
3160
|
-
}
|
|
3161
|
-
if (result.fontRefs.length > 0 || result.variableRefs.length > 0) {
|
|
3162
|
-
try {
|
|
3163
|
-
if (onProgress) {
|
|
3164
|
-
onProgress({ type: "typeface-patching" });
|
|
3165
|
-
}
|
|
3166
|
-
await updateTypefaceDocument(
|
|
3167
|
-
docId,
|
|
3168
|
-
result.fontRefs,
|
|
3169
|
-
result.variableRefs,
|
|
3170
|
-
subfamilies,
|
|
3171
|
-
[...uniqueSubfamilies],
|
|
3172
|
-
(stylesObject == null ? void 0 : stylesObject.subfamilies) || [],
|
|
3173
|
-
preferredStyleRef,
|
|
3174
|
-
newPreferredStyle,
|
|
3175
|
-
stylesObject,
|
|
3176
|
-
client,
|
|
3177
|
-
(msg) => {
|
|
3178
|
-
if (onProgress) onProgress({ type: "typeface-status", message: msg });
|
|
3179
|
-
},
|
|
3180
|
-
(err) => {
|
|
3181
|
-
if (err) console.error("Typeface patch error flag set");
|
|
3182
|
-
}
|
|
3183
|
-
);
|
|
3184
|
-
if (onProgress) {
|
|
3185
|
-
onProgress({ type: "typeface-patched" });
|
|
3186
|
-
}
|
|
3187
|
-
} catch (err) {
|
|
3188
|
-
result.typefacePatchError = err.message;
|
|
3189
|
-
result.success = false;
|
|
3190
|
-
console.error("Typeface patch failed:", err.message);
|
|
3191
|
-
if (onProgress) {
|
|
3192
|
-
onProgress({ type: "typeface-error", error: err.message });
|
|
3193
|
-
}
|
|
3194
|
-
}
|
|
3195
|
-
}
|
|
3196
|
-
if (onProgress) {
|
|
3197
|
-
onProgress({ type: "execution-complete", result });
|
|
3198
|
-
}
|
|
3199
|
-
return result;
|
|
3200
|
-
}
|
|
3201
|
-
async function executeSingleFont({ entry, plan, client, progress, onProgress }) {
|
|
3202
|
-
var _a, _b, _c, _d;
|
|
3203
|
-
const fontProgress = progress[entry.tempId];
|
|
3204
|
-
fontProgress.status = EXECUTION_STATUS.UPLOADING_ASSETS;
|
|
3205
|
-
if (onProgress) {
|
|
3206
|
-
onProgress({ type: "font-upload-start", tempId: entry.tempId, fontProgress: { ...fontProgress } });
|
|
3207
|
-
}
|
|
3208
|
-
const decision = entry.decisions.existingDocument;
|
|
3209
|
-
const userChoice = decision.userChoice;
|
|
3210
|
-
const recommendation = decision.recommendation;
|
|
3211
|
-
const shouldUpdate = userChoice === "update" || !userChoice && (recommendation === RECOMMENDATION.USE_EXACT || recommendation === RECOMMENDATION.USE_CANDIDATE);
|
|
3212
|
-
const existingDoc = shouldUpdate ? decision.selectedCandidate || decision.exact || decision.candidates[0] : null;
|
|
3213
|
-
const fileInput = {};
|
|
3214
|
-
for (let j = 0; j < entry.files.length; j++) {
|
|
3215
|
-
const file = entry.files[j];
|
|
3216
|
-
const fileType = determineFileType(file);
|
|
3217
|
-
if (!fileType) continue;
|
|
3218
|
-
if (fontProgress.assetRefs[fileType]) {
|
|
3219
|
-
fileInput[fileType] = {
|
|
3220
|
-
_type: "file",
|
|
3221
|
-
asset: { _type: "reference", _ref: fontProgress.assetRefs[fileType] }
|
|
3222
|
-
};
|
|
3223
|
-
fontProgress.filesComplete++;
|
|
3224
|
-
continue;
|
|
3225
|
-
}
|
|
3226
|
-
fontProgress.currentFile = fileType;
|
|
3227
|
-
try {
|
|
3228
|
-
const assetFilename = plan.settings.preserveFileNames && entry.originalFilename ? `${entry.originalFilename}.${fileType}` : `${entry.documentId}.${fileType}`;
|
|
3229
|
-
const baseAsset = await uploadWithRetry(
|
|
3230
|
-
() => client.assets.upload("file", file, { filename: assetFilename })
|
|
3231
|
-
);
|
|
3232
|
-
if (plan.settings.preserveFileNames && baseAsset.originalFilename !== assetFilename) {
|
|
3233
|
-
try {
|
|
3234
|
-
await client.patch(baseAsset._id).set({ originalFilename: assetFilename }).commit();
|
|
3235
|
-
} catch (renameErr) {
|
|
3236
|
-
console.warn("Could not rename asset:", renameErr.message);
|
|
3237
|
-
}
|
|
3238
|
-
}
|
|
3239
|
-
fileInput[fileType] = {
|
|
3240
|
-
_type: "file",
|
|
3241
|
-
asset: { _type: "reference", _ref: baseAsset._id }
|
|
3242
|
-
};
|
|
3243
|
-
fontProgress.assetRefs[fileType] = baseAsset._id;
|
|
3244
|
-
fontProgress.filesComplete++;
|
|
3245
|
-
if (onProgress) {
|
|
3246
|
-
onProgress({ type: "file-uploaded", tempId: entry.tempId, fileType, fontProgress: { ...fontProgress } });
|
|
3247
|
-
}
|
|
3248
|
-
} catch (err) {
|
|
3249
|
-
fontProgress.status = EXECUTION_STATUS.ERROR;
|
|
3250
|
-
fontProgress.error = err.message;
|
|
3251
|
-
throw new Error(`Asset upload failed for ${fileType}: ${err.message}`);
|
|
3252
|
-
}
|
|
3253
|
-
}
|
|
3254
|
-
if (fileInput.woff2 || fileInput.woff) {
|
|
3255
|
-
fontProgress.status = EXECUTION_STATUS.GENERATING_CSS;
|
|
3256
|
-
try {
|
|
3257
|
-
const woff2File = entry.files.find((f) => f.name.endsWith(".woff2") || f.name.endsWith(".woff"));
|
|
3258
|
-
if (woff2File) {
|
|
3259
|
-
const updatedFileInput = await generateCssFile({
|
|
3260
|
-
woff2File,
|
|
3261
|
-
fileInput,
|
|
3262
|
-
fileName: entry.documentId,
|
|
3263
|
-
fontName: entry.title,
|
|
3264
|
-
variableFont: entry.variableFont,
|
|
3265
|
-
weight: entry.weight,
|
|
3266
|
-
style: entry.style,
|
|
3267
|
-
client
|
|
3268
|
-
});
|
|
3269
|
-
Object.assign(fileInput, updatedFileInput);
|
|
3270
|
-
if (onProgress) {
|
|
3271
|
-
onProgress({ type: "css-generated", tempId: entry.tempId, fontProgress: { ...fontProgress } });
|
|
3272
|
-
}
|
|
3273
|
-
}
|
|
3274
|
-
} catch (err) {
|
|
3275
|
-
console.warn("CSS generation failed for", entry.title, "\u2014 document created without CSS:", err.message);
|
|
3276
|
-
}
|
|
3277
|
-
}
|
|
3278
|
-
if (fileInput.ttf || fileInput.otf) {
|
|
3279
|
-
fontProgress.status = EXECUTION_STATUS.GENERATING_METADATA;
|
|
3280
|
-
try {
|
|
3281
|
-
const ttfAssetRef = ((_b = (_a = fileInput.ttf) == null ? void 0 : _a.asset) == null ? void 0 : _b._ref) || ((_d = (_c = fileInput.otf) == null ? void 0 : _c.asset) == null ? void 0 : _d._ref);
|
|
3282
|
-
if (ttfAssetRef) {
|
|
3283
|
-
const metadata = await generateFontData({
|
|
3284
|
-
fileInput,
|
|
3285
|
-
fontKit: null,
|
|
3286
|
-
// Will re-parse from URL
|
|
3287
|
-
fontId: entry.documentId,
|
|
3288
|
-
client,
|
|
3289
|
-
commit: false
|
|
3290
|
-
// Don't patch yet — we'll include in the document creation
|
|
3291
|
-
});
|
|
3292
|
-
Object.assign(entry, {
|
|
3293
|
-
metaData: metadata.metaData,
|
|
3294
|
-
metrics: metadata.metrics,
|
|
3295
|
-
variableAxes: metadata.variableAxes,
|
|
3296
|
-
variableInstances: metadata.variableInstances,
|
|
3297
|
-
opentypeFeatures: metadata.opentypeFeatures,
|
|
3298
|
-
characterSet: metadata.characterSet,
|
|
3299
|
-
glyphCount: metadata.glyphCount,
|
|
3300
|
-
variableFont: metadata.variableFont
|
|
3301
|
-
});
|
|
3302
|
-
if (onProgress) {
|
|
3303
|
-
onProgress({ type: "metadata-generated", tempId: entry.tempId, fontProgress: { ...fontProgress } });
|
|
3304
|
-
}
|
|
3305
|
-
}
|
|
3306
|
-
} catch (err) {
|
|
3307
|
-
console.warn("Metadata generation failed for", entry.title, ":", err.message);
|
|
3308
|
-
}
|
|
3309
|
-
}
|
|
3310
|
-
fontProgress.status = EXECUTION_STATUS.CREATING_DOCUMENT;
|
|
3311
|
-
const fontDocId = shouldUpdate && existingDoc ? existingDoc._id : entry.documentId;
|
|
3312
|
-
const isNew = !shouldUpdate;
|
|
3313
|
-
const fontDoc = {
|
|
3314
|
-
_id: fontDocId,
|
|
3315
|
-
_type: "font",
|
|
3316
|
-
_key: nanoid5(),
|
|
3317
|
-
title: entry.title,
|
|
3318
|
-
slug: { _type: "slug", current: fontDocId },
|
|
3319
|
-
typefaceName: plan.settings.typefaceTitle || entry.title,
|
|
3320
|
-
style: entry.style,
|
|
3321
|
-
variableFont: entry.variableFont,
|
|
3322
|
-
weightName: entry.weightName,
|
|
3323
|
-
subfamily: entry.subfamily,
|
|
3324
|
-
weight: entry.weight,
|
|
3325
|
-
price: plan.settings.price,
|
|
3326
|
-
sell: plan.settings.price > 0,
|
|
3327
|
-
normalWeight: true,
|
|
3328
|
-
fileInput
|
|
3329
|
-
};
|
|
3330
|
-
if (entry.metaData) fontDoc.metaData = entry.metaData;
|
|
3331
|
-
if (entry.metrics) fontDoc.metrics = entry.metrics;
|
|
3332
|
-
if (entry.variableAxes) fontDoc.variableAxes = entry.variableAxes;
|
|
3333
|
-
if (entry.variableInstances) fontDoc.variableInstances = entry.variableInstances;
|
|
3334
|
-
if (entry.opentypeFeatures) fontDoc.opentypeFeatures = entry.opentypeFeatures;
|
|
3335
|
-
if (entry.characterSet) fontDoc.characterSet = entry.characterSet;
|
|
3336
|
-
if (entry.glyphCount) fontDoc.glyphCount = entry.glyphCount;
|
|
3337
|
-
try {
|
|
3338
|
-
if (shouldUpdate && existingDoc) {
|
|
3339
|
-
if (existingDoc.fileInput) {
|
|
3340
|
-
Object.keys(existingDoc.fileInput).forEach((key) => {
|
|
3341
|
-
if (!fontDoc.fileInput[key]) fontDoc.fileInput[key] = existingDoc.fileInput[key];
|
|
3342
|
-
});
|
|
3343
|
-
}
|
|
3344
|
-
if (!fontDoc.metaData && existingDoc.metaData) fontDoc.metaData = existingDoc.metaData;
|
|
3345
|
-
if (!fontDoc.metrics && existingDoc.metrics) fontDoc.metrics = existingDoc.metrics;
|
|
3346
|
-
if (existingDoc.scriptFileInput) fontDoc.scriptFileInput = existingDoc.scriptFileInput;
|
|
3347
|
-
if (existingDoc.variableInstanceReferences) {
|
|
3348
|
-
fontDoc.variableInstanceReferences = existingDoc.variableInstanceReferences;
|
|
3349
|
-
}
|
|
3350
|
-
await client.patch(fontDocId).set(fontDoc).commit();
|
|
3351
|
-
console.log("Updated existing font:", fontDocId, entry.title);
|
|
3352
|
-
} else {
|
|
3353
|
-
await client.createOrReplace(fontDoc);
|
|
3354
|
-
console.log("Created new font:", fontDocId, entry.title);
|
|
3355
|
-
}
|
|
3356
|
-
fontProgress.status = EXECUTION_STATUS.COMPLETE;
|
|
3357
|
-
if (onProgress) {
|
|
3358
|
-
onProgress({ type: "document-created", tempId: entry.tempId, isNew, fontProgress: { ...fontProgress } });
|
|
3359
|
-
}
|
|
3360
|
-
return {
|
|
3361
|
-
ref: {
|
|
3362
|
-
_key: nanoid5(),
|
|
3363
|
-
_type: "reference",
|
|
3364
|
-
_ref: fontDocId,
|
|
3365
|
-
_weak: true
|
|
3366
|
-
},
|
|
3367
|
-
isNew
|
|
3368
|
-
};
|
|
3369
|
-
} catch (err) {
|
|
3370
|
-
fontProgress.status = EXECUTION_STATUS.ERROR;
|
|
3371
|
-
fontProgress.error = err.message;
|
|
3372
|
-
throw new Error(`Document creation failed: ${err.message}`);
|
|
3373
|
-
}
|
|
3374
|
-
}
|
|
3375
|
-
function determineFileType(file) {
|
|
3376
|
-
if (file.name.endsWith(".ttf")) return "ttf";
|
|
3377
|
-
if (file.name.endsWith(".otf")) return "otf";
|
|
3378
|
-
if (file.name.endsWith(".woff")) return "woff";
|
|
3379
|
-
if (file.name.endsWith(".woff2")) return "woff2";
|
|
3380
|
-
if (file.name.endsWith(".eot")) return "eot";
|
|
3381
|
-
if (file.name.endsWith(".svg")) return "svg";
|
|
3382
|
-
return "";
|
|
3383
|
-
}
|
|
3384
|
-
async function uploadWithRetry(uploadFn) {
|
|
3385
|
-
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
3386
|
-
try {
|
|
3387
|
-
return await uploadFn();
|
|
3388
|
-
} catch (err) {
|
|
3389
|
-
const is429 = err.statusCode === 429 || err.status === 429;
|
|
3390
|
-
if (is429 && attempt < MAX_RETRIES) {
|
|
3391
|
-
const delay = backoffWithJitter(attempt);
|
|
3392
|
-
console.warn(`Rate limited (429), retrying in ${delay}ms (attempt ${attempt + 1}/${MAX_RETRIES})`);
|
|
3393
|
-
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
3394
|
-
} else {
|
|
3395
|
-
throw err;
|
|
3396
|
-
}
|
|
3397
|
-
}
|
|
3398
|
-
}
|
|
3399
|
-
}
|
|
3400
|
-
|
|
3401
|
-
// src/utils/executionReducer.js
|
|
3402
|
-
function createInitialExecutionState() {
|
|
3403
|
-
return {
|
|
3404
|
-
status: "idle",
|
|
3405
|
-
progress: {},
|
|
3406
|
-
error: null
|
|
3407
|
-
};
|
|
3408
|
-
}
|
|
3409
|
-
function executionReducer(state, action) {
|
|
3410
|
-
switch (action.type) {
|
|
3411
|
-
case "SET_EXECUTION_STATUS": {
|
|
3412
|
-
return { ...state, status: action.status };
|
|
3413
|
-
}
|
|
3414
|
-
case "SET_FONT_EXECUTION_PROGRESS": {
|
|
3415
|
-
const { tempId, progress } = action;
|
|
3416
|
-
return {
|
|
3417
|
-
...state,
|
|
3418
|
-
progress: {
|
|
3419
|
-
...state.progress,
|
|
3420
|
-
[tempId]: {
|
|
3421
|
-
...state.progress[tempId] || {},
|
|
3422
|
-
...progress
|
|
3423
|
-
}
|
|
3424
|
-
}
|
|
3425
|
-
};
|
|
3426
|
-
}
|
|
3427
|
-
case "SET_EXECUTION_ERROR": {
|
|
3428
|
-
return {
|
|
3429
|
-
...state,
|
|
3430
|
-
status: "error",
|
|
3431
|
-
error: action.error
|
|
3432
|
-
};
|
|
3433
|
-
}
|
|
3434
|
-
default:
|
|
3435
|
-
return state;
|
|
3436
|
-
}
|
|
3437
|
-
}
|
|
3438
|
-
|
|
3439
|
-
// src/components/UploadStep3Execute.jsx
|
|
3440
|
-
var formatElapsed = (s) => {
|
|
3441
|
-
const m = Math.floor(s / 60);
|
|
3442
|
-
const sec = s % 60;
|
|
3443
|
-
return m > 0 ? `${m}m ${sec}s` : `${sec}s`;
|
|
3444
|
-
};
|
|
3445
|
-
function UploadStep3Execute({
|
|
3446
|
-
plan,
|
|
3447
|
-
client,
|
|
3448
|
-
docId,
|
|
3449
|
-
stylesObject,
|
|
3450
|
-
preferredStyleRef,
|
|
3451
|
-
retryTempIds,
|
|
3452
|
-
onComplete
|
|
3453
|
-
}) {
|
|
3454
|
-
const [execState, execDispatch] = useReducer(executionReducer, null, createInitialExecutionState);
|
|
3455
|
-
const [result, setResult] = useState4(null);
|
|
3456
|
-
const [elapsedSeconds, setElapsedSeconds] = useState4(0);
|
|
3457
|
-
const startedRef = useRef2(false);
|
|
3458
|
-
const timerRef = useRef2(null);
|
|
3459
|
-
const wakeLockRef = useRef2(null);
|
|
3460
|
-
const executionPlan = useMemo5(() => {
|
|
3461
|
-
if (!retryTempIds) return plan;
|
|
3462
|
-
return {
|
|
3463
|
-
...plan,
|
|
3464
|
-
fonts: Object.fromEntries(
|
|
3465
|
-
Object.entries(plan.fonts).filter(([tempId]) => retryTempIds.includes(tempId))
|
|
3466
|
-
)
|
|
3467
|
-
};
|
|
3468
|
-
}, [plan, retryTempIds]);
|
|
3469
|
-
const fontEntries = useMemo5(
|
|
3470
|
-
() => Object.values(executionPlan.fonts).filter((f) => f.status !== "error"),
|
|
3471
|
-
[executionPlan]
|
|
3472
|
-
);
|
|
3473
|
-
useEffect3(() => {
|
|
3474
|
-
var _a;
|
|
3475
|
-
if (startedRef.current) return;
|
|
3476
|
-
startedRef.current = true;
|
|
3477
|
-
timerRef.current = setInterval(() => setElapsedSeconds((s) => s + 1), 1e3);
|
|
3478
|
-
(_a = navigator.wakeLock) == null ? void 0 : _a.request("screen").then((lock) => {
|
|
3479
|
-
wakeLockRef.current = lock;
|
|
3480
|
-
}).catch(() => {
|
|
3481
|
-
});
|
|
3482
|
-
execDispatch({ type: "SET_EXECUTION_STATUS", status: "uploading" });
|
|
3483
|
-
executeUploadPlan({
|
|
3484
|
-
plan: executionPlan,
|
|
3485
|
-
client,
|
|
3486
|
-
docId,
|
|
3487
|
-
stylesObject,
|
|
3488
|
-
preferredStyleRef,
|
|
3489
|
-
onProgress: (event) => {
|
|
3490
|
-
if (event.type === "font-upload-start" || event.type === "file-uploaded" || event.type === "css-generated" || event.type === "metadata-generated" || event.type === "document-created") {
|
|
3491
|
-
if (event.fontProgress) {
|
|
3492
|
-
execDispatch({
|
|
3493
|
-
type: "SET_FONT_EXECUTION_PROGRESS",
|
|
3494
|
-
tempId: event.tempId,
|
|
3495
|
-
progress: event.fontProgress
|
|
3496
|
-
});
|
|
3497
|
-
}
|
|
3498
|
-
} else if (event.type === "typeface-patching") {
|
|
3499
|
-
execDispatch({ type: "SET_EXECUTION_STATUS", status: "patching-typeface" });
|
|
3500
|
-
} else if (event.type === "typeface-error") {
|
|
3501
|
-
execDispatch({ type: "SET_EXECUTION_ERROR", error: event.error });
|
|
3502
|
-
}
|
|
3503
|
-
}
|
|
3504
|
-
}).then((executionResult) => {
|
|
3505
|
-
var _a2;
|
|
3506
|
-
setResult(executionResult);
|
|
3507
|
-
clearInterval(timerRef.current);
|
|
3508
|
-
(_a2 = wakeLockRef.current) == null ? void 0 : _a2.release().catch(() => {
|
|
3509
|
-
});
|
|
3510
|
-
execDispatch({ type: "SET_EXECUTION_STATUS", status: executionResult.success ? "complete" : "error" });
|
|
3511
|
-
onComplete(executionResult);
|
|
3512
|
-
}).catch((err) => {
|
|
3513
|
-
var _a2;
|
|
3514
|
-
clearInterval(timerRef.current);
|
|
3515
|
-
(_a2 = wakeLockRef.current) == null ? void 0 : _a2.release().catch(() => {
|
|
3516
|
-
});
|
|
3517
|
-
execDispatch({ type: "SET_EXECUTION_ERROR", error: err.message });
|
|
3518
|
-
const errorResult = {
|
|
3519
|
-
success: false,
|
|
3520
|
-
created: 0,
|
|
3521
|
-
updated: 0,
|
|
3522
|
-
failed: 1,
|
|
3523
|
-
skipped: 0,
|
|
3524
|
-
failedFonts: [{ title: "Unknown", tempId: "unknown", error: err.message, failedAt: "unknown" }],
|
|
3525
|
-
fontRefs: [],
|
|
3526
|
-
variableRefs: [],
|
|
3527
|
-
typefacePatchError: err.message
|
|
3528
|
-
};
|
|
3529
|
-
setResult(errorResult);
|
|
3530
|
-
onComplete(errorResult);
|
|
3531
|
-
});
|
|
3532
|
-
return () => {
|
|
3533
|
-
var _a2;
|
|
3534
|
-
clearInterval(timerRef.current);
|
|
3535
|
-
(_a2 = wakeLockRef.current) == null ? void 0 : _a2.release().catch(() => {
|
|
3536
|
-
});
|
|
3537
|
-
};
|
|
3538
|
-
}, []);
|
|
3539
|
-
const completedCount = Object.values(execState.progress).filter(
|
|
3540
|
-
(p) => p.status === EXECUTION_STATUS.COMPLETE || p.status === EXECUTION_STATUS.ERROR
|
|
3541
|
-
).length;
|
|
3542
|
-
return /* @__PURE__ */ React7.createElement(Stack5, { space: 4 }, /* @__PURE__ */ React7.createElement(Card5, { border: true, padding: 3, radius: 2 }, /* @__PURE__ */ React7.createElement(Stack5, { space: 3 }, /* @__PURE__ */ React7.createElement(Flex7, { align: "center", gap: 3 }, execState.status === "complete" ? /* @__PURE__ */ React7.createElement(CheckmarkCircleIcon, { style: { color: "#43b649", fontSize: 20 } }) : execState.status === "error" ? /* @__PURE__ */ React7.createElement(WarningOutlineIcon, { style: { color: "var(--card-badge-critical-bg-color)", fontSize: 20 } }) : /* @__PURE__ */ React7.createElement(Spinner2, null), /* @__PURE__ */ React7.createElement(Text7, { size: 1, weight: "semibold" }, execState.status === "patching-typeface" ? "Updating typeface document..." : execState.status === "complete" ? "Upload complete" : execState.status === "error" ? "Upload failed" : `Uploading ${completedCount} of ${fontEntries.length} fonts...`), /* @__PURE__ */ React7.createElement(Text7, { size: 1, muted: true, style: { marginLeft: "auto" } }, formatElapsed(elapsedSeconds))), /* @__PURE__ */ React7.createElement(Box6, { style: { height: 4, background: "var(--card-border-color)", borderRadius: 2, overflow: "hidden" } }, /* @__PURE__ */ React7.createElement(
|
|
3543
|
-
Box6,
|
|
3544
|
-
{
|
|
3545
|
-
style: {
|
|
3546
|
-
height: "100%",
|
|
3547
|
-
width: "100%",
|
|
3548
|
-
transformOrigin: "left",
|
|
3549
|
-
transform: `scaleX(${fontEntries.length > 0 ? completedCount / fontEntries.length : 0})`,
|
|
3550
|
-
background: execState.status === "error" ? "var(--card-badge-critical-bg-color)" : "var(--card-badge-positive-bg-color)",
|
|
3551
|
-
transition: "transform 0.3s ease-out",
|
|
3552
|
-
borderRadius: 2
|
|
3553
|
-
}
|
|
3554
|
-
}
|
|
3555
|
-
)))), execState.status !== "complete" && execState.status !== "error" && /* @__PURE__ */ React7.createElement(Card5, { tone: "caution", border: true, radius: 2, padding: 2 }, /* @__PURE__ */ React7.createElement(Flex7, { align: "center", gap: 2 }, /* @__PURE__ */ React7.createElement(WarningOutlineIcon, { style: { flexShrink: 0 } }), /* @__PURE__ */ React7.createElement(Text7, { size: 1, weight: "semibold" }, "Do not close or reload this tab"))), /* @__PURE__ */ React7.createElement(Box6, { style: { maxHeight: 400, overflowY: "auto" } }, /* @__PURE__ */ React7.createElement(Stack5, { space: 1 }, fontEntries.map((entry) => {
|
|
3556
|
-
const progress = execState.progress[entry.tempId];
|
|
3557
|
-
const status = (progress == null ? void 0 : progress.status) || EXECUTION_STATUS.QUEUED;
|
|
3558
|
-
return /* @__PURE__ */ React7.createElement(Card5, { key: entry.tempId, border: true, radius: 1, padding: 2 }, /* @__PURE__ */ React7.createElement(Flex7, { align: "center", gap: 2 }, /* @__PURE__ */ React7.createElement(Text7, { size: 1, style: { flex: 1 } }, entry.title), /* @__PURE__ */ React7.createElement(Box6, { style: { width: 120, flexShrink: 0, textAlign: "right" } }, status === EXECUTION_STATUS.QUEUED && /* @__PURE__ */ React7.createElement(Badge5, { mode: "outline", fontSize: 0 }, "Queued"), status === EXECUTION_STATUS.UPLOADING_ASSETS && /* @__PURE__ */ React7.createElement(Flex7, { gap: 1, align: "center", justify: "flex-end" }, /* @__PURE__ */ React7.createElement(Text7, { size: 0, muted: true }, (progress == null ? void 0 : progress.currentFile) || "Uploading..."), /* @__PURE__ */ React7.createElement(Spinner2, { style: { width: 12, height: 12 } })), (status === EXECUTION_STATUS.GENERATING_CSS || status === EXECUTION_STATUS.GENERATING_METADATA) && /* @__PURE__ */ React7.createElement(Flex7, { gap: 1, align: "center", justify: "flex-end" }, /* @__PURE__ */ React7.createElement(Text7, { size: 0, muted: true }, status === EXECUTION_STATUS.GENERATING_CSS ? "CSS" : "Metadata"), /* @__PURE__ */ React7.createElement(Spinner2, { style: { width: 12, height: 12 } })), status === EXECUTION_STATUS.CREATING_DOCUMENT && /* @__PURE__ */ React7.createElement(Flex7, { gap: 1, align: "center", justify: "flex-end" }, /* @__PURE__ */ React7.createElement(Text7, { size: 0, muted: true }, "Creating doc"), /* @__PURE__ */ React7.createElement(Spinner2, { style: { width: 12, height: 12 } })), status === EXECUTION_STATUS.COMPLETE && /* @__PURE__ */ React7.createElement(Badge5, { tone: "positive", fontSize: 0 }, "Done"), status === EXECUTION_STATUS.ERROR && /* @__PURE__ */ React7.createElement(Badge5, { tone: "critical", fontSize: 0 }, "Failed"))), status === EXECUTION_STATUS.ERROR && (progress == null ? void 0 : progress.error) && /* @__PURE__ */ React7.createElement(Text7, { size: 0, muted: true, style: { marginTop: 4 } }, progress.error));
|
|
3559
|
-
}))), execState.error && /* @__PURE__ */ React7.createElement(Card5, { tone: "critical", border: true, padding: 3, radius: 2 }, /* @__PURE__ */ React7.createElement(Text7, { size: 1 }, execState.error)));
|
|
3560
|
-
}
|
|
3561
|
-
|
|
3562
|
-
// src/components/UploadStep3bInstances.jsx
|
|
3563
|
-
import React8, { useState as useState5, useEffect as useEffect4, useCallback as useCallback4, useMemo as useMemo6 } from "react";
|
|
3564
|
-
import { Box as Box7, Stack as Stack6, Flex as Flex8, Text as Text8, Card as Card6, Badge as Badge6, Button as Button6, Spinner as Spinner3, Autocomplete } from "@sanity/ui";
|
|
3565
|
-
import { CheckmarkCircleIcon as CheckmarkCircleIcon2, CloseCircleIcon, SearchIcon as SearchIcon2 } from "@sanity/icons";
|
|
3566
|
-
import { nanoid as nanoid6 } from "nanoid";
|
|
3567
|
-
function UploadStep3bInstances({
|
|
3568
|
-
plan,
|
|
3569
|
-
executionResult,
|
|
3570
|
-
client,
|
|
3571
|
-
typefaceTitle,
|
|
3572
|
-
onComplete
|
|
3573
|
-
}) {
|
|
3574
|
-
const [loading, setLoading] = useState5(true);
|
|
3575
|
-
const [saving, setSaving] = useState5(false);
|
|
3576
|
-
const [vfMappings, setVfMappings] = useState5({});
|
|
3577
|
-
const [allStaticFonts, setAllStaticFonts] = useState5([]);
|
|
3578
|
-
const [filterUnmatched, setFilterUnmatched] = useState5(false);
|
|
3579
|
-
const vfEntries = useMemo6(
|
|
3580
|
-
() => Object.values(plan.fonts).filter((f) => f.variableFont && f.status !== "error"),
|
|
3581
|
-
[plan.fonts]
|
|
3582
|
-
);
|
|
3583
|
-
const runMatching = useCallback4(async () => {
|
|
3584
|
-
var _a;
|
|
3585
|
-
setLoading(true);
|
|
3586
|
-
const mappings = {};
|
|
3587
|
-
let staticFonts = [];
|
|
3588
|
-
try {
|
|
3589
|
-
const typeface = await client.fetch(
|
|
3590
|
-
`*[_type == 'typeface' && title == $typefaceTitle][0]{
|
|
3591
|
-
'fonts': styles.fonts[]-> {
|
|
3592
|
-
_id, title, subfamily, style, weight, weightName, metaData, variableFont
|
|
3593
|
-
}
|
|
3594
|
-
}`,
|
|
3595
|
-
{ typefaceTitle }
|
|
3596
|
-
);
|
|
3597
|
-
if (((_a = typeface == null ? void 0 : typeface.fonts) == null ? void 0 : _a.length) > 0) {
|
|
3598
|
-
staticFonts = typeface.fonts.filter((f) => !f.variableFont);
|
|
3599
|
-
}
|
|
3600
|
-
if (staticFonts.length === 0) {
|
|
3601
|
-
staticFonts = await client.fetch(
|
|
3602
|
-
`*[_type == 'font' && typefaceName == $typefaceTitle && variableFont != true]{
|
|
3603
|
-
_id, title, subfamily, style, weight, weightName, metaData
|
|
3604
|
-
}`,
|
|
3605
|
-
{ typefaceTitle }
|
|
3606
|
-
);
|
|
3607
|
-
}
|
|
3608
|
-
} catch (err) {
|
|
3609
|
-
console.error("[InstanceMapper] Failed to fetch static fonts:", err);
|
|
3610
|
-
}
|
|
3611
|
-
const deduped = /* @__PURE__ */ new Map();
|
|
3612
|
-
staticFonts.forEach((f) => {
|
|
3613
|
-
if (f._id && !deduped.has(f._id)) deduped.set(f._id, f);
|
|
3614
|
-
});
|
|
3615
|
-
staticFonts = [...deduped.values()];
|
|
3616
|
-
console.log(`[InstanceMapper] Found ${staticFonts.length} static fonts for "${typefaceTitle}"`);
|
|
3617
|
-
setAllStaticFonts(staticFonts);
|
|
3618
|
-
for (const vf of vfEntries) {
|
|
3619
|
-
try {
|
|
3620
|
-
const vfDoc = await client.fetch(
|
|
3621
|
-
`*[_id == $id][0]{
|
|
3622
|
-
_id, title, typefaceName, variableFont, variableInstances, metaData
|
|
3623
|
-
}`,
|
|
3624
|
-
{ id: vf.documentId }
|
|
3625
|
-
);
|
|
3626
|
-
if (!vfDoc) {
|
|
3627
|
-
console.warn(`[InstanceMapper] VF document not found: ${vf.documentId}`);
|
|
3628
|
-
mappings[vf.tempId] = [];
|
|
3629
|
-
continue;
|
|
3630
|
-
}
|
|
3631
|
-
console.log(`[InstanceMapper] Running matcher for VF: ${vfDoc.title}, variableInstances: ${vfDoc.variableInstances ? "present" : "missing"}`);
|
|
3632
|
-
const instanceMappings = await parseVariableFontInstances(vfDoc, client);
|
|
3633
|
-
console.log(`[InstanceMapper] Matched ${instanceMappings.filter((m) => m.value).length}/${instanceMappings.length} instances for ${vfDoc.title}`);
|
|
3634
|
-
mappings[vf.tempId] = instanceMappings.map((m) => {
|
|
3635
|
-
var _a2;
|
|
3636
|
-
return {
|
|
3637
|
-
instanceName: m.key,
|
|
3638
|
-
matchedFontId: ((_a2 = m.value) == null ? void 0 : _a2._ref) || "",
|
|
3639
|
-
matchedFontTitle: "",
|
|
3640
|
-
_key: m._key || nanoid6()
|
|
3641
|
-
};
|
|
3642
|
-
});
|
|
3643
|
-
} catch (err) {
|
|
3644
|
-
console.error(`[InstanceMapper] Error matching VF ${vf.documentId}:`, err);
|
|
3645
|
-
mappings[vf.tempId] = [];
|
|
3646
|
-
}
|
|
3647
|
-
}
|
|
3648
|
-
const allMatchedIds = /* @__PURE__ */ new Set();
|
|
3649
|
-
Object.values(mappings).forEach((m) => m.forEach((i) => {
|
|
3650
|
-
if (i.matchedFontId) allMatchedIds.add(i.matchedFontId);
|
|
3651
|
-
}));
|
|
3652
|
-
if (allMatchedIds.size > 0) {
|
|
3653
|
-
try {
|
|
3654
|
-
const titles = await client.fetch(`*[_id in $ids]{ _id, title }`, { ids: [...allMatchedIds] });
|
|
3655
|
-
const titleMap = {};
|
|
3656
|
-
titles.forEach((t) => {
|
|
3657
|
-
titleMap[t._id] = t.title;
|
|
3658
|
-
});
|
|
3659
|
-
Object.values(mappings).forEach((m) => {
|
|
3660
|
-
m.forEach((i) => {
|
|
3661
|
-
if (i.matchedFontId) i.matchedFontTitle = titleMap[i.matchedFontId] || i.matchedFontId;
|
|
3662
|
-
});
|
|
3663
|
-
});
|
|
3664
|
-
} catch (err) {
|
|
3665
|
-
console.warn("[InstanceMapper] Failed to resolve font titles:", err);
|
|
3666
|
-
}
|
|
3667
|
-
}
|
|
3668
|
-
setVfMappings(mappings);
|
|
3669
|
-
setLoading(false);
|
|
3670
|
-
}, [vfEntries, client, typefaceTitle]);
|
|
3671
|
-
useEffect4(() => {
|
|
3672
|
-
runMatching();
|
|
3673
|
-
}, [runMatching]);
|
|
3674
|
-
const claimedFontIds = useMemo6(() => {
|
|
3675
|
-
const claimed = /* @__PURE__ */ new Set();
|
|
3676
|
-
Object.values(vfMappings).forEach((mappings) => {
|
|
3677
|
-
mappings.forEach((m) => {
|
|
3678
|
-
if (m.matchedFontId) claimed.add(m.matchedFontId);
|
|
3679
|
-
});
|
|
3680
|
-
});
|
|
3681
|
-
return claimed;
|
|
3682
|
-
}, [vfMappings]);
|
|
3683
|
-
const handleMappingChange = useCallback4((vfTempId, instanceKey, fontId) => {
|
|
3684
|
-
const font = allStaticFonts.find((sf) => sf._id === fontId);
|
|
3685
|
-
setVfMappings((prev) => ({
|
|
3686
|
-
...prev,
|
|
3687
|
-
[vfTempId]: prev[vfTempId].map(
|
|
3688
|
-
(m) => m._key === instanceKey ? { ...m, matchedFontId: fontId, matchedFontTitle: (font == null ? void 0 : font.title) || fontId } : m
|
|
3689
|
-
)
|
|
3690
|
-
}));
|
|
3691
|
-
}, [allStaticFonts]);
|
|
3692
|
-
const handleSave = useCallback4(async () => {
|
|
3693
|
-
setSaving(true);
|
|
3694
|
-
const errors = [];
|
|
3695
|
-
for (const vf of vfEntries) {
|
|
3696
|
-
const mappings = vfMappings[vf.tempId] || [];
|
|
3697
|
-
const references = mappings.filter((m) => m.matchedFontId).map((m) => ({
|
|
3698
|
-
_key: nanoid6(),
|
|
3699
|
-
_type: "object",
|
|
3700
|
-
key: m.instanceName,
|
|
3701
|
-
value: {
|
|
3702
|
-
_type: "reference",
|
|
3703
|
-
_ref: m.matchedFontId,
|
|
3704
|
-
_weak: true
|
|
3705
|
-
}
|
|
3706
|
-
}));
|
|
3707
|
-
try {
|
|
3708
|
-
await client.patch(vf.documentId).set({
|
|
3709
|
-
variableInstanceReferences: references
|
|
3710
|
-
}).commit();
|
|
3711
|
-
console.log(`Patched VF instance mappings: ${vf.documentId} (${references.length} instances)`);
|
|
3712
|
-
} catch (err) {
|
|
3713
|
-
console.error(`Failed to patch VF ${vf.documentId}:`, err);
|
|
3714
|
-
errors.push({ vfTitle: vf.title, error: err.message });
|
|
3715
|
-
}
|
|
3716
|
-
}
|
|
3717
|
-
setSaving(false);
|
|
3718
|
-
onComplete({ success: errors.length === 0, errors });
|
|
3719
|
-
}, [vfEntries, vfMappings, client, onComplete]);
|
|
3720
|
-
const totalInstances = Object.values(vfMappings).reduce((sum, m) => sum + m.length, 0);
|
|
3721
|
-
const matchedInstances = Object.values(vfMappings).reduce(
|
|
3722
|
-
(sum, m) => sum + m.filter((i) => i.matchedFontId).length,
|
|
3723
|
-
0
|
|
3724
|
-
);
|
|
3725
|
-
const unmatchedInstances = totalInstances - matchedInstances;
|
|
3726
|
-
const getAutocompleteOptions = useCallback4((currentFontId) => {
|
|
3727
|
-
return allStaticFonts.filter((sf) => !claimedFontIds.has(sf._id) || sf._id === currentFontId).map((sf) => ({
|
|
3728
|
-
value: sf._id,
|
|
3729
|
-
payload: sf
|
|
3730
|
-
}));
|
|
3731
|
-
}, [allStaticFonts, claimedFontIds]);
|
|
3732
|
-
if (loading) {
|
|
3733
|
-
return /* @__PURE__ */ React8.createElement(Card6, { border: true, padding: 4, radius: 2 }, /* @__PURE__ */ React8.createElement(Flex8, { align: "center", gap: 3, justify: "center" }, /* @__PURE__ */ React8.createElement(Spinner3, null), /* @__PURE__ */ React8.createElement(Text8, { size: 1 }, "Matching variable font instances to static fonts...")));
|
|
3734
|
-
}
|
|
3735
|
-
if (vfEntries.length === 0) {
|
|
3736
|
-
onComplete({ success: true, errors: [] });
|
|
3737
|
-
return null;
|
|
3738
|
-
}
|
|
3739
|
-
return /* @__PURE__ */ React8.createElement(Stack6, { space: 4 }, /* @__PURE__ */ React8.createElement(Stack6, { space: 2 }, /* @__PURE__ */ React8.createElement(Text8, { size: 2, weight: "semibold" }, "Map Variable Font Instances"), /* @__PURE__ */ React8.createElement(Text8, { size: 1, muted: true }, "Review the auto-matched instances below. Each named instance should map to its corresponding static font document.")), /* @__PURE__ */ React8.createElement(Flex8, { gap: 2, align: "center" }, /* @__PURE__ */ React8.createElement(
|
|
3740
|
-
Button6,
|
|
3741
|
-
{
|
|
3742
|
-
mode: "ghost",
|
|
3743
|
-
tone: "primary",
|
|
3744
|
-
icon: SearchIcon2,
|
|
3745
|
-
text: "Re-run Matching",
|
|
3746
|
-
fontSize: 0,
|
|
3747
|
-
padding: 2,
|
|
3748
|
-
onClick: runMatching,
|
|
3749
|
-
disabled: loading,
|
|
3750
|
-
style: { cursor: "pointer" }
|
|
3751
|
-
}
|
|
3752
|
-
), /* @__PURE__ */ React8.createElement(Badge6, { tone: "positive", fontSize: 0 }, matchedInstances, " matched"), unmatchedInstances > 0 && /* @__PURE__ */ React8.createElement(
|
|
3753
|
-
Badge6,
|
|
3754
|
-
{
|
|
3755
|
-
tone: "critical",
|
|
3756
|
-
fontSize: 0,
|
|
3757
|
-
style: { cursor: "pointer" },
|
|
3758
|
-
onClick: () => setFilterUnmatched((v) => !v)
|
|
3759
|
-
},
|
|
3760
|
-
unmatchedInstances,
|
|
3761
|
-
" unmatched ",
|
|
3762
|
-
filterUnmatched ? "(showing)" : ""
|
|
3763
|
-
), filterUnmatched && /* @__PURE__ */ React8.createElement(
|
|
3764
|
-
Badge6,
|
|
3765
|
-
{
|
|
3766
|
-
mode: "outline",
|
|
3767
|
-
fontSize: 0,
|
|
3768
|
-
style: { cursor: "pointer" },
|
|
3769
|
-
onClick: () => setFilterUnmatched(false)
|
|
3770
|
-
},
|
|
3771
|
-
"Clear filter"
|
|
3772
|
-
), /* @__PURE__ */ React8.createElement(Badge6, { mode: "outline", fontSize: 0 }, allStaticFonts.length, " fonts available")), vfEntries.map((vf) => {
|
|
3773
|
-
const mappings = vfMappings[vf.tempId] || [];
|
|
3774
|
-
const displayMappings = filterUnmatched ? mappings.filter((m) => !m.matchedFontId) : mappings;
|
|
3775
|
-
const vfMatched = mappings.filter((m) => m.matchedFontId).length;
|
|
3776
|
-
return /* @__PURE__ */ React8.createElement(Card6, { key: vf.tempId, border: true, padding: 3, radius: 2 }, /* @__PURE__ */ React8.createElement(Stack6, { space: 3 }, /* @__PURE__ */ React8.createElement(Flex8, { align: "center", gap: 2 }, /* @__PURE__ */ React8.createElement(Badge6, { tone: "primary", fontSize: 0 }, "VF"), /* @__PURE__ */ React8.createElement(Text8, { size: 1, weight: "semibold" }, vf.title), /* @__PURE__ */ React8.createElement(Text8, { size: 0, muted: true, style: { marginLeft: "auto" } }, vfMatched, "/", mappings.length, " matched")), /* @__PURE__ */ React8.createElement(Flex8, { align: "center", gap: 2, paddingY: 1, style: { borderBottom: "1px solid var(--card-border-color)" } }, /* @__PURE__ */ React8.createElement(Box7, { style: { width: 20 } }), /* @__PURE__ */ React8.createElement(Text8, { size: 0, weight: "semibold", muted: true, style: { flex: 1 } }, "Instance"), /* @__PURE__ */ React8.createElement(Text8, { size: 0, weight: "semibold", muted: true, style: { flex: 2 } }, "Static Font Document")), /* @__PURE__ */ React8.createElement(Stack6, { space: 1 }, displayMappings.map((mapping) => {
|
|
3777
|
-
const isMatched = !!mapping.matchedFontId;
|
|
3778
|
-
const options = getAutocompleteOptions(mapping.matchedFontId);
|
|
3779
|
-
return /* @__PURE__ */ React8.createElement(
|
|
3780
|
-
Flex8,
|
|
3781
|
-
{
|
|
3782
|
-
key: mapping._key,
|
|
3783
|
-
align: "center",
|
|
3784
|
-
gap: 2,
|
|
3785
|
-
paddingY: 2,
|
|
3786
|
-
style: { borderBottom: "1px solid var(--card-border-color)" }
|
|
3787
|
-
},
|
|
3788
|
-
/* @__PURE__ */ React8.createElement(Box7, { style: { width: 20, flexShrink: 0 } }, isMatched ? /* @__PURE__ */ React8.createElement(CheckmarkCircleIcon2, { style: { color: "#43b649", fontSize: 16 } }) : /* @__PURE__ */ React8.createElement(CloseCircleIcon, { style: { color: "#f03e2f", fontSize: 16 } })),
|
|
3789
|
-
/* @__PURE__ */ React8.createElement(Text8, { size: 1, style: { flex: 1, whiteSpace: "nowrap" } }, mapping.instanceName),
|
|
3790
|
-
/* @__PURE__ */ React8.createElement(Box7, { style: { flex: 2 } }, /* @__PURE__ */ React8.createElement(
|
|
3791
|
-
Autocomplete,
|
|
3792
|
-
{
|
|
3793
|
-
id: `instance-${mapping._key}`,
|
|
3794
|
-
options,
|
|
3795
|
-
value: mapping.matchedFontId,
|
|
3796
|
-
placeholder: "Search for a font...",
|
|
3797
|
-
icon: SearchIcon2,
|
|
3798
|
-
fontSize: 1,
|
|
3799
|
-
filterOption: (query, option) => {
|
|
3800
|
-
var _a, _b, _c, _d;
|
|
3801
|
-
const sf = option.payload;
|
|
3802
|
-
const q = query.toLowerCase();
|
|
3803
|
-
return ((_a = sf.title) == null ? void 0 : _a.toLowerCase().includes(q)) || ((_b = sf._id) == null ? void 0 : _b.toLowerCase().includes(q)) || ((_c = sf.weightName) == null ? void 0 : _c.toLowerCase().includes(q)) || String(sf.weight).includes(q) || ((_d = sf.subfamily) == null ? void 0 : _d.toLowerCase().includes(q));
|
|
3804
|
-
},
|
|
3805
|
-
renderOption: (option) => {
|
|
3806
|
-
const sf = option.payload;
|
|
3807
|
-
const isClaimed = claimedFontIds.has(sf._id) && sf._id !== mapping.matchedFontId;
|
|
3808
|
-
return /* @__PURE__ */ React8.createElement(Card6, { as: "button", padding: 2, style: { opacity: isClaimed ? 0.4 : 1 } }, /* @__PURE__ */ React8.createElement(Flex8, { align: "center", gap: 2 }, /* @__PURE__ */ React8.createElement(Text8, { size: 1 }, sf.title), /* @__PURE__ */ React8.createElement(Text8, { size: 0, muted: true }, sf.weight, " ", sf.style), sf.subfamily && sf.subfamily !== "Regular" && /* @__PURE__ */ React8.createElement(Badge6, { mode: "outline", fontSize: 0 }, sf.subfamily)));
|
|
3809
|
-
},
|
|
3810
|
-
renderValue: (value, option) => {
|
|
3811
|
-
if (option == null ? void 0 : option.payload) return option.payload.title;
|
|
3812
|
-
if (mapping.matchedFontTitle) return mapping.matchedFontTitle;
|
|
3813
|
-
const font = allStaticFonts.find((sf) => sf._id === value);
|
|
3814
|
-
return (font == null ? void 0 : font.title) || value;
|
|
3815
|
-
},
|
|
3816
|
-
onSelect: (value) => handleMappingChange(vf.tempId, mapping._key, value),
|
|
3817
|
-
openButton: true
|
|
3818
|
-
}
|
|
3819
|
-
))
|
|
3820
|
-
);
|
|
3821
|
-
})), mappings.length === 0 && /* @__PURE__ */ React8.createElement(Text8, { size: 1, muted: true }, "No named instances found in this variable font.")));
|
|
3822
|
-
}), /* @__PURE__ */ React8.createElement(Flex8, { justify: "flex-end", gap: 2, style: { position: "sticky", bottom: 0, background: "var(--card-bg-color)", paddingTop: 8, paddingBottom: 4 } }, /* @__PURE__ */ React8.createElement(
|
|
3823
|
-
Button6,
|
|
3824
|
-
{
|
|
3825
|
-
mode: "ghost",
|
|
3826
|
-
text: "Skip \u2014 I'll map instances later",
|
|
3827
|
-
fontSize: 1,
|
|
3828
|
-
padding: 3,
|
|
3829
|
-
onClick: () => onComplete({ success: true, errors: [], skipped: true }),
|
|
3830
|
-
style: { cursor: "pointer" }
|
|
3831
|
-
}
|
|
3832
|
-
), /* @__PURE__ */ React8.createElement(
|
|
3833
|
-
Button6,
|
|
3834
|
-
{
|
|
3835
|
-
mode: "default",
|
|
3836
|
-
tone: "positive",
|
|
3837
|
-
text: saving ? "Saving..." : `Save Mappings (${matchedInstances}/${totalInstances})`,
|
|
3838
|
-
fontSize: 1,
|
|
3839
|
-
padding: 3,
|
|
3840
|
-
disabled: saving,
|
|
3841
|
-
onClick: handleSave,
|
|
3842
|
-
style: { cursor: "pointer" }
|
|
3843
|
-
}
|
|
3844
|
-
)));
|
|
3845
|
-
}
|
|
3846
|
-
|
|
3847
|
-
// src/components/UploadSummary.jsx
|
|
3848
|
-
import React9, { useState as useState6, useCallback as useCallback5 } from "react";
|
|
3849
|
-
import { Stack as Stack7, Flex as Flex9, Text as Text9, Card as Card7, Badge as Badge7, Button as Button7, Box as Box8, Spinner as Spinner4 } from "@sanity/ui";
|
|
3850
|
-
import { CheckmarkCircleIcon as CheckmarkCircleIcon3, WarningOutlineIcon as WarningOutlineIcon2, ResetIcon as ResetIcon2 } from "@sanity/icons";
|
|
3851
|
-
function UploadSummary({
|
|
3852
|
-
plan,
|
|
3853
|
-
result,
|
|
3854
|
-
onClose,
|
|
3855
|
-
onRetry,
|
|
3856
|
-
client,
|
|
3857
|
-
docId,
|
|
3858
|
-
stylesObject,
|
|
3859
|
-
preferredStyleRef
|
|
3860
|
-
}) {
|
|
3861
|
-
var _a;
|
|
3862
|
-
const [retryingPatch, setRetryingPatch] = useState6(false);
|
|
3863
|
-
const [patchRetryResult, setPatchRetryResult] = useState6(null);
|
|
3864
|
-
const hasFailedFonts = ((_a = result == null ? void 0 : result.failedFonts) == null ? void 0 : _a.length) > 0;
|
|
3865
|
-
const hasTypefacePatchError = (result == null ? void 0 : result.typefacePatchError) && !(patchRetryResult == null ? void 0 : patchRetryResult.success);
|
|
3866
|
-
const allSuccess = (result == null ? void 0 : result.success) && !hasTypefacePatchError;
|
|
3867
|
-
const handleRetryTypefacePatch = useCallback5(async () => {
|
|
3868
|
-
var _a2, _b;
|
|
3869
|
-
if (!result || !client || !docId) return;
|
|
3870
|
-
setRetryingPatch(true);
|
|
3871
|
-
setPatchRetryResult(null);
|
|
3872
|
-
try {
|
|
3873
|
-
const subfamilies = {};
|
|
3874
|
-
const uniqueSubfamilies = /* @__PURE__ */ new Set();
|
|
3875
|
-
for (const entry of Object.values(plan.fonts)) {
|
|
3876
|
-
if (entry.status === "error") continue;
|
|
3877
|
-
subfamilies[entry.documentId] = entry.subfamily;
|
|
3878
|
-
if (entry.subfamily) uniqueSubfamilies.add(entry.subfamily);
|
|
3879
|
-
}
|
|
3880
|
-
await updateTypefaceDocument(
|
|
3881
|
-
docId,
|
|
3882
|
-
result.fontRefs || [],
|
|
3883
|
-
result.variableRefs || [],
|
|
3884
|
-
subfamilies,
|
|
3885
|
-
[...uniqueSubfamilies],
|
|
3886
|
-
(stylesObject == null ? void 0 : stylesObject.subfamilies) || [],
|
|
3887
|
-
preferredStyleRef || {},
|
|
3888
|
-
{ weight: -100, style: "Italic", _ref: ((_b = (_a2 = result.fontRefs) == null ? void 0 : _a2[0]) == null ? void 0 : _b._ref) || "" },
|
|
3889
|
-
stylesObject || {},
|
|
3890
|
-
client,
|
|
3891
|
-
() => {
|
|
3892
|
-
},
|
|
3893
|
-
() => {
|
|
3894
|
-
}
|
|
3895
|
-
);
|
|
3896
|
-
setPatchRetryResult({ success: true });
|
|
3897
|
-
} catch (err) {
|
|
3898
|
-
console.error("Typeface patch retry failed:", err);
|
|
3899
|
-
setPatchRetryResult({ success: false, error: err.message });
|
|
3900
|
-
}
|
|
3901
|
-
setRetryingPatch(false);
|
|
3902
|
-
}, [result, client, docId, plan, stylesObject, preferredStyleRef]);
|
|
3903
|
-
return /* @__PURE__ */ React9.createElement(Stack7, { space: 4 }, /* @__PURE__ */ React9.createElement(Flex9, { align: "center", gap: 3, ref: (el) => {
|
|
3904
|
-
var _a2;
|
|
3905
|
-
return (_a2 = el == null ? void 0 : el.focus) == null ? void 0 : _a2.call(el);
|
|
3906
|
-
}, tabIndex: -1 }, allSuccess ? /* @__PURE__ */ React9.createElement(CheckmarkCircleIcon3, { style: { color: "#43b649", fontSize: 28 } }) : /* @__PURE__ */ React9.createElement(WarningOutlineIcon2, { style: { color: "#f03e2f", fontSize: 28 } }), /* @__PURE__ */ React9.createElement(Text9, { size: 2, weight: "semibold" }, allSuccess ? "Upload Complete" : "Upload Completed with Issues")), result && /* @__PURE__ */ React9.createElement(Card7, { border: true, padding: 4, radius: 2 }, /* @__PURE__ */ React9.createElement(Stack7, { space: 3 }, /* @__PURE__ */ React9.createElement(Flex9, { gap: 2, wrap: "wrap" }, result.created > 0 && /* @__PURE__ */ React9.createElement(Badge7, { tone: "positive", fontSize: 1 }, result.created, " created"), result.updated > 0 && /* @__PURE__ */ React9.createElement(Badge7, { tone: "caution", fontSize: 1 }, result.updated, " updated"), result.failed > 0 && /* @__PURE__ */ React9.createElement(Badge7, { tone: "critical", fontSize: 1 }, result.failed, " failed"), result.skipped > 0 && /* @__PURE__ */ React9.createElement(Badge7, { mode: "outline", fontSize: 1 }, result.skipped, " skipped")))), hasFailedFonts && /* @__PURE__ */ React9.createElement(Stack7, { space: 3 }, /* @__PURE__ */ React9.createElement(Flex9, { align: "center", justify: "space-between" }, /* @__PURE__ */ React9.createElement(Flex9, { align: "center", gap: 2 }, /* @__PURE__ */ React9.createElement(Text9, { size: 1, weight: "semibold" }, "Failed Fonts"), /* @__PURE__ */ React9.createElement(Badge7, { tone: "critical", fontSize: 0 }, result.failedFonts.length)), /* @__PURE__ */ React9.createElement(
|
|
3907
|
-
Button7,
|
|
3908
|
-
{
|
|
3909
|
-
mode: "ghost",
|
|
3910
|
-
tone: "primary",
|
|
3911
|
-
icon: ResetIcon2,
|
|
3912
|
-
text: "Retry Failed",
|
|
3913
|
-
fontSize: 1,
|
|
3914
|
-
padding: 2,
|
|
3915
|
-
onClick: () => onRetry(result.failedFonts.map((f) => f.tempId).filter(Boolean))
|
|
3916
|
-
}
|
|
3917
|
-
)), /* @__PURE__ */ React9.createElement(Stack7, { space: 2 }, result.failedFonts.map((f, i) => /* @__PURE__ */ React9.createElement(Card7, { key: i, tone: "critical", border: true, padding: 3, radius: 2 }, /* @__PURE__ */ React9.createElement(Stack7, { space: 2 }, /* @__PURE__ */ React9.createElement(Flex9, { align: "center", gap: 2 }, /* @__PURE__ */ React9.createElement(Text9, { size: 1, weight: "semibold" }, f.title), f.failedAt && f.failedAt !== "unknown" && /* @__PURE__ */ React9.createElement(Badge7, { tone: "critical", fontSize: 0, mode: "outline" }, "Failed at: ", f.failedAt)), /* @__PURE__ */ React9.createElement(Text9, { size: 1 }, f.error)))))), hasTypefacePatchError && /* @__PURE__ */ React9.createElement(Card7, { tone: "caution", border: true, padding: 4, radius: 2 }, /* @__PURE__ */ React9.createElement(Stack7, { space: 3 }, /* @__PURE__ */ React9.createElement(Text9, { size: 1, weight: "semibold" }, "Typeface Document Not Updated"), /* @__PURE__ */ React9.createElement(Text9, { size: 1 }, result.created + result.updated, " font document", result.created + result.updated === 1 ? "" : "s", " created/updated successfully, but the typeface document could not be patched to reference them."), /* @__PURE__ */ React9.createElement(Text9, { size: 1, muted: true }, result.typefacePatchError), /* @__PURE__ */ React9.createElement(Flex9, { gap: 2 }, /* @__PURE__ */ React9.createElement(
|
|
3918
|
-
Button7,
|
|
3919
|
-
{
|
|
3920
|
-
mode: "default",
|
|
3921
|
-
tone: "primary",
|
|
3922
|
-
icon: retryingPatch ? void 0 : ResetIcon2,
|
|
3923
|
-
text: retryingPatch ? "Retrying..." : "Retry Typeface Patch",
|
|
3924
|
-
disabled: retryingPatch,
|
|
3925
|
-
onClick: handleRetryTypefacePatch
|
|
3926
|
-
}
|
|
3927
|
-
), retryingPatch && /* @__PURE__ */ React9.createElement(Spinner4, null)))), (patchRetryResult == null ? void 0 : patchRetryResult.success) && /* @__PURE__ */ React9.createElement(Card7, { tone: "positive", border: true, padding: 3, radius: 2 }, /* @__PURE__ */ React9.createElement(Text9, { size: 1 }, "Typeface document updated successfully on retry.")), patchRetryResult && !patchRetryResult.success && /* @__PURE__ */ React9.createElement(Card7, { tone: "critical", border: true, padding: 3, radius: 2 }, /* @__PURE__ */ React9.createElement(Text9, { size: 1 }, "Retry failed: ", patchRetryResult.error)), /* @__PURE__ */ React9.createElement(Flex9, { justify: "flex-end" }, /* @__PURE__ */ React9.createElement(
|
|
3928
|
-
Button7,
|
|
3929
|
-
{
|
|
3930
|
-
mode: "default",
|
|
3931
|
-
tone: "primary",
|
|
3932
|
-
text: "Close",
|
|
3933
|
-
onClick: onClose
|
|
3934
|
-
}
|
|
3935
|
-
)));
|
|
3936
|
-
}
|
|
3937
|
-
|
|
3938
|
-
// src/components/UploadModal.jsx
|
|
3939
|
-
var STEPS = [
|
|
3940
|
-
{ key: 1, label: "Upload Files" },
|
|
3941
|
-
{ key: 2, label: "Review" },
|
|
3942
|
-
{ key: 3, label: "Upload" },
|
|
3943
|
-
{ key: 4, label: "Map Instances" }
|
|
3944
|
-
];
|
|
3945
|
-
function phaseToStep(phase) {
|
|
3946
|
-
switch (phase) {
|
|
3947
|
-
case PLAN_PHASE.IDLE:
|
|
3948
|
-
return 1;
|
|
3949
|
-
case PLAN_PHASE.PROCESSING:
|
|
3950
|
-
return 2;
|
|
3951
|
-
case PLAN_PHASE.REVIEWING:
|
|
3952
|
-
return 2;
|
|
3953
|
-
case PLAN_PHASE.READY:
|
|
3954
|
-
return 2;
|
|
3955
|
-
case PLAN_PHASE.EXECUTING:
|
|
3956
|
-
return 3;
|
|
3957
|
-
case PLAN_PHASE.COMPLETE:
|
|
3958
|
-
return 3;
|
|
3959
|
-
case PLAN_PHASE.ERROR:
|
|
3960
|
-
return 3;
|
|
3961
|
-
default:
|
|
3962
|
-
return 1;
|
|
3963
|
-
}
|
|
3964
|
-
}
|
|
3965
|
-
function UploadModal({
|
|
3966
|
-
open,
|
|
3967
|
-
onClose,
|
|
3968
|
-
client,
|
|
3969
|
-
docId,
|
|
3970
|
-
typefaceTitle,
|
|
3971
|
-
stylesObject,
|
|
3972
|
-
preferredStyleRef,
|
|
3973
|
-
slug
|
|
3974
|
-
}) {
|
|
3975
|
-
const [plan, dispatch] = useReducer2(planReducer, null, () => createEmptyPlan());
|
|
3976
|
-
const [processingCancelled, setProcessingCancelled] = useState7(false);
|
|
3977
|
-
const [executionResult, setExecutionResult] = useState7(null);
|
|
3978
|
-
const [retryTempIds, setRetryTempIds] = useState7(null);
|
|
3979
|
-
const [instanceMappingPhase, setInstanceMappingPhase] = useState7(false);
|
|
3980
|
-
const [instanceMappingResult, setInstanceMappingResult] = useState7(null);
|
|
3981
|
-
const cancelRef = useRef3(false);
|
|
3982
|
-
const focusRef = useRef3(null);
|
|
3983
|
-
const { weightKeywordList, italicKeywordList } = useMemo7(() => generateStyleKeywords(), []);
|
|
3984
|
-
const hasVFs = useMemo7(
|
|
3985
|
-
() => Object.values(plan.fonts).some((f) => f.variableFont && f.status !== "error"),
|
|
3986
|
-
[plan.fonts]
|
|
3987
|
-
);
|
|
3988
|
-
const baseStep = phaseToStep(plan.phase);
|
|
3989
|
-
const currentStep = instanceMappingPhase ? 4 : plan.phase === PLAN_PHASE.COMPLETE && !instanceMappingResult ? baseStep : baseStep;
|
|
3990
|
-
const isExecuting = plan.phase === PLAN_PHASE.EXECUTING;
|
|
3991
|
-
useEffect5(() => {
|
|
3992
|
-
if (!open || !isExecuting) return;
|
|
3993
|
-
const handler = (e) => {
|
|
3994
|
-
e.preventDefault();
|
|
3995
|
-
e.returnValue = "";
|
|
3996
|
-
};
|
|
3997
|
-
window.addEventListener("beforeunload", handler);
|
|
3998
|
-
return () => window.removeEventListener("beforeunload", handler);
|
|
3999
|
-
}, [open, isExecuting]);
|
|
4000
|
-
useEffect5(() => {
|
|
4001
|
-
if (focusRef.current) {
|
|
4002
|
-
focusRef.current.focus();
|
|
4003
|
-
}
|
|
4004
|
-
}, [currentStep]);
|
|
4005
|
-
const handleClose = useCallback6(() => {
|
|
4006
|
-
if (isExecuting) return;
|
|
4007
|
-
const hasFonts = Object.keys(plan.fonts).length > 0;
|
|
4008
|
-
if (hasFonts && plan.phase !== PLAN_PHASE.COMPLETE) {
|
|
4009
|
-
if (!window.confirm("Close the upload modal? All progress will be lost.")) return;
|
|
4010
|
-
}
|
|
4011
|
-
dispatch({ type: "SET_PHASE", phase: PLAN_PHASE.IDLE });
|
|
4012
|
-
setExecutionResult(null);
|
|
4013
|
-
onClose();
|
|
4014
|
-
}, [plan, isExecuting, onClose]);
|
|
4015
|
-
const handleStartProcessing = useCallback6(async (files, settings) => {
|
|
4016
|
-
dispatch({ type: "SET_SETTINGS", settings: { ...settings, typefaceTitle } });
|
|
4017
|
-
dispatch({ type: "SET_PHASE", phase: PLAN_PHASE.PROCESSING, totalFiles: files.length });
|
|
4018
|
-
cancelRef.current = false;
|
|
4019
|
-
setProcessingCancelled(false);
|
|
4020
|
-
setExecutionResult(null);
|
|
4021
|
-
try {
|
|
4022
|
-
const builtPlan = await buildUploadPlan({
|
|
4023
|
-
files,
|
|
4024
|
-
typefaceTitle,
|
|
4025
|
-
docId,
|
|
4026
|
-
settings,
|
|
4027
|
-
client,
|
|
4028
|
-
stylesObject,
|
|
4029
|
-
weightKeywordList,
|
|
4030
|
-
italicKeywordList,
|
|
4031
|
-
onProgress: (event) => {
|
|
4032
|
-
if (cancelRef.current) return;
|
|
4033
|
-
if (event.type === "font-processed" || event.type === "font-error") {
|
|
4034
|
-
dispatch({ type: "UPDATE_PROCESSING_PROGRESS", progress: event.progress });
|
|
4035
|
-
}
|
|
4036
|
-
}
|
|
4037
|
-
});
|
|
4038
|
-
if (!cancelRef.current) {
|
|
4039
|
-
for (const [tempId, entry] of Object.entries(builtPlan.fonts)) {
|
|
4040
|
-
dispatch({ type: "ADD_PROCESSED_FONT", tempId, fontEntry: entry });
|
|
4041
|
-
}
|
|
4042
|
-
dispatch({ type: "SET_PHASE", phase: PLAN_PHASE.REVIEWING });
|
|
4043
|
-
}
|
|
4044
|
-
} catch (err) {
|
|
4045
|
-
console.error("Processing failed:", err);
|
|
4046
|
-
dispatch({ type: "SET_PHASE", phase: PLAN_PHASE.REVIEWING });
|
|
4047
|
-
}
|
|
4048
|
-
}, [typefaceTitle, docId, client, stylesObject, weightKeywordList, italicKeywordList]);
|
|
4049
|
-
const handleCancelProcessing = useCallback6(() => {
|
|
4050
|
-
cancelRef.current = true;
|
|
4051
|
-
setProcessingCancelled(true);
|
|
4052
|
-
dispatch({ type: "SET_PHASE", phase: PLAN_PHASE.IDLE });
|
|
4053
|
-
}, []);
|
|
4054
|
-
const handleStartExecution = useCallback6(() => {
|
|
4055
|
-
dispatch({ type: "SET_PHASE", phase: PLAN_PHASE.EXECUTING });
|
|
4056
|
-
}, []);
|
|
4057
|
-
const handleExecutionComplete = useCallback6((result) => {
|
|
4058
|
-
setExecutionResult(result);
|
|
4059
|
-
if (hasVFs && result.success !== false) {
|
|
4060
|
-
setInstanceMappingPhase(true);
|
|
4061
|
-
dispatch({ type: "SET_PHASE", phase: PLAN_PHASE.COMPLETE });
|
|
4062
|
-
} else {
|
|
4063
|
-
dispatch({ type: "SET_PHASE", phase: PLAN_PHASE.COMPLETE });
|
|
4064
|
-
}
|
|
4065
|
-
}, [hasVFs]);
|
|
4066
|
-
const handleInstanceMappingComplete = useCallback6((result) => {
|
|
4067
|
-
setInstanceMappingResult(result);
|
|
4068
|
-
setInstanceMappingPhase(false);
|
|
4069
|
-
}, []);
|
|
4070
|
-
if (!open) return null;
|
|
4071
|
-
const handleStepClick = useCallback6((stepKey) => {
|
|
4072
|
-
if (isExecuting) return;
|
|
4073
|
-
if (stepKey === currentStep) return;
|
|
4074
|
-
if (stepKey === 1 && currentStep > 1) {
|
|
4075
|
-
if (Object.keys(plan.fonts).length > 0) {
|
|
4076
|
-
if (!window.confirm("Go back to settings? Current review progress will be lost.")) return;
|
|
4077
|
-
}
|
|
4078
|
-
dispatch({ type: "SET_PHASE", phase: PLAN_PHASE.IDLE });
|
|
4079
|
-
}
|
|
4080
|
-
}, [currentStep, isExecuting, plan.fonts, dispatch]);
|
|
4081
|
-
return /* @__PURE__ */ React10.createElement(
|
|
4082
|
-
Dialog,
|
|
4083
|
-
{
|
|
4084
|
-
id: "upload-modal",
|
|
4085
|
-
header: /* @__PURE__ */ React10.createElement(Flex10, { direction: "column", gap: 3, style: { width: "100%" } }, /* @__PURE__ */ React10.createElement(Text10, { weight: "semibold", size: 2 }, "Upload Fonts"), /* @__PURE__ */ React10.createElement(Flex10, { gap: 1, style: { width: "100%" } }, STEPS.filter((step) => step.key !== 4 || hasVFs).map((step, i) => {
|
|
4086
|
-
const isActive = currentStep === step.key;
|
|
4087
|
-
const isCompleted = currentStep > step.key;
|
|
4088
|
-
const isClickable = !isExecuting && step.key < currentStep;
|
|
4089
|
-
return /* @__PURE__ */ React10.createElement(
|
|
4090
|
-
Box9,
|
|
4091
|
-
{
|
|
4092
|
-
key: step.key,
|
|
4093
|
-
as: isClickable ? "button" : "div",
|
|
4094
|
-
onClick: isClickable ? () => handleStepClick(step.key) : void 0,
|
|
4095
|
-
style: {
|
|
4096
|
-
flex: 1,
|
|
4097
|
-
padding: "10px 12px",
|
|
4098
|
-
border: "none",
|
|
4099
|
-
borderRadius: 4,
|
|
4100
|
-
cursor: isClickable ? "pointer" : "default",
|
|
4101
|
-
background: isActive ? "var(--card-badge-primary-bg-color)" : isCompleted ? "var(--card-badge-positive-bg-color)" : "var(--card-muted-bg-color)",
|
|
4102
|
-
color: isActive || isCompleted ? "#fff" : "var(--card-muted-fg-color)",
|
|
4103
|
-
textAlign: "center",
|
|
4104
|
-
transition: "background 0.15s ease",
|
|
4105
|
-
opacity: !isActive && !isCompleted ? 0.6 : 1
|
|
4106
|
-
}
|
|
4107
|
-
},
|
|
4108
|
-
/* @__PURE__ */ React10.createElement(
|
|
4109
|
-
Text10,
|
|
4110
|
-
{
|
|
4111
|
-
size: 1,
|
|
4112
|
-
weight: isActive ? "bold" : "medium",
|
|
4113
|
-
style: { color: "inherit" }
|
|
4114
|
-
},
|
|
4115
|
-
step.key,
|
|
4116
|
-
". ",
|
|
4117
|
-
step.label
|
|
4118
|
-
)
|
|
4119
|
-
);
|
|
4120
|
-
}))),
|
|
4121
|
-
width: 2,
|
|
4122
|
-
onClose: isExecuting ? void 0 : handleClose,
|
|
4123
|
-
onClickOutside: () => {
|
|
4124
|
-
}
|
|
4125
|
-
},
|
|
4126
|
-
/* @__PURE__ */ React10.createElement(Box9, { padding: 4 }, currentStep === 1 && /* @__PURE__ */ React10.createElement(
|
|
4127
|
-
UploadStep1Settings,
|
|
4128
|
-
{
|
|
4129
|
-
settings: plan.settings,
|
|
4130
|
-
onStartProcessing: handleStartProcessing
|
|
4131
|
-
}
|
|
4132
|
-
), currentStep === 2 && /* @__PURE__ */ React10.createElement(
|
|
4133
|
-
UploadStep2Review,
|
|
4134
|
-
{
|
|
4135
|
-
plan,
|
|
4136
|
-
dispatch,
|
|
4137
|
-
onCancelProcessing: handleCancelProcessing,
|
|
4138
|
-
onStartExecution: handleStartExecution,
|
|
4139
|
-
processingCancelled
|
|
4140
|
-
}
|
|
4141
|
-
), currentStep === 3 && plan.phase !== PLAN_PHASE.COMPLETE && /* @__PURE__ */ React10.createElement(
|
|
4142
|
-
UploadStep3Execute,
|
|
4143
|
-
{
|
|
4144
|
-
key: retryTempIds ? "retry" : "initial",
|
|
4145
|
-
plan,
|
|
4146
|
-
client,
|
|
4147
|
-
docId,
|
|
4148
|
-
stylesObject,
|
|
4149
|
-
preferredStyleRef,
|
|
4150
|
-
retryTempIds,
|
|
4151
|
-
onComplete: (result) => {
|
|
4152
|
-
setRetryTempIds(null);
|
|
4153
|
-
handleExecutionComplete(result);
|
|
4154
|
-
}
|
|
4155
|
-
}
|
|
4156
|
-
), plan.phase === PLAN_PHASE.COMPLETE && instanceMappingPhase && /* @__PURE__ */ React10.createElement(
|
|
4157
|
-
UploadStep3bInstances,
|
|
4158
|
-
{
|
|
4159
|
-
plan,
|
|
4160
|
-
executionResult,
|
|
4161
|
-
client,
|
|
4162
|
-
typefaceTitle,
|
|
4163
|
-
onComplete: handleInstanceMappingComplete
|
|
4164
|
-
}
|
|
4165
|
-
), plan.phase === PLAN_PHASE.COMPLETE && !instanceMappingPhase && /* @__PURE__ */ React10.createElement(
|
|
4166
|
-
UploadSummary,
|
|
4167
|
-
{
|
|
4168
|
-
plan,
|
|
4169
|
-
result: executionResult,
|
|
4170
|
-
instanceMappingResult,
|
|
4171
|
-
onClose: handleClose,
|
|
4172
|
-
onRetry: (failedTempIds) => {
|
|
4173
|
-
setRetryTempIds(failedTempIds || null);
|
|
4174
|
-
setExecutionResult(null);
|
|
4175
|
-
setInstanceMappingPhase(false);
|
|
4176
|
-
setInstanceMappingResult(null);
|
|
4177
|
-
dispatch({ type: "SET_PHASE", phase: PLAN_PHASE.EXECUTING });
|
|
4178
|
-
},
|
|
4179
|
-
client,
|
|
4180
|
-
docId,
|
|
4181
|
-
stylesObject,
|
|
4182
|
-
preferredStyleRef
|
|
4183
|
-
}
|
|
4184
|
-
))
|
|
4185
|
-
);
|
|
4186
|
-
}
|
|
4187
|
-
|
|
4188
|
-
export {
|
|
4189
|
-
parseFont,
|
|
4190
|
-
getNameString,
|
|
4191
|
-
getAllFeatureTags,
|
|
4192
|
-
getCharacterSet,
|
|
4193
|
-
getVariationAxes,
|
|
4194
|
-
getNamedInstances,
|
|
4195
|
-
getFontMetrics,
|
|
4196
|
-
getFontMetadata,
|
|
4197
|
-
getWeightClass,
|
|
4198
|
-
getFsSelection,
|
|
4199
|
-
getMacStyle,
|
|
4200
|
-
getItalicAngle,
|
|
4201
|
-
getGlyphCount,
|
|
4202
|
-
getFamilyClass,
|
|
4203
|
-
escapeCssFontName,
|
|
4204
|
-
reverseSpellingLookup,
|
|
4205
|
-
expandAbbreviations,
|
|
4206
|
-
removeWeightNames,
|
|
4207
|
-
generateStyleKeywords,
|
|
4208
|
-
sanitizeForSanityId,
|
|
4209
|
-
readFontFile,
|
|
4210
|
-
processFontFiles,
|
|
4211
|
-
extractFontMetadata,
|
|
4212
|
-
extractWeightName,
|
|
4213
|
-
extractWeightFromFullName,
|
|
4214
|
-
processSubfamilyName,
|
|
4215
|
-
processItalicKeywords,
|
|
4216
|
-
formatFontTitle,
|
|
4217
|
-
addItalicToFontTitle,
|
|
4218
|
-
createFontObject,
|
|
4219
|
-
determineWeight,
|
|
4220
|
-
sortFontObjects,
|
|
4221
|
-
logFontInfo,
|
|
4222
|
-
generateCssFile,
|
|
4223
|
-
generateFontData,
|
|
4224
|
-
parseVariableFontInstances,
|
|
4225
|
-
parseVariableFontInstances_default,
|
|
4226
|
-
updateTypefaceDocument,
|
|
4227
|
-
PriceInput_default,
|
|
4228
|
-
FONT_STATUS,
|
|
4229
|
-
PLAN_PHASE,
|
|
4230
|
-
RECOMMENDATION,
|
|
4231
|
-
EXECUTION_STATUS,
|
|
4232
|
-
PLAN_VERSION,
|
|
4233
|
-
createFontDecisions,
|
|
4234
|
-
createEmptyPlan,
|
|
4235
|
-
planReducer,
|
|
4236
|
-
resolveExistingFont,
|
|
4237
|
-
buildUploadPlan,
|
|
4238
|
-
UploadStep1Settings,
|
|
4239
|
-
ExistingDocumentResolver,
|
|
4240
|
-
FontReviewCard_default,
|
|
4241
|
-
BulkActions,
|
|
4242
|
-
UploadStep2Review,
|
|
4243
|
-
executeUploadPlan,
|
|
4244
|
-
createInitialExecutionState,
|
|
4245
|
-
executionReducer,
|
|
4246
|
-
UploadStep3Execute,
|
|
4247
|
-
UploadStep3bInstances,
|
|
4248
|
-
UploadSummary,
|
|
4249
|
-
UploadModal
|
|
4250
|
-
};
|