refactor: streamline Electron API integration and enhance UI components

- Removed unused Electron API methods and simplified the main process.
- Introduced a new workspace picker modal for improved project selection.
- Enhanced error handling for authentication issues across various components.
- Updated UI styles for dark mode support and added new CSS variables.
- Refactored session management to utilize a centralized API access method.
- Added server routes for workspace management, including directory listing and configuration checks.
This commit is contained in:
Cody Seibert
2025-12-12 02:14:52 -05:00
parent 4b9bd2641f
commit 8e65f0b338
24 changed files with 1217 additions and 2390 deletions

View File

@@ -25,6 +25,123 @@ export class FeatureLoader {
return path.join(projectPath, ".automaker", "features");
}
/**
* Get the images directory path for a feature
*/
getFeatureImagesDir(projectPath: string, featureId: string): string {
return path.join(this.getFeatureDir(projectPath, featureId), "images");
}
/**
* Delete images that were removed from a feature
*/
private async deleteOrphanedImages(
projectPath: string,
oldPaths: Array<string | { path: string; [key: string]: unknown }> | undefined,
newPaths: Array<string | { path: string; [key: string]: unknown }> | undefined
): Promise<void> {
if (!oldPaths || oldPaths.length === 0) {
return;
}
// Build sets of paths for comparison
const oldPathSet = new Set(
oldPaths.map((p) => (typeof p === "string" ? p : p.path))
);
const newPathSet = new Set(
(newPaths || []).map((p) => (typeof p === "string" ? p : p.path))
);
// Find images that were removed
for (const oldPath of oldPathSet) {
if (!newPathSet.has(oldPath)) {
try {
const fullPath = path.isAbsolute(oldPath)
? oldPath
: path.join(projectPath, oldPath);
await fs.unlink(fullPath);
console.log(`[FeatureLoader] Deleted orphaned image: ${oldPath}`);
} catch (error) {
// Ignore errors when deleting (file may already be gone)
console.warn(`[FeatureLoader] Failed to delete image: ${oldPath}`, error);
}
}
}
}
/**
* Copy images from temp directory to feature directory and update paths
*/
private async migrateImages(
projectPath: string,
featureId: string,
imagePaths?: Array<string | { path: string; [key: string]: unknown }>
): Promise<Array<string | { path: string; [key: string]: unknown }> | undefined> {
if (!imagePaths || imagePaths.length === 0) {
return imagePaths;
}
const featureImagesDir = this.getFeatureImagesDir(projectPath, featureId);
await fs.mkdir(featureImagesDir, { recursive: true });
const updatedPaths: Array<string | { path: string; [key: string]: unknown }> = [];
for (const imagePath of imagePaths) {
try {
const originalPath = typeof imagePath === "string" ? imagePath : imagePath.path;
// Skip if already in feature directory
if (originalPath.includes(`/features/${featureId}/images/`)) {
updatedPaths.push(imagePath);
continue;
}
// Resolve the full path
const fullOriginalPath = path.isAbsolute(originalPath)
? originalPath
: path.join(projectPath, originalPath);
// Check if file exists
try {
await fs.access(fullOriginalPath);
} catch {
console.warn(`[FeatureLoader] Image not found, skipping: ${fullOriginalPath}`);
continue;
}
// Get filename and create new path
const filename = path.basename(originalPath);
const newPath = path.join(featureImagesDir, filename);
const relativePath = `.automaker/features/${featureId}/images/${filename}`;
// Copy the file
await fs.copyFile(fullOriginalPath, newPath);
console.log(`[FeatureLoader] Copied image: ${originalPath} -> ${relativePath}`);
// Try to delete the original temp file
try {
await fs.unlink(fullOriginalPath);
} catch {
// Ignore errors when deleting temp file
}
// Update the path in the result
if (typeof imagePath === "string") {
updatedPaths.push(relativePath);
} else {
updatedPaths.push({ ...imagePath, path: relativePath });
}
} catch (error) {
console.error(`[FeatureLoader] Failed to migrate image:`, error);
// Keep original path if migration fails
updatedPaths.push(imagePath);
}
}
return updatedPaths;
}
/**
* Get the path to a specific feature folder
*/
@@ -151,12 +268,20 @@ export class FeatureLoader {
// Create feature directory
await fs.mkdir(featureDir, { recursive: true });
// Migrate images from temp directory to feature directory
const migratedImagePaths = await this.migrateImages(
projectPath,
featureId,
featureData.imagePaths
);
// Ensure feature has required fields
const feature: Feature = {
category: featureData.category || "Uncategorized",
description: featureData.description || "",
...featureData,
id: featureId,
imagePaths: migratedImagePaths,
};
// Write feature.json
@@ -179,8 +304,30 @@ export class FeatureLoader {
throw new Error(`Feature ${featureId} not found`);
}
// Handle image path changes
let updatedImagePaths = updates.imagePaths;
if (updates.imagePaths !== undefined) {
// Delete orphaned images (images that were removed)
await this.deleteOrphanedImages(
projectPath,
feature.imagePaths,
updates.imagePaths
);
// Migrate any new images
updatedImagePaths = await this.migrateImages(
projectPath,
featureId,
updates.imagePaths
);
}
// Merge updates
const updatedFeature: Feature = { ...feature, ...updates };
const updatedFeature: Feature = {
...feature,
...updates,
...(updatedImagePaths !== undefined ? { imagePaths: updatedImagePaths } : {}),
};
// Write back to file
const featureJsonPath = this.getFeatureJsonPath(projectPath, featureId);