@gallop.software/studio 2.3.135 → 2.3.136
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/client/index.html
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
|
12
12
|
}
|
|
13
13
|
</style>
|
|
14
|
-
<script type="module" crossorigin src="/assets/index-
|
|
14
|
+
<script type="module" crossorigin src="/assets/index-8i66yX4y.js"></script>
|
|
15
15
|
<link rel="stylesheet" crossorigin href="/assets/index-DfPQBmNf.css">
|
|
16
16
|
</head>
|
|
17
17
|
<body>
|
package/dist/server/index.js
CHANGED
|
@@ -3967,8 +3967,8 @@ import sharp4 from "sharp";
|
|
|
3967
3967
|
function parseImageUrl(url) {
|
|
3968
3968
|
const parsed = new URL(url);
|
|
3969
3969
|
const base = `${parsed.protocol}//${parsed.host}`;
|
|
3970
|
-
const
|
|
3971
|
-
return { base, path:
|
|
3970
|
+
const path12 = parsed.pathname;
|
|
3971
|
+
return { base, path: path12 };
|
|
3972
3972
|
}
|
|
3973
3973
|
async function processRemoteImage(url) {
|
|
3974
3974
|
const response = await fetch(url);
|
|
@@ -4029,10 +4029,10 @@ async function handleImportUrls(request) {
|
|
|
4029
4029
|
const url = urls[i].trim();
|
|
4030
4030
|
if (!url) continue;
|
|
4031
4031
|
try {
|
|
4032
|
-
const { base, path:
|
|
4033
|
-
const existingEntry = getMetaEntry(meta,
|
|
4032
|
+
const { base, path: path12 } = parseImageUrl(url);
|
|
4033
|
+
const existingEntry = getMetaEntry(meta, path12);
|
|
4034
4034
|
if (existingEntry) {
|
|
4035
|
-
skipped.push(
|
|
4035
|
+
skipped.push(path12);
|
|
4036
4036
|
sendEvent({
|
|
4037
4037
|
type: "progress",
|
|
4038
4038
|
current: i + 1,
|
|
@@ -4045,13 +4045,13 @@ async function handleImportUrls(request) {
|
|
|
4045
4045
|
}
|
|
4046
4046
|
const cdnIndex = getOrAddCdnIndex(meta, base);
|
|
4047
4047
|
const imageData = await processRemoteImage(url);
|
|
4048
|
-
setMetaEntry(meta,
|
|
4048
|
+
setMetaEntry(meta, path12, {
|
|
4049
4049
|
o: imageData.o,
|
|
4050
4050
|
b: imageData.b,
|
|
4051
4051
|
c: cdnIndex
|
|
4052
4052
|
});
|
|
4053
4053
|
await saveMeta(meta);
|
|
4054
|
-
added.push(
|
|
4054
|
+
added.push(path12);
|
|
4055
4055
|
sendEvent({
|
|
4056
4056
|
type: "progress",
|
|
4057
4057
|
current: i + 1,
|
|
@@ -4572,6 +4572,198 @@ async function handleEditImage(request) {
|
|
|
4572
4572
|
}
|
|
4573
4573
|
}
|
|
4574
4574
|
|
|
4575
|
+
// src/handlers/fonts.ts
|
|
4576
|
+
import { promises as fs12 } from "fs";
|
|
4577
|
+
import path11 from "path";
|
|
4578
|
+
var VALID_WEIGHTS = [
|
|
4579
|
+
"thin",
|
|
4580
|
+
"extralight",
|
|
4581
|
+
"light",
|
|
4582
|
+
"regular",
|
|
4583
|
+
"medium",
|
|
4584
|
+
"semibold",
|
|
4585
|
+
"bold",
|
|
4586
|
+
"extrabold",
|
|
4587
|
+
"black"
|
|
4588
|
+
];
|
|
4589
|
+
function parseFontFilename(filename) {
|
|
4590
|
+
const nameWithoutExt = filename.replace(/\.(ttf|woff2?|otf)$/i, "");
|
|
4591
|
+
const nameLower = nameWithoutExt.toLowerCase();
|
|
4592
|
+
const hasItalic = nameLower.includes("italic");
|
|
4593
|
+
const style = hasItalic ? "italic" : "normal";
|
|
4594
|
+
const parts = nameWithoutExt.split(/[-_]/);
|
|
4595
|
+
if (parts.length === 1) {
|
|
4596
|
+
return {
|
|
4597
|
+
basename: nameLower.replace("italic", "").trim(),
|
|
4598
|
+
weight: "regular",
|
|
4599
|
+
style,
|
|
4600
|
+
isValid: false
|
|
4601
|
+
};
|
|
4602
|
+
}
|
|
4603
|
+
const basename = parts[0].toLowerCase();
|
|
4604
|
+
const weightPart = parts.slice(1).join("").toLowerCase().replace("italic", "");
|
|
4605
|
+
let weight = "regular";
|
|
4606
|
+
let isValid = false;
|
|
4607
|
+
for (const validWeight of VALID_WEIGHTS) {
|
|
4608
|
+
if (weightPart.includes(validWeight)) {
|
|
4609
|
+
weight = validWeight;
|
|
4610
|
+
isValid = true;
|
|
4611
|
+
break;
|
|
4612
|
+
}
|
|
4613
|
+
}
|
|
4614
|
+
if (!isValid) {
|
|
4615
|
+
if (weightPart === "" || weightPart === "regular" || weightPart === "normal") {
|
|
4616
|
+
weight = "regular";
|
|
4617
|
+
isValid = true;
|
|
4618
|
+
}
|
|
4619
|
+
}
|
|
4620
|
+
return { basename, weight, style, isValid };
|
|
4621
|
+
}
|
|
4622
|
+
function generateFontFilename(basename, weight, style, ext) {
|
|
4623
|
+
const styleSuffix = style === "italic" ? "italic" : "";
|
|
4624
|
+
const weightAndStyle = weight + styleSuffix;
|
|
4625
|
+
return `${basename}-${weightAndStyle}${ext}`.toLowerCase();
|
|
4626
|
+
}
|
|
4627
|
+
async function handleFontsList() {
|
|
4628
|
+
try {
|
|
4629
|
+
const fontsDir = getWorkspacePath("_fonts");
|
|
4630
|
+
const configDir = getWorkspacePath("src", "fonts");
|
|
4631
|
+
const families = [];
|
|
4632
|
+
const configs = [];
|
|
4633
|
+
try {
|
|
4634
|
+
const entries = await fs12.readdir(fontsDir, { withFileTypes: true });
|
|
4635
|
+
for (const entry of entries) {
|
|
4636
|
+
if (entry.isDirectory()) {
|
|
4637
|
+
const familyName = entry.name.toLowerCase();
|
|
4638
|
+
const familyPath = path11.join(fontsDir, entry.name);
|
|
4639
|
+
const files = await fs12.readdir(familyPath);
|
|
4640
|
+
const fontFiles = [];
|
|
4641
|
+
const weightsSet = /* @__PURE__ */ new Set();
|
|
4642
|
+
for (const file of files) {
|
|
4643
|
+
if (file.match(/\.(ttf|woff2?)$/i)) {
|
|
4644
|
+
const parsed = parseFontFilename(file);
|
|
4645
|
+
fontFiles.push({
|
|
4646
|
+
name: file,
|
|
4647
|
+
weight: parsed.weight,
|
|
4648
|
+
style: parsed.style,
|
|
4649
|
+
path: `_fonts/${entry.name}/${file}`
|
|
4650
|
+
});
|
|
4651
|
+
weightsSet.add(parsed.weight);
|
|
4652
|
+
}
|
|
4653
|
+
}
|
|
4654
|
+
if (fontFiles.length > 0) {
|
|
4655
|
+
families.push({
|
|
4656
|
+
name: familyName,
|
|
4657
|
+
files: fontFiles.sort((a, b) => {
|
|
4658
|
+
const weightOrder = VALID_WEIGHTS.indexOf(a.weight) - VALID_WEIGHTS.indexOf(b.weight);
|
|
4659
|
+
if (weightOrder !== 0) return weightOrder;
|
|
4660
|
+
return a.style === "normal" ? -1 : 1;
|
|
4661
|
+
}),
|
|
4662
|
+
fileCount: fontFiles.length,
|
|
4663
|
+
weights: Array.from(weightsSet).sort(
|
|
4664
|
+
(a, b) => VALID_WEIGHTS.indexOf(a) - VALID_WEIGHTS.indexOf(b)
|
|
4665
|
+
)
|
|
4666
|
+
});
|
|
4667
|
+
}
|
|
4668
|
+
}
|
|
4669
|
+
}
|
|
4670
|
+
} catch {
|
|
4671
|
+
}
|
|
4672
|
+
try {
|
|
4673
|
+
const configFiles = await fs12.readdir(configDir);
|
|
4674
|
+
for (const file of configFiles) {
|
|
4675
|
+
if (file.endsWith(".ts") && !file.startsWith("_")) {
|
|
4676
|
+
const configPath = path11.join(configDir, file);
|
|
4677
|
+
const content = await fs12.readFile(configPath, "utf-8");
|
|
4678
|
+
const exportMatch = content.match(/export\s+const\s+(\w+)\s*=/);
|
|
4679
|
+
const exportName = exportMatch ? exportMatch[1] : file.replace(".ts", "") + "Font";
|
|
4680
|
+
const pathMatch = content.match(/path:\s*['"]\.\.\/\.\.\/\_fonts\/([^\/]+)\//);
|
|
4681
|
+
const family = pathMatch ? pathMatch[1].toLowerCase() : "unknown";
|
|
4682
|
+
const type = file.replace(".ts", "");
|
|
4683
|
+
configs.push({
|
|
4684
|
+
type,
|
|
4685
|
+
family,
|
|
4686
|
+
path: `src/fonts/${file}`,
|
|
4687
|
+
exportName
|
|
4688
|
+
});
|
|
4689
|
+
}
|
|
4690
|
+
}
|
|
4691
|
+
} catch {
|
|
4692
|
+
}
|
|
4693
|
+
families.sort((a, b) => a.name.localeCompare(b.name));
|
|
4694
|
+
return jsonResponse({ families, configs });
|
|
4695
|
+
} catch (error) {
|
|
4696
|
+
console.error("Error listing fonts:", error);
|
|
4697
|
+
return jsonResponse({ error: "Failed to list fonts" }, { status: 500 });
|
|
4698
|
+
}
|
|
4699
|
+
}
|
|
4700
|
+
async function handleFontsUpload(request) {
|
|
4701
|
+
try {
|
|
4702
|
+
const formData = await request.formData();
|
|
4703
|
+
const files = formData.getAll("files");
|
|
4704
|
+
const renamesJson = formData.get("renames");
|
|
4705
|
+
if (!files || files.length === 0) {
|
|
4706
|
+
return jsonResponse({ error: "No files provided" }, { status: 400 });
|
|
4707
|
+
}
|
|
4708
|
+
let renames = {};
|
|
4709
|
+
if (renamesJson) {
|
|
4710
|
+
try {
|
|
4711
|
+
renames = JSON.parse(renamesJson);
|
|
4712
|
+
} catch {
|
|
4713
|
+
}
|
|
4714
|
+
}
|
|
4715
|
+
const fontsDir = getWorkspacePath("_fonts");
|
|
4716
|
+
const uploaded = [];
|
|
4717
|
+
const errors = [];
|
|
4718
|
+
for (const file of files) {
|
|
4719
|
+
try {
|
|
4720
|
+
if (!file.name.toLowerCase().endsWith(".ttf")) {
|
|
4721
|
+
errors.push({ file: file.name, error: "Only TTF files are supported" });
|
|
4722
|
+
continue;
|
|
4723
|
+
}
|
|
4724
|
+
const bytes = await file.arrayBuffer();
|
|
4725
|
+
const buffer = Buffer.from(bytes);
|
|
4726
|
+
let basename;
|
|
4727
|
+
let weight;
|
|
4728
|
+
let style;
|
|
4729
|
+
if (renames[file.name]) {
|
|
4730
|
+
basename = renames[file.name].basename.toLowerCase();
|
|
4731
|
+
weight = renames[file.name].weight.toLowerCase();
|
|
4732
|
+
style = renames[file.name].style.toLowerCase();
|
|
4733
|
+
} else {
|
|
4734
|
+
const parsed = parseFontFilename(file.name);
|
|
4735
|
+
basename = parsed.basename;
|
|
4736
|
+
weight = parsed.weight;
|
|
4737
|
+
style = parsed.style;
|
|
4738
|
+
}
|
|
4739
|
+
const newFilename = generateFontFilename(basename, weight, style, ".ttf");
|
|
4740
|
+
const familyDir = path11.join(fontsDir, basename);
|
|
4741
|
+
await fs12.mkdir(familyDir, { recursive: true });
|
|
4742
|
+
const filePath = path11.join(familyDir, newFilename);
|
|
4743
|
+
await fs12.writeFile(filePath, buffer);
|
|
4744
|
+
uploaded.push({
|
|
4745
|
+
original: file.name,
|
|
4746
|
+
saved: newFilename,
|
|
4747
|
+
path: `_fonts/${basename}/${newFilename}`
|
|
4748
|
+
});
|
|
4749
|
+
} catch (err) {
|
|
4750
|
+
errors.push({
|
|
4751
|
+
file: file.name,
|
|
4752
|
+
error: err instanceof Error ? err.message : "Unknown error"
|
|
4753
|
+
});
|
|
4754
|
+
}
|
|
4755
|
+
}
|
|
4756
|
+
return jsonResponse({
|
|
4757
|
+
success: true,
|
|
4758
|
+
uploaded,
|
|
4759
|
+
errors: errors.length > 0 ? errors : void 0
|
|
4760
|
+
});
|
|
4761
|
+
} catch (error) {
|
|
4762
|
+
console.error("Error uploading fonts:", error);
|
|
4763
|
+
return jsonResponse({ error: "Failed to upload fonts" }, { status: 500 });
|
|
4764
|
+
}
|
|
4765
|
+
}
|
|
4766
|
+
|
|
4575
4767
|
// src/server/index.ts
|
|
4576
4768
|
var __filename = fileURLToPath(import.meta.url);
|
|
4577
4769
|
var __dirname = dirname(__filename);
|
|
@@ -4617,15 +4809,16 @@ async function startServer(options) {
|
|
|
4617
4809
|
if (!process.env.STUDIO_DEV_SITE_URL && process.env.NEXT_PUBLIC_PRODUCTION_URL) {
|
|
4618
4810
|
process.env.STUDIO_DEV_SITE_URL = process.env.NEXT_PUBLIC_PRODUCTION_URL;
|
|
4619
4811
|
}
|
|
4812
|
+
const rawBodyPaths = ["/api/studio/upload", "/api/studio/fonts/upload"];
|
|
4620
4813
|
app.use((req, res, next) => {
|
|
4621
|
-
if (req.path
|
|
4814
|
+
if (rawBodyPaths.includes(req.path)) {
|
|
4622
4815
|
next();
|
|
4623
4816
|
} else {
|
|
4624
4817
|
express.json({ limit: "50mb" })(req, res, next);
|
|
4625
4818
|
}
|
|
4626
4819
|
});
|
|
4627
4820
|
app.use((req, res, next) => {
|
|
4628
|
-
if (req.path
|
|
4821
|
+
if (rawBodyPaths.includes(req.path)) {
|
|
4629
4822
|
next();
|
|
4630
4823
|
} else {
|
|
4631
4824
|
express.urlencoded({ extended: true, limit: "50mb" })(req, res, next);
|
|
@@ -4645,6 +4838,8 @@ async function startServer(options) {
|
|
|
4645
4838
|
"/api/studio/featured-image-options",
|
|
4646
4839
|
wrapHandler(handleGetFeaturedImageOptions)
|
|
4647
4840
|
);
|
|
4841
|
+
app.get("/api/studio/fonts/list", wrapHandler(handleFontsList));
|
|
4842
|
+
app.post("/api/studio/fonts/upload", wrapRawHandler(handleFontsUpload));
|
|
4648
4843
|
app.post("/api/studio/upload", wrapRawHandler(handleUpload));
|
|
4649
4844
|
app.post("/api/studio/create-folder", wrapHandler(handleCreateFolder));
|
|
4650
4845
|
app.post("/api/studio/rename", wrapHandler(handleRename));
|