@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.
- package/LICENSE +21 -0
- package/README.md +695 -437
- package/dist/index.js +1628 -17701
- package/dist/index.mjs +1537 -17599
- package/package.json +83 -83
- package/src/components/BatchUploadFonts.jsx +655 -653
- package/src/components/BulkActions.jsx +99 -99
- package/src/components/ExistingDocumentResolver.jsx +152 -152
- package/src/components/FontReviewCard.jsx +455 -455
- package/src/components/FontScriptUploaderComponent.jsx +463 -463
- package/src/components/GenerateCollectionsPairsComponent.jsx +259 -259
- package/src/components/KeyValueInput.jsx +95 -95
- package/src/components/KeyValueReferenceInput.jsx +267 -267
- package/src/components/NestedObjectArraySelector.jsx +146 -146
- package/src/components/PriceInput.jsx +26 -26
- package/src/components/PrimaryCollectionGeneratorTypeface.jsx +116 -116
- package/src/components/RegenerateSubfamiliesComponent.jsx +185 -185
- package/src/components/SetOTF.jsx +87 -87
- package/src/components/SingleUploaderTool.jsx +674 -674
- package/src/components/StatusDisplay.jsx +26 -26
- package/src/components/StyleCountInput.jsx +16 -16
- package/src/components/UpdateScriptsComponent.jsx +76 -76
- package/src/components/UploadButton.jsx +43 -43
- package/src/components/UploadModal.jsx +309 -304
- package/src/components/UploadScriptsComponent.jsx +539 -539
- package/src/components/UploadStep1Settings.jsx +272 -272
- package/src/components/UploadStep2Review.jsx +478 -474
- package/src/components/UploadStep3Execute.jsx +234 -234
- package/src/components/UploadStep3bInstances.jsx +396 -396
- package/src/components/UploadSummary.jsx +196 -196
- package/src/components/VariableInstanceReferencesInput.jsx +190 -190
- package/src/hooks/useNestedObjects.js +92 -92
- package/src/hooks/useSanityClient.js +9 -9
- package/src/index.js +120 -120
- package/src/schema/openTypeField.js +1995 -1995
- package/src/schema/styleCountField.js +12 -12
- package/src/schema/stylesField.js +302 -302
- package/src/schema/stylisticSetField.js +301 -301
- package/src/utils/buildUploadPlan.js +326 -326
- package/src/utils/executeUploadPlan.js +430 -430
- package/src/utils/executionReducer.js +56 -56
- package/src/utils/fontHelpers.js +281 -267
- package/src/utils/generateCssFile.js +207 -207
- package/src/utils/generateFontData.js +98 -98
- package/src/utils/generateFontFile.js +38 -38
- package/src/utils/generateKeywords.js +185 -185
- package/src/utils/generateSubset.js +45 -45
- package/src/utils/getEmptyFontKit.js +101 -101
- package/src/utils/parseFont.js +56 -56
- package/src/utils/parseVariableFontInstances.js +301 -301
- package/src/utils/planReducer.js +531 -517
- package/src/utils/planTypes.js +183 -183
- package/src/utils/processFontFiles.js +530 -530
- package/src/utils/regenerateFontData.js +146 -146
- package/src/utils/resolveExistingFont.js +87 -87
- package/src/utils/retitleFontEntries.js +154 -0
- package/src/utils/sanitizeForSanityId.js +65 -65
- package/src/utils/setupDecompressors.js +27 -27
- package/src/utils/updateFontPrices.js +94 -94
- package/src/utils/updateTypefaceDocument.js +162 -162
- package/src/utils/uploadFontFiles.js +405 -405
- package/src/utils/utils.js +24 -24
|
@@ -1,65 +1,65 @@
|
|
|
1
|
-
// Converts arbitrary strings into valid Sanity document IDs (lowercase, hyphens, no special characters)
|
|
2
|
-
import slugify from 'slugify';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Sanitizes a string into a valid Sanity document ID.
|
|
6
|
-
*
|
|
7
|
-
* Sanity ID requirements:
|
|
8
|
-
* - Must start with a letter or underscore (not a number or hyphen)
|
|
9
|
-
* - Can only contain lowercase letters (a-z), numbers (0-9), hyphens (-), and underscores (_)
|
|
10
|
-
* - Must be between 1 and 128 characters
|
|
11
|
-
*
|
|
12
|
-
* @param {string} str - The raw string (e.g. font title or filename) to sanitize
|
|
13
|
-
* @returns {string} A valid Sanity document ID
|
|
14
|
-
*/
|
|
15
|
-
export function sanitizeForSanityId(str) {
|
|
16
|
-
if (!str || typeof str !== 'string') {
|
|
17
|
-
return 'font-' + Date.now();
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
let sanitized = str.toLowerCase().trim();
|
|
21
|
-
|
|
22
|
-
// Replace common symbols before slugify
|
|
23
|
-
sanitized = sanitized.replace(/\+/g, 'plus');
|
|
24
|
-
sanitized = sanitized.replace(/&/g, 'and');
|
|
25
|
-
sanitized = sanitized.replace(/@/g, 'at');
|
|
26
|
-
|
|
27
|
-
sanitized = slugify(sanitized, {
|
|
28
|
-
replacement: '-',
|
|
29
|
-
remove: /[^\w\s-]/g,
|
|
30
|
-
lower: true,
|
|
31
|
-
strict: true,
|
|
32
|
-
locale: 'en',
|
|
33
|
-
trim: true,
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
// Strip any characters that still aren't lowercase-alphanumeric, hyphens, or underscores
|
|
37
|
-
sanitized = sanitized.replace(/[^a-z0-9\-_]/g, '-');
|
|
38
|
-
|
|
39
|
-
// Collapse repeated hyphens and strip leading/trailing hyphens or underscores
|
|
40
|
-
sanitized = sanitized.replace(/-+/g, '-');
|
|
41
|
-
sanitized = sanitized.replace(/^[-_]+|[-_]+$/g, '');
|
|
42
|
-
|
|
43
|
-
// IDs must not start with a number or hyphen
|
|
44
|
-
if (sanitized && !/^[a-z_]/.test(sanitized)) {
|
|
45
|
-
sanitized = 'font_' + sanitized;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
if (!sanitized) {
|
|
49
|
-
sanitized = 'font_' + Date.now();
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// Sanity hard-caps IDs at 128 characters
|
|
53
|
-
if (sanitized.length > 128) {
|
|
54
|
-
const hash = Math.random().toString(36).substring(2, 8);
|
|
55
|
-
sanitized = sanitized.substring(0, 120) + '_' + hash;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// Paranoid final validation
|
|
59
|
-
if (!/^[a-z_][a-z0-9\-_]*$/.test(sanitized)) {
|
|
60
|
-
console.warn(`ID sanitization produced invalid result: "${sanitized}", using fallback`);
|
|
61
|
-
sanitized = 'font_' + Date.now();
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return sanitized;
|
|
65
|
-
}
|
|
1
|
+
// Converts arbitrary strings into valid Sanity document IDs (lowercase, hyphens, no special characters)
|
|
2
|
+
import slugify from 'slugify';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Sanitizes a string into a valid Sanity document ID.
|
|
6
|
+
*
|
|
7
|
+
* Sanity ID requirements:
|
|
8
|
+
* - Must start with a letter or underscore (not a number or hyphen)
|
|
9
|
+
* - Can only contain lowercase letters (a-z), numbers (0-9), hyphens (-), and underscores (_)
|
|
10
|
+
* - Must be between 1 and 128 characters
|
|
11
|
+
*
|
|
12
|
+
* @param {string} str - The raw string (e.g. font title or filename) to sanitize
|
|
13
|
+
* @returns {string} A valid Sanity document ID
|
|
14
|
+
*/
|
|
15
|
+
export function sanitizeForSanityId(str) {
|
|
16
|
+
if (!str || typeof str !== 'string') {
|
|
17
|
+
return 'font-' + Date.now();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
let sanitized = str.toLowerCase().trim();
|
|
21
|
+
|
|
22
|
+
// Replace common symbols before slugify
|
|
23
|
+
sanitized = sanitized.replace(/\+/g, 'plus');
|
|
24
|
+
sanitized = sanitized.replace(/&/g, 'and');
|
|
25
|
+
sanitized = sanitized.replace(/@/g, 'at');
|
|
26
|
+
|
|
27
|
+
sanitized = slugify(sanitized, {
|
|
28
|
+
replacement: '-',
|
|
29
|
+
remove: /[^\w\s-]/g,
|
|
30
|
+
lower: true,
|
|
31
|
+
strict: true,
|
|
32
|
+
locale: 'en',
|
|
33
|
+
trim: true,
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// Strip any characters that still aren't lowercase-alphanumeric, hyphens, or underscores
|
|
37
|
+
sanitized = sanitized.replace(/[^a-z0-9\-_]/g, '-');
|
|
38
|
+
|
|
39
|
+
// Collapse repeated hyphens and strip leading/trailing hyphens or underscores
|
|
40
|
+
sanitized = sanitized.replace(/-+/g, '-');
|
|
41
|
+
sanitized = sanitized.replace(/^[-_]+|[-_]+$/g, '');
|
|
42
|
+
|
|
43
|
+
// IDs must not start with a number or hyphen
|
|
44
|
+
if (sanitized && !/^[a-z_]/.test(sanitized)) {
|
|
45
|
+
sanitized = 'font_' + sanitized;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (!sanitized) {
|
|
49
|
+
sanitized = 'font_' + Date.now();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Sanity hard-caps IDs at 128 characters
|
|
53
|
+
if (sanitized.length > 128) {
|
|
54
|
+
const hash = Math.random().toString(36).substring(2, 8);
|
|
55
|
+
sanitized = sanitized.substring(0, 120) + '_' + hash;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Paranoid final validation
|
|
59
|
+
if (!/^[a-z_][a-z0-9\-_]*$/.test(sanitized)) {
|
|
60
|
+
console.warn(`ID sanitization produced invalid result: "${sanitized}", using fallback`);
|
|
61
|
+
sanitized = 'font_' + Date.now();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return sanitized;
|
|
65
|
+
}
|
|
@@ -1,27 +1,27 @@
|
|
|
1
|
-
// Sets up globalThis.pako and globalThis.unbrotli for lib-font WOFF/WOFF2 decompression.
|
|
2
|
-
// Must be imported before lib-font.
|
|
3
|
-
|
|
4
|
-
import pako from 'pako';
|
|
5
|
-
|
|
6
|
-
// Set pako for WOFF (zlib) decompression
|
|
7
|
-
globalThis.pako = pako;
|
|
8
|
-
|
|
9
|
-
// Set unbrotli for WOFF2 (brotli) decompression
|
|
10
|
-
// The vendor unbrotli.js UMD sets globalThis.unbrotli in browser contexts.
|
|
11
|
-
// In Node/bundler contexts it exports via module.exports instead.
|
|
12
|
-
// We use a side-effect import and then check if the global was set.
|
|
13
|
-
import '../vendor/unbrotli.js';
|
|
14
|
-
|
|
15
|
-
// If the UMD didn't set the global (CJS path in Node/bundler), try to require it
|
|
16
|
-
if (!globalThis.unbrotli) {
|
|
17
|
-
try {
|
|
18
|
-
// In bundler context, the UMD file's module.exports is available
|
|
19
|
-
// tsup will resolve this at build time
|
|
20
|
-
const brotli = require('../vendor/unbrotli.js');
|
|
21
|
-
if (typeof brotli === 'function') {
|
|
22
|
-
globalThis.unbrotli = brotli;
|
|
23
|
-
}
|
|
24
|
-
} catch {
|
|
25
|
-
// Silently fail — WOFF2 parsing will error gracefully
|
|
26
|
-
}
|
|
27
|
-
}
|
|
1
|
+
// Sets up globalThis.pako and globalThis.unbrotli for lib-font WOFF/WOFF2 decompression.
|
|
2
|
+
// Must be imported before lib-font.
|
|
3
|
+
|
|
4
|
+
import pako from 'pako';
|
|
5
|
+
|
|
6
|
+
// Set pako for WOFF (zlib) decompression
|
|
7
|
+
globalThis.pako = pako;
|
|
8
|
+
|
|
9
|
+
// Set unbrotli for WOFF2 (brotli) decompression
|
|
10
|
+
// The vendor unbrotli.js UMD sets globalThis.unbrotli in browser contexts.
|
|
11
|
+
// In Node/bundler contexts it exports via module.exports instead.
|
|
12
|
+
// We use a side-effect import and then check if the global was set.
|
|
13
|
+
import '../vendor/unbrotli.js';
|
|
14
|
+
|
|
15
|
+
// If the UMD didn't set the global (CJS path in Node/bundler), try to require it
|
|
16
|
+
if (!globalThis.unbrotli) {
|
|
17
|
+
try {
|
|
18
|
+
// In bundler context, the UMD file's module.exports is available
|
|
19
|
+
// tsup will resolve this at build time
|
|
20
|
+
const brotli = require('../vendor/unbrotli.js');
|
|
21
|
+
if (typeof brotli === 'function') {
|
|
22
|
+
globalThis.unbrotli = brotli;
|
|
23
|
+
}
|
|
24
|
+
} catch {
|
|
25
|
+
// Silently fail — WOFF2 parsing will error gracefully
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -1,94 +1,94 @@
|
|
|
1
|
-
// Bulk-updates the price field across all font documents linked to a typeface
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Sets the same price on every font document referenced by a typeface.
|
|
5
|
-
*
|
|
6
|
-
* @param {Object} params
|
|
7
|
-
* @param {Object} params.client - Sanity client
|
|
8
|
-
* @param {string} params.title - Typeface title
|
|
9
|
-
* @param {Object} params.slug - Typeface slug object
|
|
10
|
-
* @param {string} params.inputPrice - New price value (will be coerced to Number)
|
|
11
|
-
* @param {string} params.doc_id - Document ID (used to detect draft state)
|
|
12
|
-
* @param {Function} params.setStatus
|
|
13
|
-
* @param {Function} params.setError
|
|
14
|
-
* @returns {Promise<Object>}
|
|
15
|
-
*/
|
|
16
|
-
export const updateFontPrices = async ({
|
|
17
|
-
client,
|
|
18
|
-
title,
|
|
19
|
-
slug,
|
|
20
|
-
inputPrice,
|
|
21
|
-
doc_id,
|
|
22
|
-
setStatus,
|
|
23
|
-
setError,
|
|
24
|
-
}) => {
|
|
25
|
-
try {
|
|
26
|
-
if (!title) {
|
|
27
|
-
setStatus('Typeface needs a title');
|
|
28
|
-
setError(true);
|
|
29
|
-
console.error('Typeface needs title');
|
|
30
|
-
return { success: false, message: 'Typeface needs title' };
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
if (!slug?.current) {
|
|
34
|
-
setStatus('Typeface needs a slug');
|
|
35
|
-
setError(true);
|
|
36
|
-
console.error('Typeface needs slug');
|
|
37
|
-
return { success: false, message: 'Typeface needs slug' };
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const price = Number(inputPrice);
|
|
41
|
-
if (isNaN(price)) {
|
|
42
|
-
setStatus('Invalid price value');
|
|
43
|
-
setError(true);
|
|
44
|
-
console.error('Invalid price value');
|
|
45
|
-
return { success: false, message: 'Invalid price value' };
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
setStatus('Fetching typeface document...');
|
|
49
|
-
const typeface = await client.fetch(
|
|
50
|
-
`*[_type == "typeface" && slug.current == $slug][0]`,
|
|
51
|
-
{ slug: slug.current }
|
|
52
|
-
);
|
|
53
|
-
|
|
54
|
-
if (!typeface) {
|
|
55
|
-
setStatus('Typeface not found');
|
|
56
|
-
setError(true);
|
|
57
|
-
console.error('Typeface not found');
|
|
58
|
-
return { success: false, message: 'Typeface not found' };
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
if (!typeface.styles?.fonts?.length) {
|
|
62
|
-
setStatus('No fonts found in typeface');
|
|
63
|
-
setError(true);
|
|
64
|
-
console.error('No fonts found in typeface');
|
|
65
|
-
return { success: false, message: 'No fonts found in typeface' };
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const fontRefs = typeface.styles.fonts;
|
|
69
|
-
setStatus(`Updating prices for ${fontRefs.length} fonts...`);
|
|
70
|
-
|
|
71
|
-
let updatedCount = 0;
|
|
72
|
-
for (let i = 0; i < fontRefs.length; i++) {
|
|
73
|
-
try {
|
|
74
|
-
await client.patch(fontRefs[i]._ref).set({ price, sell: price > 0 }).commit();
|
|
75
|
-
updatedCount++;
|
|
76
|
-
setStatus(`Updated ${updatedCount}/${fontRefs.length} fonts...`);
|
|
77
|
-
} catch (err) {
|
|
78
|
-
console.error(`Error updating font ${fontRefs[i]._ref}:`, err);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
const successMessage = `Successfully updated prices for ${updatedCount} fonts to $${price}`;
|
|
83
|
-
setStatus(successMessage);
|
|
84
|
-
console.log(successMessage);
|
|
85
|
-
|
|
86
|
-
return { success: true, message: successMessage, updatedCount };
|
|
87
|
-
} catch (err) {
|
|
88
|
-
const errorMessage = `Error: ${err.message}`;
|
|
89
|
-
console.error('Error updating font prices:', err);
|
|
90
|
-
setError(true);
|
|
91
|
-
setStatus(errorMessage);
|
|
92
|
-
return { success: false, message: errorMessage };
|
|
93
|
-
}
|
|
94
|
-
};
|
|
1
|
+
// Bulk-updates the price field across all font documents linked to a typeface
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Sets the same price on every font document referenced by a typeface.
|
|
5
|
+
*
|
|
6
|
+
* @param {Object} params
|
|
7
|
+
* @param {Object} params.client - Sanity client
|
|
8
|
+
* @param {string} params.title - Typeface title
|
|
9
|
+
* @param {Object} params.slug - Typeface slug object
|
|
10
|
+
* @param {string} params.inputPrice - New price value (will be coerced to Number)
|
|
11
|
+
* @param {string} params.doc_id - Document ID (used to detect draft state)
|
|
12
|
+
* @param {Function} params.setStatus
|
|
13
|
+
* @param {Function} params.setError
|
|
14
|
+
* @returns {Promise<Object>}
|
|
15
|
+
*/
|
|
16
|
+
export const updateFontPrices = async ({
|
|
17
|
+
client,
|
|
18
|
+
title,
|
|
19
|
+
slug,
|
|
20
|
+
inputPrice,
|
|
21
|
+
doc_id,
|
|
22
|
+
setStatus,
|
|
23
|
+
setError,
|
|
24
|
+
}) => {
|
|
25
|
+
try {
|
|
26
|
+
if (!title) {
|
|
27
|
+
setStatus('Typeface needs a title');
|
|
28
|
+
setError(true);
|
|
29
|
+
console.error('Typeface needs title');
|
|
30
|
+
return { success: false, message: 'Typeface needs title' };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (!slug?.current) {
|
|
34
|
+
setStatus('Typeface needs a slug');
|
|
35
|
+
setError(true);
|
|
36
|
+
console.error('Typeface needs slug');
|
|
37
|
+
return { success: false, message: 'Typeface needs slug' };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const price = Number(inputPrice);
|
|
41
|
+
if (isNaN(price)) {
|
|
42
|
+
setStatus('Invalid price value');
|
|
43
|
+
setError(true);
|
|
44
|
+
console.error('Invalid price value');
|
|
45
|
+
return { success: false, message: 'Invalid price value' };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
setStatus('Fetching typeface document...');
|
|
49
|
+
const typeface = await client.fetch(
|
|
50
|
+
`*[_type == "typeface" && slug.current == $slug][0]`,
|
|
51
|
+
{ slug: slug.current }
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
if (!typeface) {
|
|
55
|
+
setStatus('Typeface not found');
|
|
56
|
+
setError(true);
|
|
57
|
+
console.error('Typeface not found');
|
|
58
|
+
return { success: false, message: 'Typeface not found' };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (!typeface.styles?.fonts?.length) {
|
|
62
|
+
setStatus('No fonts found in typeface');
|
|
63
|
+
setError(true);
|
|
64
|
+
console.error('No fonts found in typeface');
|
|
65
|
+
return { success: false, message: 'No fonts found in typeface' };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const fontRefs = typeface.styles.fonts;
|
|
69
|
+
setStatus(`Updating prices for ${fontRefs.length} fonts...`);
|
|
70
|
+
|
|
71
|
+
let updatedCount = 0;
|
|
72
|
+
for (let i = 0; i < fontRefs.length; i++) {
|
|
73
|
+
try {
|
|
74
|
+
await client.patch(fontRefs[i]._ref).set({ price, sell: price > 0 }).commit();
|
|
75
|
+
updatedCount++;
|
|
76
|
+
setStatus(`Updated ${updatedCount}/${fontRefs.length} fonts...`);
|
|
77
|
+
} catch (err) {
|
|
78
|
+
console.error(`Error updating font ${fontRefs[i]._ref}:`, err);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const successMessage = `Successfully updated prices for ${updatedCount} fonts to $${price}`;
|
|
83
|
+
setStatus(successMessage);
|
|
84
|
+
console.log(successMessage);
|
|
85
|
+
|
|
86
|
+
return { success: true, message: successMessage, updatedCount };
|
|
87
|
+
} catch (err) {
|
|
88
|
+
const errorMessage = `Error: ${err.message}`;
|
|
89
|
+
console.error('Error updating font prices:', err);
|
|
90
|
+
setError(true);
|
|
91
|
+
setStatus(errorMessage);
|
|
92
|
+
return { success: false, message: errorMessage };
|
|
93
|
+
}
|
|
94
|
+
};
|