@gallop.software/studio 2.3.158 → 2.3.160
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-Ceh3JCXY.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
|
@@ -4693,25 +4693,40 @@ async function handleFontsUpload(request) {
|
|
|
4693
4693
|
try {
|
|
4694
4694
|
const formData = await request.formData();
|
|
4695
4695
|
const file = formData.get("file");
|
|
4696
|
-
const
|
|
4696
|
+
const basePath = formData.get("path") || "_fonts";
|
|
4697
4697
|
if (!file) {
|
|
4698
4698
|
return jsonResponse({ error: "No file provided" }, { status: 400 });
|
|
4699
4699
|
}
|
|
4700
4700
|
if (!file.name.toLowerCase().endsWith(".ttf")) {
|
|
4701
4701
|
return jsonResponse({ error: "Only TTF files are supported" }, { status: 400 });
|
|
4702
4702
|
}
|
|
4703
|
-
if (!
|
|
4703
|
+
if (!basePath.startsWith("_fonts")) {
|
|
4704
4704
|
return jsonResponse({ error: "Can only upload to _fonts/" }, { status: 400 });
|
|
4705
4705
|
}
|
|
4706
4706
|
const bytes = await file.arrayBuffer();
|
|
4707
4707
|
const buffer = Buffer.from(bytes);
|
|
4708
|
+
const fileName = file.name.toLowerCase();
|
|
4709
|
+
const dashIndex = fileName.indexOf("-");
|
|
4710
|
+
let folderName;
|
|
4711
|
+
if (dashIndex > 0) {
|
|
4712
|
+
folderName = fileName.substring(0, dashIndex);
|
|
4713
|
+
} else {
|
|
4714
|
+
folderName = fileName.replace(".ttf", "");
|
|
4715
|
+
}
|
|
4716
|
+
let targetPath;
|
|
4717
|
+
if (basePath === "_fonts") {
|
|
4718
|
+
targetPath = `_fonts/${folderName}`;
|
|
4719
|
+
} else {
|
|
4720
|
+
targetPath = basePath;
|
|
4721
|
+
}
|
|
4708
4722
|
const uploadDir = getWorkspacePath(targetPath);
|
|
4709
4723
|
await fs12.mkdir(uploadDir, { recursive: true });
|
|
4710
|
-
const filePath = path11.join(uploadDir,
|
|
4724
|
+
const filePath = path11.join(uploadDir, fileName);
|
|
4711
4725
|
await fs12.writeFile(filePath, buffer);
|
|
4712
4726
|
return jsonResponse({
|
|
4713
4727
|
success: true,
|
|
4714
|
-
path: `${targetPath}/${
|
|
4728
|
+
path: `${targetPath}/${fileName}`,
|
|
4729
|
+
folder: targetPath
|
|
4715
4730
|
});
|
|
4716
4731
|
} catch (error) {
|
|
4717
4732
|
console.error("Error uploading font:", error);
|
|
@@ -4776,6 +4791,66 @@ async function handleFontsDelete(request) {
|
|
|
4776
4791
|
return jsonResponse({ error: "Failed to delete" }, { status: 500 });
|
|
4777
4792
|
}
|
|
4778
4793
|
}
|
|
4794
|
+
async function handleFontsDeleteStream(request) {
|
|
4795
|
+
try {
|
|
4796
|
+
const { paths } = await request.json();
|
|
4797
|
+
if (!paths || !Array.isArray(paths) || paths.length === 0) {
|
|
4798
|
+
return jsonResponse({ error: "Paths are required" }, { status: 400 });
|
|
4799
|
+
}
|
|
4800
|
+
for (const p of paths) {
|
|
4801
|
+
if (!p.startsWith("_fonts/")) {
|
|
4802
|
+
return jsonResponse({ error: `Path not allowed: ${p}` }, { status: 400 });
|
|
4803
|
+
}
|
|
4804
|
+
}
|
|
4805
|
+
const encoder = new TextEncoder();
|
|
4806
|
+
const stream = new ReadableStream({
|
|
4807
|
+
async start(controller) {
|
|
4808
|
+
const send = (data) => {
|
|
4809
|
+
controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}
|
|
4810
|
+
|
|
4811
|
+
`));
|
|
4812
|
+
};
|
|
4813
|
+
const deleted = [];
|
|
4814
|
+
const errors = [];
|
|
4815
|
+
for (let i = 0; i < paths.length; i++) {
|
|
4816
|
+
const p = paths[i];
|
|
4817
|
+
const fileName = p.split("/").pop() || p;
|
|
4818
|
+
send({ status: "progress", message: `Deleting ${fileName}...`, current: i + 1, total: paths.length });
|
|
4819
|
+
await new Promise((resolve2) => setTimeout(resolve2, 1e3));
|
|
4820
|
+
try {
|
|
4821
|
+
const fullPath = getWorkspacePath(p);
|
|
4822
|
+
const stat = await fs12.stat(fullPath);
|
|
4823
|
+
if (stat.isDirectory()) {
|
|
4824
|
+
await fs12.rm(fullPath, { recursive: true });
|
|
4825
|
+
} else {
|
|
4826
|
+
await fs12.unlink(fullPath);
|
|
4827
|
+
}
|
|
4828
|
+
deleted.push(p);
|
|
4829
|
+
} catch (err) {
|
|
4830
|
+
errors.push(`Failed to delete ${p}: ${err instanceof Error ? err.message : "Unknown error"}`);
|
|
4831
|
+
}
|
|
4832
|
+
}
|
|
4833
|
+
send({
|
|
4834
|
+
status: "complete",
|
|
4835
|
+
message: `Deleted ${deleted.length} item${deleted.length !== 1 ? "s" : ""}`,
|
|
4836
|
+
deleted,
|
|
4837
|
+
errors: errors.length > 0 ? errors : void 0
|
|
4838
|
+
});
|
|
4839
|
+
controller.close();
|
|
4840
|
+
}
|
|
4841
|
+
});
|
|
4842
|
+
return new Response(stream, {
|
|
4843
|
+
headers: {
|
|
4844
|
+
"Content-Type": "text/event-stream",
|
|
4845
|
+
"Cache-Control": "no-cache",
|
|
4846
|
+
"Connection": "keep-alive"
|
|
4847
|
+
}
|
|
4848
|
+
});
|
|
4849
|
+
} catch (error) {
|
|
4850
|
+
console.error("Error deleting:", error);
|
|
4851
|
+
return jsonResponse({ error: "Failed to delete" }, { status: 500 });
|
|
4852
|
+
}
|
|
4853
|
+
}
|
|
4779
4854
|
async function handleFontsRename(request) {
|
|
4780
4855
|
try {
|
|
4781
4856
|
const { oldPath, newName } = await request.json();
|
|
@@ -4835,6 +4910,92 @@ async function handleFontsRename(request) {
|
|
|
4835
4910
|
return jsonResponse({ error: "Failed to rename" }, { status: 500 });
|
|
4836
4911
|
}
|
|
4837
4912
|
}
|
|
4913
|
+
async function handleFontsRenameStream(request) {
|
|
4914
|
+
try {
|
|
4915
|
+
const { oldPath, newName } = await request.json();
|
|
4916
|
+
if (!oldPath || !newName) {
|
|
4917
|
+
return jsonResponse({ error: "oldPath and newName are required" }, { status: 400 });
|
|
4918
|
+
}
|
|
4919
|
+
if (!oldPath.startsWith("_fonts/")) {
|
|
4920
|
+
return jsonResponse({ error: "Can only rename items in _fonts/" }, { status: 400 });
|
|
4921
|
+
}
|
|
4922
|
+
if (newName.includes("/") || newName.includes("\\")) {
|
|
4923
|
+
return jsonResponse({ error: "Invalid folder name" }, { status: 400 });
|
|
4924
|
+
}
|
|
4925
|
+
const oldFullPath = getWorkspacePath(oldPath);
|
|
4926
|
+
const parentDir = path11.dirname(oldPath);
|
|
4927
|
+
const oldFolderName = path11.basename(oldPath).toLowerCase();
|
|
4928
|
+
const newFolderName = newName.toLowerCase();
|
|
4929
|
+
const newPath = `${parentDir}/${newFolderName}`;
|
|
4930
|
+
const newFullPath = getWorkspacePath(newPath);
|
|
4931
|
+
let isDirectory = false;
|
|
4932
|
+
try {
|
|
4933
|
+
const stat = await fs12.stat(oldFullPath);
|
|
4934
|
+
isDirectory = stat.isDirectory();
|
|
4935
|
+
} catch {
|
|
4936
|
+
return jsonResponse({ error: "Path not found" }, { status: 404 });
|
|
4937
|
+
}
|
|
4938
|
+
try {
|
|
4939
|
+
await fs12.stat(newFullPath);
|
|
4940
|
+
return jsonResponse({ error: "A folder with that name already exists" }, { status: 400 });
|
|
4941
|
+
} catch {
|
|
4942
|
+
}
|
|
4943
|
+
const encoder = new TextEncoder();
|
|
4944
|
+
const stream = new ReadableStream({
|
|
4945
|
+
async start(controller) {
|
|
4946
|
+
const send = (data) => {
|
|
4947
|
+
controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}
|
|
4948
|
+
|
|
4949
|
+
`));
|
|
4950
|
+
};
|
|
4951
|
+
try {
|
|
4952
|
+
send({ status: "progress", message: `Renaming folder to ${newFolderName}...`, current: 0, total: 1 });
|
|
4953
|
+
await new Promise((resolve2) => setTimeout(resolve2, 1e3));
|
|
4954
|
+
await fs12.rename(oldFullPath, newFullPath);
|
|
4955
|
+
if (isDirectory) {
|
|
4956
|
+
const entries = await fs12.readdir(newFullPath);
|
|
4957
|
+
const filesToRename = entries.filter((entry) => {
|
|
4958
|
+
const entryLower = entry.toLowerCase();
|
|
4959
|
+
return entryLower.startsWith(oldFolderName + "-") || entryLower.startsWith(oldFolderName + "_");
|
|
4960
|
+
});
|
|
4961
|
+
let renamed = 0;
|
|
4962
|
+
for (const entry of filesToRename) {
|
|
4963
|
+
const entryLower = entry.toLowerCase();
|
|
4964
|
+
const separator = entryLower.startsWith(oldFolderName + "-") ? "-" : "_";
|
|
4965
|
+
const suffix = entry.substring(oldFolderName.length);
|
|
4966
|
+
const newFileName = newFolderName + suffix.toLowerCase();
|
|
4967
|
+
send({ status: "progress", message: `Renaming ${entry} \u2192 ${newFileName}...`, current: renamed + 1, total: filesToRename.length });
|
|
4968
|
+
await new Promise((resolve2) => setTimeout(resolve2, 1e3));
|
|
4969
|
+
const oldFilePath = path11.join(newFullPath, entry);
|
|
4970
|
+
const newFilePath = path11.join(newFullPath, newFileName);
|
|
4971
|
+
await fs12.rename(oldFilePath, newFilePath);
|
|
4972
|
+
renamed++;
|
|
4973
|
+
}
|
|
4974
|
+
}
|
|
4975
|
+
send({
|
|
4976
|
+
status: "complete",
|
|
4977
|
+
message: `Renamed to ${newFolderName}`,
|
|
4978
|
+
oldPath,
|
|
4979
|
+
newPath
|
|
4980
|
+
});
|
|
4981
|
+
} catch (err) {
|
|
4982
|
+
send({ status: "error", message: String(err) });
|
|
4983
|
+
}
|
|
4984
|
+
controller.close();
|
|
4985
|
+
}
|
|
4986
|
+
});
|
|
4987
|
+
return new Response(stream, {
|
|
4988
|
+
headers: {
|
|
4989
|
+
"Content-Type": "text/event-stream",
|
|
4990
|
+
"Cache-Control": "no-cache",
|
|
4991
|
+
"Connection": "keep-alive"
|
|
4992
|
+
}
|
|
4993
|
+
});
|
|
4994
|
+
} catch (error) {
|
|
4995
|
+
console.error("Error renaming:", error);
|
|
4996
|
+
return jsonResponse({ error: "Failed to rename" }, { status: 500 });
|
|
4997
|
+
}
|
|
4998
|
+
}
|
|
4838
4999
|
async function handleFontsScan(request) {
|
|
4839
5000
|
try {
|
|
4840
5001
|
const { folder } = await request.json();
|
|
@@ -4988,6 +5149,7 @@ async function handleFontsAssign(request) {
|
|
|
4988
5149
|
const baseName = path11.basename(ttfFile, ".ttf");
|
|
4989
5150
|
const woff2Name = baseName + ".woff2";
|
|
4990
5151
|
send({ status: "progress", message: `Compressing ${ttfFile}...`, current: i + 1, total: ttfFiles.length, currentFile: ttfFile });
|
|
5152
|
+
await new Promise((resolve2) => setTimeout(resolve2, 1e3));
|
|
4991
5153
|
try {
|
|
4992
5154
|
const ttfPath = path11.join(folderPath, ttfFile);
|
|
4993
5155
|
const input = readFileSync(ttfPath);
|
|
@@ -5022,6 +5184,7 @@ async function handleFontsAssign(request) {
|
|
|
5022
5184
|
for (let i = 0; i < assignments.length; i++) {
|
|
5023
5185
|
const assignmentName = assignments[i];
|
|
5024
5186
|
send({ status: "progress", message: `Writing ${assignmentName}.ts...`, current: i + 1, total: assignments.length });
|
|
5187
|
+
await new Promise((resolve2) => setTimeout(resolve2, 1e3));
|
|
5025
5188
|
try {
|
|
5026
5189
|
const fileName = `${assignmentName}.ts`;
|
|
5027
5190
|
const filePath = path11.join(srcFontsPath, fileName);
|
|
@@ -5144,7 +5307,9 @@ async function startServer(options) {
|
|
|
5144
5307
|
app.post("/api/studio/fonts/upload", wrapRawHandler(handleFontsUpload));
|
|
5145
5308
|
app.post("/api/studio/fonts/create-folder", wrapHandler(handleFontsCreateFolder));
|
|
5146
5309
|
app.post("/api/studio/fonts/delete", wrapHandler(handleFontsDelete));
|
|
5310
|
+
app.post("/api/studio/fonts/delete-stream", wrapHandler(handleFontsDeleteStream, true));
|
|
5147
5311
|
app.post("/api/studio/fonts/rename", wrapHandler(handleFontsRename));
|
|
5312
|
+
app.post("/api/studio/fonts/rename-stream", wrapHandler(handleFontsRenameStream, true));
|
|
5148
5313
|
app.post("/api/studio/fonts/scan", wrapHandler(handleFontsScan));
|
|
5149
5314
|
app.get("/api/studio/fonts/assignments", wrapHandler(handleFontsListAssignments));
|
|
5150
5315
|
app.post("/api/studio/fonts/assign-stream", wrapHandler(handleFontsAssign, true));
|