@liiift-studio/sanity-font-manager 2.5.9 → 2.7.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.
- package/dist/index.js +163 -21
- package/dist/index.mjs +163 -21
- package/package.json +1 -1
- package/src/components/BatchUploadFonts.jsx +6 -4
- package/src/components/UploadModal.jsx +6 -1
- package/src/components/UploadStep2Review.jsx +5 -1
- package/src/schema/stylesField.js +14 -0
- package/src/utils/fontHelpers.js +15 -1
- package/src/utils/planReducer.js +15 -1
- package/src/utils/retitleFontEntries.js +154 -0
package/dist/index.js
CHANGED
|
@@ -12536,7 +12536,18 @@ function getCharacterSet(font2) {
|
|
|
12536
12536
|
const cmap2 = (_b = (_a = font2.opentype) == null ? void 0 : _a.tables) == null ? void 0 : _b.cmap;
|
|
12537
12537
|
if (!cmap2) return [];
|
|
12538
12538
|
try {
|
|
12539
|
-
|
|
12539
|
+
const raw = cmap2.getSupportedCharCodes(3, 1);
|
|
12540
|
+
if (!Array.isArray(raw) || raw.length === 0) return [];
|
|
12541
|
+
if (typeof raw[0] === "object" && raw[0].start !== void 0) {
|
|
12542
|
+
const codes = [];
|
|
12543
|
+
for (const range of raw) {
|
|
12544
|
+
for (let i = range.start; i <= range.end; i++) {
|
|
12545
|
+
codes.push(i);
|
|
12546
|
+
}
|
|
12547
|
+
}
|
|
12548
|
+
return codes;
|
|
12549
|
+
}
|
|
12550
|
+
return raw;
|
|
12540
12551
|
} catch {
|
|
12541
12552
|
return [];
|
|
12542
12553
|
}
|
|
@@ -12599,6 +12610,8 @@ function getFontMetadata(font2) {
|
|
|
12599
12610
|
fullName: getNameString(font2, 4),
|
|
12600
12611
|
familyName: getNameString(font2, 1),
|
|
12601
12612
|
subfamilyName: getNameString(font2, 2),
|
|
12613
|
+
preferredFamily: getNameString(font2, 16),
|
|
12614
|
+
preferredSubfamily: getNameString(font2, 17),
|
|
12602
12615
|
copyright: getNameString(font2, 0),
|
|
12603
12616
|
version: getNameString(font2, 5),
|
|
12604
12617
|
genDate: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -13888,6 +13901,102 @@ var init_planTypes = __esm({
|
|
|
13888
13901
|
}
|
|
13889
13902
|
});
|
|
13890
13903
|
|
|
13904
|
+
// src/utils/retitleFontEntries.js
|
|
13905
|
+
function retitleFontEntry(entry, preserveShortenedNames, typefaceTitle) {
|
|
13906
|
+
var _a, _b, _c, _d;
|
|
13907
|
+
if (!entry || !entry.parsedMetadata) return entry;
|
|
13908
|
+
if ((_b = (_a = entry.decisions) == null ? void 0 : _a.title) == null ? void 0 : _b.userOverride) return entry;
|
|
13909
|
+
const meta2 = entry.parsedMetadata;
|
|
13910
|
+
const fullName = meta2.fullName || "";
|
|
13911
|
+
const familyName = meta2.familyName || "";
|
|
13912
|
+
const trimmedTitle = (typefaceTitle || "").trim();
|
|
13913
|
+
let weightName = ((_d = (_c = entry.decisions) == null ? void 0 : _c.weightName) == null ? void 0 : _d.detected) || entry.weightName || "";
|
|
13914
|
+
if (!preserveShortenedNames) {
|
|
13915
|
+
weightName = expandAbbreviations(weightName);
|
|
13916
|
+
}
|
|
13917
|
+
const nameId4Remainder = fullName ? fullName.replace(trimmedTitle, "").trim() : "";
|
|
13918
|
+
const nameId1Remainder = familyName ? familyName.replace(trimmedTitle, "").trim() : "";
|
|
13919
|
+
let subfamilyName = nameId4Remainder || nameId1Remainder;
|
|
13920
|
+
if (!preserveShortenedNames) {
|
|
13921
|
+
subfamilyName = expandAbbreviations(subfamilyName);
|
|
13922
|
+
}
|
|
13923
|
+
subfamilyName = processSubfamilyName(subfamilyName, WEIGHT_KW, ITALIC_KW, preserveShortenedNames);
|
|
13924
|
+
subfamilyName = subfamilyName.replace(/\b(Italic|Slant|Slanted|Oblique|Backslant|Roman|Upright)\b/gi, "").replace(/\s+/g, " ").trim();
|
|
13925
|
+
if (subfamilyName !== "") {
|
|
13926
|
+
weightName = weightName.replace(`${subfamilyName} `, "").replace(` ${subfamilyName}`, "").trim();
|
|
13927
|
+
}
|
|
13928
|
+
let fontTitle = fullName.trim() || "";
|
|
13929
|
+
fontTitle = formatFontTitle(fontTitle, preserveShortenedNames);
|
|
13930
|
+
if (entry.variableFont) {
|
|
13931
|
+
if (!fontTitle.toLowerCase().includes("vf")) {
|
|
13932
|
+
fontTitle = fontTitle + " VF";
|
|
13933
|
+
}
|
|
13934
|
+
subfamilyName = "";
|
|
13935
|
+
}
|
|
13936
|
+
if (!(entry.variableFont && fontTitle.toLowerCase().includes("italic"))) {
|
|
13937
|
+
const italicKW = entry.parsedMetadata.italicAngle !== 0 ? "Italic" : "";
|
|
13938
|
+
fontTitle = addItalicToFontTitle(
|
|
13939
|
+
{ opentype: { tables: { post: { italicAngle: entry.parsedMetadata.italicAngle || 0 } } } },
|
|
13940
|
+
fontTitle,
|
|
13941
|
+
italicKW,
|
|
13942
|
+
entry.style,
|
|
13943
|
+
preserveShortenedNames
|
|
13944
|
+
);
|
|
13945
|
+
}
|
|
13946
|
+
const documentId = sanitizeForSanityId(fontTitle);
|
|
13947
|
+
if (entry.title === fontTitle && entry.documentId === documentId && entry.weightName === weightName && entry.subfamily === (entry.variableFont ? "" : subfamilyName || "Regular")) {
|
|
13948
|
+
return entry;
|
|
13949
|
+
}
|
|
13950
|
+
const finalSubfamily = entry.variableFont ? "" : subfamilyName || "Regular";
|
|
13951
|
+
return {
|
|
13952
|
+
...entry,
|
|
13953
|
+
title: fontTitle,
|
|
13954
|
+
documentId,
|
|
13955
|
+
weightName,
|
|
13956
|
+
subfamily: finalSubfamily,
|
|
13957
|
+
decisions: {
|
|
13958
|
+
...entry.decisions,
|
|
13959
|
+
title: { ...entry.decisions.title, processed: fontTitle },
|
|
13960
|
+
documentId: { ...entry.decisions.documentId, generated: documentId },
|
|
13961
|
+
weightName: { ...entry.decisions.weightName, detected: weightName },
|
|
13962
|
+
subfamily: { ...entry.decisions.subfamily, detected: finalSubfamily }
|
|
13963
|
+
}
|
|
13964
|
+
};
|
|
13965
|
+
}
|
|
13966
|
+
function retitleAllFonts(fonts, preserveShortenedNames, typefaceTitle) {
|
|
13967
|
+
const updated = {};
|
|
13968
|
+
for (const [tempId, entry] of Object.entries(fonts)) {
|
|
13969
|
+
updated[tempId] = retitleFontEntry(entry, preserveShortenedNames, typefaceTitle);
|
|
13970
|
+
}
|
|
13971
|
+
const idMap = {};
|
|
13972
|
+
for (const [tempId, font2] of Object.entries(updated)) {
|
|
13973
|
+
updated[tempId] = { ...font2, _idConflict: false };
|
|
13974
|
+
const docId = font2.documentId;
|
|
13975
|
+
if (!idMap[docId]) {
|
|
13976
|
+
idMap[docId] = [tempId];
|
|
13977
|
+
} else {
|
|
13978
|
+
idMap[docId].push(tempId);
|
|
13979
|
+
}
|
|
13980
|
+
}
|
|
13981
|
+
for (const ids of Object.values(idMap)) {
|
|
13982
|
+
if (ids.length > 1) {
|
|
13983
|
+
for (const id of ids) {
|
|
13984
|
+
updated[id] = { ...updated[id], _idConflict: true };
|
|
13985
|
+
}
|
|
13986
|
+
}
|
|
13987
|
+
}
|
|
13988
|
+
return updated;
|
|
13989
|
+
}
|
|
13990
|
+
var WEIGHT_KW, ITALIC_KW;
|
|
13991
|
+
var init_retitleFontEntries = __esm({
|
|
13992
|
+
"src/utils/retitleFontEntries.js"() {
|
|
13993
|
+
init_generateKeywords();
|
|
13994
|
+
init_processFontFiles();
|
|
13995
|
+
init_sanitizeForSanityId();
|
|
13996
|
+
({ weightKeywordList: WEIGHT_KW, italicKeywordList: ITALIC_KW } = generateStyleKeywords());
|
|
13997
|
+
}
|
|
13998
|
+
});
|
|
13999
|
+
|
|
13891
14000
|
// src/utils/planReducer.js
|
|
13892
14001
|
function planReducer(state, action) {
|
|
13893
14002
|
switch (action.type) {
|
|
@@ -13920,7 +14029,16 @@ function planReducer(state, action) {
|
|
|
13920
14029
|
console.warn("SET_SETTINGS blocked \u2014 settings locked during processing/execution");
|
|
13921
14030
|
return state;
|
|
13922
14031
|
}
|
|
13923
|
-
|
|
14032
|
+
const newSettings = { ...state.settings, ...action.settings };
|
|
14033
|
+
let newState = { ...state, settings: newSettings };
|
|
14034
|
+
const shortenedChanged = "preserveShortenedNames" in action.settings && action.settings.preserveShortenedNames !== state.settings.preserveShortenedNames;
|
|
14035
|
+
if (shortenedChanged && Object.keys(state.fonts).length > 0) {
|
|
14036
|
+
const typefaceTitle = action.typefaceTitle || state.settings.typefaceTitle || "";
|
|
14037
|
+
const retitled = retitleAllFonts(state.fonts, newSettings.preserveShortenedNames, typefaceTitle);
|
|
14038
|
+
const subfamilyGroups = rebuildSubfamilyGroups(retitled);
|
|
14039
|
+
newState = { ...newState, fonts: retitled, subfamilyGroups };
|
|
14040
|
+
}
|
|
14041
|
+
return newState;
|
|
13924
14042
|
}
|
|
13925
14043
|
// ---------------------------------------------------------------
|
|
13926
14044
|
// Processing (Phase 1) — dispatched by buildUploadPlan callbacks
|
|
@@ -14303,6 +14421,7 @@ var init_planReducer = __esm({
|
|
|
14303
14421
|
"src/utils/planReducer.js"() {
|
|
14304
14422
|
init_planTypes();
|
|
14305
14423
|
init_sanitizeForSanityId();
|
|
14424
|
+
init_retitleFontEntries();
|
|
14306
14425
|
VALID_TRANSITIONS = {
|
|
14307
14426
|
[PLAN_PHASE.IDLE]: [PLAN_PHASE.PROCESSING],
|
|
14308
14427
|
[PLAN_PHASE.PROCESSING]: [PLAN_PHASE.REVIEWING],
|
|
@@ -15504,8 +15623,13 @@ function UploadStep2Review({
|
|
|
15504
15623
|
{
|
|
15505
15624
|
checked: localPreserveShortenedNames,
|
|
15506
15625
|
onChange: (e) => {
|
|
15626
|
+
var _a2;
|
|
15507
15627
|
setLocalPreserveShortenedNames(e.target.checked);
|
|
15508
|
-
dispatch({
|
|
15628
|
+
dispatch({
|
|
15629
|
+
type: "SET_SETTINGS",
|
|
15630
|
+
settings: { preserveShortenedNames: e.target.checked },
|
|
15631
|
+
typefaceTitle: ((_a2 = plan.settings) == null ? void 0 : _a2.typefaceTitle) || ""
|
|
15632
|
+
});
|
|
15509
15633
|
}
|
|
15510
15634
|
}
|
|
15511
15635
|
), /* @__PURE__ */ import_react9.default.createElement(
|
|
@@ -16562,9 +16686,14 @@ function UploadModal({
|
|
|
16562
16686
|
typefaceTitle,
|
|
16563
16687
|
stylesObject,
|
|
16564
16688
|
preferredStyleRef,
|
|
16565
|
-
slug
|
|
16689
|
+
slug,
|
|
16690
|
+
defaults = {}
|
|
16566
16691
|
}) {
|
|
16567
|
-
const [plan, dispatch] = (0, import_react13.useReducer)(planReducer, null, () => createEmptyPlan(
|
|
16692
|
+
const [plan, dispatch] = (0, import_react13.useReducer)(planReducer, null, () => createEmptyPlan({
|
|
16693
|
+
price: defaults.price ?? 0,
|
|
16694
|
+
preserveShortenedNames: defaults.preserveShortenedNames ?? false,
|
|
16695
|
+
preserveFileNames: defaults.preserveFileNames ?? false
|
|
16696
|
+
}));
|
|
16568
16697
|
const [processingCancelled, setProcessingCancelled] = (0, import_react13.useState)(false);
|
|
16569
16698
|
const [executionResult, setExecutionResult] = (0, import_react13.useState)(null);
|
|
16570
16699
|
const [retryTempIds, setRetryTempIds] = (0, import_react13.useState)(null);
|
|
@@ -17500,13 +17629,15 @@ var formatElapsed2 = (s) => {
|
|
|
17500
17629
|
const sec = s % 60;
|
|
17501
17630
|
return m > 0 ? `${m}m ${sec}s` : `${sec}s`;
|
|
17502
17631
|
};
|
|
17503
|
-
var BatchUploadFonts = () => {
|
|
17632
|
+
var BatchUploadFonts = (props) => {
|
|
17633
|
+
var _a, _b;
|
|
17634
|
+
const defaults = ((_b = (_a = props == null ? void 0 : props.schemaType) == null ? void 0 : _a.options) == null ? void 0 : _b.defaults) || {};
|
|
17504
17635
|
const [status, setStatus] = (0, import_react14.useState)("ready");
|
|
17505
17636
|
const [ready, setReady] = (0, import_react14.useState)(true);
|
|
17506
|
-
const [inputPrice, setInputPrice] = (0, import_react14.useState)("0");
|
|
17637
|
+
const [inputPrice, setInputPrice] = (0, import_react14.useState)(String(defaults.price ?? "0"));
|
|
17507
17638
|
const [error, setError] = (0, import_react14.useState)(false);
|
|
17508
|
-
const [preserveShortenedNames, setPreserveShortenedNames] = (0, import_react14.useState)(true);
|
|
17509
|
-
const [preserveFileNames, setPreserveFileNames] = (0, import_react14.useState)(false);
|
|
17639
|
+
const [preserveShortenedNames, setPreserveShortenedNames] = (0, import_react14.useState)(defaults.preserveShortenedNames ?? true);
|
|
17640
|
+
const [preserveFileNames, setPreserveFileNames] = (0, import_react14.useState)(defaults.preserveFileNames ?? false);
|
|
17510
17641
|
const [showUtilities, setShowUtilities] = (0, import_react14.useState)(false);
|
|
17511
17642
|
const [pendingFiles, setPendingFiles] = (0, import_react14.useState)([]);
|
|
17512
17643
|
const [isDragging, setIsDragging] = (0, import_react14.useState)(false);
|
|
@@ -17545,9 +17676,9 @@ var BatchUploadFonts = () => {
|
|
|
17545
17676
|
}
|
|
17546
17677
|
}, [ready]);
|
|
17547
17678
|
(0, import_react14.useEffect)(() => {
|
|
17548
|
-
var
|
|
17679
|
+
var _a2;
|
|
17549
17680
|
if (ready !== true) {
|
|
17550
|
-
(
|
|
17681
|
+
(_a2 = navigator.wakeLock) == null ? void 0 : _a2.request("screen").then((lock) => {
|
|
17551
17682
|
wakeLockRef.current = lock;
|
|
17552
17683
|
}).catch(() => {
|
|
17553
17684
|
});
|
|
@@ -17588,8 +17719,8 @@ var BatchUploadFonts = () => {
|
|
|
17588
17719
|
files: failedFiles,
|
|
17589
17720
|
names: failedFiles.map((f) => f.name),
|
|
17590
17721
|
metadata: failedFiles.map((f) => {
|
|
17591
|
-
var
|
|
17592
|
-
return (
|
|
17722
|
+
var _a2, _b2;
|
|
17723
|
+
return (_b2 = (_a2 = f == null ? void 0 : f.fk) == null ? void 0 : _a2.name) == null ? void 0 : _b2.records;
|
|
17593
17724
|
})
|
|
17594
17725
|
});
|
|
17595
17726
|
setError2(true);
|
|
@@ -17714,7 +17845,7 @@ var BatchUploadFonts = () => {
|
|
|
17714
17845
|
setReady(true);
|
|
17715
17846
|
}, [title, slug, client, doc_id, inputPrice]);
|
|
17716
17847
|
const handleRegenerateCssFiles = (0, import_react14.useCallback)(async () => {
|
|
17717
|
-
var
|
|
17848
|
+
var _a2, _b2, _c, _d;
|
|
17718
17849
|
try {
|
|
17719
17850
|
setStatus("Regenerating CSS files...");
|
|
17720
17851
|
setReady("css");
|
|
@@ -17741,7 +17872,7 @@ var BatchUploadFonts = () => {
|
|
|
17741
17872
|
setReady(true);
|
|
17742
17873
|
return false;
|
|
17743
17874
|
}
|
|
17744
|
-
if (!((
|
|
17875
|
+
if (!((_b2 = (_a2 = typeface.styles) == null ? void 0 : _a2.fonts) == null ? void 0 : _b2.length)) {
|
|
17745
17876
|
setStatus("No fonts found in typeface");
|
|
17746
17877
|
setError(true);
|
|
17747
17878
|
setReady(true);
|
|
@@ -17851,8 +17982,8 @@ var BatchUploadFonts = () => {
|
|
|
17851
17982
|
padding: 2,
|
|
17852
17983
|
text: "Browse files",
|
|
17853
17984
|
onClick: () => {
|
|
17854
|
-
var
|
|
17855
|
-
return (
|
|
17985
|
+
var _a2;
|
|
17986
|
+
return (_a2 = fileInputRef.current) == null ? void 0 : _a2.click();
|
|
17856
17987
|
}
|
|
17857
17988
|
}
|
|
17858
17989
|
)))
|
|
@@ -17924,8 +18055,8 @@ var BatchUploadFonts = () => {
|
|
|
17924
18055
|
padding: 1,
|
|
17925
18056
|
text: "browse",
|
|
17926
18057
|
onClick: () => {
|
|
17927
|
-
var
|
|
17928
|
-
return (
|
|
18058
|
+
var _a2;
|
|
18059
|
+
return (_a2 = fileInputRef.current) == null ? void 0 : _a2.click();
|
|
17929
18060
|
}
|
|
17930
18061
|
}
|
|
17931
18062
|
))
|
|
@@ -17982,7 +18113,8 @@ var BatchUploadFonts = () => {
|
|
|
17982
18113
|
typefaceTitle: title,
|
|
17983
18114
|
stylesObject,
|
|
17984
18115
|
preferredStyleRef,
|
|
17985
|
-
slug
|
|
18116
|
+
slug,
|
|
18117
|
+
defaults
|
|
17986
18118
|
}
|
|
17987
18119
|
)), showUtilities && /* @__PURE__ */ import_react14.default.createElement(import_ui13.Card, { border: true, padding: 3, shadow: 1, radius: 2, marginTop: 3 }, /* @__PURE__ */ import_react14.default.createElement(import_ui13.Stack, { space: 4 }, /* @__PURE__ */ import_react14.default.createElement(import_ui13.Stack, { space: 2 }, /* @__PURE__ */ import_react14.default.createElement(import_ui13.Text, { size: 1, weight: "semibold", style: { lineHeight: 1.6 } }, "Regenerate Subfamilies"), /* @__PURE__ */ import_react14.default.createElement(RegenerateSubfamiliesComponent, null)), /* @__PURE__ */ import_react14.default.createElement(import_ui13.Stack, { space: 3 }, /* @__PURE__ */ import_react14.default.createElement(import_ui13.Text, { size: 1, weight: "semibold", style: { lineHeight: 1.6 } }, "Rename Fonts (name table, Full Name)"), /* @__PURE__ */ import_react14.default.createElement(import_ui13.Flex, { align: "center", gap: 2 }, /* @__PURE__ */ import_react14.default.createElement(
|
|
17988
18120
|
import_ui13.Switch,
|
|
@@ -22805,6 +22937,14 @@ var stylisticSetField = {
|
|
|
22805
22937
|
// src/schema/stylesField.js
|
|
22806
22938
|
var import_react30 = __toESM(require("react"));
|
|
22807
22939
|
var import_sanity_advanced_reference_array = require("@liiift-studio/sanity-advanced-reference-array");
|
|
22940
|
+
var inlineStyleCountField = {
|
|
22941
|
+
name: "styleCount",
|
|
22942
|
+
type: "number",
|
|
22943
|
+
components: {
|
|
22944
|
+
input: StyleCountInput
|
|
22945
|
+
},
|
|
22946
|
+
readOnly: true
|
|
22947
|
+
};
|
|
22808
22948
|
var typefaceParams = (doc) => ({ typefaceName: (doc == null ? void 0 : doc.title) || "" });
|
|
22809
22949
|
var FontsRefArray = (props) => import_react30.default.createElement(import_sanity_advanced_reference_array.AdvancedRefArray, {
|
|
22810
22950
|
...props,
|
|
@@ -22865,7 +23005,8 @@ function createStylesField({
|
|
|
22865
23005
|
subfamilyPreview = false,
|
|
22866
23006
|
pairs = true,
|
|
22867
23007
|
generateCollections = false,
|
|
22868
|
-
generateFullFamilyCollection = false
|
|
23008
|
+
generateFullFamilyCollection = false,
|
|
23009
|
+
styleCount = false
|
|
22869
23010
|
} = {}) {
|
|
22870
23011
|
const subfamilyFields = [
|
|
22871
23012
|
{
|
|
@@ -22965,6 +23106,7 @@ function createStylesField({
|
|
|
22965
23106
|
initialValue: false,
|
|
22966
23107
|
description: "Check if this typeface includes serif letterforms. Used for typeface overview serif/sans filters. Frontend automatically treats non-serif typefaces as sans serif."
|
|
22967
23108
|
}),
|
|
23109
|
+
...field(styleCount, inlineStyleCountField),
|
|
22968
23110
|
{
|
|
22969
23111
|
title: "Fonts",
|
|
22970
23112
|
name: "fonts",
|
package/dist/index.mjs
CHANGED
|
@@ -12541,7 +12541,18 @@ function getCharacterSet(font2) {
|
|
|
12541
12541
|
const cmap2 = (_b = (_a = font2.opentype) == null ? void 0 : _a.tables) == null ? void 0 : _b.cmap;
|
|
12542
12542
|
if (!cmap2) return [];
|
|
12543
12543
|
try {
|
|
12544
|
-
|
|
12544
|
+
const raw = cmap2.getSupportedCharCodes(3, 1);
|
|
12545
|
+
if (!Array.isArray(raw) || raw.length === 0) return [];
|
|
12546
|
+
if (typeof raw[0] === "object" && raw[0].start !== void 0) {
|
|
12547
|
+
const codes = [];
|
|
12548
|
+
for (const range of raw) {
|
|
12549
|
+
for (let i = range.start; i <= range.end; i++) {
|
|
12550
|
+
codes.push(i);
|
|
12551
|
+
}
|
|
12552
|
+
}
|
|
12553
|
+
return codes;
|
|
12554
|
+
}
|
|
12555
|
+
return raw;
|
|
12545
12556
|
} catch {
|
|
12546
12557
|
return [];
|
|
12547
12558
|
}
|
|
@@ -12604,6 +12615,8 @@ function getFontMetadata(font2) {
|
|
|
12604
12615
|
fullName: getNameString(font2, 4),
|
|
12605
12616
|
familyName: getNameString(font2, 1),
|
|
12606
12617
|
subfamilyName: getNameString(font2, 2),
|
|
12618
|
+
preferredFamily: getNameString(font2, 16),
|
|
12619
|
+
preferredSubfamily: getNameString(font2, 17),
|
|
12607
12620
|
copyright: getNameString(font2, 0),
|
|
12608
12621
|
version: getNameString(font2, 5),
|
|
12609
12622
|
genDate: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -13892,6 +13905,102 @@ var init_planTypes = __esm({
|
|
|
13892
13905
|
}
|
|
13893
13906
|
});
|
|
13894
13907
|
|
|
13908
|
+
// src/utils/retitleFontEntries.js
|
|
13909
|
+
function retitleFontEntry(entry, preserveShortenedNames, typefaceTitle) {
|
|
13910
|
+
var _a, _b, _c, _d;
|
|
13911
|
+
if (!entry || !entry.parsedMetadata) return entry;
|
|
13912
|
+
if ((_b = (_a = entry.decisions) == null ? void 0 : _a.title) == null ? void 0 : _b.userOverride) return entry;
|
|
13913
|
+
const meta2 = entry.parsedMetadata;
|
|
13914
|
+
const fullName = meta2.fullName || "";
|
|
13915
|
+
const familyName = meta2.familyName || "";
|
|
13916
|
+
const trimmedTitle = (typefaceTitle || "").trim();
|
|
13917
|
+
let weightName = ((_d = (_c = entry.decisions) == null ? void 0 : _c.weightName) == null ? void 0 : _d.detected) || entry.weightName || "";
|
|
13918
|
+
if (!preserveShortenedNames) {
|
|
13919
|
+
weightName = expandAbbreviations(weightName);
|
|
13920
|
+
}
|
|
13921
|
+
const nameId4Remainder = fullName ? fullName.replace(trimmedTitle, "").trim() : "";
|
|
13922
|
+
const nameId1Remainder = familyName ? familyName.replace(trimmedTitle, "").trim() : "";
|
|
13923
|
+
let subfamilyName = nameId4Remainder || nameId1Remainder;
|
|
13924
|
+
if (!preserveShortenedNames) {
|
|
13925
|
+
subfamilyName = expandAbbreviations(subfamilyName);
|
|
13926
|
+
}
|
|
13927
|
+
subfamilyName = processSubfamilyName(subfamilyName, WEIGHT_KW, ITALIC_KW, preserveShortenedNames);
|
|
13928
|
+
subfamilyName = subfamilyName.replace(/\b(Italic|Slant|Slanted|Oblique|Backslant|Roman|Upright)\b/gi, "").replace(/\s+/g, " ").trim();
|
|
13929
|
+
if (subfamilyName !== "") {
|
|
13930
|
+
weightName = weightName.replace(`${subfamilyName} `, "").replace(` ${subfamilyName}`, "").trim();
|
|
13931
|
+
}
|
|
13932
|
+
let fontTitle = fullName.trim() || "";
|
|
13933
|
+
fontTitle = formatFontTitle(fontTitle, preserveShortenedNames);
|
|
13934
|
+
if (entry.variableFont) {
|
|
13935
|
+
if (!fontTitle.toLowerCase().includes("vf")) {
|
|
13936
|
+
fontTitle = fontTitle + " VF";
|
|
13937
|
+
}
|
|
13938
|
+
subfamilyName = "";
|
|
13939
|
+
}
|
|
13940
|
+
if (!(entry.variableFont && fontTitle.toLowerCase().includes("italic"))) {
|
|
13941
|
+
const italicKW = entry.parsedMetadata.italicAngle !== 0 ? "Italic" : "";
|
|
13942
|
+
fontTitle = addItalicToFontTitle(
|
|
13943
|
+
{ opentype: { tables: { post: { italicAngle: entry.parsedMetadata.italicAngle || 0 } } } },
|
|
13944
|
+
fontTitle,
|
|
13945
|
+
italicKW,
|
|
13946
|
+
entry.style,
|
|
13947
|
+
preserveShortenedNames
|
|
13948
|
+
);
|
|
13949
|
+
}
|
|
13950
|
+
const documentId = sanitizeForSanityId(fontTitle);
|
|
13951
|
+
if (entry.title === fontTitle && entry.documentId === documentId && entry.weightName === weightName && entry.subfamily === (entry.variableFont ? "" : subfamilyName || "Regular")) {
|
|
13952
|
+
return entry;
|
|
13953
|
+
}
|
|
13954
|
+
const finalSubfamily = entry.variableFont ? "" : subfamilyName || "Regular";
|
|
13955
|
+
return {
|
|
13956
|
+
...entry,
|
|
13957
|
+
title: fontTitle,
|
|
13958
|
+
documentId,
|
|
13959
|
+
weightName,
|
|
13960
|
+
subfamily: finalSubfamily,
|
|
13961
|
+
decisions: {
|
|
13962
|
+
...entry.decisions,
|
|
13963
|
+
title: { ...entry.decisions.title, processed: fontTitle },
|
|
13964
|
+
documentId: { ...entry.decisions.documentId, generated: documentId },
|
|
13965
|
+
weightName: { ...entry.decisions.weightName, detected: weightName },
|
|
13966
|
+
subfamily: { ...entry.decisions.subfamily, detected: finalSubfamily }
|
|
13967
|
+
}
|
|
13968
|
+
};
|
|
13969
|
+
}
|
|
13970
|
+
function retitleAllFonts(fonts, preserveShortenedNames, typefaceTitle) {
|
|
13971
|
+
const updated = {};
|
|
13972
|
+
for (const [tempId, entry] of Object.entries(fonts)) {
|
|
13973
|
+
updated[tempId] = retitleFontEntry(entry, preserveShortenedNames, typefaceTitle);
|
|
13974
|
+
}
|
|
13975
|
+
const idMap = {};
|
|
13976
|
+
for (const [tempId, font2] of Object.entries(updated)) {
|
|
13977
|
+
updated[tempId] = { ...font2, _idConflict: false };
|
|
13978
|
+
const docId = font2.documentId;
|
|
13979
|
+
if (!idMap[docId]) {
|
|
13980
|
+
idMap[docId] = [tempId];
|
|
13981
|
+
} else {
|
|
13982
|
+
idMap[docId].push(tempId);
|
|
13983
|
+
}
|
|
13984
|
+
}
|
|
13985
|
+
for (const ids of Object.values(idMap)) {
|
|
13986
|
+
if (ids.length > 1) {
|
|
13987
|
+
for (const id of ids) {
|
|
13988
|
+
updated[id] = { ...updated[id], _idConflict: true };
|
|
13989
|
+
}
|
|
13990
|
+
}
|
|
13991
|
+
}
|
|
13992
|
+
return updated;
|
|
13993
|
+
}
|
|
13994
|
+
var WEIGHT_KW, ITALIC_KW;
|
|
13995
|
+
var init_retitleFontEntries = __esm({
|
|
13996
|
+
"src/utils/retitleFontEntries.js"() {
|
|
13997
|
+
init_generateKeywords();
|
|
13998
|
+
init_processFontFiles();
|
|
13999
|
+
init_sanitizeForSanityId();
|
|
14000
|
+
({ weightKeywordList: WEIGHT_KW, italicKeywordList: ITALIC_KW } = generateStyleKeywords());
|
|
14001
|
+
}
|
|
14002
|
+
});
|
|
14003
|
+
|
|
13895
14004
|
// src/utils/planReducer.js
|
|
13896
14005
|
function planReducer(state, action) {
|
|
13897
14006
|
switch (action.type) {
|
|
@@ -13924,7 +14033,16 @@ function planReducer(state, action) {
|
|
|
13924
14033
|
console.warn("SET_SETTINGS blocked \u2014 settings locked during processing/execution");
|
|
13925
14034
|
return state;
|
|
13926
14035
|
}
|
|
13927
|
-
|
|
14036
|
+
const newSettings = { ...state.settings, ...action.settings };
|
|
14037
|
+
let newState = { ...state, settings: newSettings };
|
|
14038
|
+
const shortenedChanged = "preserveShortenedNames" in action.settings && action.settings.preserveShortenedNames !== state.settings.preserveShortenedNames;
|
|
14039
|
+
if (shortenedChanged && Object.keys(state.fonts).length > 0) {
|
|
14040
|
+
const typefaceTitle = action.typefaceTitle || state.settings.typefaceTitle || "";
|
|
14041
|
+
const retitled = retitleAllFonts(state.fonts, newSettings.preserveShortenedNames, typefaceTitle);
|
|
14042
|
+
const subfamilyGroups = rebuildSubfamilyGroups(retitled);
|
|
14043
|
+
newState = { ...newState, fonts: retitled, subfamilyGroups };
|
|
14044
|
+
}
|
|
14045
|
+
return newState;
|
|
13928
14046
|
}
|
|
13929
14047
|
// ---------------------------------------------------------------
|
|
13930
14048
|
// Processing (Phase 1) — dispatched by buildUploadPlan callbacks
|
|
@@ -14307,6 +14425,7 @@ var init_planReducer = __esm({
|
|
|
14307
14425
|
"src/utils/planReducer.js"() {
|
|
14308
14426
|
init_planTypes();
|
|
14309
14427
|
init_sanitizeForSanityId();
|
|
14428
|
+
init_retitleFontEntries();
|
|
14310
14429
|
VALID_TRANSITIONS = {
|
|
14311
14430
|
[PLAN_PHASE.IDLE]: [PLAN_PHASE.PROCESSING],
|
|
14312
14431
|
[PLAN_PHASE.PROCESSING]: [PLAN_PHASE.REVIEWING],
|
|
@@ -15508,8 +15627,13 @@ function UploadStep2Review({
|
|
|
15508
15627
|
{
|
|
15509
15628
|
checked: localPreserveShortenedNames,
|
|
15510
15629
|
onChange: (e) => {
|
|
15630
|
+
var _a2;
|
|
15511
15631
|
setLocalPreserveShortenedNames(e.target.checked);
|
|
15512
|
-
dispatch({
|
|
15632
|
+
dispatch({
|
|
15633
|
+
type: "SET_SETTINGS",
|
|
15634
|
+
settings: { preserveShortenedNames: e.target.checked },
|
|
15635
|
+
typefaceTitle: ((_a2 = plan.settings) == null ? void 0 : _a2.typefaceTitle) || ""
|
|
15636
|
+
});
|
|
15513
15637
|
}
|
|
15514
15638
|
}
|
|
15515
15639
|
), /* @__PURE__ */ React8.createElement(
|
|
@@ -16561,9 +16685,14 @@ function UploadModal({
|
|
|
16561
16685
|
typefaceTitle,
|
|
16562
16686
|
stylesObject,
|
|
16563
16687
|
preferredStyleRef,
|
|
16564
|
-
slug
|
|
16688
|
+
slug,
|
|
16689
|
+
defaults = {}
|
|
16565
16690
|
}) {
|
|
16566
|
-
const [plan, dispatch] = useReducer2(planReducer, null, () => createEmptyPlan(
|
|
16691
|
+
const [plan, dispatch] = useReducer2(planReducer, null, () => createEmptyPlan({
|
|
16692
|
+
price: defaults.price ?? 0,
|
|
16693
|
+
preserveShortenedNames: defaults.preserveShortenedNames ?? false,
|
|
16694
|
+
preserveFileNames: defaults.preserveFileNames ?? false
|
|
16695
|
+
}));
|
|
16567
16696
|
const [processingCancelled, setProcessingCancelled] = useState8(false);
|
|
16568
16697
|
const [executionResult, setExecutionResult] = useState8(null);
|
|
16569
16698
|
const [retryTempIds, setRetryTempIds] = useState8(null);
|
|
@@ -17399,13 +17528,15 @@ var formatElapsed2 = (s) => {
|
|
|
17399
17528
|
const sec = s % 60;
|
|
17400
17529
|
return m > 0 ? `${m}m ${sec}s` : `${sec}s`;
|
|
17401
17530
|
};
|
|
17402
|
-
var BatchUploadFonts = () => {
|
|
17531
|
+
var BatchUploadFonts = (props) => {
|
|
17532
|
+
var _a, _b;
|
|
17533
|
+
const defaults = ((_b = (_a = props == null ? void 0 : props.schemaType) == null ? void 0 : _a.options) == null ? void 0 : _b.defaults) || {};
|
|
17403
17534
|
const [status, setStatus] = useState9("ready");
|
|
17404
17535
|
const [ready, setReady] = useState9(true);
|
|
17405
|
-
const [inputPrice, setInputPrice] = useState9("0");
|
|
17536
|
+
const [inputPrice, setInputPrice] = useState9(String(defaults.price ?? "0"));
|
|
17406
17537
|
const [error, setError] = useState9(false);
|
|
17407
|
-
const [preserveShortenedNames, setPreserveShortenedNames] = useState9(true);
|
|
17408
|
-
const [preserveFileNames, setPreserveFileNames] = useState9(false);
|
|
17538
|
+
const [preserveShortenedNames, setPreserveShortenedNames] = useState9(defaults.preserveShortenedNames ?? true);
|
|
17539
|
+
const [preserveFileNames, setPreserveFileNames] = useState9(defaults.preserveFileNames ?? false);
|
|
17409
17540
|
const [showUtilities, setShowUtilities] = useState9(false);
|
|
17410
17541
|
const [pendingFiles, setPendingFiles] = useState9([]);
|
|
17411
17542
|
const [isDragging, setIsDragging] = useState9(false);
|
|
@@ -17444,9 +17575,9 @@ var BatchUploadFonts = () => {
|
|
|
17444
17575
|
}
|
|
17445
17576
|
}, [ready]);
|
|
17446
17577
|
useEffect6(() => {
|
|
17447
|
-
var
|
|
17578
|
+
var _a2;
|
|
17448
17579
|
if (ready !== true) {
|
|
17449
|
-
(
|
|
17580
|
+
(_a2 = navigator.wakeLock) == null ? void 0 : _a2.request("screen").then((lock) => {
|
|
17450
17581
|
wakeLockRef.current = lock;
|
|
17451
17582
|
}).catch(() => {
|
|
17452
17583
|
});
|
|
@@ -17487,8 +17618,8 @@ var BatchUploadFonts = () => {
|
|
|
17487
17618
|
files: failedFiles,
|
|
17488
17619
|
names: failedFiles.map((f) => f.name),
|
|
17489
17620
|
metadata: failedFiles.map((f) => {
|
|
17490
|
-
var
|
|
17491
|
-
return (
|
|
17621
|
+
var _a2, _b2;
|
|
17622
|
+
return (_b2 = (_a2 = f == null ? void 0 : f.fk) == null ? void 0 : _a2.name) == null ? void 0 : _b2.records;
|
|
17492
17623
|
})
|
|
17493
17624
|
});
|
|
17494
17625
|
setError2(true);
|
|
@@ -17613,7 +17744,7 @@ var BatchUploadFonts = () => {
|
|
|
17613
17744
|
setReady(true);
|
|
17614
17745
|
}, [title, slug, client, doc_id, inputPrice]);
|
|
17615
17746
|
const handleRegenerateCssFiles = useCallback7(async () => {
|
|
17616
|
-
var
|
|
17747
|
+
var _a2, _b2, _c, _d;
|
|
17617
17748
|
try {
|
|
17618
17749
|
setStatus("Regenerating CSS files...");
|
|
17619
17750
|
setReady("css");
|
|
@@ -17640,7 +17771,7 @@ var BatchUploadFonts = () => {
|
|
|
17640
17771
|
setReady(true);
|
|
17641
17772
|
return false;
|
|
17642
17773
|
}
|
|
17643
|
-
if (!((
|
|
17774
|
+
if (!((_b2 = (_a2 = typeface.styles) == null ? void 0 : _a2.fonts) == null ? void 0 : _b2.length)) {
|
|
17644
17775
|
setStatus("No fonts found in typeface");
|
|
17645
17776
|
setError(true);
|
|
17646
17777
|
setReady(true);
|
|
@@ -17750,8 +17881,8 @@ var BatchUploadFonts = () => {
|
|
|
17750
17881
|
padding: 2,
|
|
17751
17882
|
text: "Browse files",
|
|
17752
17883
|
onClick: () => {
|
|
17753
|
-
var
|
|
17754
|
-
return (
|
|
17884
|
+
var _a2;
|
|
17885
|
+
return (_a2 = fileInputRef.current) == null ? void 0 : _a2.click();
|
|
17755
17886
|
}
|
|
17756
17887
|
}
|
|
17757
17888
|
)))
|
|
@@ -17823,8 +17954,8 @@ var BatchUploadFonts = () => {
|
|
|
17823
17954
|
padding: 1,
|
|
17824
17955
|
text: "browse",
|
|
17825
17956
|
onClick: () => {
|
|
17826
|
-
var
|
|
17827
|
-
return (
|
|
17957
|
+
var _a2;
|
|
17958
|
+
return (_a2 = fileInputRef.current) == null ? void 0 : _a2.click();
|
|
17828
17959
|
}
|
|
17829
17960
|
}
|
|
17830
17961
|
))
|
|
@@ -17881,7 +18012,8 @@ var BatchUploadFonts = () => {
|
|
|
17881
18012
|
typefaceTitle: title,
|
|
17882
18013
|
stylesObject,
|
|
17883
18014
|
preferredStyleRef,
|
|
17884
|
-
slug
|
|
18015
|
+
slug,
|
|
18016
|
+
defaults
|
|
17885
18017
|
}
|
|
17886
18018
|
)), showUtilities && /* @__PURE__ */ React13.createElement(Card8, { border: true, padding: 3, shadow: 1, radius: 2, marginTop: 3 }, /* @__PURE__ */ React13.createElement(Stack9, { space: 4 }, /* @__PURE__ */ React13.createElement(Stack9, { space: 2 }, /* @__PURE__ */ React13.createElement(Text13, { size: 1, weight: "semibold", style: { lineHeight: 1.6 } }, "Regenerate Subfamilies"), /* @__PURE__ */ React13.createElement(RegenerateSubfamiliesComponent, null)), /* @__PURE__ */ React13.createElement(Stack9, { space: 3 }, /* @__PURE__ */ React13.createElement(Text13, { size: 1, weight: "semibold", style: { lineHeight: 1.6 } }, "Rename Fonts (name table, Full Name)"), /* @__PURE__ */ React13.createElement(Flex12, { align: "center", gap: 2 }, /* @__PURE__ */ React13.createElement(
|
|
17887
18019
|
Switch3,
|
|
@@ -22704,6 +22836,14 @@ var stylisticSetField = {
|
|
|
22704
22836
|
// src/schema/stylesField.js
|
|
22705
22837
|
import React28 from "react";
|
|
22706
22838
|
import { AdvancedRefArray } from "@liiift-studio/sanity-advanced-reference-array";
|
|
22839
|
+
var inlineStyleCountField = {
|
|
22840
|
+
name: "styleCount",
|
|
22841
|
+
type: "number",
|
|
22842
|
+
components: {
|
|
22843
|
+
input: StyleCountInput
|
|
22844
|
+
},
|
|
22845
|
+
readOnly: true
|
|
22846
|
+
};
|
|
22707
22847
|
var typefaceParams = (doc) => ({ typefaceName: (doc == null ? void 0 : doc.title) || "" });
|
|
22708
22848
|
var FontsRefArray = (props) => React28.createElement(AdvancedRefArray, {
|
|
22709
22849
|
...props,
|
|
@@ -22764,7 +22904,8 @@ function createStylesField({
|
|
|
22764
22904
|
subfamilyPreview = false,
|
|
22765
22905
|
pairs = true,
|
|
22766
22906
|
generateCollections = false,
|
|
22767
|
-
generateFullFamilyCollection = false
|
|
22907
|
+
generateFullFamilyCollection = false,
|
|
22908
|
+
styleCount = false
|
|
22768
22909
|
} = {}) {
|
|
22769
22910
|
const subfamilyFields = [
|
|
22770
22911
|
{
|
|
@@ -22864,6 +23005,7 @@ function createStylesField({
|
|
|
22864
23005
|
initialValue: false,
|
|
22865
23006
|
description: "Check if this typeface includes serif letterforms. Used for typeface overview serif/sans filters. Frontend automatically treats non-serif typefaces as sans serif."
|
|
22866
23007
|
}),
|
|
23008
|
+
...field(styleCount, inlineStyleCountField),
|
|
22867
23009
|
{
|
|
22868
23010
|
title: "Fonts",
|
|
22869
23011
|
name: "fonts",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@liiift-studio/sanity-font-manager",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.7.0",
|
|
4
4
|
"description": "Sanity Studio plugin — full font management suite with batch upload, format conversion, metadata extraction, CSS generation, collection/pair generation, and script variant support. Supports Sanity v3, v4, and v5.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Liiift Studio",
|
|
@@ -30,13 +30,14 @@ const formatElapsed = (s) => {
|
|
|
30
30
|
return m > 0 ? `${m}m ${sec}s` : `${sec}s`;
|
|
31
31
|
};
|
|
32
32
|
|
|
33
|
-
export const BatchUploadFonts = () => {
|
|
33
|
+
export const BatchUploadFonts = (props) => {
|
|
34
|
+
const defaults = props?.schemaType?.options?.defaults || {};
|
|
34
35
|
const [status, setStatus] = useState('ready');
|
|
35
36
|
const [ready, setReady] = useState(true);
|
|
36
|
-
const [inputPrice, setInputPrice] = useState('0');
|
|
37
|
+
const [inputPrice, setInputPrice] = useState(String(defaults.price ?? '0'));
|
|
37
38
|
const [error, setError] = useState(false);
|
|
38
|
-
const [preserveShortenedNames, setPreserveShortenedNames] = useState(true);
|
|
39
|
-
const [preserveFileNames, setPreserveFileNames] = useState(false);
|
|
39
|
+
const [preserveShortenedNames, setPreserveShortenedNames] = useState(defaults.preserveShortenedNames ?? true);
|
|
40
|
+
const [preserveFileNames, setPreserveFileNames] = useState(defaults.preserveFileNames ?? false);
|
|
40
41
|
const [showUtilities, setShowUtilities] = useState(false);
|
|
41
42
|
const [pendingFiles, setPendingFiles] = useState([]);
|
|
42
43
|
const [isDragging, setIsDragging] = useState(false);
|
|
@@ -587,6 +588,7 @@ export const BatchUploadFonts = () => {
|
|
|
587
588
|
stylesObject={stylesObject}
|
|
588
589
|
preferredStyleRef={preferredStyleRef}
|
|
589
590
|
slug={slug}
|
|
591
|
+
defaults={defaults}
|
|
590
592
|
/>
|
|
591
593
|
</Suspense>
|
|
592
594
|
)}
|
|
@@ -46,8 +46,13 @@ export default function UploadModal({
|
|
|
46
46
|
stylesObject,
|
|
47
47
|
preferredStyleRef,
|
|
48
48
|
slug,
|
|
49
|
+
defaults = {},
|
|
49
50
|
}) {
|
|
50
|
-
const [plan, dispatch] = useReducer(planReducer, null, () => createEmptyPlan(
|
|
51
|
+
const [plan, dispatch] = useReducer(planReducer, null, () => createEmptyPlan({
|
|
52
|
+
price: defaults.price ?? 0,
|
|
53
|
+
preserveShortenedNames: defaults.preserveShortenedNames ?? false,
|
|
54
|
+
preserveFileNames: defaults.preserveFileNames ?? false,
|
|
55
|
+
}));
|
|
51
56
|
const [processingCancelled, setProcessingCancelled] = useState(false);
|
|
52
57
|
const [executionResult, setExecutionResult] = useState(null);
|
|
53
58
|
const [retryTempIds, setRetryTempIds] = useState(null);
|
|
@@ -313,7 +313,11 @@ export default function UploadStep2Review({
|
|
|
313
313
|
checked={localPreserveShortenedNames}
|
|
314
314
|
onChange={(e) => {
|
|
315
315
|
setLocalPreserveShortenedNames(e.target.checked);
|
|
316
|
-
dispatch({
|
|
316
|
+
dispatch({
|
|
317
|
+
type: 'SET_SETTINGS',
|
|
318
|
+
settings: { preserveShortenedNames: e.target.checked },
|
|
319
|
+
typefaceTitle: plan.settings?.typefaceTitle || '',
|
|
320
|
+
});
|
|
317
321
|
}}
|
|
318
322
|
/>
|
|
319
323
|
<Tooltip
|
|
@@ -4,6 +4,17 @@ import { AdvancedRefArray } from '@liiift-studio/sanity-advanced-reference-array
|
|
|
4
4
|
import { RegenerateSubfamiliesComponent } from '../components/RegenerateSubfamiliesComponent.jsx';
|
|
5
5
|
import { GenerateCollectionsPairsComponent } from '../components/GenerateCollectionsPairsComponent.jsx';
|
|
6
6
|
import { PrimaryCollectionGeneratorTypeface } from '../components/PrimaryCollectionGeneratorTypeface.jsx';
|
|
7
|
+
import { StyleCountInput } from '../components/StyleCountInput.jsx';
|
|
8
|
+
|
|
9
|
+
// Inline style count field — injected into the styles object when styleCount option is true
|
|
10
|
+
const inlineStyleCountField = {
|
|
11
|
+
name: 'styleCount',
|
|
12
|
+
type: 'number',
|
|
13
|
+
components: {
|
|
14
|
+
input: StyleCountInput,
|
|
15
|
+
},
|
|
16
|
+
readOnly: true,
|
|
17
|
+
};
|
|
7
18
|
|
|
8
19
|
// Returns extra GROQ params scoped to the current typeface document
|
|
9
20
|
const typefaceParams = (doc) => ({ typefaceName: doc?.title || '' });
|
|
@@ -80,6 +91,7 @@ const subfamilyPreferredStyleFilter = async ({ getClient, document, parent }) =>
|
|
|
80
91
|
* @param {boolean} [options.subfamilyFontFilter=false] - Filter subfamily font picker to typeface fonts only
|
|
81
92
|
* @param {boolean} [options.subfamilyPreview=false] - Include preview on subfamily array items
|
|
82
93
|
* @param {boolean} [options.pairs=true] - Show pairs array to editors
|
|
94
|
+
* @param {boolean} [options.styleCount=false] - Include style count display above fonts array
|
|
83
95
|
*/
|
|
84
96
|
export function createStylesField({
|
|
85
97
|
free = false,
|
|
@@ -97,6 +109,7 @@ export function createStylesField({
|
|
|
97
109
|
pairs = true,
|
|
98
110
|
generateCollections = false,
|
|
99
111
|
generateFullFamilyCollection = false,
|
|
112
|
+
styleCount = false,
|
|
100
113
|
} = {}) {
|
|
101
114
|
|
|
102
115
|
const subfamilyFields = [
|
|
@@ -199,6 +212,7 @@ export function createStylesField({
|
|
|
199
212
|
initialValue: false,
|
|
200
213
|
description: 'Check if this typeface includes serif letterforms. Used for typeface overview serif/sans filters. Frontend automatically treats non-serif typefaces as sans serif.',
|
|
201
214
|
}),
|
|
215
|
+
...field(styleCount, inlineStyleCountField),
|
|
202
216
|
{
|
|
203
217
|
title: 'Fonts',
|
|
204
218
|
name: 'fonts',
|
package/src/utils/fontHelpers.js
CHANGED
|
@@ -80,7 +80,19 @@ export function getCharacterSet(font) {
|
|
|
80
80
|
const cmap = font.opentype?.tables?.cmap;
|
|
81
81
|
if (!cmap) return [];
|
|
82
82
|
try {
|
|
83
|
-
|
|
83
|
+
const raw = cmap.getSupportedCharCodes(3, 1);
|
|
84
|
+
if (!Array.isArray(raw) || raw.length === 0) return [];
|
|
85
|
+
// getSupportedCharCodes may return range objects { start, end } — expand to individual codepoints
|
|
86
|
+
if (typeof raw[0] === 'object' && raw[0].start !== undefined) {
|
|
87
|
+
const codes = [];
|
|
88
|
+
for (const range of raw) {
|
|
89
|
+
for (let i = range.start; i <= range.end; i++) {
|
|
90
|
+
codes.push(i);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return codes;
|
|
94
|
+
}
|
|
95
|
+
return raw;
|
|
84
96
|
} catch {
|
|
85
97
|
return [];
|
|
86
98
|
}
|
|
@@ -175,6 +187,8 @@ export function getFontMetadata(font) {
|
|
|
175
187
|
fullName: getNameString(font, 4),
|
|
176
188
|
familyName: getNameString(font, 1),
|
|
177
189
|
subfamilyName: getNameString(font, 2),
|
|
190
|
+
preferredFamily: getNameString(font, 16),
|
|
191
|
+
preferredSubfamily: getNameString(font, 17),
|
|
178
192
|
copyright: getNameString(font, 0),
|
|
179
193
|
version: getNameString(font, 5),
|
|
180
194
|
genDate: new Date().toISOString(),
|
package/src/utils/planReducer.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { PLAN_PHASE, FONT_STATUS, PROCESSING_OWNED_FIELDS } from './planTypes';
|
|
4
4
|
import { sanitizeForSanityId } from './sanitizeForSanityId';
|
|
5
|
+
import { retitleAllFonts } from './retitleFontEntries';
|
|
5
6
|
|
|
6
7
|
/** Valid phase transitions — any phase can transition to 'idle' (reset) */
|
|
7
8
|
const VALID_TRANSITIONS = {
|
|
@@ -57,7 +58,20 @@ export function planReducer(state, action) {
|
|
|
57
58
|
console.warn('SET_SETTINGS blocked — settings locked during processing/execution');
|
|
58
59
|
return state;
|
|
59
60
|
}
|
|
60
|
-
|
|
61
|
+
const newSettings = { ...state.settings, ...action.settings };
|
|
62
|
+
let newState = { ...state, settings: newSettings };
|
|
63
|
+
|
|
64
|
+
// Retitle fonts when preserveShortenedNames changes during review
|
|
65
|
+
const shortenedChanged = 'preserveShortenedNames' in action.settings
|
|
66
|
+
&& action.settings.preserveShortenedNames !== state.settings.preserveShortenedNames;
|
|
67
|
+
if (shortenedChanged && Object.keys(state.fonts).length > 0) {
|
|
68
|
+
const typefaceTitle = action.typefaceTitle || state.settings.typefaceTitle || '';
|
|
69
|
+
const retitled = retitleAllFonts(state.fonts, newSettings.preserveShortenedNames, typefaceTitle);
|
|
70
|
+
const subfamilyGroups = rebuildSubfamilyGroups(retitled);
|
|
71
|
+
newState = { ...newState, fonts: retitled, subfamilyGroups };
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return newState;
|
|
61
75
|
}
|
|
62
76
|
|
|
63
77
|
// ---------------------------------------------------------------
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
// Lightweight retitle pass — re-derives title, documentId, weightName, and
|
|
2
|
+
// subfamilyName from cached parsedMetadata when preserveShortenedNames changes.
|
|
3
|
+
// No fontkit parsing, no file I/O, no Sanity queries — pure string manipulation.
|
|
4
|
+
|
|
5
|
+
import { expandAbbreviations, generateStyleKeywords } from './generateKeywords.js';
|
|
6
|
+
import { formatFontTitle, addItalicToFontTitle, processSubfamilyName } from './processFontFiles.js';
|
|
7
|
+
import { sanitizeForSanityId } from './sanitizeForSanityId.js';
|
|
8
|
+
|
|
9
|
+
const { weightKeywordList: WEIGHT_KW, italicKeywordList: ITALIC_KW } = generateStyleKeywords();
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Re-derive title, documentId, weightName, and subfamilyName for a single
|
|
13
|
+
* font entry using cached metadata and the new preserveShortenedNames setting.
|
|
14
|
+
* Skips entries with user overrides on title (user edits take precedence).
|
|
15
|
+
*
|
|
16
|
+
* @param {object} entry - Font plan entry with parsedMetadata and decisions
|
|
17
|
+
* @param {boolean} preserveShortenedNames - New setting value
|
|
18
|
+
* @param {string} typefaceTitle - Parent typeface title (for prefix stripping)
|
|
19
|
+
* @returns {object} Updated entry (shallow copy if changed, same ref if unchanged)
|
|
20
|
+
*/
|
|
21
|
+
export function retitleFontEntry(entry, preserveShortenedNames, typefaceTitle) {
|
|
22
|
+
if (!entry || !entry.parsedMetadata) return entry;
|
|
23
|
+
|
|
24
|
+
// Skip entries with user overrides on title — user edits take precedence
|
|
25
|
+
if (entry.decisions?.title?.userOverride) return entry;
|
|
26
|
+
|
|
27
|
+
const meta = entry.parsedMetadata;
|
|
28
|
+
const fullName = meta.fullName || '';
|
|
29
|
+
const familyName = meta.familyName || '';
|
|
30
|
+
const trimmedTitle = (typefaceTitle || '').trim();
|
|
31
|
+
|
|
32
|
+
// Re-derive weightName from the original detected value
|
|
33
|
+
let weightName = entry.decisions?.weightName?.detected || entry.weightName || '';
|
|
34
|
+
if (!preserveShortenedNames) {
|
|
35
|
+
weightName = expandAbbreviations(weightName);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Re-derive subfamilyName
|
|
39
|
+
const nameId4Remainder = fullName ? fullName.replace(trimmedTitle, '').trim() : '';
|
|
40
|
+
const nameId1Remainder = familyName ? familyName.replace(trimmedTitle, '').trim() : '';
|
|
41
|
+
let subfamilyName = nameId4Remainder || nameId1Remainder;
|
|
42
|
+
|
|
43
|
+
if (!preserveShortenedNames) {
|
|
44
|
+
subfamilyName = expandAbbreviations(subfamilyName);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
subfamilyName = processSubfamilyName(subfamilyName, WEIGHT_KW, ITALIC_KW, preserveShortenedNames);
|
|
48
|
+
|
|
49
|
+
// Strip style-only names from subfamily
|
|
50
|
+
subfamilyName = subfamilyName
|
|
51
|
+
.replace(/\b(Italic|Slant|Slanted|Oblique|Backslant|Roman|Upright)\b/gi, '')
|
|
52
|
+
.replace(/\s+/g, ' ')
|
|
53
|
+
.trim();
|
|
54
|
+
|
|
55
|
+
// Strip subfamily from weightName to avoid duplication
|
|
56
|
+
if (subfamilyName !== '') {
|
|
57
|
+
weightName = weightName
|
|
58
|
+
.replace(`${subfamilyName} `, '')
|
|
59
|
+
.replace(` ${subfamilyName}`, '')
|
|
60
|
+
.trim();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Re-derive title
|
|
64
|
+
let fontTitle = fullName.trim() || '';
|
|
65
|
+
fontTitle = formatFontTitle(fontTitle, preserveShortenedNames);
|
|
66
|
+
|
|
67
|
+
// Variable fonts get " VF" suffix
|
|
68
|
+
if (entry.variableFont) {
|
|
69
|
+
if (!fontTitle.toLowerCase().includes('vf')) {
|
|
70
|
+
fontTitle = fontTitle + ' VF';
|
|
71
|
+
}
|
|
72
|
+
subfamilyName = '';
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Add italic suffix if needed
|
|
76
|
+
if (!(entry.variableFont && fontTitle.toLowerCase().includes('italic'))) {
|
|
77
|
+
// Build a minimal font-like object for addItalicToFontTitle
|
|
78
|
+
const italicKW = entry.parsedMetadata.italicAngle !== 0 ? 'Italic' : '';
|
|
79
|
+
fontTitle = addItalicToFontTitle(
|
|
80
|
+
{ opentype: { tables: { post: { italicAngle: entry.parsedMetadata.italicAngle || 0 } } } },
|
|
81
|
+
fontTitle,
|
|
82
|
+
italicKW,
|
|
83
|
+
entry.style,
|
|
84
|
+
preserveShortenedNames
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const documentId = sanitizeForSanityId(fontTitle);
|
|
89
|
+
|
|
90
|
+
// Only return a new object if something actually changed
|
|
91
|
+
if (
|
|
92
|
+
entry.title === fontTitle &&
|
|
93
|
+
entry.documentId === documentId &&
|
|
94
|
+
entry.weightName === weightName &&
|
|
95
|
+
entry.subfamily === (entry.variableFont ? '' : (subfamilyName || 'Regular'))
|
|
96
|
+
) {
|
|
97
|
+
return entry;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const finalSubfamily = entry.variableFont ? '' : (subfamilyName || 'Regular');
|
|
101
|
+
|
|
102
|
+
return {
|
|
103
|
+
...entry,
|
|
104
|
+
title: fontTitle,
|
|
105
|
+
documentId,
|
|
106
|
+
weightName,
|
|
107
|
+
subfamily: finalSubfamily,
|
|
108
|
+
decisions: {
|
|
109
|
+
...entry.decisions,
|
|
110
|
+
title: { ...entry.decisions.title, processed: fontTitle },
|
|
111
|
+
documentId: { ...entry.decisions.documentId, generated: documentId },
|
|
112
|
+
weightName: { ...entry.decisions.weightName, detected: weightName },
|
|
113
|
+
subfamily: { ...entry.decisions.subfamily, detected: finalSubfamily },
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Retitle all font entries in a plan. Returns a new fonts map.
|
|
120
|
+
* Runs collision detection after retitling.
|
|
121
|
+
*
|
|
122
|
+
* @param {object} fonts - plan.fonts map
|
|
123
|
+
* @param {boolean} preserveShortenedNames
|
|
124
|
+
* @param {string} typefaceTitle
|
|
125
|
+
* @returns {object} New fonts map with updated titles and _idConflict flags
|
|
126
|
+
*/
|
|
127
|
+
export function retitleAllFonts(fonts, preserveShortenedNames, typefaceTitle) {
|
|
128
|
+
const updated = {};
|
|
129
|
+
for (const [tempId, entry] of Object.entries(fonts)) {
|
|
130
|
+
updated[tempId] = retitleFontEntry(entry, preserveShortenedNames, typefaceTitle);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Run collision detection
|
|
134
|
+
const idMap = {};
|
|
135
|
+
for (const [tempId, font] of Object.entries(updated)) {
|
|
136
|
+
updated[tempId] = { ...font, _idConflict: false };
|
|
137
|
+
const docId = font.documentId;
|
|
138
|
+
if (!idMap[docId]) {
|
|
139
|
+
idMap[docId] = [tempId];
|
|
140
|
+
} else {
|
|
141
|
+
idMap[docId].push(tempId);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
for (const ids of Object.values(idMap)) {
|
|
146
|
+
if (ids.length > 1) {
|
|
147
|
+
for (const id of ids) {
|
|
148
|
+
updated[id] = { ...updated[id], _idConflict: true };
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return updated;
|
|
154
|
+
}
|