@gallop.software/studio 2.3.151 → 2.3.152
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-DWAQ5-8C.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
|
@@ -5,7 +5,7 @@ import express from "express";
|
|
|
5
5
|
import { resolve, join } from "path";
|
|
6
6
|
import { fileURLToPath } from "url";
|
|
7
7
|
import { dirname } from "path";
|
|
8
|
-
import { existsSync, readFileSync } from "fs";
|
|
8
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
9
9
|
import { config as loadEnv } from "dotenv";
|
|
10
10
|
import { createServer } from "net";
|
|
11
11
|
|
|
@@ -4574,7 +4574,55 @@ async function handleEditImage(request) {
|
|
|
4574
4574
|
|
|
4575
4575
|
// src/handlers/fonts.ts
|
|
4576
4576
|
import { promises as fs12 } from "fs";
|
|
4577
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
|
|
4577
4578
|
import path11 from "path";
|
|
4579
|
+
var weightMap = {
|
|
4580
|
+
thin: "100",
|
|
4581
|
+
extralight: "200",
|
|
4582
|
+
light: "300",
|
|
4583
|
+
regular: "400",
|
|
4584
|
+
medium: "500",
|
|
4585
|
+
semibold: "600",
|
|
4586
|
+
bold: "700",
|
|
4587
|
+
extrabold: "800",
|
|
4588
|
+
black: "900"
|
|
4589
|
+
};
|
|
4590
|
+
function parseFontMetadata(filename) {
|
|
4591
|
+
const name = filename.toLowerCase();
|
|
4592
|
+
let weight = "400";
|
|
4593
|
+
let style = "normal";
|
|
4594
|
+
const isVariable = name.includes("variable");
|
|
4595
|
+
if (isVariable) {
|
|
4596
|
+
weight = "100 900";
|
|
4597
|
+
} else {
|
|
4598
|
+
if (name.includes("extralight")) weight = weightMap.extralight;
|
|
4599
|
+
else if (name.includes("extrabold")) weight = weightMap.extrabold;
|
|
4600
|
+
else if (name.includes("semibold")) weight = weightMap.semibold;
|
|
4601
|
+
else if (name.includes("thin")) weight = weightMap.thin;
|
|
4602
|
+
else if (name.includes("light")) weight = weightMap.light;
|
|
4603
|
+
else if (name.includes("black")) weight = weightMap.black;
|
|
4604
|
+
else if (name.includes("bold")) weight = weightMap.bold;
|
|
4605
|
+
else if (name.includes("medium")) weight = weightMap.medium;
|
|
4606
|
+
else if (name.includes("regular")) weight = weightMap.regular;
|
|
4607
|
+
}
|
|
4608
|
+
if (name.includes("italic")) style = "italic";
|
|
4609
|
+
return { weight, style, isVariable };
|
|
4610
|
+
}
|
|
4611
|
+
function getWeightName(weight) {
|
|
4612
|
+
if (weight === "100 900") return "Variable";
|
|
4613
|
+
const names = {
|
|
4614
|
+
"100": "Thin",
|
|
4615
|
+
"200": "ExtraLight",
|
|
4616
|
+
"300": "Light",
|
|
4617
|
+
"400": "Regular",
|
|
4618
|
+
"500": "Medium",
|
|
4619
|
+
"600": "SemiBold",
|
|
4620
|
+
"700": "Bold",
|
|
4621
|
+
"800": "ExtraBold",
|
|
4622
|
+
"900": "Black"
|
|
4623
|
+
};
|
|
4624
|
+
return names[weight] || weight;
|
|
4625
|
+
}
|
|
4578
4626
|
async function handleFontsList(request) {
|
|
4579
4627
|
const searchParams = new URL(request.url).searchParams;
|
|
4580
4628
|
const requestedPath = searchParams.get("path") || "_fonts";
|
|
@@ -4787,12 +4835,242 @@ async function handleFontsRename(request) {
|
|
|
4787
4835
|
return jsonResponse({ error: "Failed to rename" }, { status: 500 });
|
|
4788
4836
|
}
|
|
4789
4837
|
}
|
|
4838
|
+
async function handleFontsScan(request) {
|
|
4839
|
+
try {
|
|
4840
|
+
const { folder } = await request.json();
|
|
4841
|
+
if (!folder || !folder.startsWith("_fonts/")) {
|
|
4842
|
+
return jsonResponse({ error: "Invalid folder path" }, { status: 400 });
|
|
4843
|
+
}
|
|
4844
|
+
const folderPath = getWorkspacePath(folder);
|
|
4845
|
+
const folderName = path11.basename(folder);
|
|
4846
|
+
try {
|
|
4847
|
+
const stat = await fs12.stat(folderPath);
|
|
4848
|
+
if (!stat.isDirectory()) {
|
|
4849
|
+
return jsonResponse({ error: "Path is not a folder" }, { status: 400 });
|
|
4850
|
+
}
|
|
4851
|
+
} catch {
|
|
4852
|
+
return jsonResponse({ error: "Folder not found" }, { status: 404 });
|
|
4853
|
+
}
|
|
4854
|
+
const entries = await fs12.readdir(folderPath);
|
|
4855
|
+
const ttfFiles = [];
|
|
4856
|
+
const woff2Files = [];
|
|
4857
|
+
const detectedFonts = [];
|
|
4858
|
+
for (const entry of entries) {
|
|
4859
|
+
const ext = path11.extname(entry).toLowerCase();
|
|
4860
|
+
if (ext === ".ttf") {
|
|
4861
|
+
ttfFiles.push(entry);
|
|
4862
|
+
} else if (ext === ".woff2") {
|
|
4863
|
+
woff2Files.push(entry);
|
|
4864
|
+
const baseName = path11.basename(entry, ".woff2");
|
|
4865
|
+
const { weight, style } = parseFontMetadata(baseName);
|
|
4866
|
+
detectedFonts.push({
|
|
4867
|
+
file: entry,
|
|
4868
|
+
weight,
|
|
4869
|
+
weightName: getWeightName(weight),
|
|
4870
|
+
style
|
|
4871
|
+
});
|
|
4872
|
+
}
|
|
4873
|
+
}
|
|
4874
|
+
const needsGeneration = ttfFiles.length > 0 && woff2Files.length === 0;
|
|
4875
|
+
const srcFontsPath = getWorkspacePath("src/fonts");
|
|
4876
|
+
const assignments = [];
|
|
4877
|
+
try {
|
|
4878
|
+
const srcEntries = await fs12.readdir(srcFontsPath);
|
|
4879
|
+
for (const entry of srcEntries) {
|
|
4880
|
+
if (entry.endsWith(".ts")) {
|
|
4881
|
+
const filePath = path11.join(srcFontsPath, entry);
|
|
4882
|
+
const content = await fs12.readFile(filePath, "utf8");
|
|
4883
|
+
if (content.includes(`/_fonts/${folderName}/`)) {
|
|
4884
|
+
assignments.push(path11.basename(entry, ".ts"));
|
|
4885
|
+
}
|
|
4886
|
+
}
|
|
4887
|
+
}
|
|
4888
|
+
} catch {
|
|
4889
|
+
}
|
|
4890
|
+
return jsonResponse({
|
|
4891
|
+
folder,
|
|
4892
|
+
folderName,
|
|
4893
|
+
ttfFiles,
|
|
4894
|
+
woff2Files,
|
|
4895
|
+
detectedFonts,
|
|
4896
|
+
needsGeneration,
|
|
4897
|
+
assignments
|
|
4898
|
+
});
|
|
4899
|
+
} catch (error) {
|
|
4900
|
+
console.error("Error scanning fonts:", error);
|
|
4901
|
+
return jsonResponse({ error: "Failed to scan fonts" }, { status: 500 });
|
|
4902
|
+
}
|
|
4903
|
+
}
|
|
4904
|
+
async function handleFontsListAssignments() {
|
|
4905
|
+
try {
|
|
4906
|
+
const srcFontsPath = getWorkspacePath("src/fonts");
|
|
4907
|
+
const assignments = [];
|
|
4908
|
+
try {
|
|
4909
|
+
const entries = await fs12.readdir(srcFontsPath);
|
|
4910
|
+
for (const entry of entries) {
|
|
4911
|
+
if (entry.endsWith(".ts")) {
|
|
4912
|
+
const name = path11.basename(entry, ".ts");
|
|
4913
|
+
const filePath = path11.join(srcFontsPath, entry);
|
|
4914
|
+
const content = await fs12.readFile(filePath, "utf8");
|
|
4915
|
+
const match = content.match(/\/_fonts\/([^/]+)\//);
|
|
4916
|
+
const folder = match ? match[1] : "unknown";
|
|
4917
|
+
assignments.push({ name, folder });
|
|
4918
|
+
}
|
|
4919
|
+
}
|
|
4920
|
+
} catch {
|
|
4921
|
+
}
|
|
4922
|
+
return jsonResponse({ assignments });
|
|
4923
|
+
} catch (error) {
|
|
4924
|
+
console.error("Error listing assignments:", error);
|
|
4925
|
+
return jsonResponse({ error: "Failed to list assignments" }, { status: 500 });
|
|
4926
|
+
}
|
|
4927
|
+
}
|
|
4928
|
+
async function handleFontsDeleteAssignment(request) {
|
|
4929
|
+
try {
|
|
4930
|
+
const { name } = await request.json();
|
|
4931
|
+
if (!name || typeof name !== "string") {
|
|
4932
|
+
return jsonResponse({ error: "Assignment name is required" }, { status: 400 });
|
|
4933
|
+
}
|
|
4934
|
+
if (name.includes("/") || name.includes("\\") || name.includes("..")) {
|
|
4935
|
+
return jsonResponse({ error: "Invalid assignment name" }, { status: 400 });
|
|
4936
|
+
}
|
|
4937
|
+
const filePath = getWorkspacePath("src/fonts", `${name}.ts`);
|
|
4938
|
+
try {
|
|
4939
|
+
await fs12.unlink(filePath);
|
|
4940
|
+
return jsonResponse({ success: true });
|
|
4941
|
+
} catch {
|
|
4942
|
+
return jsonResponse({ error: "Assignment not found" }, { status: 404 });
|
|
4943
|
+
}
|
|
4944
|
+
} catch (error) {
|
|
4945
|
+
console.error("Error deleting assignment:", error);
|
|
4946
|
+
return jsonResponse({ error: "Failed to delete assignment" }, { status: 500 });
|
|
4947
|
+
}
|
|
4948
|
+
}
|
|
4949
|
+
async function handleFontsAssign(request) {
|
|
4950
|
+
try {
|
|
4951
|
+
const { folder, assignments } = await request.json();
|
|
4952
|
+
if (!folder || !folder.startsWith("_fonts/")) {
|
|
4953
|
+
return jsonResponse({ error: "Invalid folder path" }, { status: 400 });
|
|
4954
|
+
}
|
|
4955
|
+
if (!assignments || !Array.isArray(assignments) || assignments.length === 0) {
|
|
4956
|
+
return jsonResponse({ error: "At least one assignment is required" }, { status: 400 });
|
|
4957
|
+
}
|
|
4958
|
+
for (const name of assignments) {
|
|
4959
|
+
if (!/^[a-zA-Z][a-zA-Z0-9]*$/.test(name)) {
|
|
4960
|
+
return jsonResponse({ error: `Invalid assignment name: ${name}` }, { status: 400 });
|
|
4961
|
+
}
|
|
4962
|
+
}
|
|
4963
|
+
const folderPath = getWorkspacePath(folder);
|
|
4964
|
+
const folderName = path11.basename(folder);
|
|
4965
|
+
const encoder = new TextEncoder();
|
|
4966
|
+
const stream = new ReadableStream({
|
|
4967
|
+
async start(controller) {
|
|
4968
|
+
const send = (data) => {
|
|
4969
|
+
controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}
|
|
4970
|
+
|
|
4971
|
+
`));
|
|
4972
|
+
};
|
|
4973
|
+
try {
|
|
4974
|
+
const entries = await fs12.readdir(folderPath);
|
|
4975
|
+
const ttfFiles = entries.filter((f) => f.toLowerCase().endsWith(".ttf"));
|
|
4976
|
+
let woff2Files = entries.filter((f) => f.toLowerCase().endsWith(".woff2"));
|
|
4977
|
+
if (ttfFiles.length === 0 && woff2Files.length === 0) {
|
|
4978
|
+
send({ status: "error", message: "No font files found in folder" });
|
|
4979
|
+
controller.close();
|
|
4980
|
+
return;
|
|
4981
|
+
}
|
|
4982
|
+
if (woff2Files.length === 0 && ttfFiles.length > 0) {
|
|
4983
|
+
send({ status: "progress", message: "Generating woff2 files...", current: 0, total: ttfFiles.length });
|
|
4984
|
+
const ttf2woff2Module = await import("ttf2woff2");
|
|
4985
|
+
const ttf2woff2 = ttf2woff2Module.default;
|
|
4986
|
+
for (let i = 0; i < ttfFiles.length; i++) {
|
|
4987
|
+
const ttfFile = ttfFiles[i];
|
|
4988
|
+
const baseName = path11.basename(ttfFile, ".ttf");
|
|
4989
|
+
const woff2Name = baseName + ".woff2";
|
|
4990
|
+
send({ status: "progress", message: `Compressing ${ttfFile}...`, current: i + 1, total: ttfFiles.length, currentFile: ttfFile });
|
|
4991
|
+
try {
|
|
4992
|
+
const ttfPath = path11.join(folderPath, ttfFile);
|
|
4993
|
+
const input = readFileSync(ttfPath);
|
|
4994
|
+
const woff2Data = ttf2woff2(input);
|
|
4995
|
+
writeFileSync(path11.join(folderPath, woff2Name), woff2Data);
|
|
4996
|
+
woff2Files.push(woff2Name);
|
|
4997
|
+
} catch (err) {
|
|
4998
|
+
send({ status: "progress", message: `Failed to compress ${ttfFile}`, error: String(err) });
|
|
4999
|
+
}
|
|
5000
|
+
}
|
|
5001
|
+
}
|
|
5002
|
+
if (woff2Files.length === 0) {
|
|
5003
|
+
send({ status: "error", message: "No woff2 files available" });
|
|
5004
|
+
controller.close();
|
|
5005
|
+
return;
|
|
5006
|
+
}
|
|
5007
|
+
const fontMap = woff2Files.map((file) => {
|
|
5008
|
+
const baseName = path11.basename(file, ".woff2");
|
|
5009
|
+
const { weight, style } = parseFontMetadata(baseName);
|
|
5010
|
+
return {
|
|
5011
|
+
path: `${folderName}/${file}`,
|
|
5012
|
+
weight,
|
|
5013
|
+
style
|
|
5014
|
+
};
|
|
5015
|
+
});
|
|
5016
|
+
const srcFontsPath = getWorkspacePath("src/fonts");
|
|
5017
|
+
if (!existsSync(srcFontsPath)) {
|
|
5018
|
+
mkdirSync(srcFontsPath, { recursive: true });
|
|
5019
|
+
}
|
|
5020
|
+
const created = [];
|
|
5021
|
+
const errors = [];
|
|
5022
|
+
for (let i = 0; i < assignments.length; i++) {
|
|
5023
|
+
const assignmentName = assignments[i];
|
|
5024
|
+
send({ status: "progress", message: `Writing ${assignmentName}.ts...`, current: i + 1, total: assignments.length });
|
|
5025
|
+
try {
|
|
5026
|
+
const fileName = `${assignmentName}.ts`;
|
|
5027
|
+
const filePath = path11.join(srcFontsPath, fileName);
|
|
5028
|
+
const variableName = `${assignmentName}Font`;
|
|
5029
|
+
const srcArray = fontMap.map((font) => ` { path: '../../_fonts/${font.path}', weight: '${font.weight}', style: '${font.style}' },`).join("\n");
|
|
5030
|
+
const template = `import localFont from 'next/font/local'
|
|
5031
|
+
|
|
5032
|
+
export const ${variableName} = localFont({
|
|
5033
|
+
src: [
|
|
5034
|
+
${srcArray}
|
|
5035
|
+
],
|
|
5036
|
+
})
|
|
5037
|
+
`;
|
|
5038
|
+
writeFileSync(filePath, template, "utf8");
|
|
5039
|
+
created.push(assignmentName);
|
|
5040
|
+
} catch (err) {
|
|
5041
|
+
errors.push(`Failed to write ${assignmentName}.ts: ${err}`);
|
|
5042
|
+
}
|
|
5043
|
+
}
|
|
5044
|
+
send({
|
|
5045
|
+
status: "complete",
|
|
5046
|
+
message: `Created ${created.length} font assignment${created.length !== 1 ? "s" : ""}`,
|
|
5047
|
+
created,
|
|
5048
|
+
errors: errors.length > 0 ? errors : void 0
|
|
5049
|
+
});
|
|
5050
|
+
} catch (err) {
|
|
5051
|
+
send({ status: "error", message: String(err) });
|
|
5052
|
+
}
|
|
5053
|
+
controller.close();
|
|
5054
|
+
}
|
|
5055
|
+
});
|
|
5056
|
+
return new Response(stream, {
|
|
5057
|
+
headers: {
|
|
5058
|
+
"Content-Type": "text/event-stream",
|
|
5059
|
+
"Cache-Control": "no-cache",
|
|
5060
|
+
"Connection": "keep-alive"
|
|
5061
|
+
}
|
|
5062
|
+
});
|
|
5063
|
+
} catch (error) {
|
|
5064
|
+
console.error("Error assigning fonts:", error);
|
|
5065
|
+
return jsonResponse({ error: "Failed to assign fonts" }, { status: 500 });
|
|
5066
|
+
}
|
|
5067
|
+
}
|
|
4790
5068
|
|
|
4791
5069
|
// src/server/index.ts
|
|
4792
5070
|
var __filename = fileURLToPath(import.meta.url);
|
|
4793
5071
|
var __dirname = dirname(__filename);
|
|
4794
5072
|
var packageJsonPath = resolve(__dirname, "../../package.json");
|
|
4795
|
-
var packageJson = JSON.parse(
|
|
5073
|
+
var packageJson = JSON.parse(readFileSync2(packageJsonPath, "utf-8"));
|
|
4796
5074
|
var version = packageJson.version;
|
|
4797
5075
|
function isPortAvailable(port) {
|
|
4798
5076
|
return new Promise((resolve2) => {
|
|
@@ -4827,7 +5105,7 @@ async function startServer(options) {
|
|
|
4827
5105
|
const app = express();
|
|
4828
5106
|
process.env.STUDIO_WORKSPACE = workspace;
|
|
4829
5107
|
const envLocalPath = join(workspace, ".env.local");
|
|
4830
|
-
if (
|
|
5108
|
+
if (existsSync2(envLocalPath)) {
|
|
4831
5109
|
loadEnv({ path: envLocalPath, quiet: true });
|
|
4832
5110
|
}
|
|
4833
5111
|
if (!process.env.STUDIO_DEV_SITE_URL && process.env.NEXT_PUBLIC_PRODUCTION_URL) {
|
|
@@ -4867,6 +5145,10 @@ async function startServer(options) {
|
|
|
4867
5145
|
app.post("/api/studio/fonts/create-folder", wrapHandler(handleFontsCreateFolder));
|
|
4868
5146
|
app.post("/api/studio/fonts/delete", wrapHandler(handleFontsDelete));
|
|
4869
5147
|
app.post("/api/studio/fonts/rename", wrapHandler(handleFontsRename));
|
|
5148
|
+
app.post("/api/studio/fonts/scan", wrapHandler(handleFontsScan));
|
|
5149
|
+
app.get("/api/studio/fonts/assignments", wrapHandler(handleFontsListAssignments));
|
|
5150
|
+
app.post("/api/studio/fonts/assign-stream", wrapHandler(handleFontsAssign, true));
|
|
5151
|
+
app.post("/api/studio/fonts/delete-assignment", wrapHandler(handleFontsDeleteAssignment));
|
|
4870
5152
|
app.post("/api/studio/upload", wrapRawHandler(handleUpload));
|
|
4871
5153
|
app.post("/api/studio/create-folder", wrapHandler(handleCreateFolder));
|
|
4872
5154
|
app.post("/api/studio/rename", wrapHandler(handleRename));
|
|
@@ -4914,8 +5196,8 @@ async function startServer(options) {
|
|
|
4914
5196
|
const clientDir = resolve(__dirname, "../client");
|
|
4915
5197
|
app.get("/", (req, res) => {
|
|
4916
5198
|
const htmlPath = join(clientDir, "index.html");
|
|
4917
|
-
if (
|
|
4918
|
-
let html =
|
|
5199
|
+
if (existsSync2(htmlPath)) {
|
|
5200
|
+
let html = readFileSync2(htmlPath, "utf-8");
|
|
4919
5201
|
const siteUrl = process.env.STUDIO_DEV_SITE_URL || "";
|
|
4920
5202
|
const script = `<script>
|
|
4921
5203
|
window.__STUDIO_WORKSPACE__ = ${JSON.stringify(workspace)};
|