@gallop.software/studio 2.3.24 → 2.3.26

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.
@@ -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-HYnbxDJg.js"></script>
14
+ <script type="module" crossorigin src="/assets/index-5mrVXxKd.js"></script>
15
15
  </head>
16
16
  <body>
17
17
  <div id="root"></div>
@@ -95,6 +95,17 @@ function getFileEntries(meta) {
95
95
 
96
96
  // src/handlers/utils/files.ts
97
97
  import path2 from "path";
98
+ function slugifyFilename(filename) {
99
+ const ext = path2.extname(filename).toLowerCase();
100
+ const baseName = path2.basename(filename, path2.extname(filename));
101
+ const slugged = baseName.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[_\s]+/g, "-").replace(/[^a-z0-9-]/g, "").replace(/-+/g, "-").replace(/^-|-$/g, "");
102
+ const finalSlug = slugged || "file";
103
+ return finalSlug + ext;
104
+ }
105
+ function slugifyFolderName(name) {
106
+ const slugged = name.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[_\s]+/g, "-").replace(/[^a-z0-9-]/g, "").replace(/-+/g, "-").replace(/^-|-$/g, "");
107
+ return slugged || "folder";
108
+ }
98
109
  function isImageFile(filename) {
99
110
  const ext = path2.extname(filename).toLowerCase();
100
111
  return [".jpg", ".jpeg", ".png", ".gif", ".webp", ".svg", ".ico", ".bmp", ".tiff", ".tif"].includes(ext);
@@ -1033,7 +1044,7 @@ async function handleUpload(request) {
1033
1044
  }
1034
1045
  const bytes = await file.arrayBuffer();
1035
1046
  const buffer = Buffer.from(bytes);
1036
- const fileName = file.name;
1047
+ const fileName = slugifyFilename(file.name);
1037
1048
  const ext = path6.extname(fileName).toLowerCase();
1038
1049
  const isImage = isImageFile(fileName);
1039
1050
  const isMedia = isMediaFile(fileName);
@@ -1225,7 +1236,7 @@ async function handleCreateFolder(request) {
1225
1236
  if (!name || typeof name !== "string") {
1226
1237
  return jsonResponse({ error: "Folder name is required" }, { status: 400 });
1227
1238
  }
1228
- const sanitizedName = name.replace(/[<>:"/\\|?*]/g, "").trim();
1239
+ const sanitizedName = slugifyFolderName(name);
1229
1240
  if (!sanitizedName) {
1230
1241
  return jsonResponse({ error: "Invalid folder name" }, { status: 400 });
1231
1242
  }
@@ -1252,14 +1263,8 @@ async function handleRename(request) {
1252
1263
  if (!oldPath || !newName) {
1253
1264
  return jsonResponse({ error: "Path and new name are required" }, { status: 400 });
1254
1265
  }
1255
- const sanitizedName = newName.replace(/[<>:"/\\|?*]/g, "").trim();
1256
- if (!sanitizedName) {
1257
- return jsonResponse({ error: "Invalid name" }, { status: 400 });
1258
- }
1259
1266
  const safePath = oldPath.replace(/\.\./g, "");
1260
1267
  const absoluteOldPath = getWorkspacePath(safePath);
1261
- const parentDir = path6.dirname(absoluteOldPath);
1262
- const absoluteNewPath = path6.join(parentDir, sanitizedName);
1263
1268
  if (!absoluteOldPath.startsWith(getPublicPath())) {
1264
1269
  return jsonResponse({ error: "Invalid path" }, { status: 400 });
1265
1270
  }
@@ -1268,14 +1273,20 @@ async function handleRename(request) {
1268
1273
  } catch {
1269
1274
  return jsonResponse({ error: "File or folder not found" }, { status: 404 });
1270
1275
  }
1276
+ const stats = await fs6.stat(absoluteOldPath);
1277
+ const isFile = stats.isFile();
1278
+ const isImage = isFile && isImageFile(path6.basename(oldPath));
1279
+ const sanitizedName = isFile ? slugifyFilename(newName) : slugifyFolderName(newName);
1280
+ if (!sanitizedName) {
1281
+ return jsonResponse({ error: "Invalid name" }, { status: 400 });
1282
+ }
1283
+ const parentDir = path6.dirname(absoluteOldPath);
1284
+ const absoluteNewPath = path6.join(parentDir, sanitizedName);
1271
1285
  try {
1272
1286
  await fs6.access(absoluteNewPath);
1273
1287
  return jsonResponse({ error: "An item with this name already exists" }, { status: 400 });
1274
1288
  } catch {
1275
1289
  }
1276
- const stats = await fs6.stat(absoluteOldPath);
1277
- const isFile = stats.isFile();
1278
- const isImage = isFile && isImageFile(path6.basename(oldPath));
1279
1290
  await fs6.rename(absoluteOldPath, absoluteNewPath);
1280
1291
  if (isImage) {
1281
1292
  const meta = await loadMeta();
@@ -2363,6 +2374,26 @@ async function handleScanStream() {
2363
2374
  }
2364
2375
  continue;
2365
2376
  }
2377
+ const dirName = path8.dirname(relativePath);
2378
+ const originalFileName = path8.basename(relativePath);
2379
+ const sluggedFileName = slugifyFilename(originalFileName);
2380
+ if (sluggedFileName !== originalFileName) {
2381
+ const newRelativePath = dirName === "." ? sluggedFileName : `${dirName}/${sluggedFileName}`;
2382
+ const newFullPath = getPublicPath(newRelativePath);
2383
+ const newKey = "/" + newRelativePath;
2384
+ if (!meta[newKey] && !existingKeys.has(newKey)) {
2385
+ try {
2386
+ await fs8.mkdir(path8.dirname(newFullPath), { recursive: true });
2387
+ await fs8.rename(fullPath, newFullPath);
2388
+ renamed.push({ from: relativePath, to: newRelativePath });
2389
+ relativePath = newRelativePath;
2390
+ fullPath = newFullPath;
2391
+ imageKey = newKey;
2392
+ } catch (err) {
2393
+ console.error(`Failed to slugify ${relativePath}:`, err);
2394
+ }
2395
+ }
2396
+ }
2366
2397
  if (meta[imageKey]) {
2367
2398
  const ext = path8.extname(relativePath);
2368
2399
  const baseName = relativePath.slice(0, -ext.length);