@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,183 +1,183 @@
1
- // Type constants and enums for the two-phase upload plan — single source of truth
2
-
3
- /** @enum {string} — Font processing status within the plan */
4
- export const FONT_STATUS = {
5
- PENDING: 'pending',
6
- PROCESSING: 'processing',
7
- PROCESSED: 'processed',
8
- ERROR: 'error',
9
- };
10
-
11
- /** @enum {string} — Modal phase state machine */
12
- export const 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
-
22
- /** @enum {string} — Document resolution recommendation from resolveExistingFont */
23
- export const RECOMMENDATION = {
24
- USE_EXACT: 'use-exact',
25
- USE_CANDIDATE: 'use-candidate',
26
- AMBIGUOUS: 'ambiguous',
27
- CREATE: 'create',
28
- };
29
-
30
- /** @enum {string} — Per-font execution progress status */
31
- export const EXECUTION_STATUS = {
32
- QUEUED: 'queued',
33
- UPLOADING_ASSETS: 'uploading-assets',
34
- GENERATING_CSS: 'generating-css',
35
- GENERATING_METADATA: 'generating-metadata',
36
- CREATING_DOCUMENT: 'creating-document',
37
- PATCHING_TYPEFACE: 'patching-typeface',
38
- COMPLETE: 'complete',
39
- ERROR: 'error',
40
- SKIPPED: 'skipped',
41
- };
42
-
43
- /** Processing-owned fields — only written by ADD_PROCESSED_FONT, never by user edit actions */
44
- export const PROCESSING_OWNED_FIELDS = ['parsedMetadata', 'glyphCount', 'opentypeFeatures', 'variationAxes', 'status'];
45
-
46
- /** User-owned fields — only written by user edit actions via decisions.*.userOverride */
47
- export const USER_OWNED_FIELDS = ['title', 'documentId', 'weight', 'weightName', 'style', 'subfamily'];
48
-
49
- /** Current plan schema version — increment on breaking shape changes */
50
- export const PLAN_VERSION = 1;
51
-
52
- /** Maximum concurrent asset uploads */
53
- export const CONCURRENCY_LIMIT = 3;
54
-
55
- /** Maximum retry attempts for 429 rate-limited requests */
56
- export const MAX_RETRIES = 3;
57
-
58
- /** Base backoff delay in ms for exponential retry */
59
- export const BASE_BACKOFF_MS = 1000;
60
-
61
- /** Jitter factor for exponential backoff (±25%) */
62
- export const JITTER_FACTOR = 0.25;
63
-
64
- /**
65
- * Calculate backoff delay with jitter for retry logic.
66
- * @param {number} attempt - Zero-based attempt number
67
- * @returns {number} Delay in milliseconds
68
- */
69
- export function backoffWithJitter(attempt) {
70
- const base = BASE_BACKOFF_MS * Math.pow(2, attempt);
71
- const jitter = base * JITTER_FACTOR * (Math.random() * 2 - 1);
72
- return Math.round(base + jitter);
73
- }
74
-
75
- /**
76
- * Creates an empty FontDecisions object with default sources.
77
- * @param {object} params
78
- * @param {string} params.titleSource
79
- * @param {string} params.title
80
- * @param {string} params.titleOriginal
81
- * @param {string} params.documentId
82
- * @param {number} params.weight
83
- * @param {string} params.weightSource
84
- * @param {string|null} params.matchedKeyword
85
- * @param {string} params.weightName
86
- * @param {string} params.weightNameSource
87
- * @param {string} params.style
88
- * @param {string} params.styleSource
89
- * @param {string} params.styleReason
90
- * @param {string} params.subfamily
91
- * @param {string} params.subfamilySource
92
- * @param {Array} params.titleAlternatives
93
- * @returns {object}
94
- */
95
- export function createFontDecisions({
96
- titleSource = 'fontkit-fullName',
97
- title = '',
98
- titleOriginal = '',
99
- documentId = '',
100
- weight = 400,
101
- weightSource = 'default-400',
102
- matchedKeyword = null,
103
- weightName = '',
104
- weightNameSource = 'nameId17-preferredSubfamily',
105
- style = 'Regular',
106
- styleSource = 'default-regular',
107
- styleReason = '',
108
- subfamily = '',
109
- subfamilySource = 'default-empty',
110
- titleAlternatives = [],
111
- }) {
112
- return {
113
- title: {
114
- source: titleSource,
115
- original: titleOriginal,
116
- processed: title,
117
- alternatives: titleAlternatives,
118
- userOverride: null,
119
- },
120
- documentId: {
121
- source: 'derived-from-title',
122
- generated: documentId,
123
- userOverride: null,
124
- },
125
- weight: {
126
- source: weightSource,
127
- detected: weight,
128
- matchedKeyword,
129
- rawWeightName: weightName,
130
- userOverride: null,
131
- },
132
- weightName: {
133
- source: weightNameSource,
134
- detected: weightName,
135
- userOverride: null,
136
- },
137
- style: {
138
- source: styleSource,
139
- detected: style,
140
- reason: styleReason,
141
- userOverride: null,
142
- },
143
- subfamily: {
144
- source: subfamilySource,
145
- detected: subfamily,
146
- userOverride: null,
147
- },
148
- existingDocument: {
149
- recommendation: RECOMMENDATION.CREATE,
150
- exact: null,
151
- candidates: [],
152
- userChoice: null,
153
- selectedCandidate: null,
154
- lookupFailed: false,
155
- },
156
- };
157
- }
158
-
159
- /**
160
- * Creates an initial empty UploadPlan.
161
- * @param {object} settings
162
- * @returns {object}
163
- */
164
- export function createEmptyPlan(settings = {}) {
165
- return {
166
- version: PLAN_VERSION,
167
- settings: {
168
- price: 0,
169
- preserveShortenedNames: false,
170
- preserveFileNames: false,
171
- ...settings,
172
- },
173
- fonts: {},
174
- subfamilyGroups: {},
175
- phase: PLAN_PHASE.IDLE,
176
- processingProgress: {
177
- total: 0,
178
- completed: 0,
179
- failed: 0,
180
- currentFile: null,
181
- },
182
- };
183
- }
1
+ // Type constants and enums for the two-phase upload plan — single source of truth
2
+
3
+ /** @enum {string} — Font processing status within the plan */
4
+ export const FONT_STATUS = {
5
+ PENDING: 'pending',
6
+ PROCESSING: 'processing',
7
+ PROCESSED: 'processed',
8
+ ERROR: 'error',
9
+ };
10
+
11
+ /** @enum {string} — Modal phase state machine */
12
+ export const 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
+
22
+ /** @enum {string} — Document resolution recommendation from resolveExistingFont */
23
+ export const RECOMMENDATION = {
24
+ USE_EXACT: 'use-exact',
25
+ USE_CANDIDATE: 'use-candidate',
26
+ AMBIGUOUS: 'ambiguous',
27
+ CREATE: 'create',
28
+ };
29
+
30
+ /** @enum {string} — Per-font execution progress status */
31
+ export const EXECUTION_STATUS = {
32
+ QUEUED: 'queued',
33
+ UPLOADING_ASSETS: 'uploading-assets',
34
+ GENERATING_CSS: 'generating-css',
35
+ GENERATING_METADATA: 'generating-metadata',
36
+ CREATING_DOCUMENT: 'creating-document',
37
+ PATCHING_TYPEFACE: 'patching-typeface',
38
+ COMPLETE: 'complete',
39
+ ERROR: 'error',
40
+ SKIPPED: 'skipped',
41
+ };
42
+
43
+ /** Processing-owned fields — only written by ADD_PROCESSED_FONT, never by user edit actions */
44
+ export const PROCESSING_OWNED_FIELDS = ['parsedMetadata', 'glyphCount', 'opentypeFeatures', 'variationAxes', 'status'];
45
+
46
+ /** User-owned fields — only written by user edit actions via decisions.*.userOverride */
47
+ export const USER_OWNED_FIELDS = ['title', 'documentId', 'weight', 'weightName', 'style', 'subfamily'];
48
+
49
+ /** Current plan schema version — increment on breaking shape changes */
50
+ export const PLAN_VERSION = 1;
51
+
52
+ /** Maximum concurrent asset uploads */
53
+ export const CONCURRENCY_LIMIT = 3;
54
+
55
+ /** Maximum retry attempts for 429 rate-limited requests */
56
+ export const MAX_RETRIES = 3;
57
+
58
+ /** Base backoff delay in ms for exponential retry */
59
+ export const BASE_BACKOFF_MS = 1000;
60
+
61
+ /** Jitter factor for exponential backoff (±25%) */
62
+ export const JITTER_FACTOR = 0.25;
63
+
64
+ /**
65
+ * Calculate backoff delay with jitter for retry logic.
66
+ * @param {number} attempt - Zero-based attempt number
67
+ * @returns {number} Delay in milliseconds
68
+ */
69
+ export function backoffWithJitter(attempt) {
70
+ const base = BASE_BACKOFF_MS * Math.pow(2, attempt);
71
+ const jitter = base * JITTER_FACTOR * (Math.random() * 2 - 1);
72
+ return Math.round(base + jitter);
73
+ }
74
+
75
+ /**
76
+ * Creates an empty FontDecisions object with default sources.
77
+ * @param {object} params
78
+ * @param {string} params.titleSource
79
+ * @param {string} params.title
80
+ * @param {string} params.titleOriginal
81
+ * @param {string} params.documentId
82
+ * @param {number} params.weight
83
+ * @param {string} params.weightSource
84
+ * @param {string|null} params.matchedKeyword
85
+ * @param {string} params.weightName
86
+ * @param {string} params.weightNameSource
87
+ * @param {string} params.style
88
+ * @param {string} params.styleSource
89
+ * @param {string} params.styleReason
90
+ * @param {string} params.subfamily
91
+ * @param {string} params.subfamilySource
92
+ * @param {Array} params.titleAlternatives
93
+ * @returns {object}
94
+ */
95
+ export function createFontDecisions({
96
+ titleSource = 'fontkit-fullName',
97
+ title = '',
98
+ titleOriginal = '',
99
+ documentId = '',
100
+ weight = 400,
101
+ weightSource = 'default-400',
102
+ matchedKeyword = null,
103
+ weightName = '',
104
+ weightNameSource = 'nameId17-preferredSubfamily',
105
+ style = 'Regular',
106
+ styleSource = 'default-regular',
107
+ styleReason = '',
108
+ subfamily = '',
109
+ subfamilySource = 'default-empty',
110
+ titleAlternatives = [],
111
+ }) {
112
+ return {
113
+ title: {
114
+ source: titleSource,
115
+ original: titleOriginal,
116
+ processed: title,
117
+ alternatives: titleAlternatives,
118
+ userOverride: null,
119
+ },
120
+ documentId: {
121
+ source: 'derived-from-title',
122
+ generated: documentId,
123
+ userOverride: null,
124
+ },
125
+ weight: {
126
+ source: weightSource,
127
+ detected: weight,
128
+ matchedKeyword,
129
+ rawWeightName: weightName,
130
+ userOverride: null,
131
+ },
132
+ weightName: {
133
+ source: weightNameSource,
134
+ detected: weightName,
135
+ userOverride: null,
136
+ },
137
+ style: {
138
+ source: styleSource,
139
+ detected: style,
140
+ reason: styleReason,
141
+ userOverride: null,
142
+ },
143
+ subfamily: {
144
+ source: subfamilySource,
145
+ detected: subfamily,
146
+ userOverride: null,
147
+ },
148
+ existingDocument: {
149
+ recommendation: RECOMMENDATION.CREATE,
150
+ exact: null,
151
+ candidates: [],
152
+ userChoice: null,
153
+ selectedCandidate: null,
154
+ lookupFailed: false,
155
+ },
156
+ };
157
+ }
158
+
159
+ /**
160
+ * Creates an initial empty UploadPlan.
161
+ * @param {object} settings
162
+ * @returns {object}
163
+ */
164
+ export function createEmptyPlan(settings = {}) {
165
+ return {
166
+ version: PLAN_VERSION,
167
+ settings: {
168
+ price: 0,
169
+ preserveShortenedNames: false,
170
+ preserveFileNames: false,
171
+ ...settings,
172
+ },
173
+ fonts: {},
174
+ subfamilyGroups: {},
175
+ phase: PLAN_PHASE.IDLE,
176
+ processingProgress: {
177
+ total: 0,
178
+ completed: 0,
179
+ failed: 0,
180
+ currentFile: null,
181
+ },
182
+ };
183
+ }