@liiift-studio/sanity-font-manager 2.3.19 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/README.md +437 -437
  2. package/dist/index.js +103 -48
  3. package/dist/index.mjs +103 -48
  4. package/package.json +85 -85
  5. package/src/components/BatchUploadFonts.jsx +640 -639
  6. package/src/components/FontScriptUploaderComponent.jsx +463 -463
  7. package/src/components/GenerateCollectionsPairsComponent.jsx +259 -259
  8. package/src/components/KeyValueInput.jsx +95 -95
  9. package/src/components/KeyValueReferenceInput.jsx +254 -254
  10. package/src/components/NestedObjectArraySelector.jsx +146 -146
  11. package/src/components/PriceInput.jsx +26 -26
  12. package/src/components/PrimaryCollectionGeneratorTypeface.jsx +116 -116
  13. package/src/components/RegenerateSubfamiliesComponent.jsx +185 -185
  14. package/src/components/SetOTF.jsx +87 -87
  15. package/src/components/SingleUploaderTool.jsx +673 -673
  16. package/src/components/StatusDisplay.jsx +26 -26
  17. package/src/components/StyleCountInput.jsx +16 -16
  18. package/src/components/UpdateScriptsComponent.jsx +76 -76
  19. package/src/components/UploadButton.jsx +43 -43
  20. package/src/components/UploadScriptsComponent.jsx +537 -537
  21. package/src/components/VariableInstanceReferencesInput.jsx +190 -190
  22. package/src/hooks/useNestedObjects.js +92 -92
  23. package/src/hooks/useSanityClient.js +9 -9
  24. package/src/index.js +70 -70
  25. package/src/schema/openTypeField.js +1945 -1945
  26. package/src/schema/styleCountField.js +12 -12
  27. package/src/schema/stylesField.js +268 -268
  28. package/src/schema/stylisticSetField.js +301 -301
  29. package/src/utils/generateCssFile.js +205 -205
  30. package/src/utils/generateFontData.js +145 -145
  31. package/src/utils/generateFontFile.js +38 -38
  32. package/src/utils/generateKeywords.js +185 -185
  33. package/src/utils/generateSubset.js +45 -45
  34. package/src/utils/getEmptyFontKit.js +99 -99
  35. package/src/utils/parseVariableFontInstances.js +211 -211
  36. package/src/utils/processFontFiles.js +487 -477
  37. package/src/utils/regenerateFontData.js +146 -146
  38. package/src/utils/sanitizeForSanityId.js +65 -65
  39. package/src/utils/updateFontPrices.js +94 -94
  40. package/src/utils/updateTypefaceDocument.js +149 -160
  41. package/src/utils/uploadFontFiles.js +115 -26
  42. package/src/utils/utils.js +24 -24
@@ -1,160 +1,149 @@
1
- // Patches the parent typeface document's styles.fonts array with newly uploaded font references
2
-
3
- import { nanoid } from 'nanoid';
4
-
5
- /**
6
- * Patches a typeface document (draft and published) with the new font references,
7
- * subfamily structure, and preferred style derived from the upload batch.
8
- *
9
- * @param {string} doc_id - The Sanity document ID (may be a draft)
10
- * @param {Object[]} fontRefs - New regular font references
11
- * @param {Object[]} variableRefs - New variable font references
12
- * @param {Object} subfamilies - Map of font ID → subfamily name
13
- * @param {string[]} uniqueSubfamilies
14
- * @param {Object[]} subfamiliesArray - Existing subfamilies array from the typeface
15
- * @param {Object} preferredStyleRef - Existing preferred style reference
16
- * @param {Object} newPreferredStyle - Candidate preferred style from the upload
17
- * @param {Object} stylesObject - Existing typeface styles object
18
- * @param {Object} client - Sanity client
19
- * @param {Function} setStatus
20
- * @param {Function} setError
21
- */
22
- export const updateTypefaceDocument = async (
23
- doc_id,
24
- fontRefs,
25
- variableRefs,
26
- subfamilies,
27
- uniqueSubfamilies,
28
- subfamiliesArray,
29
- preferredStyleRef,
30
- newPreferredStyle,
31
- stylesObject,
32
- client,
33
- setStatus,
34
- setError,
35
- ) => {
36
- console.log('Updating typeface document with new fonts:', { fontRefs, variableRefs, subfamilies, uniqueSubfamilies });
37
- setStatus('Updating typeface references...');
38
-
39
- let patch = {
40
- styles: {
41
- fonts: stylesObject.fonts ? [...stylesObject.fonts, ...fontRefs] : [...fontRefs],
42
- subfamilies: uniqueSubfamilies.length > 1 ? uniqueSubfamilies : [],
43
- variableFont: stylesObject?.variableFont ? [...stylesObject.variableFont, ...variableRefs] : [...variableRefs],
44
- },
45
- };
46
-
47
- setStatus('Organising font subfamilies...');
48
- subfamiliesArray = subfamiliesArray || [];
49
-
50
- // Create any missing subfamily groups
51
- uniqueSubfamilies.forEach(subfamilyName => {
52
- if (!subfamiliesArray.find(sf => sf.title === subfamilyName)) {
53
- subfamiliesArray.push({
54
- title: subfamilyName,
55
- _key: nanoid(),
56
- _type: 'object',
57
- fonts: [],
58
- });
59
- }
60
- });
61
-
62
- // Associate fonts with their subfamily groups (skip VF fonts)
63
- if (subfamiliesArray.length > 0) {
64
- Object.entries(subfamilies).forEach(([id, subfamilyName]) => {
65
- if (id.toLowerCase().includes('vf')) return;
66
-
67
- const subfamilyIndex = subfamiliesArray.findIndex(sf => sf.title === subfamilyName);
68
- if (subfamilyIndex !== -1) {
69
- subfamiliesArray[subfamilyIndex].fonts.push({
70
- _ref: id,
71
- _key: nanoid(),
72
- _type: 'reference',
73
- _weak: true,
74
- });
75
- }
76
- });
77
-
78
- // Deduplicate references within each subfamily
79
- subfamiliesArray = subfamiliesArray.map(subfamily => ({
80
- ...subfamily,
81
- fonts: subfamily.fonts.filter((font, index, self) =>
82
- index === self.findIndex(f => f._ref === font._ref)
83
- ),
84
- }));
85
- }
86
-
87
- patch.styles.subfamilies = subfamiliesArray;
88
-
89
- // Optionally update preferred style
90
- await updatePreferredStyle(doc_id, preferredStyleRef, newPreferredStyle, patch, client);
91
-
92
- console.log('doc_id: ', doc_id);
93
- console.log('Typeface patch: ', patch);
94
- console.log('New preferred style: ', newPreferredStyle);
95
- console.log('SubfamiliesArray:', subfamiliesArray);
96
-
97
- try {
98
- await client.patch(doc_id).set(patch).commit();
99
- console.log(`Updated document: ${doc_id}`);
100
-
101
- if (doc_id.startsWith('drafts.')) {
102
- await updatePublishedDocument(doc_id, patch, client);
103
- }
104
- } catch (err) {
105
- console.error('Error updating document:', err.message);
106
- setStatus('Error updating typeface');
107
- setError(true);
108
- }
109
- };
110
-
111
- /**
112
- * Conditionally sets a new preferredStyle on the patch when the candidate has a higher weight.
113
- * @param {string} doc_id
114
- * @param {Object} preferredStyleRef
115
- * @param {Object} newPreferredStyle
116
- * @param {Object} patch
117
- * @param {Object} client
118
- */
119
- const updatePreferredStyle = async (doc_id, preferredStyleRef, newPreferredStyle, patch, client) => {
120
- if (
121
- preferredStyleRef?._ref &&
122
- preferredStyleRef._ref !== '' &&
123
- preferredStyleRef._ref !== null &&
124
- newPreferredStyle._ref !== preferredStyleRef._ref
125
- ) {
126
- // Parameterized — doc_id comes from Sanity's useFormValue but we parameterize defensively
127
- const prefStyleResult = await client.fetch(
128
- `*[_id == $docId]{ preferredStyle->{ weight, style, _id } }`,
129
- { docId: doc_id }
130
- );
131
- const prefStyle = prefStyleResult[0]?.preferredStyle;
132
-
133
- if (!prefStyle?.weight || prefStyle === null || prefStyle.weight < newPreferredStyle.weight) {
134
- patch.preferredStyle = {
135
- _type: 'reference',
136
- _ref: newPreferredStyle._ref,
137
- _weak: true,
138
- };
139
- }
140
- }
141
- };
142
-
143
- /**
144
- * Applies the same patch to the published document if it exists.
145
- * @param {string} doc_id - Draft document ID
146
- * @param {Object} patch
147
- * @param {Object} client
148
- */
149
- const updatePublishedDocument = async (doc_id, patch, client) => {
150
- const publishedId = doc_id.replace('drafts.', '');
151
- // Parameterized to prevent injection from any draft ID edge cases
152
- const publishedDoc = await client.fetch(`*[_id == $publishedId]`, { publishedId }).then(res => res[0]);
153
-
154
- if (publishedDoc) {
155
- await client.patch(publishedId).set(patch).commit();
156
- console.log(`Updated published document: ${publishedId}`);
157
- } else {
158
- console.log(`No published document found for ${publishedId}, skipping`);
159
- }
160
- };
1
+ // Patches the parent typeface document's styles.fonts array with newly uploaded font references
2
+
3
+ import { nanoid } from 'nanoid';
4
+
5
+ /**
6
+ * Patches a typeface document (draft and published) with the new font references,
7
+ * subfamily structure, and preferred style derived from the upload batch.
8
+ *
9
+ * @param {string} doc_id - The Sanity document ID (may be a draft)
10
+ * @param {Object[]} fontRefs - New regular font references
11
+ * @param {Object[]} variableRefs - New variable font references
12
+ * @param {Object} subfamilies - Map of font ID → subfamily name
13
+ * @param {string[]} uniqueSubfamilies
14
+ * @param {Object[]} subfamiliesArray - Existing subfamilies array from the typeface
15
+ * @param {Object} preferredStyleRef - Existing preferred style reference
16
+ * @param {Object} newPreferredStyle - Candidate preferred style from the upload
17
+ * @param {Object} stylesObject - Existing typeface styles object
18
+ * @param {Object} client - Sanity client
19
+ * @param {Function} setStatus
20
+ * @param {Function} setError
21
+ */
22
+ export const updateTypefaceDocument = async (
23
+ doc_id,
24
+ fontRefs,
25
+ variableRefs,
26
+ subfamilies,
27
+ uniqueSubfamilies,
28
+ subfamiliesArray,
29
+ preferredStyleRef,
30
+ newPreferredStyle,
31
+ stylesObject,
32
+ client,
33
+ setStatus,
34
+ setError,
35
+ ) => {
36
+ console.log('Updating typeface document with new fonts:', { fontRefs, variableRefs, subfamilies, uniqueSubfamilies });
37
+ setStatus('Updating typeface references...');
38
+
39
+ // Use dot-path keys so .set() does not clobber sibling fields
40
+ // (styles.collections, styles.pairs, styles.free, styles.displayStyles)
41
+ let patch = {
42
+ 'styles.fonts': stylesObject.fonts ? [...stylesObject.fonts, ...fontRefs] : [...fontRefs],
43
+ 'styles.variableFont': stylesObject?.variableFont ? [...stylesObject.variableFont, ...variableRefs] : [...variableRefs],
44
+ };
45
+
46
+ setStatus('Organising font subfamilies...');
47
+ subfamiliesArray = subfamiliesArray || [];
48
+
49
+ // Create any missing subfamily groups
50
+ uniqueSubfamilies.forEach(subfamilyName => {
51
+ if (!subfamiliesArray.find(sf => sf.title === subfamilyName)) {
52
+ subfamiliesArray.push({
53
+ title: subfamilyName,
54
+ _key: nanoid(),
55
+ _type: 'object',
56
+ fonts: [],
57
+ });
58
+ }
59
+ });
60
+
61
+ // Associate fonts with their subfamily groups (skip VF fonts)
62
+ if (subfamiliesArray.length > 0) {
63
+ Object.entries(subfamilies).forEach(([id, subfamilyName]) => {
64
+ if (id.toLowerCase().includes('vf')) return;
65
+
66
+ const subfamilyIndex = subfamiliesArray.findIndex(sf => sf.title === subfamilyName);
67
+ if (subfamilyIndex !== -1) {
68
+ subfamiliesArray[subfamilyIndex].fonts.push({
69
+ _ref: id,
70
+ _key: nanoid(),
71
+ _type: 'reference',
72
+ _weak: true,
73
+ });
74
+ }
75
+ });
76
+
77
+ // Deduplicate references within each subfamily
78
+ subfamiliesArray = subfamiliesArray.map(subfamily => ({
79
+ ...subfamily,
80
+ fonts: subfamily.fonts.filter((font, index, self) =>
81
+ index === self.findIndex(f => f._ref === font._ref)
82
+ ),
83
+ }));
84
+ }
85
+
86
+ patch['styles.subfamilies'] = subfamiliesArray;
87
+
88
+ // Optionally update preferred style
89
+ await updatePreferredStyle(doc_id, preferredStyleRef, newPreferredStyle, patch, client);
90
+
91
+ console.log('doc_id: ', doc_id);
92
+ console.log('Typeface patch: ', patch);
93
+ console.log('New preferred style: ', newPreferredStyle);
94
+ console.log('SubfamiliesArray:', subfamiliesArray);
95
+
96
+ try {
97
+ await client.patch(doc_id).set(patch).commit();
98
+ console.log(`Updated document: ${doc_id}`);
99
+
100
+ if (doc_id.startsWith('drafts.')) {
101
+ await updatePublishedDocument(doc_id, patch, client);
102
+ }
103
+ } catch (err) {
104
+ console.error('Error updating document:', err.message);
105
+ setStatus('Error updating typeface');
106
+ setError(true);
107
+ }
108
+ };
109
+
110
+ /**
111
+ * Sets preferredStyle on the patch only when currently empty.
112
+ * Does not overwrite an existing preferredStyle the user's choice is sticky.
113
+ * @param {string} doc_id
114
+ * @param {Object} preferredStyleRef
115
+ * @param {Object} newPreferredStyle
116
+ * @param {Object} patch
117
+ * @param {Object} client
118
+ */
119
+ const updatePreferredStyle = async (doc_id, preferredStyleRef, newPreferredStyle, patch, client) => {
120
+ const isCurrentlyEmpty = !preferredStyleRef?._ref || preferredStyleRef._ref === '' || preferredStyleRef._ref === null;
121
+ const hasCandidate = newPreferredStyle?._ref && newPreferredStyle._ref !== '';
122
+
123
+ if (isCurrentlyEmpty && hasCandidate) {
124
+ patch.preferredStyle = {
125
+ _type: 'reference',
126
+ _ref: newPreferredStyle._ref,
127
+ _weak: true,
128
+ };
129
+ }
130
+ };
131
+
132
+ /**
133
+ * Applies the same patch to the published document if it exists.
134
+ * @param {string} doc_id - Draft document ID
135
+ * @param {Object} patch
136
+ * @param {Object} client
137
+ */
138
+ const updatePublishedDocument = async (doc_id, patch, client) => {
139
+ const publishedId = doc_id.replace('drafts.', '');
140
+ // Parameterized to prevent injection from any draft ID edge cases
141
+ const publishedDoc = await client.fetch(`*[_id == $publishedId]`, { publishedId }).then(res => res[0]);
142
+
143
+ if (publishedDoc) {
144
+ await client.patch(publishedId).set(patch).commit();
145
+ console.log(`Updated published document: ${publishedId}`);
146
+ } else {
147
+ console.log(`No published document found for ${publishedId}, skipping`);
148
+ }
149
+ };
@@ -1,4 +1,4 @@
1
- // Core batch upload orchestrator — uploads each format to Sanity, generates CSS and metadata, then creates or updates font documents
1
+ // Core batch upload orchestrator — uploads each format to Sanity, generates CSS and metadata, resolves existing documents, then creates or updates font documents
2
2
 
3
3
  import { nanoid } from 'nanoid';
4
4
  import generateCssFile from './generateCssFile';
@@ -14,6 +14,7 @@ import { parseVariableFontInstances } from './parseVariableFontInstances';
14
14
  * @param {Object} stylesObject - Existing typeface styles object
15
15
  * @param {Function} setStatus
16
16
  * @param {Function} setError
17
+ * @param {boolean} preserveFileNames - Use original filenames for asset naming
17
18
  * @returns {Promise<Object>} fontRefs, variableRefs, failedFiles
18
19
  */
19
20
  export const uploadFontFiles = async (
@@ -24,6 +25,7 @@ export const uploadFontFiles = async (
24
25
  stylesObject,
25
26
  setStatus,
26
27
  setError,
28
+ preserveFileNames = false,
27
29
  ) => {
28
30
  let fontRefs = [];
29
31
  let variableRefs = [];
@@ -47,11 +49,27 @@ export const uploadFontFiles = async (
47
49
  const file = files[j];
48
50
  const fileType = determineFileType(file);
49
51
 
50
- console.log(`[${i + 1}/${fontObjectKeys.length}][${j + 1}/${files.length}] Uploading font file: ${fontObject._id}.${fileType}`);
51
- setStatus(`[${i + 1}/${fontObjectKeys.length}][${j + 1}/${files.length}] Uploading font file: ${fontObject._id}.${fileType}`);
52
+ // Use original filename for asset naming when preserveFileNames is enabled
53
+ const assetFilename = preserveFileNames && fontObject.originalFilename
54
+ ? `${fontObject.originalFilename}.${fileType}`
55
+ : `${fontObject._id}.${fileType}`;
56
+
57
+ console.log(`[${i + 1}/${fontObjectKeys.length}][${j + 1}/${files.length}] Uploading font file: ${assetFilename}`);
58
+ setStatus(`[${i + 1}/${fontObjectKeys.length}][${j + 1}/${files.length}] Uploading font file: ${assetFilename}`);
52
59
 
53
60
  try {
54
- const baseAsset = await client.assets.upload('file', file, { filename: fontObject._id + '.' + fileType });
61
+ const baseAsset = await client.assets.upload('file', file, { filename: assetFilename });
62
+
63
+ // Sanity deduplicates assets by SHA1 hash and reuses the existing asset doc,
64
+ // which means originalFilename may reflect a previous upload. Patch it to
65
+ // match the intended filename so downstream consumers see the correct name.
66
+ if (preserveFileNames && baseAsset.originalFilename !== assetFilename) {
67
+ try {
68
+ await client.patch(baseAsset._id).set({ originalFilename: assetFilename }).commit();
69
+ } catch (renameErr) {
70
+ console.warn('Could not rename asset — permissions may be restricted:', renameErr.message);
71
+ }
72
+ }
55
73
 
56
74
  newFileInput[fileType] = {
57
75
  _type: 'file',
@@ -86,7 +104,7 @@ export const uploadFontFiles = async (
86
104
  Object.assign(fontObject, metadata);
87
105
  }
88
106
  } catch (err) {
89
- console.error('Error uploading font: ', fontObject.title, err.message);
107
+ console.error('Error uploading font:', fontObject.title, err.message);
90
108
  setStatus('Error uploading font: ' + err.message);
91
109
  setError(true);
92
110
  failedFiles.push({ name: file.name, fk: fontKit });
@@ -136,8 +154,87 @@ const determineFileType = (file) => {
136
154
  return '';
137
155
  };
138
156
 
157
+ /**
158
+ * Resolves whether a font document already exists in Sanity, returning match details
159
+ * and a recommendation for how to proceed.
160
+ *
161
+ * Resolution strategies (in priority order):
162
+ * 1. Exact _id match or draft _id match or slug.current match
163
+ * 2. Content match by typefaceName + weightName + style + subfamily + variableFont
164
+ *
165
+ * @param {Object} font - The font object with _id, typefaceName, weightName, style, subfamily, variableFont
166
+ * @param {Object} client - Sanity client (parameterized queries only)
167
+ * @returns {Promise<{ exact: Object|null, candidates: Object[], recommendation: string }>}
168
+ */
169
+ export const resolveExistingFont = async (font, client) => {
170
+ const result = { exact: null, candidates: [], recommendation: 'create' };
171
+
172
+ try {
173
+ // Strategy 1: ID / slug match
174
+ const idMatches = await client.fetch(
175
+ `*[_type == 'font' && (_id == $id || _id == $draftId || slug.current == $id)]{
176
+ _id, title, weight, style, weightName, typefaceName, subfamily, variableFont,
177
+ fileInput, description, metaData, metrics, opentypeFeatures, characterSet,
178
+ scriptFileInput, variableInstanceReferences
179
+ }`,
180
+ { id: font._id, draftId: `drafts.${font._id}` }
181
+ );
182
+
183
+ if (idMatches.length > 0) {
184
+ result.exact = idMatches[0];
185
+ result.recommendation = 'use-exact';
186
+ return result;
187
+ }
188
+
189
+ // Strategy 2: Content match (only when ID query returns nothing)
190
+ const subfamily = font.subfamily || '';
191
+ const contentMatches = await client.fetch(
192
+ `*[_type == 'font'
193
+ && lower(typefaceName) == lower($typefaceName)
194
+ && lower(weightName) == lower($weightName)
195
+ && lower(style) == lower($style)
196
+ && (variableFont == $variableFont || (!defined(variableFont) && $variableFont == false))
197
+ && (
198
+ lower(coalesce(subfamily, '')) == lower($subfamily)
199
+ || (lower(coalesce(subfamily, '')) in ['', 'regular'] && lower($subfamily) in ['', 'regular'])
200
+ )
201
+ ]{
202
+ _id, title, weight, style, weightName, typefaceName, subfamily, variableFont,
203
+ fileInput, description, metaData, metrics, opentypeFeatures, characterSet,
204
+ scriptFileInput, variableInstanceReferences
205
+ }`,
206
+ {
207
+ typefaceName: font.typefaceName,
208
+ weightName: font.weightName || '',
209
+ style: font.style || 'Regular',
210
+ variableFont: font.variableFont || false,
211
+ subfamily: subfamily === '' ? 'regular' : subfamily,
212
+ }
213
+ );
214
+
215
+ if (contentMatches.length === 1) {
216
+ result.candidates = contentMatches;
217
+ result.recommendation = 'use-candidate';
218
+ return result;
219
+ }
220
+
221
+ if (contentMatches.length > 1) {
222
+ result.candidates = contentMatches;
223
+ result.recommendation = 'ambiguous';
224
+ console.warn(`Ambiguous font match for "${font.title}" — ${contentMatches.length} candidates found:`,
225
+ contentMatches.map(c => c._id));
226
+ return result;
227
+ }
228
+ } catch (err) {
229
+ console.error('Error resolving existing font:', font._id, err.message);
230
+ }
231
+
232
+ return result;
233
+ };
234
+
139
235
  /**
140
236
  * Creates a new font document or updates an existing one, returning its reference.
237
+ * Uses resolveExistingFont to determine whether to create or update.
141
238
  * @param {Object} font
142
239
  * @param {Object} client
143
240
  * @param {Function} setError
@@ -145,21 +242,7 @@ const determineFileType = (file) => {
145
242
  */
146
243
  const createOrUpdateFontDocument = async (font, client, setError) => {
147
244
  try {
148
- // Parameterized query prevents GROQ injection via font._id
149
- const existingFont = await client.fetch(
150
- `*[_type == 'font' && _id == $fontId]{
151
- fileInput,
152
- description,
153
- metaData,
154
- metrics,
155
- opentypeFeatures,
156
- characterSet,
157
- subfamily,
158
- scriptFileInput,
159
- variableInstanceReferences
160
- }`,
161
- { fontId: font._id }
162
- ).then(res => res[0]);
245
+ const { exact, candidates, recommendation } = await resolveExistingFont(font, client);
163
246
 
164
247
  const { files, fontKit } = font;
165
248
  delete font.files;
@@ -175,9 +258,15 @@ const createOrUpdateFontDocument = async (font, client, setError) => {
175
258
  }
176
259
 
177
260
  let fontResponse;
178
- if (existingFont) {
179
- fontResponse = await updateExistingFont(font, existingFont, client);
261
+ if (recommendation === 'use-exact' && exact) {
262
+ fontResponse = await updateExistingFont(font, exact, client);
263
+ } else if (recommendation === 'use-candidate' && candidates.length === 1) {
264
+ // Reassign font._id to match the existing document so inbound references resolve
265
+ console.log(`Content-match: reassigning "${font._id}" → "${candidates[0]._id}"`);
266
+ font._id = candidates[0]._id;
267
+ fontResponse = await updateExistingFont(font, candidates[0], client);
180
268
  } else {
269
+ // 'ambiguous' or 'create' — create a new document
181
270
  fontResponse = await createNewFont(font, client);
182
271
  }
183
272
 
@@ -188,7 +277,7 @@ const createOrUpdateFontDocument = async (font, client, setError) => {
188
277
  _weak: true,
189
278
  };
190
279
  } catch (e) {
191
- console.error('Error creating font: ', font.title, font.subfamily, e);
280
+ console.error('Error creating font:', font.title, font.subfamily, e);
192
281
  setError(true);
193
282
  return null;
194
283
  }
@@ -231,7 +320,7 @@ const updateExistingFont = async (font, existingFont, client) => {
231
320
  font.variableInstanceReferences = existingFont.variableInstanceReferences;
232
321
  }
233
322
 
234
- console.log('Updating existing font: ', font._id, font.title);
323
+ console.log('Updating existing font:', font._id, font.title);
235
324
 
236
325
  const patchObject = {
237
326
  fileInput: font.fileInput,
@@ -253,7 +342,7 @@ const updateExistingFont = async (font, existingFont, client) => {
253
342
  * @returns {Promise<Object>}
254
343
  */
255
344
  const createNewFont = async (font, client) => {
256
- console.log('Creating new font: ', font._id, font.title);
345
+ console.log('Creating new font:', font._id, font.title);
257
346
  if (font.metaData) cleanMetadataValues(font);
258
347
 
259
348
  const newDocument = {
@@ -276,7 +365,7 @@ const cleanMetadataValues = (font) => {
276
365
  if (font.metaData[key] == null) {
277
366
  font.metaData[key] = '';
278
367
  } else {
279
- font.metaData[key] = font.metaData[key].replace(/[-]/g, '');
368
+ font.metaData[key] = font.metaData[key].replace(/[\x00-\x1f]/g, '');
280
369
  }
281
370
  });
282
371
  };
@@ -1,24 +1,24 @@
1
- // Script variant constants (SCRIPTS, SCRIPTS_OBJECT) and HtmlDescription component for the supported script list
2
-
3
- import React from 'react'
4
-
5
- /** Renders children as-is; used for rich-text fields that return HTML strings */
6
- export const HtmlDescription = ({ children }) => {
7
- return children || ''
8
- }
9
-
10
- /** Script variants available for this studio instance — comma-separated SANITY_STUDIO_SCRIPTS env var */
11
- export const SCRIPTS = (process.env.SANITY_STUDIO_SCRIPTS || '').split(',').map((script) => script.trim()).filter(Boolean);
12
-
13
- /** SCRIPTS as Sanity select option objects */
14
- export const SCRIPTS_OBJECT = SCRIPTS.map((script) => {
15
- return {title: script[0].toUpperCase() + script.slice(1), value: script}
16
- });
17
-
18
- /** Discount requirement types — comma-separated SANITY_STUDIO_DISCOUNT_REQ_TYPES env var */
19
- export const DISCOUNT_REQUIREMENT_TYPES = (process.env.SANITY_STUDIO_DISCOUNT_REQ_TYPES || '').split(',').map((type) => type.trim()).filter(Boolean);
20
-
21
- /** DISCOUNT_REQUIREMENT_TYPES as Sanity select option objects */
22
- export const DISCOUNT_REQUIREMENT_TYPES_OBJECT = DISCOUNT_REQUIREMENT_TYPES.map((type) => {
23
- return {title: type[0].toUpperCase() + type.slice(1), value: type}
24
- });
1
+ // Script variant constants (SCRIPTS, SCRIPTS_OBJECT) and HtmlDescription component for the supported script list
2
+
3
+ import React from 'react'
4
+
5
+ /** Renders children as-is; used for rich-text fields that return HTML strings */
6
+ export const HtmlDescription = ({ children }) => {
7
+ return children || ''
8
+ }
9
+
10
+ /** Script variants available for this studio instance — comma-separated SANITY_STUDIO_SCRIPTS env var */
11
+ export const SCRIPTS = (process.env.SANITY_STUDIO_SCRIPTS || '').split(',').map((script) => script.trim()).filter(Boolean);
12
+
13
+ /** SCRIPTS as Sanity select option objects */
14
+ export const SCRIPTS_OBJECT = SCRIPTS.map((script) => {
15
+ return {title: script[0].toUpperCase() + script.slice(1), value: script}
16
+ });
17
+
18
+ /** Discount requirement types — comma-separated SANITY_STUDIO_DISCOUNT_REQ_TYPES env var */
19
+ export const DISCOUNT_REQUIREMENT_TYPES = (process.env.SANITY_STUDIO_DISCOUNT_REQ_TYPES || '').split(',').map((type) => type.trim()).filter(Boolean);
20
+
21
+ /** DISCOUNT_REQUIREMENT_TYPES as Sanity select option objects */
22
+ export const DISCOUNT_REQUIREMENT_TYPES_OBJECT = DISCOUNT_REQUIREMENT_TYPES.map((type) => {
23
+ return {title: type[0].toUpperCase() + type.slice(1), value: type}
24
+ });