@gallop.software/studio 2.3.136 → 2.3.137
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/assets/index-3VggyaFt.js +94 -0
- package/dist/client/index.html +1 -1
- package/dist/server/index.js +146 -153
- package/dist/server/index.js.map +1 -1
- package/package.json +1 -1
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-3VggyaFt.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
|
@@ -4575,123 +4575,86 @@ async function handleEditImage(request) {
|
|
|
4575
4575
|
// src/handlers/fonts.ts
|
|
4576
4576
|
import { promises as fs12 } from "fs";
|
|
4577
4577
|
import path11 from "path";
|
|
4578
|
-
|
|
4579
|
-
|
|
4580
|
-
"
|
|
4581
|
-
|
|
4582
|
-
|
|
4583
|
-
|
|
4584
|
-
|
|
4585
|
-
|
|
4586
|
-
|
|
4587
|
-
|
|
4588
|
-
|
|
4589
|
-
|
|
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;
|
|
4578
|
+
async function handleFontsList(request) {
|
|
4579
|
+
const searchParams = new URL(request.url).searchParams;
|
|
4580
|
+
const requestedPath = searchParams.get("path") || "_fonts";
|
|
4581
|
+
try {
|
|
4582
|
+
const items = [];
|
|
4583
|
+
let fsPath;
|
|
4584
|
+
let allowedPaths = ["_fonts", "src/fonts", "src"];
|
|
4585
|
+
const isAllowed = allowedPaths.some(
|
|
4586
|
+
(allowed) => requestedPath === allowed || requestedPath.startsWith(allowed + "/")
|
|
4587
|
+
);
|
|
4588
|
+
if (!isAllowed) {
|
|
4589
|
+
return jsonResponse({ items: [], error: "Path not allowed" }, { status: 400 });
|
|
4612
4590
|
}
|
|
4613
|
-
|
|
4614
|
-
|
|
4615
|
-
|
|
4616
|
-
|
|
4617
|
-
|
|
4591
|
+
if (requestedPath === "src") {
|
|
4592
|
+
const fontsFolderPath = getWorkspacePath("src", "fonts");
|
|
4593
|
+
try {
|
|
4594
|
+
const stat = await fs12.stat(fontsFolderPath);
|
|
4595
|
+
if (stat.isDirectory()) {
|
|
4596
|
+
items.push({
|
|
4597
|
+
name: "fonts",
|
|
4598
|
+
path: "src/fonts",
|
|
4599
|
+
type: "folder"
|
|
4600
|
+
});
|
|
4601
|
+
}
|
|
4602
|
+
} catch {
|
|
4603
|
+
}
|
|
4604
|
+
return jsonResponse({ items, canCreate: true });
|
|
4618
4605
|
}
|
|
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 = [];
|
|
4606
|
+
fsPath = getWorkspacePath(requestedPath);
|
|
4633
4607
|
try {
|
|
4634
|
-
const
|
|
4635
|
-
|
|
4636
|
-
|
|
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
|
-
}
|
|
4608
|
+
const stat = await fs12.stat(fsPath);
|
|
4609
|
+
if (!stat.isDirectory()) {
|
|
4610
|
+
return jsonResponse({ items: [] });
|
|
4669
4611
|
}
|
|
4670
4612
|
} catch {
|
|
4613
|
+
return jsonResponse({ items: [], canCreate: true });
|
|
4671
4614
|
}
|
|
4672
|
-
|
|
4673
|
-
|
|
4674
|
-
|
|
4675
|
-
|
|
4676
|
-
|
|
4677
|
-
|
|
4678
|
-
const
|
|
4679
|
-
|
|
4680
|
-
|
|
4681
|
-
|
|
4682
|
-
|
|
4683
|
-
|
|
4684
|
-
|
|
4685
|
-
|
|
4686
|
-
|
|
4687
|
-
|
|
4615
|
+
const entries = await fs12.readdir(fsPath, { withFileTypes: true });
|
|
4616
|
+
for (const entry of entries) {
|
|
4617
|
+
const itemPath = `${requestedPath}/${entry.name}`;
|
|
4618
|
+
if (entry.isDirectory()) {
|
|
4619
|
+
let fileCount = 0;
|
|
4620
|
+
try {
|
|
4621
|
+
const subEntries = await fs12.readdir(path11.join(fsPath, entry.name));
|
|
4622
|
+
fileCount = subEntries.filter(
|
|
4623
|
+
(f) => f.match(/\.(ttf|woff2?|otf|ts|tsx|js)$/i)
|
|
4624
|
+
).length;
|
|
4625
|
+
} catch {
|
|
4626
|
+
}
|
|
4627
|
+
items.push({
|
|
4628
|
+
name: entry.name,
|
|
4629
|
+
path: itemPath,
|
|
4630
|
+
type: "folder",
|
|
4631
|
+
fileCount
|
|
4632
|
+
});
|
|
4633
|
+
} else {
|
|
4634
|
+
const ext = path11.extname(entry.name).toLowerCase();
|
|
4635
|
+
const allowedExts = [".ttf", ".woff", ".woff2", ".otf", ".ts", ".tsx", ".js"];
|
|
4636
|
+
if (allowedExts.includes(ext)) {
|
|
4637
|
+
let size = 0;
|
|
4638
|
+
try {
|
|
4639
|
+
const fileStat = await fs12.stat(path11.join(fsPath, entry.name));
|
|
4640
|
+
size = fileStat.size;
|
|
4641
|
+
} catch {
|
|
4642
|
+
}
|
|
4643
|
+
items.push({
|
|
4644
|
+
name: entry.name,
|
|
4645
|
+
path: itemPath,
|
|
4646
|
+
type: "file",
|
|
4647
|
+
size
|
|
4688
4648
|
});
|
|
4689
4649
|
}
|
|
4690
4650
|
}
|
|
4691
|
-
} catch {
|
|
4692
4651
|
}
|
|
4693
|
-
|
|
4694
|
-
|
|
4652
|
+
items.sort((a, b) => {
|
|
4653
|
+
if (a.type === "folder" && b.type !== "folder") return -1;
|
|
4654
|
+
if (a.type !== "folder" && b.type === "folder") return 1;
|
|
4655
|
+
return a.name.localeCompare(b.name);
|
|
4656
|
+
});
|
|
4657
|
+
return jsonResponse({ items });
|
|
4695
4658
|
} catch (error) {
|
|
4696
4659
|
console.error("Error listing fonts:", error);
|
|
4697
4660
|
return jsonResponse({ error: "Failed to list fonts" }, { status: 500 });
|
|
@@ -4700,67 +4663,95 @@ async function handleFontsList() {
|
|
|
4700
4663
|
async function handleFontsUpload(request) {
|
|
4701
4664
|
try {
|
|
4702
4665
|
const formData = await request.formData();
|
|
4703
|
-
const
|
|
4704
|
-
const
|
|
4705
|
-
if (!
|
|
4706
|
-
return jsonResponse({ error: "No
|
|
4666
|
+
const file = formData.get("file");
|
|
4667
|
+
const targetPath = formData.get("path") || "_fonts";
|
|
4668
|
+
if (!file) {
|
|
4669
|
+
return jsonResponse({ error: "No file provided" }, { status: 400 });
|
|
4707
4670
|
}
|
|
4708
|
-
|
|
4709
|
-
|
|
4710
|
-
|
|
4711
|
-
|
|
4712
|
-
}
|
|
4671
|
+
if (!file.name.toLowerCase().endsWith(".ttf")) {
|
|
4672
|
+
return jsonResponse({ error: "Only TTF files are supported" }, { status: 400 });
|
|
4673
|
+
}
|
|
4674
|
+
if (!targetPath.startsWith("_fonts")) {
|
|
4675
|
+
return jsonResponse({ error: "Can only upload to _fonts/" }, { status: 400 });
|
|
4676
|
+
}
|
|
4677
|
+
const bytes = await file.arrayBuffer();
|
|
4678
|
+
const buffer = Buffer.from(bytes);
|
|
4679
|
+
const uploadDir = getWorkspacePath(targetPath);
|
|
4680
|
+
await fs12.mkdir(uploadDir, { recursive: true });
|
|
4681
|
+
const filePath = path11.join(uploadDir, file.name.toLowerCase());
|
|
4682
|
+
await fs12.writeFile(filePath, buffer);
|
|
4683
|
+
return jsonResponse({
|
|
4684
|
+
success: true,
|
|
4685
|
+
path: `${targetPath}/${file.name.toLowerCase()}`
|
|
4686
|
+
});
|
|
4687
|
+
} catch (error) {
|
|
4688
|
+
console.error("Error uploading font:", error);
|
|
4689
|
+
return jsonResponse({ error: "Failed to upload font" }, { status: 500 });
|
|
4690
|
+
}
|
|
4691
|
+
}
|
|
4692
|
+
async function handleFontsCreateFolder(request) {
|
|
4693
|
+
try {
|
|
4694
|
+
const { path: targetPath, name } = await request.json();
|
|
4695
|
+
if (!targetPath || !name) {
|
|
4696
|
+
return jsonResponse({ error: "Path and name are required" }, { status: 400 });
|
|
4697
|
+
}
|
|
4698
|
+
const allowedPaths = ["_fonts", "src/fonts", "src"];
|
|
4699
|
+
const isAllowed = allowedPaths.some(
|
|
4700
|
+
(allowed) => targetPath === allowed || targetPath.startsWith(allowed + "/")
|
|
4701
|
+
);
|
|
4702
|
+
if (!isAllowed) {
|
|
4703
|
+
return jsonResponse({ error: "Path not allowed" }, { status: 400 });
|
|
4704
|
+
}
|
|
4705
|
+
const folderPath = getWorkspacePath(targetPath, name.toLowerCase());
|
|
4706
|
+
await fs12.mkdir(folderPath, { recursive: true });
|
|
4707
|
+
return jsonResponse({
|
|
4708
|
+
success: true,
|
|
4709
|
+
path: `${targetPath}/${name.toLowerCase()}`
|
|
4710
|
+
});
|
|
4711
|
+
} catch (error) {
|
|
4712
|
+
console.error("Error creating folder:", error);
|
|
4713
|
+
return jsonResponse({ error: "Failed to create folder" }, { status: 500 });
|
|
4714
|
+
}
|
|
4715
|
+
}
|
|
4716
|
+
async function handleFontsDelete(request) {
|
|
4717
|
+
try {
|
|
4718
|
+
const { paths } = await request.json();
|
|
4719
|
+
if (!paths || !Array.isArray(paths) || paths.length === 0) {
|
|
4720
|
+
return jsonResponse({ error: "Paths are required" }, { status: 400 });
|
|
4721
|
+
}
|
|
4722
|
+
const allowedPaths = ["_fonts", "src/fonts"];
|
|
4723
|
+
for (const p of paths) {
|
|
4724
|
+
const isAllowed = allowedPaths.some(
|
|
4725
|
+
(allowed) => p.startsWith(allowed + "/")
|
|
4726
|
+
);
|
|
4727
|
+
if (!isAllowed) {
|
|
4728
|
+
return jsonResponse({ error: `Path not allowed: ${p}` }, { status: 400 });
|
|
4713
4729
|
}
|
|
4714
4730
|
}
|
|
4715
|
-
const
|
|
4716
|
-
const uploaded = [];
|
|
4731
|
+
const deleted = [];
|
|
4717
4732
|
const errors = [];
|
|
4718
|
-
for (const
|
|
4733
|
+
for (const p of paths) {
|
|
4719
4734
|
try {
|
|
4720
|
-
|
|
4721
|
-
|
|
4722
|
-
|
|
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();
|
|
4735
|
+
const fullPath = getWorkspacePath(p);
|
|
4736
|
+
const stat = await fs12.stat(fullPath);
|
|
4737
|
+
if (stat.isDirectory()) {
|
|
4738
|
+
await fs12.rm(fullPath, { recursive: true });
|
|
4733
4739
|
} else {
|
|
4734
|
-
|
|
4735
|
-
basename = parsed.basename;
|
|
4736
|
-
weight = parsed.weight;
|
|
4737
|
-
style = parsed.style;
|
|
4740
|
+
await fs12.unlink(fullPath);
|
|
4738
4741
|
}
|
|
4739
|
-
|
|
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
|
-
});
|
|
4742
|
+
deleted.push(p);
|
|
4749
4743
|
} catch (err) {
|
|
4750
|
-
errors.push({
|
|
4751
|
-
file: file.name,
|
|
4752
|
-
error: err instanceof Error ? err.message : "Unknown error"
|
|
4753
|
-
});
|
|
4744
|
+
errors.push(`Failed to delete ${p}: ${err instanceof Error ? err.message : "Unknown error"}`);
|
|
4754
4745
|
}
|
|
4755
4746
|
}
|
|
4756
4747
|
return jsonResponse({
|
|
4757
4748
|
success: true,
|
|
4758
|
-
|
|
4749
|
+
deleted,
|
|
4759
4750
|
errors: errors.length > 0 ? errors : void 0
|
|
4760
4751
|
});
|
|
4761
4752
|
} catch (error) {
|
|
4762
|
-
console.error("Error
|
|
4763
|
-
return jsonResponse({ error: "Failed to
|
|
4753
|
+
console.error("Error deleting:", error);
|
|
4754
|
+
return jsonResponse({ error: "Failed to delete" }, { status: 500 });
|
|
4764
4755
|
}
|
|
4765
4756
|
}
|
|
4766
4757
|
|
|
@@ -4840,6 +4831,8 @@ async function startServer(options) {
|
|
|
4840
4831
|
);
|
|
4841
4832
|
app.get("/api/studio/fonts/list", wrapHandler(handleFontsList));
|
|
4842
4833
|
app.post("/api/studio/fonts/upload", wrapRawHandler(handleFontsUpload));
|
|
4834
|
+
app.post("/api/studio/fonts/create-folder", wrapHandler(handleFontsCreateFolder));
|
|
4835
|
+
app.post("/api/studio/fonts/delete", wrapHandler(handleFontsDelete));
|
|
4843
4836
|
app.post("/api/studio/upload", wrapRawHandler(handleUpload));
|
|
4844
4837
|
app.post("/api/studio/create-folder", wrapHandler(handleCreateFolder));
|
|
4845
4838
|
app.post("/api/studio/rename", wrapHandler(handleRename));
|