@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-PGSHWNGW.js
DELETED
|
@@ -1,4250 +0,0 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } }// src/components/UploadModal.jsx
|
|
2
|
-
var _react = require('react'); var _react2 = _interopRequireDefault(_react);
|
|
3
|
-
var _ui = require('@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
|
-
var _slugify = require('slugify'); var _slugify2 = _interopRequireDefault(_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 = _slugify2.default.call(void 0, 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
|
-
var _nanoid = require('nanoid');
|
|
593
|
-
|
|
594
|
-
// src/utils/parseFont.js
|
|
595
|
-
var _Font = null;
|
|
596
|
-
async function getFont() {
|
|
597
|
-
if (!_Font) {
|
|
598
|
-
const pako = await Promise.resolve().then(() => _interopRequireWildcard(require("pako")));
|
|
599
|
-
globalThis.pako = pako.default || pako;
|
|
600
|
-
await Promise.resolve().then(() => _interopRequireWildcard(require("./unbrotli-NSXTNE2T.js")));
|
|
601
|
-
const mod = await Promise.resolve().then(() => _interopRequireWildcard(require("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 (e2) {
|
|
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 : _nullishCoalesce(_nullishCoalesce((hhea == null ? void 0 : hhea.ascender), () => ( (os2 == null ? void 0 : os2.sTypoAscender))), () => ( 0)),
|
|
715
|
-
descender: useTypo ? (os2 == null ? void 0 : os2.sTypoDescender) || 0 : _nullishCoalesce(_nullishCoalesce((hhea == null ? void 0 : hhea.descender), () => ( (os2 == null ? void 0 : os2.sTypoDescender))), () => ( 0)),
|
|
716
|
-
lineGap: useTypo ? (os2 == null ? void 0 : os2.sTypoLineGap) || 0 : _nullishCoalesce(_nullishCoalesce((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
|
-
|
|
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.nanoid.call(void 0, ),
|
|
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) + "-" + _nanoid.nanoid.call(void 0, 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 + "-" + _nanoid.nanoid.call(void 0, 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
|
-
|
|
1536
|
-
|
|
1537
|
-
var _icons = require('@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] = _react.useState.call(void 0, []);
|
|
1549
|
-
const [isDragging, setIsDragging] = _react.useState.call(void 0, false);
|
|
1550
|
-
const [filterType, setFilterType] = _react.useState.call(void 0, null);
|
|
1551
|
-
const fileInputRef = _react.useRef.call(void 0, null);
|
|
1552
|
-
const handleFileSelect = _react.useCallback.call(void 0, (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 = _react.useCallback.call(void 0, (file) => {
|
|
1558
|
-
setPendingFiles((prev) => prev.filter((f) => f !== file));
|
|
1559
|
-
}, []);
|
|
1560
|
-
const handleDragEnter = _react.useCallback.call(void 0, (e) => {
|
|
1561
|
-
e.preventDefault();
|
|
1562
|
-
setIsDragging(true);
|
|
1563
|
-
}, []);
|
|
1564
|
-
const handleDragOver = _react.useCallback.call(void 0, (e) => {
|
|
1565
|
-
e.preventDefault();
|
|
1566
|
-
}, []);
|
|
1567
|
-
const handleDragLeave = _react.useCallback.call(void 0, (e) => {
|
|
1568
|
-
e.preventDefault();
|
|
1569
|
-
setIsDragging(false);
|
|
1570
|
-
}, []);
|
|
1571
|
-
const handleDrop = _react.useCallback.call(void 0, (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 = _react.useCallback.call(void 0, () => {
|
|
1578
|
-
const sorted = sortFilesByType(pendingFiles);
|
|
1579
|
-
onStartProcessing(sorted, settings);
|
|
1580
|
-
}, [pendingFiles, settings, onStartProcessing]);
|
|
1581
|
-
const typeBreakdown = _react.useMemo.call(void 0, () => {
|
|
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 = _react.useMemo.call(void 0, () => {
|
|
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__ */ _react2.default.createElement(_ui.Stack, { space: 4 }, /* @__PURE__ */ _react2.default.createElement(
|
|
1613
|
-
_ui.Box,
|
|
1614
|
-
{
|
|
1615
|
-
onDragEnter: handleDragEnter,
|
|
1616
|
-
onDragOver: handleDragOver,
|
|
1617
|
-
onDragLeave: handleDragLeave,
|
|
1618
|
-
onDrop: handleDrop,
|
|
1619
|
-
style: dropZoneStyle
|
|
1620
|
-
},
|
|
1621
|
-
/* @__PURE__ */ _react2.default.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__ */ _react2.default.createElement(_ui.Stack, { space: 3 }, /* @__PURE__ */ _react2.default.createElement(_ui.Text, { size: 1, muted: true }, isDragging ? "Release to add files" : "Drop font files here"), /* @__PURE__ */ _react2.default.createElement(_ui.Flex, { justify: "center" }, /* @__PURE__ */ _react2.default.createElement(
|
|
1633
|
-
_ui.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__ */ _react2.default.createElement(_ui.Flex, { align: "center", justify: "center", gap: 2 }, /* @__PURE__ */ _react2.default.createElement(_ui.Text, { size: 1, muted: true }, isDragging ? "Release to add" : "Drop more files or"), /* @__PURE__ */ _react2.default.createElement(
|
|
1646
|
-
_ui.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__ */ _react2.default.createElement(_ui.Stack, { space: 3 }, /* @__PURE__ */ _react2.default.createElement(_ui.Flex, { align: "center", justify: "space-between" }, /* @__PURE__ */ _react2.default.createElement(_ui.Flex, { align: "center", gap: 2 }, /* @__PURE__ */ _react2.default.createElement(_ui.Text, { size: 1, weight: "semibold" }, filterType ? `${displayedFiles.length} of ${pendingFiles.length} files (${filterType.toUpperCase()})` : `${pendingFiles.length} file${pendingFiles.length === 1 ? "" : "s"}`), /* @__PURE__ */ _react2.default.createElement(_ui.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__ */ _react2.default.createElement(
|
|
1664
|
-
_ui.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__ */ _react2.default.createElement(
|
|
1678
|
-
_ui.Badge,
|
|
1679
|
-
{
|
|
1680
|
-
mode: "outline",
|
|
1681
|
-
fontSize: 0,
|
|
1682
|
-
style: { cursor: "pointer" },
|
|
1683
|
-
onClick: () => setFilterType(null)
|
|
1684
|
-
},
|
|
1685
|
-
"Clear filter"
|
|
1686
|
-
))), /* @__PURE__ */ _react2.default.createElement(
|
|
1687
|
-
_ui.Button,
|
|
1688
|
-
{
|
|
1689
|
-
mode: "bleed",
|
|
1690
|
-
tone: "default",
|
|
1691
|
-
fontSize: 1,
|
|
1692
|
-
padding: 1,
|
|
1693
|
-
text: "Clear all",
|
|
1694
|
-
onClick: () => setPendingFiles([])
|
|
1695
|
-
}
|
|
1696
|
-
)), /* @__PURE__ */ _react2.default.createElement(_ui.Box, { style: { maxHeight: 350, overflowY: "auto" } }, /* @__PURE__ */ _react2.default.createElement(
|
|
1697
|
-
_ui.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__ */ _react2.default.createElement(_ui.Text, { size: 0, weight: "semibold", muted: true, style: { width: 56, flexShrink: 0 } }, "Type"),
|
|
1706
|
-
/* @__PURE__ */ _react2.default.createElement(_ui.Text, { size: 0, weight: "semibold", muted: true, style: { flex: 1 } }, "File Name"),
|
|
1707
|
-
/* @__PURE__ */ _react2.default.createElement(_ui.Box, { style: { width: 32 } })
|
|
1708
|
-
), /* @__PURE__ */ _react2.default.createElement(_ui.Stack, { space: 0 }, displayedFiles.map((file, i) => {
|
|
1709
|
-
const ext = file.name.split(".").pop().toLowerCase();
|
|
1710
|
-
return /* @__PURE__ */ _react2.default.createElement(
|
|
1711
|
-
_ui.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__ */ _react2.default.createElement(
|
|
1723
|
-
_ui.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__ */ _react2.default.createElement(_ui.Text, { size: 1, style: { flex: 1 } }, file.name),
|
|
1733
|
-
/* @__PURE__ */ _react2.default.createElement(
|
|
1734
|
-
_ui.Button,
|
|
1735
|
-
{
|
|
1736
|
-
mode: "bleed",
|
|
1737
|
-
tone: "critical",
|
|
1738
|
-
icon: _icons.TrashIcon,
|
|
1739
|
-
padding: 1,
|
|
1740
|
-
onClick: () => handleRemoveFile(file),
|
|
1741
|
-
style: { flexShrink: 0 }
|
|
1742
|
-
}
|
|
1743
|
-
)
|
|
1744
|
-
);
|
|
1745
|
-
})))), pendingFiles.length > 0 && /* @__PURE__ */ _react2.default.createElement(
|
|
1746
|
-
_ui.Button,
|
|
1747
|
-
{
|
|
1748
|
-
mode: "default",
|
|
1749
|
-
tone: "primary",
|
|
1750
|
-
icon: _icons.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
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
// src/components/FontReviewCard.jsx
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
// src/components/ExistingDocumentResolver.jsx
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
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.default.createElement(_ui.Card, { tone: "caution", border: true, padding: 2, radius: 1 }, /* @__PURE__ */ _react2.default.createElement(_ui.Text, { size: 0 }, "Could not check for existing documents \u2014 will create new."));
|
|
1788
|
-
}
|
|
1789
|
-
if (recommendation === RECOMMENDATION.CREATE && !userChoice) {
|
|
1790
|
-
return /* @__PURE__ */ _react2.default.createElement(_ui.Stack, { space: 2 }, /* @__PURE__ */ _react2.default.createElement(_ui.Label, { size: 0 }, "Existing Document"), /* @__PURE__ */ _react2.default.createElement(_ui.Card, { tone: "default", border: true, padding: 2, radius: 1 }, /* @__PURE__ */ _react2.default.createElement(_ui.Text, { 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.default.createElement(_ui.Stack, { space: 2 }, /* @__PURE__ */ _react2.default.createElement(_ui.Label, { size: 0 }, "Existing Document"), /* @__PURE__ */ _react2.default.createElement(_ui.Flex, { align: "center", gap: 2 }, /* @__PURE__ */ _react2.default.createElement(
|
|
1797
|
-
_ui.Switch,
|
|
1798
|
-
{
|
|
1799
|
-
checked: isUpdating,
|
|
1800
|
-
onChange: handleToggle,
|
|
1801
|
-
style: { cursor: "pointer" }
|
|
1802
|
-
}
|
|
1803
|
-
), /* @__PURE__ */ _react2.default.createElement(_ui.Stack, { space: 1 }, /* @__PURE__ */ _react2.default.createElement(_ui.Text, { size: 1, weight: "semibold" }, isUpdating ? "Update existing document" : "Create new document"), /* @__PURE__ */ _react2.default.createElement(_ui.Text, { 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.default.createElement(
|
|
1804
|
-
_ui.Card,
|
|
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.default.createElement(_ui.Stack, { space: 2 }, /* @__PURE__ */ _react2.default.createElement(_ui.Flex, { align: "center", gap: 2 }, /* @__PURE__ */ _react2.default.createElement(_ui.Badge, { tone: isUpdating ? matchTone : "default", fontSize: 0 }, matchType), /* @__PURE__ */ _react2.default.createElement(_ui.Text, { size: 1, style: { fontFamily: "monospace" } }, matchDoc == null ? void 0 : matchDoc._id)), /* @__PURE__ */ _react2.default.createElement(_ui.Text, { 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.default.createElement(_ui.Stack, { space: 1 }, /* @__PURE__ */ _react2.default.createElement(_ui.Text, { size: 0, muted: true }, "Select which document to update:"), candidates.map((candidate) => /* @__PURE__ */ _react2.default.createElement(
|
|
1814
|
-
_ui.Card,
|
|
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.default.createElement(_ui.Flex, { align: "center", gap: 2 }, /* @__PURE__ */ _react2.default.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.default.createElement(_ui.Stack, { space: 1, style: { flex: 1 } }, /* @__PURE__ */ _react2.default.createElement(_ui.Text, { size: 1, style: { fontFamily: "monospace" } }, candidate._id), /* @__PURE__ */ _react2.default.createElement(_ui.Text, { 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.default.createElement(_ui.Stack, { space: 2 }, /* @__PURE__ */ _react2.default.createElement(_ui.Label, { size: 0 }, "Existing Document"), /* @__PURE__ */ _react2.default.createElement(_ui.Card, { border: true, padding: 2, radius: 1 }, /* @__PURE__ */ _react2.default.createElement(_ui.Text, { 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 = _react.memo.call(void 0, 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] = _react.useState.call(void 0, false);
|
|
1848
|
-
const [showAllFileTypes, setShowAllFileTypes] = _react.useState.call(void 0, false);
|
|
1849
|
-
const [showDocPreview, setShowDocPreview] = _react.useState.call(void 0, false);
|
|
1850
|
-
_react.useEffect.call(void 0, () => {
|
|
1851
|
-
setExpanded(allExpanded);
|
|
1852
|
-
}, [allExpanded]);
|
|
1853
|
-
const [localTitle, setLocalTitle] = _react.useState.call(void 0, entry.title);
|
|
1854
|
-
const [localWeight, setLocalWeight] = _react.useState.call(void 0, String(entry.weight));
|
|
1855
|
-
const [localWeightName, setLocalWeightName] = _react.useState.call(void 0, entry.weightName);
|
|
1856
|
-
const [localSubfamily, setLocalSubfamily] = _react.useState.call(void 0, entry.subfamily);
|
|
1857
|
-
const [localDocId, setLocalDocId] = _react.useState.call(void 0, 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 = _react.useMemo.call(void 0, () => {
|
|
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 = _react.useMemo.call(void 0, () => {
|
|
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 = _react.useCallback.call(void 0, () => {
|
|
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 = _react.useCallback.call(void 0, () => {
|
|
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 = _react.useCallback.call(void 0, () => {
|
|
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 = _react.useCallback.call(void 0, () => {
|
|
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 = _react.useCallback.call(void 0, (e) => {
|
|
1903
|
-
dispatch({ type: "SET_FONT_STYLE", tempId: entry.tempId, style: e.target.value });
|
|
1904
|
-
}, [entry.tempId, dispatch]);
|
|
1905
|
-
const handleSubfamilyBlur = _react.useCallback.call(void 0, () => {
|
|
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 = _react.useCallback.call(void 0, () => {
|
|
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 = _react.useCallback.call(void 0, () => {
|
|
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__ */ _react2.default.createElement(_ui.Card, { border: true, radius: 2, tone: cardTone, style: { marginBottom: -1 } }, /* @__PURE__ */ _react2.default.createElement(
|
|
1934
|
-
_ui.Box,
|
|
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__ */ _react2.default.createElement(_ui.Flex, { align: "center", gap: 2, paddingX: 2, paddingY: 2 }, /* @__PURE__ */ _react2.default.createElement(_ui.Box, { style: { width: 20, flexShrink: 0 } }, !isError && (expanded ? /* @__PURE__ */ _react2.default.createElement(_icons.ChevronDownIcon, null) : /* @__PURE__ */ _react2.default.createElement(_icons.ChevronRightIcon, null))), /* @__PURE__ */ _react2.default.createElement(_ui.Box, { style: { flex: 1, whiteSpace: "nowrap" } }, /* @__PURE__ */ _react2.default.createElement(_ui.Text, { size: 1, weight: "semibold", style: { whiteSpace: "nowrap" } }, entry.title || entry.sourceFileName, entry.variableFont && /* @__PURE__ */ _react2.default.createElement(_ui.Badge, { tone: "primary", fontSize: 0, style: { marginLeft: 6 } }, "VF"), hasConflict && /* @__PURE__ */ _react2.default.createElement(_ui.Badge, { tone: "caution", fontSize: 0, style: { marginLeft: 6 } }, "ID Conflict"))), /* @__PURE__ */ _react2.default.createElement(_ui.Text, { size: 0, style: { width: 50, textAlign: "center", flexShrink: 0 } }, entry.weight), /* @__PURE__ */ _react2.default.createElement(_ui.Text, { size: 0, style: { width: 50, textAlign: "center", flexShrink: 0 } }, entry.style), /* @__PURE__ */ _react2.default.createElement(_ui.Text, { size: 0, style: { width: 40, textAlign: "center", flexShrink: 0 } }, fileCount), /* @__PURE__ */ _react2.default.createElement(_ui.Box, { style: { width: 55, textAlign: "center", flexShrink: 0 } }, /* @__PURE__ */ _react2.default.createElement(_ui.Badge, { tone: isError ? "critical" : isUpdate ? "caution" : "positive", fontSize: 0 }, isError ? "Error" : isUpdate ? "Update" : "Create")))
|
|
1955
|
-
), isError && /* @__PURE__ */ _react2.default.createElement(_ui.Box, { paddingX: 2, paddingBottom: 2 }, /* @__PURE__ */ _react2.default.createElement(_ui.Flex, { justify: "space-between", align: "center" }, /* @__PURE__ */ _react2.default.createElement(_ui.Text, { size: 0, muted: true }, entry.error), /* @__PURE__ */ _react2.default.createElement(_ui.Button, { mode: "bleed", tone: "critical", icon: _icons.TrashIcon, padding: 1, onClick: handleRemove }))), expanded && !isError && /* @__PURE__ */ _react2.default.createElement(_ui.Box, { padding: 3, style: { borderTop: "1px solid var(--card-border-color)", background: "var(--card-muted-bg-color)" } }, /* @__PURE__ */ _react2.default.createElement(_ui.Stack, { space: 4 }, /* @__PURE__ */ _react2.default.createElement(_ui.Stack, { space: 2 }, /* @__PURE__ */ _react2.default.createElement(_ui.Flex, { align: "center", gap: 2 }, /* @__PURE__ */ _react2.default.createElement(_ui.Label, { size: 0 }, "Files (", fileCount, ")"), /* @__PURE__ */ _react2.default.createElement(
|
|
1956
|
-
_ui.Button,
|
|
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__ */ _react2.default.createElement(_ui.Flex, { gap: 1, wrap: "wrap" }, STANDARD_TYPES.map((ext) => /* @__PURE__ */ _react2.default.createElement(
|
|
1966
|
-
_ui.Badge,
|
|
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__ */ _react2.default.createElement(
|
|
1977
|
-
_ui.Badge,
|
|
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__ */ _react2.default.createElement(_ui.Stack, { space: 2 }, /* @__PURE__ */ _react2.default.createElement(_ui.Label, { size: 0 }, "Font Title"), /* @__PURE__ */ _react2.default.createElement(
|
|
1988
|
-
_ui.TextInput,
|
|
1989
|
-
{
|
|
1990
|
-
value: localTitle,
|
|
1991
|
-
onChange: (e) => setLocalTitle(e.target.value),
|
|
1992
|
-
onBlur: handleTitleBlur,
|
|
1993
|
-
fontSize: 1
|
|
1994
|
-
}
|
|
1995
|
-
), /* @__PURE__ */ _react2.default.createElement(_ui.Text, { 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__ */ _react2.default.createElement(_ui.Flex, { gap: 1, wrap: "wrap" }, entry.decisions.title.alternatives.filter((a) => a.value).map((alt, i) => /* @__PURE__ */ _react2.default.createElement(_ui.Tooltip, { key: i, content: /* @__PURE__ */ _react2.default.createElement(_ui.Box, { padding: 1 }, /* @__PURE__ */ _react2.default.createElement(_ui.Text, { size: 0 }, alt.source.replace(/nameId(\d+)-(\w+)/g, "nameId$1 ($2)"))), portal: true }, /* @__PURE__ */ _react2.default.createElement(
|
|
1996
|
-
_ui.Badge,
|
|
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__ */ _react2.default.createElement(_ui.Stack, { space: 2 }, /* @__PURE__ */ _react2.default.createElement(_ui.Flex, { align: "center", gap: 1 }, /* @__PURE__ */ _react2.default.createElement(_ui.Label, { size: 0 }, "Document ID"), !docIdEditable && /* @__PURE__ */ _react2.default.createElement(
|
|
2008
|
-
_ui.Tooltip,
|
|
2009
|
-
{
|
|
2010
|
-
content: /* @__PURE__ */ _react2.default.createElement(_ui.Box, { padding: 2, style: { maxWidth: 260 } }, /* @__PURE__ */ _react2.default.createElement(_ui.Text, { 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__ */ _react2.default.createElement(_icons.InfoOutlineIcon, { style: { opacity: 0.4, fontSize: 12 } })
|
|
2015
|
-
)), /* @__PURE__ */ _react2.default.createElement(
|
|
2016
|
-
_ui.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__ */ _react2.default.createElement(_ui.Text, { size: 0, tone: "caution" }, "This ID conflicts with another font in this batch \u2014 edit to make unique"), isCreateNewOverride && !hasConflict && /* @__PURE__ */ _react2.default.createElement(_ui.Text, { size: 0, tone: "caution" }, "Creating new document \u2014 edit the ID to avoid overwriting the existing document"), !docIdEditable && /* @__PURE__ */ _react2.default.createElement(_ui.Text, { size: 0, muted: true }, "Auto-derived from font title")), /* @__PURE__ */ _react2.default.createElement(_ui.Flex, { gap: 3 }, /* @__PURE__ */ _react2.default.createElement(_ui.Box, { style: { flex: 1 } }, /* @__PURE__ */ _react2.default.createElement(_ui.Stack, { space: 2 }, /* @__PURE__ */ _react2.default.createElement(_ui.Label, { size: 0 }, "Weight"), /* @__PURE__ */ _react2.default.createElement(
|
|
2026
|
-
_ui.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__ */ _react2.default.createElement(_ui.Text, { size: 0, muted: true }, formatSource(entry.decisions.weight)))), /* @__PURE__ */ _react2.default.createElement(_ui.Box, { style: { flex: 1 } }, /* @__PURE__ */ _react2.default.createElement(_ui.Stack, { space: 2 }, /* @__PURE__ */ _react2.default.createElement(_ui.Label, { size: 0 }, "Weight Name"), /* @__PURE__ */ _react2.default.createElement(
|
|
2035
|
-
_ui.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__ */ _react2.default.createElement(_ui.Text, { size: 0, muted: true }, formatSource(entry.decisions.weightName))))), /* @__PURE__ */ _react2.default.createElement(_ui.Flex, { gap: 3 }, /* @__PURE__ */ _react2.default.createElement(_ui.Box, { style: { flex: 1 } }, /* @__PURE__ */ _react2.default.createElement(_ui.Stack, { space: 2 }, /* @__PURE__ */ _react2.default.createElement(_ui.Label, { size: 0 }, "Style"), /* @__PURE__ */ _react2.default.createElement(_ui.Select, { value: entry.style, onChange: handleStyleChange, fontSize: 1 }, /* @__PURE__ */ _react2.default.createElement("option", { value: "Regular" }, "Regular"), /* @__PURE__ */ _react2.default.createElement("option", { value: "Italic" }, "Italic")), ((_j = entry.decisions) == null ? void 0 : _j.style) && /* @__PURE__ */ _react2.default.createElement(_ui.Text, { size: 0, muted: true }, formatSource(entry.decisions.style)))), /* @__PURE__ */ _react2.default.createElement(_ui.Box, { style: { flex: 1 } }, /* @__PURE__ */ _react2.default.createElement(_ui.Stack, { space: 2 }, /* @__PURE__ */ _react2.default.createElement(_ui.Label, { size: 0 }, "Subfamily"), /* @__PURE__ */ _react2.default.createElement(
|
|
2043
|
-
_ui.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__ */ _react2.default.createElement(_ui.Text, { size: 0, muted: true }, formatSource(entry.decisions.subfamily))))), entry.variableFont && entry.variationAxes && /* @__PURE__ */ _react2.default.createElement(_ui.Stack, { space: 2 }, /* @__PURE__ */ _react2.default.createElement(_ui.Label, { size: 0 }, "Variable Font Axes"), /* @__PURE__ */ _react2.default.createElement(_ui.Flex, { gap: 1, wrap: "wrap" }, Object.entries(entry.variationAxes).map(([tag, axis]) => /* @__PURE__ */ _react2.default.createElement(_ui.Badge, { 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__ */ _react2.default.createElement(_ui.Text, { size: 0, tone: "caution" }, "Weight ", entry.weight, " is outside the wght axis range (", entry.variationAxes.wght.min, "\u2013", entry.variationAxes.wght.max, ")"))), /* @__PURE__ */ _react2.default.createElement(
|
|
2051
|
-
ExistingDocumentResolver,
|
|
2052
|
-
{
|
|
2053
|
-
decision: (_o = entry.decisions) == null ? void 0 : _o.existingDocument,
|
|
2054
|
-
tempId: entry.tempId,
|
|
2055
|
-
dispatch
|
|
2056
|
-
}
|
|
2057
|
-
), /* @__PURE__ */ _react2.default.createElement(_ui.Stack, { space: 2 }, /* @__PURE__ */ _react2.default.createElement(
|
|
2058
|
-
_ui.Button,
|
|
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__ */ _react2.default.createElement(_ui.Card, { border: true, padding: 3, radius: 1, style: { fontFamily: "monospace", fontSize: 12 } }, /* @__PURE__ */ _react2.default.createElement(_ui.Stack, { 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", _nullishCoalesce(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__ */ _react2.default.createElement(_ui.Flex, { key, gap: 2 }, /* @__PURE__ */ _react2.default.createElement(_ui.Text, { size: 0, muted: true, style: { width: 120, flexShrink: 0 } }, key), /* @__PURE__ */ _react2.default.createElement(_ui.Text, { size: 0, style: { wordBreak: "break-all" } }, String(value))))))), /* @__PURE__ */ _react2.default.createElement(_ui.Flex, { justify: "flex-end", gap: 2 }, hasUserOverrides && /* @__PURE__ */ _react2.default.createElement(
|
|
2083
|
-
_ui.Button,
|
|
2084
|
-
{
|
|
2085
|
-
mode: "ghost",
|
|
2086
|
-
tone: "default",
|
|
2087
|
-
icon: _icons.ResetIcon,
|
|
2088
|
-
text: "Reset to Suggestions",
|
|
2089
|
-
fontSize: 1,
|
|
2090
|
-
padding: 2,
|
|
2091
|
-
onClick: handleReset
|
|
2092
|
-
}
|
|
2093
|
-
), /* @__PURE__ */ _react2.default.createElement(
|
|
2094
|
-
_ui.Button,
|
|
2095
|
-
{
|
|
2096
|
-
mode: "ghost",
|
|
2097
|
-
tone: "critical",
|
|
2098
|
-
icon: _icons.TrashIcon,
|
|
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
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
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 = _react.useMemo.call(void 0, () => Object.values(fonts), [fonts]);
|
|
2131
|
-
const fontCount = fontEntries.length;
|
|
2132
|
-
const visibleCount = visibleTempIds.length;
|
|
2133
|
-
const filterCounts = _react.useMemo.call(void 0, () => {
|
|
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 = _react.useMemo.call(void 0,
|
|
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__ */ _react2.default.createElement(_ui.Flex, { gap: 2, align: "center", wrap: "wrap", style: { position: "sticky", top: 0, zIndex: 10, background: "var(--card-bg-color)", paddingBottom: 8, paddingTop: 4 } }, /* @__PURE__ */ _react2.default.createElement(_ui.Box, { style: { flex: 1, minWidth: 150 } }, /* @__PURE__ */ _react2.default.createElement(
|
|
2157
|
-
_ui.TextInput,
|
|
2158
|
-
{
|
|
2159
|
-
icon: _icons.SearchIcon,
|
|
2160
|
-
placeholder: "Search fonts...",
|
|
2161
|
-
value: searchQuery,
|
|
2162
|
-
onChange: (e) => onSearchChange(e.target.value),
|
|
2163
|
-
fontSize: 1
|
|
2164
|
-
}
|
|
2165
|
-
)), /* @__PURE__ */ _react2.default.createElement(_ui.Flex, { align: "center", gap: 1 }, /* @__PURE__ */ _react2.default.createElement(_ui.Label, { size: 0, style: { whiteSpace: "nowrap" } }, "Filter"), /* @__PURE__ */ _react2.default.createElement(_ui.Select, { value: filterBy, onChange: (e) => onFilterChange(e.target.value), fontSize: 1, style: { minWidth: 140 } }, /* @__PURE__ */ _react2.default.createElement("option", { value: "all" }, "All (", fontCount, ")"), filterCounts.createCount > 0 && /* @__PURE__ */ _react2.default.createElement("option", { value: "create" }, "Create (", filterCounts.createCount, ")"), filterCounts.updateCount > 0 && /* @__PURE__ */ _react2.default.createElement("option", { value: "update" }, "Update (", filterCounts.updateCount, ")"), filterCounts.regularCount > 0 && /* @__PURE__ */ _react2.default.createElement("option", { value: "style:regular" }, "Regular (", filterCounts.regularCount, ")"), filterCounts.italicCount > 0 && /* @__PURE__ */ _react2.default.createElement("option", { value: "style:italic" }, "Italic (", filterCounts.italicCount, ")"), filterCounts.errorCount > 0 && /* @__PURE__ */ _react2.default.createElement("option", { value: "error" }, "Errors (", filterCounts.errorCount, ")"), filterCounts.conflictCount > 0 && /* @__PURE__ */ _react2.default.createElement("option", { value: "conflict" }, "Conflicts (", filterCounts.conflictCount, ")"), subfamilies.length > 1 && subfamilies.map((sf) => /* @__PURE__ */ _react2.default.createElement("option", { key: sf, value: `sf:${sf}` }, sf, " (", filterCounts.subfamilyCounts[sf], ")")))), visibleCount !== fontCount && /* @__PURE__ */ _react2.default.createElement(_ui.Text, { size: 0, muted: true }, visibleCount, " of ", fontCount));
|
|
2166
|
-
}
|
|
2167
|
-
|
|
2168
|
-
// src/components/PriceInput.jsx
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
var PriceInput = ({ inputPrice, handleInputChange }) => /* @__PURE__ */ _react2.default.createElement(_ui.Flex, { align: "center", gap: 2 }, /* @__PURE__ */ _react2.default.createElement(_ui.Text, { size: 1, muted: true }, "Price:"), /* @__PURE__ */ _react2.default.createElement(_ui.Text, { size: 1, muted: true }, "$"), /* @__PURE__ */ _react2.default.createElement(
|
|
2172
|
-
"input",
|
|
2173
|
-
{
|
|
2174
|
-
value: inputPrice,
|
|
2175
|
-
onChange: handleInputChange,
|
|
2176
|
-
type: "number",
|
|
2177
|
-
style: { textAlign: "end", padding: "5px", maxWidth: "75px" }
|
|
2178
|
-
}
|
|
2179
|
-
), /* @__PURE__ */ _react2.default.createElement(_ui.Text, { 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] = _react.useState.call(void 0, String(((_a = plan.settings) == null ? void 0 : _a.price) || 0));
|
|
2202
|
-
const [localPreserveShortenedNames, setLocalPreserveShortenedNames] = _react.useState.call(void 0, _nullishCoalesce(((_b = plan.settings) == null ? void 0 : _b.preserveShortenedNames), () => ( true)));
|
|
2203
|
-
const [localPreserveFileNames, setLocalPreserveFileNames] = _react.useState.call(void 0, _nullishCoalesce(((_c = plan.settings) == null ? void 0 : _c.preserveFileNames), () => ( false)));
|
|
2204
|
-
const [searchQuery, setSearchQuery] = _react.useState.call(void 0, "");
|
|
2205
|
-
const [filterBy, setFilterBy] = _react.useState.call(void 0, "all");
|
|
2206
|
-
const [allExpanded, setAllExpanded] = _react.useState.call(void 0, false);
|
|
2207
|
-
const [sortBy, setSortBy] = _react.useState.call(void 0, "weight");
|
|
2208
|
-
const [sortDir, setSortDir] = _react.useState.call(void 0, "asc");
|
|
2209
|
-
const fontEntries = _react.useMemo.call(void 0, () => 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
|
-
_react.useEffect.call(void 0, () => {
|
|
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 = _react.useMemo.call(void 0,
|
|
2247
|
-
() => fontEntries.filter((f) => f.status !== FONT_STATUS.ERROR && !isUpdateEntry2(f)).length,
|
|
2248
|
-
[fontEntries]
|
|
2249
|
-
);
|
|
2250
|
-
const updateCount = _react.useMemo.call(void 0,
|
|
2251
|
-
() => fontEntries.filter((f) => f.status !== FONT_STATUS.ERROR && isUpdateEntry2(f)).length,
|
|
2252
|
-
[fontEntries]
|
|
2253
|
-
);
|
|
2254
|
-
const visibleEntries = _react.useMemo.call(void 0, () => {
|
|
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 = _react.useMemo.call(void 0, () => visibleEntries.map((e) => e.tempId), [visibleEntries]);
|
|
2284
|
-
const hasConflicts = fontEntries.some((f) => f._idConflict);
|
|
2285
|
-
const sortEntries = _react.useCallback.call(void 0, (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 = _react.useCallback.call(void 0, (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 = _react.useMemo.call(void 0, () => {
|
|
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 = _react.useMemo.call(void 0, () => {
|
|
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 = _react.useCallback.call(void 0, () => {
|
|
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 = _react.useCallback.call(void 0, () => {
|
|
2370
|
-
setAllExpanded((v) => !v);
|
|
2371
|
-
}, []);
|
|
2372
|
-
return /* @__PURE__ */ _react2.default.createElement(_ui.Stack, { space: 3 }, isProcessing && /* @__PURE__ */ _react2.default.createElement(_ui.Card, { border: true, padding: 3, radius: 2 }, /* @__PURE__ */ _react2.default.createElement(_ui.Stack, { space: 3 }, /* @__PURE__ */ _react2.default.createElement(_ui.Flex, { align: "center", gap: 3 }, /* @__PURE__ */ _react2.default.createElement(_ui.Spinner, null), /* @__PURE__ */ _react2.default.createElement(_ui.Text, { size: 1 }, "Processing ", plan.processingProgress.completed, " of ", totalCount, " files...")), /* @__PURE__ */ _react2.default.createElement(_ui.Box, { style: { height: 4, background: "var(--card-border-color)", borderRadius: 2, overflow: "hidden" } }, /* @__PURE__ */ _react2.default.createElement(
|
|
2373
|
-
_ui.Box,
|
|
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__ */ _react2.default.createElement(_ui.Text, { size: 0, muted: true, style: { fontFamily: "monospace" } }, plan.processingProgress.currentFile), /* @__PURE__ */ _react2.default.createElement(_ui.Flex, { justify: "flex-end" }, /* @__PURE__ */ _react2.default.createElement(
|
|
2386
|
-
_ui.Button,
|
|
2387
|
-
{
|
|
2388
|
-
mode: "ghost",
|
|
2389
|
-
tone: "caution",
|
|
2390
|
-
text: "Cancel Processing",
|
|
2391
|
-
fontSize: 1,
|
|
2392
|
-
padding: 2,
|
|
2393
|
-
onClick: onCancelProcessing
|
|
2394
|
-
}
|
|
2395
|
-
)))), isReviewing && /* @__PURE__ */ _react2.default.createElement(_ui.Card, { tone: errorCount > 0 ? "caution" : "positive", border: true, padding: 3, radius: 2 }, /* @__PURE__ */ _react2.default.createElement(_ui.Flex, { align: "center", gap: 3 }, /* @__PURE__ */ _react2.default.createElement(_ui.Text, { size: 1, weight: "semibold" }, processedCount, " document", processedCount === 1 ? "" : "s"), /* @__PURE__ */ _react2.default.createElement(_ui.Flex, { gap: 1 }, createCount > 0 && /* @__PURE__ */ _react2.default.createElement(_ui.Badge, { tone: "positive", fontSize: 0 }, createCount, " create"), updateCount > 0 && /* @__PURE__ */ _react2.default.createElement(_ui.Badge, { tone: "caution", fontSize: 0 }, updateCount, " update"), errorCount > 0 && /* @__PURE__ */ _react2.default.createElement(_ui.Badge, { tone: "critical", fontSize: 0 }, errorCount, " error", errorCount === 1 ? "" : "s")))), isReviewing && /* @__PURE__ */ _react2.default.createElement(_react2.default.Fragment, null, /* @__PURE__ */ _react2.default.createElement(_ui.Text, { size: 1, weight: "semibold" }, "Settings"), /* @__PURE__ */ _react2.default.createElement(_ui.Card, { border: true, padding: 3, radius: 2 }, /* @__PURE__ */ _react2.default.createElement(_ui.Stack, { space: 3 }, /* @__PURE__ */ _react2.default.createElement(_ui.Grid, { columns: [2], gap: 4 }, /* @__PURE__ */ _react2.default.createElement(_ui.Box, null, /* @__PURE__ */ _react2.default.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__ */ _react2.default.createElement(_ui.Stack, { space: 3 }, /* @__PURE__ */ _react2.default.createElement(_ui.Flex, { align: "center", gap: 2 }, /* @__PURE__ */ _react2.default.createElement(
|
|
2405
|
-
_ui.Switch,
|
|
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__ */ _react2.default.createElement(
|
|
2414
|
-
_ui.Tooltip,
|
|
2415
|
-
{
|
|
2416
|
-
content: /* @__PURE__ */ _react2.default.createElement(_ui.Box, { padding: 2, style: { maxWidth: 260 } }, /* @__PURE__ */ _react2.default.createElement(_ui.Text, { 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__ */ _react2.default.createElement(_ui.Flex, { align: "center", gap: 1, style: { cursor: "default" } }, /* @__PURE__ */ _react2.default.createElement(_ui.Label, null, "Preserve shortened names"), /* @__PURE__ */ _react2.default.createElement(_icons.InfoOutlineIcon, { style: { opacity: 0.5, display: "block" } }))
|
|
2421
|
-
)), /* @__PURE__ */ _react2.default.createElement(_ui.Flex, { align: "center", gap: 2 }, /* @__PURE__ */ _react2.default.createElement(
|
|
2422
|
-
_ui.Switch,
|
|
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__ */ _react2.default.createElement(
|
|
2431
|
-
_ui.Tooltip,
|
|
2432
|
-
{
|
|
2433
|
-
content: /* @__PURE__ */ _react2.default.createElement(_ui.Box, { padding: 2, style: { maxWidth: 260 } }, /* @__PURE__ */ _react2.default.createElement(_ui.Text, { 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__ */ _react2.default.createElement(_ui.Flex, { align: "center", gap: 1, style: { cursor: "default" } }, /* @__PURE__ */ _react2.default.createElement(_ui.Label, null, "Preserve file names"), /* @__PURE__ */ _react2.default.createElement(_icons.InfoOutlineIcon, { style: { opacity: 0.5, display: "block" } }))
|
|
2438
|
-
))))))), fontEntries.length > 0 && /* @__PURE__ */ _react2.default.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__ */ _react2.default.createElement(_ui.Flex, { justify: "flex-start", paddingY: 1 }, /* @__PURE__ */ _react2.default.createElement(
|
|
2452
|
-
_ui.Button,
|
|
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__ */ _react2.default.createElement(
|
|
2462
|
-
_ui.Flex,
|
|
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__ */ _react2.default.createElement(_ui.Box, { 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__ */ _react2.default.createElement(
|
|
2478
|
-
_ui.Text,
|
|
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__ */ _react2.default.createElement(_ui.Stack, { key: subfamily, space: 1 }, /* @__PURE__ */ _react2.default.createElement(_ui.Card, { padding: 2, radius: 1, style: { background: "var(--card-muted-bg-color)" } }, /* @__PURE__ */ _react2.default.createElement(_ui.Flex, { align: "center", gap: 2 }, /* @__PURE__ */ _react2.default.createElement(_ui.Text, { size: 1, weight: "semibold" }, subfamily), /* @__PURE__ */ _react2.default.createElement(_ui.Badge, { mode: "outline", fontSize: 0 }, entries.length))), /* @__PURE__ */ _react2.default.createElement(_ui.Stack, { space: 0 }, entries.map((entry) => {
|
|
2492
|
-
var _a2, _b2;
|
|
2493
|
-
return /* @__PURE__ */ _react2.default.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__ */ _react2.default.createElement(_ui.Card, { border: true, padding: 4, radius: 2 }, /* @__PURE__ */ _react2.default.createElement(_ui.Text, { size: 1, muted: true, align: "center" }, "No fonts match the current filter")), isReviewing && validationErrors.length > 0 && /* @__PURE__ */ _react2.default.createElement(_ui.Card, { tone: "caution", border: true, padding: 2, radius: 2 }, /* @__PURE__ */ _react2.default.createElement(_ui.Stack, { space: 1 }, validationErrors.map((err, i) => /* @__PURE__ */ _react2.default.createElement(_ui.Text, { key: i, size: 0, tone: "caution" }, "\u2022 ", err)))), isReviewing && processedCount > 0 && /* @__PURE__ */ _react2.default.createElement(_ui.Flex, { justify: "flex-end", gap: 2, style: { position: "sticky", bottom: 0, background: "var(--card-bg-color)", paddingTop: 8, paddingBottom: 4 } }, /* @__PURE__ */ _react2.default.createElement(
|
|
2505
|
-
_ui.Button,
|
|
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
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
// src/utils/executeUploadPlan.js
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
// src/utils/generateCssFile.js
|
|
2525
|
-
var _base64 = require('base-64'); var _base642 = _interopRequireDefault(_base64);
|
|
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 _base642.default.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 _nullishCoalesce(FAMILY_CLASS_MAP[highByte], () => ( "default"));
|
|
2589
|
-
} catch (e3) {
|
|
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
|
-
|
|
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: _nanoid.nanoid.call(void 0, )
|
|
2973
|
-
};
|
|
2974
|
-
});
|
|
2975
|
-
return instanceMappings;
|
|
2976
|
-
};
|
|
2977
|
-
var parseVariableFontInstances_default = parseVariableFontInstances;
|
|
2978
|
-
|
|
2979
|
-
// src/utils/updateTypefaceDocument.js
|
|
2980
|
-
|
|
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: _nanoid.nanoid.call(void 0, ),
|
|
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: _nanoid.nanoid.call(void 0, ),
|
|
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: _nanoid.nanoid.call(void 0, ),
|
|
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: _nanoid.nanoid.call(void 0, ),
|
|
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] = _react.useReducer.call(void 0, executionReducer, null, createInitialExecutionState);
|
|
3455
|
-
const [result, setResult] = _react.useState.call(void 0, null);
|
|
3456
|
-
const [elapsedSeconds, setElapsedSeconds] = _react.useState.call(void 0, 0);
|
|
3457
|
-
const startedRef = _react.useRef.call(void 0, false);
|
|
3458
|
-
const timerRef = _react.useRef.call(void 0, null);
|
|
3459
|
-
const wakeLockRef = _react.useRef.call(void 0, null);
|
|
3460
|
-
const executionPlan = _react.useMemo.call(void 0, () => {
|
|
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 = _react.useMemo.call(void 0,
|
|
3470
|
-
() => Object.values(executionPlan.fonts).filter((f) => f.status !== "error"),
|
|
3471
|
-
[executionPlan]
|
|
3472
|
-
);
|
|
3473
|
-
_react.useEffect.call(void 0, () => {
|
|
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__ */ _react2.default.createElement(_ui.Stack, { space: 4 }, /* @__PURE__ */ _react2.default.createElement(_ui.Card, { border: true, padding: 3, radius: 2 }, /* @__PURE__ */ _react2.default.createElement(_ui.Stack, { space: 3 }, /* @__PURE__ */ _react2.default.createElement(_ui.Flex, { align: "center", gap: 3 }, execState.status === "complete" ? /* @__PURE__ */ _react2.default.createElement(_icons.CheckmarkCircleIcon, { style: { color: "#43b649", fontSize: 20 } }) : execState.status === "error" ? /* @__PURE__ */ _react2.default.createElement(_icons.WarningOutlineIcon, { style: { color: "var(--card-badge-critical-bg-color)", fontSize: 20 } }) : /* @__PURE__ */ _react2.default.createElement(_ui.Spinner, null), /* @__PURE__ */ _react2.default.createElement(_ui.Text, { 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__ */ _react2.default.createElement(_ui.Text, { size: 1, muted: true, style: { marginLeft: "auto" } }, formatElapsed(elapsedSeconds))), /* @__PURE__ */ _react2.default.createElement(_ui.Box, { style: { height: 4, background: "var(--card-border-color)", borderRadius: 2, overflow: "hidden" } }, /* @__PURE__ */ _react2.default.createElement(
|
|
3543
|
-
_ui.Box,
|
|
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__ */ _react2.default.createElement(_ui.Card, { tone: "caution", border: true, radius: 2, padding: 2 }, /* @__PURE__ */ _react2.default.createElement(_ui.Flex, { align: "center", gap: 2 }, /* @__PURE__ */ _react2.default.createElement(_icons.WarningOutlineIcon, { style: { flexShrink: 0 } }), /* @__PURE__ */ _react2.default.createElement(_ui.Text, { size: 1, weight: "semibold" }, "Do not close or reload this tab"))), /* @__PURE__ */ _react2.default.createElement(_ui.Box, { style: { maxHeight: 400, overflowY: "auto" } }, /* @__PURE__ */ _react2.default.createElement(_ui.Stack, { 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__ */ _react2.default.createElement(_ui.Card, { key: entry.tempId, border: true, radius: 1, padding: 2 }, /* @__PURE__ */ _react2.default.createElement(_ui.Flex, { align: "center", gap: 2 }, /* @__PURE__ */ _react2.default.createElement(_ui.Text, { size: 1, style: { flex: 1 } }, entry.title), /* @__PURE__ */ _react2.default.createElement(_ui.Box, { style: { width: 120, flexShrink: 0, textAlign: "right" } }, status === EXECUTION_STATUS.QUEUED && /* @__PURE__ */ _react2.default.createElement(_ui.Badge, { mode: "outline", fontSize: 0 }, "Queued"), status === EXECUTION_STATUS.UPLOADING_ASSETS && /* @__PURE__ */ _react2.default.createElement(_ui.Flex, { gap: 1, align: "center", justify: "flex-end" }, /* @__PURE__ */ _react2.default.createElement(_ui.Text, { size: 0, muted: true }, (progress == null ? void 0 : progress.currentFile) || "Uploading..."), /* @__PURE__ */ _react2.default.createElement(_ui.Spinner, { style: { width: 12, height: 12 } })), (status === EXECUTION_STATUS.GENERATING_CSS || status === EXECUTION_STATUS.GENERATING_METADATA) && /* @__PURE__ */ _react2.default.createElement(_ui.Flex, { gap: 1, align: "center", justify: "flex-end" }, /* @__PURE__ */ _react2.default.createElement(_ui.Text, { size: 0, muted: true }, status === EXECUTION_STATUS.GENERATING_CSS ? "CSS" : "Metadata"), /* @__PURE__ */ _react2.default.createElement(_ui.Spinner, { style: { width: 12, height: 12 } })), status === EXECUTION_STATUS.CREATING_DOCUMENT && /* @__PURE__ */ _react2.default.createElement(_ui.Flex, { gap: 1, align: "center", justify: "flex-end" }, /* @__PURE__ */ _react2.default.createElement(_ui.Text, { size: 0, muted: true }, "Creating doc"), /* @__PURE__ */ _react2.default.createElement(_ui.Spinner, { style: { width: 12, height: 12 } })), status === EXECUTION_STATUS.COMPLETE && /* @__PURE__ */ _react2.default.createElement(_ui.Badge, { tone: "positive", fontSize: 0 }, "Done"), status === EXECUTION_STATUS.ERROR && /* @__PURE__ */ _react2.default.createElement(_ui.Badge, { tone: "critical", fontSize: 0 }, "Failed"))), status === EXECUTION_STATUS.ERROR && (progress == null ? void 0 : progress.error) && /* @__PURE__ */ _react2.default.createElement(_ui.Text, { size: 0, muted: true, style: { marginTop: 4 } }, progress.error));
|
|
3559
|
-
}))), execState.error && /* @__PURE__ */ _react2.default.createElement(_ui.Card, { tone: "critical", border: true, padding: 3, radius: 2 }, /* @__PURE__ */ _react2.default.createElement(_ui.Text, { size: 1 }, execState.error)));
|
|
3560
|
-
}
|
|
3561
|
-
|
|
3562
|
-
// src/components/UploadStep3bInstances.jsx
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
function UploadStep3bInstances({
|
|
3568
|
-
plan,
|
|
3569
|
-
executionResult,
|
|
3570
|
-
client,
|
|
3571
|
-
typefaceTitle,
|
|
3572
|
-
onComplete
|
|
3573
|
-
}) {
|
|
3574
|
-
const [loading, setLoading] = _react.useState.call(void 0, true);
|
|
3575
|
-
const [saving, setSaving] = _react.useState.call(void 0, false);
|
|
3576
|
-
const [vfMappings, setVfMappings] = _react.useState.call(void 0, {});
|
|
3577
|
-
const [allStaticFonts, setAllStaticFonts] = _react.useState.call(void 0, []);
|
|
3578
|
-
const [filterUnmatched, setFilterUnmatched] = _react.useState.call(void 0, false);
|
|
3579
|
-
const vfEntries = _react.useMemo.call(void 0,
|
|
3580
|
-
() => Object.values(plan.fonts).filter((f) => f.variableFont && f.status !== "error"),
|
|
3581
|
-
[plan.fonts]
|
|
3582
|
-
);
|
|
3583
|
-
const runMatching = _react.useCallback.call(void 0, 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 || _nanoid.nanoid.call(void 0, )
|
|
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
|
-
_react.useEffect.call(void 0, () => {
|
|
3672
|
-
runMatching();
|
|
3673
|
-
}, [runMatching]);
|
|
3674
|
-
const claimedFontIds = _react.useMemo.call(void 0, () => {
|
|
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 = _react.useCallback.call(void 0, (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 = _react.useCallback.call(void 0, 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: _nanoid.nanoid.call(void 0, ),
|
|
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 = _react.useCallback.call(void 0, (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__ */ _react2.default.createElement(_ui.Card, { border: true, padding: 4, radius: 2 }, /* @__PURE__ */ _react2.default.createElement(_ui.Flex, { align: "center", gap: 3, justify: "center" }, /* @__PURE__ */ _react2.default.createElement(_ui.Spinner, null), /* @__PURE__ */ _react2.default.createElement(_ui.Text, { 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__ */ _react2.default.createElement(_ui.Stack, { space: 4 }, /* @__PURE__ */ _react2.default.createElement(_ui.Stack, { space: 2 }, /* @__PURE__ */ _react2.default.createElement(_ui.Text, { size: 2, weight: "semibold" }, "Map Variable Font Instances"), /* @__PURE__ */ _react2.default.createElement(_ui.Text, { size: 1, muted: true }, "Review the auto-matched instances below. Each named instance should map to its corresponding static font document.")), /* @__PURE__ */ _react2.default.createElement(_ui.Flex, { gap: 2, align: "center" }, /* @__PURE__ */ _react2.default.createElement(
|
|
3740
|
-
_ui.Button,
|
|
3741
|
-
{
|
|
3742
|
-
mode: "ghost",
|
|
3743
|
-
tone: "primary",
|
|
3744
|
-
icon: _icons.SearchIcon,
|
|
3745
|
-
text: "Re-run Matching",
|
|
3746
|
-
fontSize: 0,
|
|
3747
|
-
padding: 2,
|
|
3748
|
-
onClick: runMatching,
|
|
3749
|
-
disabled: loading,
|
|
3750
|
-
style: { cursor: "pointer" }
|
|
3751
|
-
}
|
|
3752
|
-
), /* @__PURE__ */ _react2.default.createElement(_ui.Badge, { tone: "positive", fontSize: 0 }, matchedInstances, " matched"), unmatchedInstances > 0 && /* @__PURE__ */ _react2.default.createElement(
|
|
3753
|
-
_ui.Badge,
|
|
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__ */ _react2.default.createElement(
|
|
3764
|
-
_ui.Badge,
|
|
3765
|
-
{
|
|
3766
|
-
mode: "outline",
|
|
3767
|
-
fontSize: 0,
|
|
3768
|
-
style: { cursor: "pointer" },
|
|
3769
|
-
onClick: () => setFilterUnmatched(false)
|
|
3770
|
-
},
|
|
3771
|
-
"Clear filter"
|
|
3772
|
-
), /* @__PURE__ */ _react2.default.createElement(_ui.Badge, { 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__ */ _react2.default.createElement(_ui.Card, { key: vf.tempId, border: true, padding: 3, radius: 2 }, /* @__PURE__ */ _react2.default.createElement(_ui.Stack, { space: 3 }, /* @__PURE__ */ _react2.default.createElement(_ui.Flex, { align: "center", gap: 2 }, /* @__PURE__ */ _react2.default.createElement(_ui.Badge, { tone: "primary", fontSize: 0 }, "VF"), /* @__PURE__ */ _react2.default.createElement(_ui.Text, { size: 1, weight: "semibold" }, vf.title), /* @__PURE__ */ _react2.default.createElement(_ui.Text, { size: 0, muted: true, style: { marginLeft: "auto" } }, vfMatched, "/", mappings.length, " matched")), /* @__PURE__ */ _react2.default.createElement(_ui.Flex, { align: "center", gap: 2, paddingY: 1, style: { borderBottom: "1px solid var(--card-border-color)" } }, /* @__PURE__ */ _react2.default.createElement(_ui.Box, { style: { width: 20 } }), /* @__PURE__ */ _react2.default.createElement(_ui.Text, { size: 0, weight: "semibold", muted: true, style: { flex: 1 } }, "Instance"), /* @__PURE__ */ _react2.default.createElement(_ui.Text, { size: 0, weight: "semibold", muted: true, style: { flex: 2 } }, "Static Font Document")), /* @__PURE__ */ _react2.default.createElement(_ui.Stack, { space: 1 }, displayMappings.map((mapping) => {
|
|
3777
|
-
const isMatched = !!mapping.matchedFontId;
|
|
3778
|
-
const options = getAutocompleteOptions(mapping.matchedFontId);
|
|
3779
|
-
return /* @__PURE__ */ _react2.default.createElement(
|
|
3780
|
-
_ui.Flex,
|
|
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__ */ _react2.default.createElement(_ui.Box, { style: { width: 20, flexShrink: 0 } }, isMatched ? /* @__PURE__ */ _react2.default.createElement(_icons.CheckmarkCircleIcon, { style: { color: "#43b649", fontSize: 16 } }) : /* @__PURE__ */ _react2.default.createElement(_icons.CloseCircleIcon, { style: { color: "#f03e2f", fontSize: 16 } })),
|
|
3789
|
-
/* @__PURE__ */ _react2.default.createElement(_ui.Text, { size: 1, style: { flex: 1, whiteSpace: "nowrap" } }, mapping.instanceName),
|
|
3790
|
-
/* @__PURE__ */ _react2.default.createElement(_ui.Box, { style: { flex: 2 } }, /* @__PURE__ */ _react2.default.createElement(
|
|
3791
|
-
_ui.Autocomplete,
|
|
3792
|
-
{
|
|
3793
|
-
id: `instance-${mapping._key}`,
|
|
3794
|
-
options,
|
|
3795
|
-
value: mapping.matchedFontId,
|
|
3796
|
-
placeholder: "Search for a font...",
|
|
3797
|
-
icon: _icons.SearchIcon,
|
|
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__ */ _react2.default.createElement(_ui.Card, { as: "button", padding: 2, style: { opacity: isClaimed ? 0.4 : 1 } }, /* @__PURE__ */ _react2.default.createElement(_ui.Flex, { align: "center", gap: 2 }, /* @__PURE__ */ _react2.default.createElement(_ui.Text, { size: 1 }, sf.title), /* @__PURE__ */ _react2.default.createElement(_ui.Text, { size: 0, muted: true }, sf.weight, " ", sf.style), sf.subfamily && sf.subfamily !== "Regular" && /* @__PURE__ */ _react2.default.createElement(_ui.Badge, { 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__ */ _react2.default.createElement(_ui.Text, { size: 1, muted: true }, "No named instances found in this variable font.")));
|
|
3822
|
-
}), /* @__PURE__ */ _react2.default.createElement(_ui.Flex, { justify: "flex-end", gap: 2, style: { position: "sticky", bottom: 0, background: "var(--card-bg-color)", paddingTop: 8, paddingBottom: 4 } }, /* @__PURE__ */ _react2.default.createElement(
|
|
3823
|
-
_ui.Button,
|
|
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__ */ _react2.default.createElement(
|
|
3833
|
-
_ui.Button,
|
|
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
|
-
|
|
3849
|
-
|
|
3850
|
-
|
|
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] = _react.useState.call(void 0, false);
|
|
3863
|
-
const [patchRetryResult, setPatchRetryResult] = _react.useState.call(void 0, 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 = _react.useCallback.call(void 0, 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__ */ _react2.default.createElement(_ui.Stack, { space: 4 }, /* @__PURE__ */ _react2.default.createElement(_ui.Flex, { 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__ */ _react2.default.createElement(_icons.CheckmarkCircleIcon, { style: { color: "#43b649", fontSize: 28 } }) : /* @__PURE__ */ _react2.default.createElement(_icons.WarningOutlineIcon, { style: { color: "#f03e2f", fontSize: 28 } }), /* @__PURE__ */ _react2.default.createElement(_ui.Text, { size: 2, weight: "semibold" }, allSuccess ? "Upload Complete" : "Upload Completed with Issues")), result && /* @__PURE__ */ _react2.default.createElement(_ui.Card, { border: true, padding: 4, radius: 2 }, /* @__PURE__ */ _react2.default.createElement(_ui.Stack, { space: 3 }, /* @__PURE__ */ _react2.default.createElement(_ui.Flex, { gap: 2, wrap: "wrap" }, result.created > 0 && /* @__PURE__ */ _react2.default.createElement(_ui.Badge, { tone: "positive", fontSize: 1 }, result.created, " created"), result.updated > 0 && /* @__PURE__ */ _react2.default.createElement(_ui.Badge, { tone: "caution", fontSize: 1 }, result.updated, " updated"), result.failed > 0 && /* @__PURE__ */ _react2.default.createElement(_ui.Badge, { tone: "critical", fontSize: 1 }, result.failed, " failed"), result.skipped > 0 && /* @__PURE__ */ _react2.default.createElement(_ui.Badge, { mode: "outline", fontSize: 1 }, result.skipped, " skipped")))), hasFailedFonts && /* @__PURE__ */ _react2.default.createElement(_ui.Stack, { space: 3 }, /* @__PURE__ */ _react2.default.createElement(_ui.Flex, { align: "center", justify: "space-between" }, /* @__PURE__ */ _react2.default.createElement(_ui.Flex, { align: "center", gap: 2 }, /* @__PURE__ */ _react2.default.createElement(_ui.Text, { size: 1, weight: "semibold" }, "Failed Fonts"), /* @__PURE__ */ _react2.default.createElement(_ui.Badge, { tone: "critical", fontSize: 0 }, result.failedFonts.length)), /* @__PURE__ */ _react2.default.createElement(
|
|
3907
|
-
_ui.Button,
|
|
3908
|
-
{
|
|
3909
|
-
mode: "ghost",
|
|
3910
|
-
tone: "primary",
|
|
3911
|
-
icon: _icons.ResetIcon,
|
|
3912
|
-
text: "Retry Failed",
|
|
3913
|
-
fontSize: 1,
|
|
3914
|
-
padding: 2,
|
|
3915
|
-
onClick: () => onRetry(result.failedFonts.map((f) => f.tempId).filter(Boolean))
|
|
3916
|
-
}
|
|
3917
|
-
)), /* @__PURE__ */ _react2.default.createElement(_ui.Stack, { space: 2 }, result.failedFonts.map((f, i) => /* @__PURE__ */ _react2.default.createElement(_ui.Card, { key: i, tone: "critical", border: true, padding: 3, radius: 2 }, /* @__PURE__ */ _react2.default.createElement(_ui.Stack, { space: 2 }, /* @__PURE__ */ _react2.default.createElement(_ui.Flex, { align: "center", gap: 2 }, /* @__PURE__ */ _react2.default.createElement(_ui.Text, { size: 1, weight: "semibold" }, f.title), f.failedAt && f.failedAt !== "unknown" && /* @__PURE__ */ _react2.default.createElement(_ui.Badge, { tone: "critical", fontSize: 0, mode: "outline" }, "Failed at: ", f.failedAt)), /* @__PURE__ */ _react2.default.createElement(_ui.Text, { size: 1 }, f.error)))))), hasTypefacePatchError && /* @__PURE__ */ _react2.default.createElement(_ui.Card, { tone: "caution", border: true, padding: 4, radius: 2 }, /* @__PURE__ */ _react2.default.createElement(_ui.Stack, { space: 3 }, /* @__PURE__ */ _react2.default.createElement(_ui.Text, { size: 1, weight: "semibold" }, "Typeface Document Not Updated"), /* @__PURE__ */ _react2.default.createElement(_ui.Text, { 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__ */ _react2.default.createElement(_ui.Text, { size: 1, muted: true }, result.typefacePatchError), /* @__PURE__ */ _react2.default.createElement(_ui.Flex, { gap: 2 }, /* @__PURE__ */ _react2.default.createElement(
|
|
3918
|
-
_ui.Button,
|
|
3919
|
-
{
|
|
3920
|
-
mode: "default",
|
|
3921
|
-
tone: "primary",
|
|
3922
|
-
icon: retryingPatch ? void 0 : _icons.ResetIcon,
|
|
3923
|
-
text: retryingPatch ? "Retrying..." : "Retry Typeface Patch",
|
|
3924
|
-
disabled: retryingPatch,
|
|
3925
|
-
onClick: handleRetryTypefacePatch
|
|
3926
|
-
}
|
|
3927
|
-
), retryingPatch && /* @__PURE__ */ _react2.default.createElement(_ui.Spinner, null)))), (patchRetryResult == null ? void 0 : patchRetryResult.success) && /* @__PURE__ */ _react2.default.createElement(_ui.Card, { tone: "positive", border: true, padding: 3, radius: 2 }, /* @__PURE__ */ _react2.default.createElement(_ui.Text, { size: 1 }, "Typeface document updated successfully on retry.")), patchRetryResult && !patchRetryResult.success && /* @__PURE__ */ _react2.default.createElement(_ui.Card, { tone: "critical", border: true, padding: 3, radius: 2 }, /* @__PURE__ */ _react2.default.createElement(_ui.Text, { size: 1 }, "Retry failed: ", patchRetryResult.error)), /* @__PURE__ */ _react2.default.createElement(_ui.Flex, { justify: "flex-end" }, /* @__PURE__ */ _react2.default.createElement(
|
|
3928
|
-
_ui.Button,
|
|
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] = _react.useReducer.call(void 0, planReducer, null, () => createEmptyPlan());
|
|
3976
|
-
const [processingCancelled, setProcessingCancelled] = _react.useState.call(void 0, false);
|
|
3977
|
-
const [executionResult, setExecutionResult] = _react.useState.call(void 0, null);
|
|
3978
|
-
const [retryTempIds, setRetryTempIds] = _react.useState.call(void 0, null);
|
|
3979
|
-
const [instanceMappingPhase, setInstanceMappingPhase] = _react.useState.call(void 0, false);
|
|
3980
|
-
const [instanceMappingResult, setInstanceMappingResult] = _react.useState.call(void 0, null);
|
|
3981
|
-
const cancelRef = _react.useRef.call(void 0, false);
|
|
3982
|
-
const focusRef = _react.useRef.call(void 0, null);
|
|
3983
|
-
const { weightKeywordList, italicKeywordList } = _react.useMemo.call(void 0, () => generateStyleKeywords(), []);
|
|
3984
|
-
const hasVFs = _react.useMemo.call(void 0,
|
|
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
|
-
_react.useEffect.call(void 0, () => {
|
|
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
|
-
_react.useEffect.call(void 0, () => {
|
|
4001
|
-
if (focusRef.current) {
|
|
4002
|
-
focusRef.current.focus();
|
|
4003
|
-
}
|
|
4004
|
-
}, [currentStep]);
|
|
4005
|
-
const handleClose = _react.useCallback.call(void 0, () => {
|
|
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 = _react.useCallback.call(void 0, 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 = _react.useCallback.call(void 0, () => {
|
|
4050
|
-
cancelRef.current = true;
|
|
4051
|
-
setProcessingCancelled(true);
|
|
4052
|
-
dispatch({ type: "SET_PHASE", phase: PLAN_PHASE.IDLE });
|
|
4053
|
-
}, []);
|
|
4054
|
-
const handleStartExecution = _react.useCallback.call(void 0, () => {
|
|
4055
|
-
dispatch({ type: "SET_PHASE", phase: PLAN_PHASE.EXECUTING });
|
|
4056
|
-
}, []);
|
|
4057
|
-
const handleExecutionComplete = _react.useCallback.call(void 0, (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 = _react.useCallback.call(void 0, (result) => {
|
|
4067
|
-
setInstanceMappingResult(result);
|
|
4068
|
-
setInstanceMappingPhase(false);
|
|
4069
|
-
}, []);
|
|
4070
|
-
if (!open) return null;
|
|
4071
|
-
const handleStepClick = _react.useCallback.call(void 0, (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__ */ _react2.default.createElement(
|
|
4082
|
-
_ui.Dialog,
|
|
4083
|
-
{
|
|
4084
|
-
id: "upload-modal",
|
|
4085
|
-
header: /* @__PURE__ */ _react2.default.createElement(_ui.Flex, { direction: "column", gap: 3, style: { width: "100%" } }, /* @__PURE__ */ _react2.default.createElement(_ui.Text, { weight: "semibold", size: 2 }, "Upload Fonts"), /* @__PURE__ */ _react2.default.createElement(_ui.Flex, { 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__ */ _react2.default.createElement(
|
|
4090
|
-
_ui.Box,
|
|
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__ */ _react2.default.createElement(
|
|
4109
|
-
_ui.Text,
|
|
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__ */ _react2.default.createElement(_ui.Box, { padding: 4 }, currentStep === 1 && /* @__PURE__ */ _react2.default.createElement(
|
|
4127
|
-
UploadStep1Settings,
|
|
4128
|
-
{
|
|
4129
|
-
settings: plan.settings,
|
|
4130
|
-
onStartProcessing: handleStartProcessing
|
|
4131
|
-
}
|
|
4132
|
-
), currentStep === 2 && /* @__PURE__ */ _react2.default.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__ */ _react2.default.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__ */ _react2.default.createElement(
|
|
4157
|
-
UploadStep3bInstances,
|
|
4158
|
-
{
|
|
4159
|
-
plan,
|
|
4160
|
-
executionResult,
|
|
4161
|
-
client,
|
|
4162
|
-
typefaceTitle,
|
|
4163
|
-
onComplete: handleInstanceMappingComplete
|
|
4164
|
-
}
|
|
4165
|
-
), plan.phase === PLAN_PHASE.COMPLETE && !instanceMappingPhase && /* @__PURE__ */ _react2.default.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
|
-
|
|
4189
|
-
|
|
4190
|
-
|
|
4191
|
-
|
|
4192
|
-
|
|
4193
|
-
|
|
4194
|
-
|
|
4195
|
-
|
|
4196
|
-
|
|
4197
|
-
|
|
4198
|
-
|
|
4199
|
-
|
|
4200
|
-
|
|
4201
|
-
|
|
4202
|
-
|
|
4203
|
-
|
|
4204
|
-
|
|
4205
|
-
|
|
4206
|
-
|
|
4207
|
-
|
|
4208
|
-
|
|
4209
|
-
|
|
4210
|
-
|
|
4211
|
-
|
|
4212
|
-
|
|
4213
|
-
|
|
4214
|
-
|
|
4215
|
-
|
|
4216
|
-
|
|
4217
|
-
|
|
4218
|
-
|
|
4219
|
-
|
|
4220
|
-
|
|
4221
|
-
|
|
4222
|
-
|
|
4223
|
-
|
|
4224
|
-
|
|
4225
|
-
|
|
4226
|
-
|
|
4227
|
-
|
|
4228
|
-
|
|
4229
|
-
|
|
4230
|
-
|
|
4231
|
-
|
|
4232
|
-
|
|
4233
|
-
|
|
4234
|
-
|
|
4235
|
-
|
|
4236
|
-
|
|
4237
|
-
|
|
4238
|
-
|
|
4239
|
-
|
|
4240
|
-
|
|
4241
|
-
|
|
4242
|
-
|
|
4243
|
-
|
|
4244
|
-
|
|
4245
|
-
|
|
4246
|
-
|
|
4247
|
-
|
|
4248
|
-
|
|
4249
|
-
|
|
4250
|
-
exports.parseFont = parseFont; exports.getNameString = getNameString; exports.getAllFeatureTags = getAllFeatureTags; exports.getCharacterSet = getCharacterSet; exports.getVariationAxes = getVariationAxes; exports.getNamedInstances = getNamedInstances; exports.getFontMetrics = getFontMetrics; exports.getFontMetadata = getFontMetadata; exports.getWeightClass = getWeightClass; exports.getFsSelection = getFsSelection; exports.getMacStyle = getMacStyle; exports.getItalicAngle = getItalicAngle; exports.getGlyphCount = getGlyphCount; exports.getFamilyClass = getFamilyClass; exports.escapeCssFontName = escapeCssFontName; exports.reverseSpellingLookup = reverseSpellingLookup; exports.expandAbbreviations = expandAbbreviations; exports.removeWeightNames = removeWeightNames; exports.generateStyleKeywords = generateStyleKeywords; exports.sanitizeForSanityId = sanitizeForSanityId; exports.readFontFile = readFontFile; exports.processFontFiles = processFontFiles; exports.extractFontMetadata = extractFontMetadata; exports.extractWeightName = extractWeightName; exports.extractWeightFromFullName = extractWeightFromFullName; exports.processSubfamilyName = processSubfamilyName; exports.processItalicKeywords = processItalicKeywords; exports.formatFontTitle = formatFontTitle; exports.addItalicToFontTitle = addItalicToFontTitle; exports.createFontObject = createFontObject; exports.determineWeight = determineWeight; exports.sortFontObjects = sortFontObjects; exports.logFontInfo = logFontInfo; exports.generateCssFile = generateCssFile; exports.generateFontData = generateFontData; exports.parseVariableFontInstances = parseVariableFontInstances; exports.parseVariableFontInstances_default = parseVariableFontInstances_default; exports.updateTypefaceDocument = updateTypefaceDocument; exports.PriceInput_default = PriceInput_default; exports.FONT_STATUS = FONT_STATUS; exports.PLAN_PHASE = PLAN_PHASE; exports.RECOMMENDATION = RECOMMENDATION; exports.EXECUTION_STATUS = EXECUTION_STATUS; exports.PLAN_VERSION = PLAN_VERSION; exports.createFontDecisions = createFontDecisions; exports.createEmptyPlan = createEmptyPlan; exports.planReducer = planReducer; exports.resolveExistingFont = resolveExistingFont; exports.buildUploadPlan = buildUploadPlan; exports.UploadStep1Settings = UploadStep1Settings; exports.ExistingDocumentResolver = ExistingDocumentResolver; exports.FontReviewCard_default = FontReviewCard_default; exports.BulkActions = BulkActions; exports.UploadStep2Review = UploadStep2Review; exports.executeUploadPlan = executeUploadPlan; exports.createInitialExecutionState = createInitialExecutionState; exports.executionReducer = executionReducer; exports.UploadStep3Execute = UploadStep3Execute; exports.UploadStep3bInstances = UploadStep3bInstances; exports.UploadSummary = UploadSummary; exports.UploadModal = UploadModal;
|