@liiift-studio/sanity-font-manager 2.6.0 → 2.7.1

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 (62) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +695 -437
  3. package/dist/index.js +1628 -17701
  4. package/dist/index.mjs +1537 -17599
  5. package/package.json +83 -83
  6. package/src/components/BatchUploadFonts.jsx +655 -653
  7. package/src/components/BulkActions.jsx +99 -99
  8. package/src/components/ExistingDocumentResolver.jsx +152 -152
  9. package/src/components/FontReviewCard.jsx +455 -455
  10. package/src/components/FontScriptUploaderComponent.jsx +463 -463
  11. package/src/components/GenerateCollectionsPairsComponent.jsx +259 -259
  12. package/src/components/KeyValueInput.jsx +95 -95
  13. package/src/components/KeyValueReferenceInput.jsx +267 -267
  14. package/src/components/NestedObjectArraySelector.jsx +146 -146
  15. package/src/components/PriceInput.jsx +26 -26
  16. package/src/components/PrimaryCollectionGeneratorTypeface.jsx +116 -116
  17. package/src/components/RegenerateSubfamiliesComponent.jsx +185 -185
  18. package/src/components/SetOTF.jsx +87 -87
  19. package/src/components/SingleUploaderTool.jsx +674 -674
  20. package/src/components/StatusDisplay.jsx +26 -26
  21. package/src/components/StyleCountInput.jsx +16 -16
  22. package/src/components/UpdateScriptsComponent.jsx +76 -76
  23. package/src/components/UploadButton.jsx +43 -43
  24. package/src/components/UploadModal.jsx +309 -304
  25. package/src/components/UploadScriptsComponent.jsx +539 -539
  26. package/src/components/UploadStep1Settings.jsx +272 -272
  27. package/src/components/UploadStep2Review.jsx +478 -474
  28. package/src/components/UploadStep3Execute.jsx +234 -234
  29. package/src/components/UploadStep3bInstances.jsx +396 -396
  30. package/src/components/UploadSummary.jsx +196 -196
  31. package/src/components/VariableInstanceReferencesInput.jsx +190 -190
  32. package/src/hooks/useNestedObjects.js +92 -92
  33. package/src/hooks/useSanityClient.js +9 -9
  34. package/src/index.js +120 -120
  35. package/src/schema/openTypeField.js +1995 -1995
  36. package/src/schema/styleCountField.js +12 -12
  37. package/src/schema/stylesField.js +302 -302
  38. package/src/schema/stylisticSetField.js +301 -301
  39. package/src/utils/buildUploadPlan.js +326 -326
  40. package/src/utils/executeUploadPlan.js +430 -430
  41. package/src/utils/executionReducer.js +56 -56
  42. package/src/utils/fontHelpers.js +281 -267
  43. package/src/utils/generateCssFile.js +207 -207
  44. package/src/utils/generateFontData.js +98 -98
  45. package/src/utils/generateFontFile.js +38 -38
  46. package/src/utils/generateKeywords.js +185 -185
  47. package/src/utils/generateSubset.js +45 -45
  48. package/src/utils/getEmptyFontKit.js +101 -101
  49. package/src/utils/parseFont.js +56 -56
  50. package/src/utils/parseVariableFontInstances.js +301 -301
  51. package/src/utils/planReducer.js +531 -517
  52. package/src/utils/planTypes.js +183 -183
  53. package/src/utils/processFontFiles.js +530 -530
  54. package/src/utils/regenerateFontData.js +146 -146
  55. package/src/utils/resolveExistingFont.js +87 -87
  56. package/src/utils/retitleFontEntries.js +154 -0
  57. package/src/utils/sanitizeForSanityId.js +65 -65
  58. package/src/utils/setupDecompressors.js +27 -27
  59. package/src/utils/updateFontPrices.js +94 -94
  60. package/src/utils/updateTypefaceDocument.js +162 -162
  61. package/src/utils/uploadFontFiles.js +405 -405
  62. package/src/utils/utils.js +24 -24
@@ -1,162 +1,162 @@
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
- // Deduplicate by _ref to prevent duplicate entries on re-upload
42
- const dedupeRefs = (existing, incoming) => {
43
- const merged = [...(existing || [])];
44
- const existingRefs = new Set(merged.map(r => r._ref).filter(Boolean));
45
- incoming.forEach(ref => {
46
- if (ref._ref && !existingRefs.has(ref._ref)) {
47
- merged.push(ref);
48
- existingRefs.add(ref._ref);
49
- }
50
- });
51
- return merged;
52
- };
53
-
54
- let patch = {
55
- 'styles.fonts': dedupeRefs(stylesObject.fonts, fontRefs),
56
- 'styles.variableFont': dedupeRefs(stylesObject?.variableFont, variableRefs),
57
- };
58
-
59
- setStatus('Organising font subfamilies...');
60
- subfamiliesArray = subfamiliesArray || [];
61
-
62
- // Create any missing subfamily groups
63
- uniqueSubfamilies.forEach(subfamilyName => {
64
- if (!subfamiliesArray.find(sf => sf.title === subfamilyName)) {
65
- subfamiliesArray.push({
66
- title: subfamilyName,
67
- _key: nanoid(),
68
- _type: 'object',
69
- fonts: [],
70
- });
71
- }
72
- });
73
-
74
- // Associate fonts with their subfamily groups (skip VF fonts)
75
- if (subfamiliesArray.length > 0) {
76
- Object.entries(subfamilies).forEach(([id, subfamilyName]) => {
77
- if (id.toLowerCase().includes('vf')) return;
78
-
79
- const subfamilyIndex = subfamiliesArray.findIndex(sf => sf.title === subfamilyName);
80
- if (subfamilyIndex !== -1) {
81
- subfamiliesArray[subfamilyIndex].fonts.push({
82
- _ref: id,
83
- _key: nanoid(),
84
- _type: 'reference',
85
- _weak: true,
86
- });
87
- }
88
- });
89
-
90
- // Deduplicate references within each subfamily
91
- subfamiliesArray = subfamiliesArray.map(subfamily => ({
92
- ...subfamily,
93
- fonts: subfamily.fonts.filter((font, index, self) =>
94
- index === self.findIndex(f => f._ref === font._ref)
95
- ),
96
- }));
97
- }
98
-
99
- patch['styles.subfamilies'] = subfamiliesArray;
100
-
101
- // Optionally update preferred style
102
- await updatePreferredStyle(doc_id, preferredStyleRef, newPreferredStyle, patch, client);
103
-
104
- console.log('doc_id: ', doc_id);
105
- console.log('Typeface patch: ', patch);
106
- console.log('New preferred style: ', newPreferredStyle);
107
- console.log('SubfamiliesArray:', subfamiliesArray);
108
-
109
- try {
110
- await client.patch(doc_id).set(patch).commit();
111
- console.log(`Updated document: ${doc_id}`);
112
-
113
- if (doc_id.startsWith('drafts.')) {
114
- await updatePublishedDocument(doc_id, patch, client);
115
- }
116
- } catch (err) {
117
- console.error('Error updating document:', err.message);
118
- setStatus('Error updating typeface');
119
- setError(true);
120
- }
121
- };
122
-
123
- /**
124
- * Sets preferredStyle on the patch only when currently empty.
125
- * Does not overwrite an existing preferredStyle — the user's choice is sticky.
126
- * @param {string} doc_id
127
- * @param {Object} preferredStyleRef
128
- * @param {Object} newPreferredStyle
129
- * @param {Object} patch
130
- * @param {Object} client
131
- */
132
- const updatePreferredStyle = async (doc_id, preferredStyleRef, newPreferredStyle, patch, client) => {
133
- const isCurrentlyEmpty = !preferredStyleRef?._ref || preferredStyleRef._ref === '' || preferredStyleRef._ref === null;
134
- const hasCandidate = newPreferredStyle?._ref && newPreferredStyle._ref !== '';
135
-
136
- if (isCurrentlyEmpty && hasCandidate) {
137
- patch.preferredStyle = {
138
- _type: 'reference',
139
- _ref: newPreferredStyle._ref,
140
- _weak: true,
141
- };
142
- }
143
- };
144
-
145
- /**
146
- * Applies the same patch to the published document if it exists.
147
- * @param {string} doc_id - Draft document ID
148
- * @param {Object} patch
149
- * @param {Object} client
150
- */
151
- const updatePublishedDocument = async (doc_id, patch, client) => {
152
- const publishedId = doc_id.replace('drafts.', '');
153
- // Parameterized to prevent injection from any draft ID edge cases
154
- const publishedDoc = await client.fetch(`*[_id == $publishedId]`, { publishedId }).then(res => res[0]);
155
-
156
- if (publishedDoc) {
157
- await client.patch(publishedId).set(patch).commit();
158
- console.log(`Updated published document: ${publishedId}`);
159
- } else {
160
- console.log(`No published document found for ${publishedId}, skipping`);
161
- }
162
- };
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
+ // Deduplicate by _ref to prevent duplicate entries on re-upload
42
+ const dedupeRefs = (existing, incoming) => {
43
+ const merged = [...(existing || [])];
44
+ const existingRefs = new Set(merged.map(r => r._ref).filter(Boolean));
45
+ incoming.forEach(ref => {
46
+ if (ref._ref && !existingRefs.has(ref._ref)) {
47
+ merged.push(ref);
48
+ existingRefs.add(ref._ref);
49
+ }
50
+ });
51
+ return merged;
52
+ };
53
+
54
+ let patch = {
55
+ 'styles.fonts': dedupeRefs(stylesObject.fonts, fontRefs),
56
+ 'styles.variableFont': dedupeRefs(stylesObject?.variableFont, variableRefs),
57
+ };
58
+
59
+ setStatus('Organising font subfamilies...');
60
+ subfamiliesArray = subfamiliesArray || [];
61
+
62
+ // Create any missing subfamily groups
63
+ uniqueSubfamilies.forEach(subfamilyName => {
64
+ if (!subfamiliesArray.find(sf => sf.title === subfamilyName)) {
65
+ subfamiliesArray.push({
66
+ title: subfamilyName,
67
+ _key: nanoid(),
68
+ _type: 'object',
69
+ fonts: [],
70
+ });
71
+ }
72
+ });
73
+
74
+ // Associate fonts with their subfamily groups (skip VF fonts)
75
+ if (subfamiliesArray.length > 0) {
76
+ Object.entries(subfamilies).forEach(([id, subfamilyName]) => {
77
+ if (id.toLowerCase().includes('vf')) return;
78
+
79
+ const subfamilyIndex = subfamiliesArray.findIndex(sf => sf.title === subfamilyName);
80
+ if (subfamilyIndex !== -1) {
81
+ subfamiliesArray[subfamilyIndex].fonts.push({
82
+ _ref: id,
83
+ _key: nanoid(),
84
+ _type: 'reference',
85
+ _weak: true,
86
+ });
87
+ }
88
+ });
89
+
90
+ // Deduplicate references within each subfamily
91
+ subfamiliesArray = subfamiliesArray.map(subfamily => ({
92
+ ...subfamily,
93
+ fonts: subfamily.fonts.filter((font, index, self) =>
94
+ index === self.findIndex(f => f._ref === font._ref)
95
+ ),
96
+ }));
97
+ }
98
+
99
+ patch['styles.subfamilies'] = subfamiliesArray;
100
+
101
+ // Optionally update preferred style
102
+ await updatePreferredStyle(doc_id, preferredStyleRef, newPreferredStyle, patch, client);
103
+
104
+ console.log('doc_id: ', doc_id);
105
+ console.log('Typeface patch: ', patch);
106
+ console.log('New preferred style: ', newPreferredStyle);
107
+ console.log('SubfamiliesArray:', subfamiliesArray);
108
+
109
+ try {
110
+ await client.patch(doc_id).set(patch).commit();
111
+ console.log(`Updated document: ${doc_id}`);
112
+
113
+ if (doc_id.startsWith('drafts.')) {
114
+ await updatePublishedDocument(doc_id, patch, client);
115
+ }
116
+ } catch (err) {
117
+ console.error('Error updating document:', err.message);
118
+ setStatus('Error updating typeface');
119
+ setError(true);
120
+ }
121
+ };
122
+
123
+ /**
124
+ * Sets preferredStyle on the patch only when currently empty.
125
+ * Does not overwrite an existing preferredStyle — the user's choice is sticky.
126
+ * @param {string} doc_id
127
+ * @param {Object} preferredStyleRef
128
+ * @param {Object} newPreferredStyle
129
+ * @param {Object} patch
130
+ * @param {Object} client
131
+ */
132
+ const updatePreferredStyle = async (doc_id, preferredStyleRef, newPreferredStyle, patch, client) => {
133
+ const isCurrentlyEmpty = !preferredStyleRef?._ref || preferredStyleRef._ref === '' || preferredStyleRef._ref === null;
134
+ const hasCandidate = newPreferredStyle?._ref && newPreferredStyle._ref !== '';
135
+
136
+ if (isCurrentlyEmpty && hasCandidate) {
137
+ patch.preferredStyle = {
138
+ _type: 'reference',
139
+ _ref: newPreferredStyle._ref,
140
+ _weak: true,
141
+ };
142
+ }
143
+ };
144
+
145
+ /**
146
+ * Applies the same patch to the published document if it exists.
147
+ * @param {string} doc_id - Draft document ID
148
+ * @param {Object} patch
149
+ * @param {Object} client
150
+ */
151
+ const updatePublishedDocument = async (doc_id, patch, client) => {
152
+ const publishedId = doc_id.replace('drafts.', '');
153
+ // Parameterized to prevent injection from any draft ID edge cases
154
+ const publishedDoc = await client.fetch(`*[_id == $publishedId]`, { publishedId }).then(res => res[0]);
155
+
156
+ if (publishedDoc) {
157
+ await client.patch(publishedId).set(patch).commit();
158
+ console.log(`Updated published document: ${publishedId}`);
159
+ } else {
160
+ console.log(`No published document found for ${publishedId}, skipping`);
161
+ }
162
+ };