@series-inc/stowkit-cli 0.1.10 → 0.1.12

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.
Files changed (2) hide show
  1. package/dist/server.js +58 -11
  2. package/package.json +3 -3
package/dist/server.js CHANGED
@@ -475,17 +475,28 @@ async function handleRequest(req, res, staticApps) {
475
475
  json(res, { error: 'Asset not found' }, 404);
476
476
  return;
477
477
  }
478
- // Build new ID: same folder, new filename
478
+ // Build new ID: same folder, new filename (preserve original extension)
479
479
  const folder = id.includes('/') ? id.slice(0, id.lastIndexOf('/') + 1) : '';
480
- const newId = folder + newFileName;
480
+ const oldBase = id.split('/').pop() ?? id;
481
+ const extMatch = oldBase.match(/\.[^.]+$/);
482
+ const ext = extMatch ? extMatch[0] : '';
483
+ // If the new name doesn't already have the right extension, add it
484
+ const fullNewFileName = ext && !newFileName.endsWith(ext) ? newFileName + ext : newFileName;
485
+ const newId = folder + fullNewFileName;
481
486
  // Rename source file, .stowmeta, and .stowcache on disk
482
487
  await renameFile(projectConfig.srcArtDir, id, newId);
483
488
  await renameFile(projectConfig.srcArtDir, `${id}.stowmeta`, `${newId}.stowmeta`);
484
489
  await renameFile(projectConfig.srcArtDir, `${id}.stowcache`, `${newId}.stowcache`);
485
490
  // Update in-memory state
486
491
  asset.id = newId;
487
- asset.fileName = newFileName;
492
+ // For materials, display name is without extension
493
+ const displayName = asset.type === AssetType.MaterialSchema
494
+ ? fullNewFileName.replace(/\.[^.]+$/, '')
495
+ : fullNewFileName;
496
+ asset.fileName = displayName;
488
497
  BlobStore.renameAll(id, newId);
498
+ // Prevent auto-rescan from triggering a full reload during rename
499
+ lastScanTime = Date.now();
489
500
  json(res, { ok: true, newId });
490
501
  return;
491
502
  }
@@ -505,6 +516,41 @@ async function handleRequest(req, res, staticApps) {
505
516
  json(res, { ok: true });
506
517
  return;
507
518
  }
519
+ // DELETE /api/folder/:path — delete folder and all contents
520
+ if (pathname.startsWith('/api/folder/') && req.method === 'DELETE') {
521
+ const folderPath = decodeURIComponent(pathname.slice('/api/folder/'.length));
522
+ if (!projectConfig) {
523
+ json(res, { error: 'No project open' }, 400);
524
+ return;
525
+ }
526
+ const prefix = folderPath + '/';
527
+ const removedIds = [];
528
+ // Remove assets inside the folder
529
+ for (const asset of assets) {
530
+ if (asset.id.startsWith(prefix)) {
531
+ removedIds.push(asset.id);
532
+ BlobStore.remove(asset.id);
533
+ deleteFile(projectConfig.srcArtDir, asset.id);
534
+ deleteFile(projectConfig.srcArtDir, `${asset.id}.stowmeta`);
535
+ deleteFile(projectConfig.srcArtDir, `${asset.id}.stowcache`);
536
+ }
537
+ }
538
+ assets = assets.filter(a => !a.id.startsWith(prefix));
539
+ // Remove sub-folders and the folder itself
540
+ folders = folders.filter(f => f !== folderPath && !f.startsWith(prefix));
541
+ // Delete the directory from disk
542
+ const fullPath = path.join(projectConfig.srcArtDir, folderPath);
543
+ try {
544
+ await fs.rm(fullPath, { recursive: true, force: true });
545
+ }
546
+ catch { /* ignore */ }
547
+ lastScanTime = Date.now();
548
+ for (const id of removedIds)
549
+ broadcast({ type: 'asset-removed', id });
550
+ broadcast({ type: 'folder-removed', path: folderPath });
551
+ json(res, { ok: true, removedAssets: removedIds.length });
552
+ return;
553
+ }
508
554
  // GET /api/asset/:id/source — serve source file for preview
509
555
  if (pathname.startsWith('/api/asset/') && pathname.endsWith('/source') && req.method === 'GET') {
510
556
  const id = decodeURIComponent(pathname.slice('/api/asset/'.length, -'/source'.length));
@@ -596,8 +642,7 @@ async function handleRequest(req, res, staticApps) {
596
642
  }
597
643
  const body = JSON.parse(await readBody(req));
598
644
  const targetFolder = body.targetFolder ?? '';
599
- const count = assets.filter(a => a.type === AssetType.MaterialSchema).length;
600
- const name = `Material ${count + 1}`;
645
+ const name = body.name?.trim() || `Material ${assets.filter(a => a.type === AssetType.MaterialSchema).length + 1}`;
601
646
  const baseName = `${name.replace(/\s+/g, '_')}.stowmat`;
602
647
  const fileName = targetFolder ? `${targetFolder}/${baseName}` : baseName;
603
648
  const settings = defaultAssetSettings();
@@ -635,13 +680,15 @@ async function handleRequest(req, res, staticApps) {
635
680
  }
636
681
  const body = JSON.parse(await readBody(req));
637
682
  const parentFolder = body.parentFolder ?? '';
638
- let folderName = 'New Folder';
639
- let counter = 1;
640
- const existingFolders = new Set(folders);
641
683
  const parentPrefix = parentFolder ? `${parentFolder}/` : '';
642
- while (existingFolders.has(`${parentPrefix}${folderName}`)) {
643
- folderName = `New Folder ${counter}`;
644
- counter++;
684
+ let folderName = body.name?.trim() || 'New Folder';
685
+ if (!body.name?.trim()) {
686
+ let counter = 1;
687
+ const existingFolders = new Set(folders);
688
+ while (existingFolders.has(`${parentPrefix}${folderName}`)) {
689
+ folderName = `New Folder ${counter}`;
690
+ counter++;
691
+ }
645
692
  }
646
693
  const folderPath = parentPrefix + folderName;
647
694
  const fullPath = path.join(projectConfig.srcArtDir, folderPath);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@series-inc/stowkit-cli",
3
- "version": "0.1.10",
3
+ "version": "0.1.12",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "stowkit": "./dist/cli.js"
@@ -17,8 +17,8 @@
17
17
  "dev": "tsc --watch"
18
18
  },
19
19
  "dependencies": {
20
- "@series-inc/stowkit-packer-gui": "^0.1.1",
21
- "@series-inc/stowkit-editor": "^0.1.1",
20
+ "@series-inc/stowkit-packer-gui": "^0.1.6",
21
+ "@series-inc/stowkit-editor": "^0.1.2",
22
22
  "draco3d": "^1.5.7",
23
23
  "fbx-parser": "^2.1.3",
24
24
  "@strangeape/ffmpeg-audio-wasm": "^0.1.0",