[
  {
    "path": "Achico/Achico.entitlements",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>com.apple.security.app-sandbox</key>\n\t<true/>\n\t<key>com.apple.security.files.user-selected.read-only</key>\n\t<true/>\n\t<key>com.apple.security.files.user-selected.read-write</key>\n\t<true/>\n\t<key>com.apple.security.network.client</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "Achico/App/AchicoApp.swift",
    "content": "import SwiftUI\nimport AppKit\n\n@main\nstruct AchicoApp: App {\n    @NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate\n    @AppStorage(\"isDarkMode\") private var isDarkMode = false\n    @StateObject private var menuBarController = MenuBarController()\n    @State private var showingUpdateSheet = false\n    \n    var body: some Scene {\n        WindowGroup {\n            ContentView()\n                .frame(minWidth: 400, minHeight: 500)\n                .preferredColorScheme(isDarkMode ? .dark : .light)\n                .background(WindowAccessor())\n                .environmentObject(menuBarController)\n                .sheet(isPresented: $showingUpdateSheet) {\n                    MenuBarView(updater: menuBarController.updater)\n                        .environmentObject(menuBarController)\n                }\n                .onAppear {\n                    // Check for updates when app launches\n                    menuBarController.updater.checkForUpdates()\n                    \n                    // Set up observer for update availability\n                    menuBarController.updater.onUpdateAvailable = {\n                        showingUpdateSheet = true\n                    }\n                }\n        }\n        .windowStyle(.hiddenTitleBar)\n        .commands {\n            CommandGroup(after: .appInfo) {\n                Button(\"Check for Updates...\") {\n                    showingUpdateSheet = true\n                    menuBarController.updater.checkForUpdates()\n                }\n                .keyboardShortcut(\"U\", modifiers: [.command])\n                \n                if menuBarController.updater.updateAvailable {\n                    Button(\"Download Update\") {\n                        if let url = menuBarController.updater.downloadURL {\n                            NSWorkspace.shared.open(url)\n                        }\n                    }\n                }\n                \n                Divider()\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Achico/App/AppDelegate.swift",
    "content": "import Cocoa\n\nclass AppDelegate: NSObject, NSApplicationDelegate {\n    func applicationWillTerminate(_ notification: Notification) {\n        CacheManager.shared.cleanupOldFiles()\n    }\n}\n"
  },
  {
    "path": "Achico/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict/>\n</plist>\n"
  },
  {
    "path": "Achico/Preview Content/Preview Assets.xcassets/AppIcon-1024.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"filename\" : \"AppIcon-1024.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "Achico/Preview Content/Preview Assets.xcassets/AppIcon-128.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"filename\" : \"AppIcon-128.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "Achico/Preview Content/Preview Assets.xcassets/AppIcon-16.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"filename\" : \"AppIcon-16.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "Achico/Preview Content/Preview Assets.xcassets/AppIcon-256.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"filename\" : \"AppIcon-256.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "Achico/Preview Content/Preview Assets.xcassets/AppIcon-32.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"filename\" : \"AppIcon-32.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "Achico/Preview Content/Preview Assets.xcassets/AppIcon-512.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"filename\" : \"AppIcon-512.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "Achico/Preview Content/Preview Assets.xcassets/AppIcon-64.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"filename\" : \"AppIcon-64.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "Achico/Preview Content/Preview Assets.xcassets/Contents.json",
    "content": "{\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "Achico/Processor/CacheManager.swift",
    "content": "// CacheManager.swift\nimport Foundation\nimport AppKit\n\nclass CacheManager {\n    static let shared = CacheManager()\n    \n    private let cacheDirectory: URL\n    private let maxCacheAge: TimeInterval = 24 * 60 * 60 // 24 hours\n    \n    private init() {\n        let cacheDir = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first!\n        cacheDirectory = cacheDir.appendingPathComponent(\"com.achico.filecache\", isDirectory: true)\n        \n        do {\n            try FileManager.default.createDirectory(at: cacheDirectory, withIntermediateDirectories: true)\n        } catch {\n            print(\"Failed to create cache directory: \\(error)\")\n        }\n        \n        // Setup automatic cleanup\n        setupAutomaticCleanup()\n    }\n    \n    func createTemporaryURL(for filename: String) throws -> URL {\n           // Clean the filename\n           let cleanFilename = filename.components(separatedBy: \"_\").last ?? filename\n           let tempFilename = \"\\(UUID().uuidString).\\(cleanFilename)\"\n           let fileURL = cacheDirectory.appendingPathComponent(tempFilename)\n           \n           // Check if file already exists and remove it\n           if FileManager.default.fileExists(atPath: fileURL.path) {\n               try FileManager.default.removeItem(at: fileURL)\n           }\n           \n           return fileURL\n       }\n    \n    func cleanupOldFiles() {\n        let fileManager = FileManager.default\n        let resourceKeys: [URLResourceKey] = [.creationDateKey, .isDirectoryKey]\n        \n        guard let enumerator = fileManager.enumerator(\n            at: cacheDirectory,\n            includingPropertiesForKeys: resourceKeys,\n            options: .skipsHiddenFiles\n        ) else { return }\n        \n        let cutoffDate = Date().addingTimeInterval(-maxCacheAge)\n        \n        while let fileURL = enumerator.nextObject() as? URL {\n            do {\n                let resourceValues = try fileURL.resourceValues(forKeys: Set(resourceKeys))\n                if let creationDate = resourceValues.creationDate,\n                   let isDirectory = resourceValues.isDirectory,\n                   !isDirectory && creationDate < cutoffDate {\n                    try fileManager.removeItem(at: fileURL)\n                }\n            } catch {\n                print(\"Error cleaning up file at \\(fileURL): \\(error)\")\n            }\n        }\n    }\n    \n    private func setupAutomaticCleanup() {\n        // Clean up on app launch\n        cleanupOldFiles()\n        \n        // Register for app termination notification\n        NotificationCenter.default.addObserver(\n            forName: NSApplication.willTerminateNotification,\n            object: nil,\n            queue: .main\n        ) { [weak self] _ in\n            self?.cleanupOldFiles()\n        }\n    }\n}\n"
  },
  {
    "path": "Achico/Processor/FileProcessor.swift",
    "content": "import Foundation\nimport PDFKit\nimport UniformTypeIdentifiers\nimport AppKit\nimport CoreGraphics\nimport AVFoundation\n\nenum CompressionError: LocalizedError {\n        case unsupportedFormat\n        case conversionFailed\n        case compressionFailed\n        case invalidInput\n        case videoProcessingFailed\n        \n        var errorDescription: String? {\n            switch self {\n            case .unsupportedFormat:\n                return \"This file format is not supported\"\n            case .conversionFailed:\n                return \"Failed to convert the file\"\n            case .compressionFailed:\n                return \"Failed to compress the file\"\n            case .invalidInput:\n                return \"The input file is invalid or corrupted\"\n            case .videoProcessingFailed:\n                return \"Failed to process video file\"\n            }\n        }\n    }\n\nclass FileProcessor: ObservableObject {\n    // MARK: - Published Properties\n    @Published var isProcessing = false\n    @Published var progress: Double = 0\n    @Published var processingResult: ProcessingResult?\n    \n    // MARK: - Private Properties\n    private let processingQueue = DispatchQueue(label: \"com.achico.fileprocessing\", qos: .userInitiated)\n    private let cacheManager = CacheManager.shared\n    private let videoProcessor = VideoProcessor()\n    \n    struct ProcessingResult {\n        let originalSize: Int64\n        let compressedSize: Int64\n        let compressedURL: URL\n        let fileName: String\n        let originalFileName: String\n        \n        var savedPercentage: Int {\n            guard originalSize > 0 else { return 0 }\n            let percentage = Int(((Double(originalSize) - Double(compressedSize)) / Double(originalSize)) * 100)\n            return max(0, percentage)\n        }\n        \n        var suggestedFileName: String {\n            // Remove UUID from filename if present\n            let cleanFilename = originalFileName\n                .replacingOccurrences(of: #\"[A-F0-9]{8}-[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{12}\\.\"#,\n                                    with: \"\",\n                                    options: .regularExpression)\n            let fileURL = URL(fileURLWithPath: cleanFilename)\n            let filenameWithoutExt = fileURL.deletingPathExtension().lastPathComponent\n            let fileExtension = compressedURL.pathExtension\n            return \"\\(filenameWithoutExt)_compressed.\\(fileExtension)\"\n        }\n    }\n    \n    private func processAudio(from url: URL, to tempURL: URL, settings: CompressionSettings) async throws {\n        let asset = AVURLAsset(url: url)\n        let fileType = FileType(url: url)\n        \n        // Create a composition to work with the audio\n        let composition = AVMutableComposition()\n        \n        // Try to get the audio track from the asset\n        guard let audioTrack = try? await asset.loadTracks(withMediaType: .audio).first,\n              let compositionTrack = composition.addMutableTrack(\n                withMediaType: .audio,\n                preferredTrackID: kCMPersistentTrackID_Invalid) else {\n            throw CompressionError.compressionFailed\n        }\n        \n        // Get asset duration using the new API\n        let duration = try await asset.load(.duration)\n        \n        // Insert the audio track into the composition\n        try compositionTrack.insertTimeRange(\n            CMTimeRange(start: .zero, duration: duration),\n            of: audioTrack,\n            at: .zero\n        )\n        \n        // Determine preset and output file type\n        let (presetName, outputFileType): (String, AVFileType) = {\n            switch fileType {\n            case .mp3, .wav, .aiff, .m4a:\n                return (AVAssetExportPresetAppleM4A, .m4a)\n            default:\n                return (AVAssetExportPresetAppleM4A, .m4a)\n            }\n        }()\n        \n        // Create export session with the composition\n        guard let exportSession = AVAssetExportSession(\n            asset: composition,\n            presetName: presetName\n        ) else {\n            throw CompressionError.compressionFailed\n        }\n        \n        // Create output URL with correct extension\n        let outputURL = tempURL.deletingPathExtension().appendingPathExtension(\"m4a\")\n        \n        // Remove any existing file at the output URL\n        if FileManager.default.fileExists(atPath: outputURL.path) {\n            try? FileManager.default.removeItem(at: outputURL)\n        }\n        \n        // Ensure the output directory exists\n        try FileManager.default.createDirectory(\n            at: outputURL.deletingLastPathComponent(),\n            withIntermediateDirectories: true,\n            attributes: nil\n        )\n        \n        // Configure export session\n        exportSession.outputURL = outputURL\n        exportSession.outputFileType = outputFileType\n        \n        // Monitor progress in a separate task\n        let progressTask = Task {\n            while !Task.isCancelled &&\n                  (exportSession.status == .waiting || exportSession.status == .exporting) {\n                await MainActor.run {\n                    self.progress = Double(exportSession.progress)\n                }\n                try? await Task.sleep(nanoseconds: 100_000_000) // Sleep for 0.1 seconds\n            }\n        }\n        \n        // Start exporting and await completion\n        try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<Void, Error>) in\n            exportSession.exportAsynchronously {\n                switch exportSession.status {\n                case .completed:\n                    // Verify the file exists after export\n                    if FileManager.default.fileExists(atPath: outputURL.path) {\n                        continuation.resume()\n                    } else {\n                        print(\"Export completed but file not found at: \\(outputURL.path)\")\n                        continuation.resume(throwing: CompressionError.compressionFailed)\n                    }\n                case .failed:\n                    if let error = exportSession.error {\n                        print(\"Export Session Failed: \\(error.localizedDescription)\")\n                        print(\"Error Details: \\(String(describing: error))\")\n                        continuation.resume(throwing: error)\n                    } else {\n                        print(\"Export Session Failed without error details\")\n                        continuation.resume(throwing: CompressionError.compressionFailed)\n                    }\n                case .cancelled:\n                    print(\"Export Session Cancelled\")\n                    continuation.resume(throwing: CompressionError.compressionFailed)\n                default:\n                    print(\"Export Session ended with status: \\(exportSession.status.rawValue)\")\n                    continuation.resume(throwing: CompressionError.compressionFailed)\n                }\n            }\n        }\n        \n        // Cancel the progress monitoring task\n        progressTask.cancel()\n        \n        // Verify the file exists one final time\n        guard FileManager.default.fileExists(atPath: outputURL.path) else {\n            throw CompressionError.compressionFailed\n        }\n        \n        // Make sure we're returning the correct path\n        if outputURL != tempURL {\n            try? FileManager.default.removeItem(at: tempURL)\n            try FileManager.default.moveItem(at: outputURL, to: tempURL)\n        }\n        \n        await MainActor.run {\n            self.progress = 1.0\n        }\n    }\n    \n    // MARK: - Lifecycle\n    deinit {\n        cleanup()\n    }\n    \n    // MARK: - Public Methods\n    @MainActor\n    func processFile(url: URL, settings: CompressionSettings? = nil, originalFileName: String? = nil) async throws {\n        \n        isProcessing = true\n        progress = 0\n        processingResult = nil\n        \n        do {\n            let result = try await processInBackground(\n                url: url,\n                settings: settings,\n                originalFileName: originalFileName ?? url.lastPathComponent\n            )\n            \n            self.processingResult = result\n        } catch {\n            isProcessing = false\n            throw error\n        }\n        \n        isProcessing = false\n        progress = 1.0\n    }\n    \n    func cleanup() {\n        processingResult = nil\n        cacheManager.cleanupOldFiles()\n    }\n    \n    // MARK: - Private Methods - Main Processing\n    private func processInBackground(url: URL, settings: CompressionSettings? = nil, originalFileName: String) async throws -> ProcessingResult {\n        let originalSize = try FileManager.default.attributesOfItem(atPath: url.path)[.size] as? Int64 ?? 0\n        let originalExtension = url.pathExtension\n        let tempURL = try cacheManager.createTemporaryURL(for: originalFileName)\n        \n        \n        \n        let compressionSettings = settings ?? CompressionSettings()\n        \n        let fileType = FileType(url: url)\n            if fileType.isVideo {\n                try await processVideo(from: url, to: tempURL, settings: compressionSettings)\n            } else if fileType.isAudio {\n                try await processAudio(from: url, to: tempURL, settings: compressionSettings)\n        } else {\n            switch url.pathExtension.lowercased() {\n            case \"pdf\":\n                try processPDF(from: url, to: tempURL)\n            case \"jpg\", \"jpeg\":\n                try processJPEG(from: url, to: tempURL, settings: compressionSettings)\n            case \"png\":\n                try processPNG(from: url, to: tempURL, settings: compressionSettings)\n            case \"heic\":\n                try processHEIC(from: url, to: tempURL, settings: compressionSettings)\n            case \"tiff\", \"tif\":\n                try processTIFF(from: url, to: tempURL, settings: compressionSettings)\n            case \"gif\":\n                try processGIF(from: url, to: tempURL)\n            case \"bmp\":\n                try processBMP(from: url, to: tempURL, settings: compressionSettings)\n            case \"webp\":\n                try processWebP(from: url, to: tempURL, settings: compressionSettings)\n            case \"svg\":\n                try processSVG(from: url, to: tempURL, settings: compressionSettings)\n            case \"raw\", \"cr2\", \"nef\", \"arw\":\n                try processRAW(from: url, to: tempURL, settings: compressionSettings)\n            case \"ico\":\n                try processICO(from: url, to: tempURL, settings: compressionSettings)\n            default:\n                throw CompressionError.unsupportedFormat\n            }\n        }\n        \n        let compressedSize = try FileManager.default.attributesOfItem(atPath: tempURL.path)[.size] as? Int64 ?? 0\n        print(\"Debug - Creating ProcessingResult:\")\n            print(\"Debug - Temp URL last component: \\(tempURL.lastPathComponent)\")\n            print(\"Debug - Original filename being used: \\(originalFileName)\")\n            return ProcessingResult(\n                originalSize: originalSize,\n                compressedSize: compressedSize,\n                compressedURL: tempURL,\n                fileName: tempURL.lastPathComponent,\n                originalFileName: originalFileName\n            )\n    }\n    \n    private func processVideo(from url: URL, to tempURL: URL, settings: CompressionSettings) async throws {\n        let videoSettings = VideoProcessor.VideoCompressionSettings(\n            quality: Float(settings.quality),\n            maxWidth: settings.maxDimension != nil ? Int(settings.maxDimension!) : nil,\n            bitrateMultiplier: 0.7,\n            frameRate: 30,\n            audioEnabled: true\n        )\n        \n        do {\n            try await videoProcessor.compressVideo(\n                inputURL: url,\n                outputURL: tempURL,\n                settings: videoSettings\n            ) { [weak self] progress in\n                Task { @MainActor in\n                    self?.progress = Double(progress)\n                }\n            }\n        } catch {\n            throw CompressionError.videoProcessingFailed\n        }\n    }\n    \n    private func processICO(from url: URL, to tempURL: URL, settings: CompressionSettings) throws {\n        guard let image = NSImage(contentsOf: url) else {\n            throw CompressionError.conversionFailed\n        }\n        \n        guard let compressedData = compressImage(image, format: .png, settings: settings) else {\n            throw CompressionError.compressionFailed\n        }\n        \n        let pngURL = tempURL.deletingPathExtension().appendingPathExtension(\"png\")\n        try compressedData.write(to: pngURL)\n    }\n    \n    private func processSVG(from url: URL, to tempURL: URL, settings: CompressionSettings) throws {\n        guard let data = try? Data(contentsOf: url),\n              let svgString = String(data: data, encoding: .utf8) else {\n            throw CompressionError.conversionFailed\n        }\n        \n        let cleanedSVG = svgString\n            .replacingOccurrences(of: \"<!--[\\\\s\\\\S]*?-->\", with: \"\", options: .regularExpression)\n            .replacingOccurrences(of: \"\\\\s+\", with: \" \", options: .regularExpression)\n            .replacingOccurrences(of: \"> <\", with: \"><\")\n            .trimmingCharacters(in: .whitespacesAndNewlines)\n        \n        try cleanedSVG.data(using: .utf8)?.write(to: tempURL)\n    }\n    \n    \n    private func processRAW(from url: URL, to tempURL: URL, settings: CompressionSettings) throws {\n        guard let imageSource = CGImageSourceCreateWithURL(url as CFURL, nil) else {\n            throw CompressionError.conversionFailed\n        }\n        \n        let options: [CFString: Any] = [\n            kCGImageSourceCreateThumbnailFromImageAlways: true,\n            kCGImageSourceCreateThumbnailWithTransform: true,\n            kCGImageSourceThumbnailMaxPixelSize: settings.maxDimension ?? 2048\n        ]\n        \n        guard let cgImage = CGImageSourceCreateThumbnailAtIndex(imageSource, 0, options as CFDictionary) else {\n            throw CompressionError.conversionFailed\n        }\n        \n        let image = NSImage(cgImage: cgImage, size: NSSize(width: cgImage.width, height: cgImage.height))\n        guard let compressedData = compressImage(image, format: .jpeg, settings: settings) else {\n            throw CompressionError.compressionFailed\n        }\n        \n        let jpegURL = tempURL.deletingPathExtension().appendingPathExtension(\"jpg\")\n        try compressedData.write(to: jpegURL)\n    }\n    \n    \n    // MARK: - Private Methods - Format-Specific Processing\n    private func processPDF(from url: URL, to tempURL: URL) throws {\n        guard let document = PDFDocument(url: url) else {\n            throw CompressionError.conversionFailed\n        }\n        \n        let newDocument = PDFDocument()\n        let totalPages = document.pageCount\n        \n        for i in 0..<totalPages {\n            autoreleasepool {\n                if let page = document.page(at: i) {\n                    if let compressedPage = try? compressPDFPage(page) {\n                        newDocument.insert(compressedPage, at: i)\n                    } else {\n                        newDocument.insert(page, at: i)\n                    }\n                }\n            }\n            \n            DispatchQueue.main.async {\n                self.progress = Double(i + 1) / Double(totalPages)\n            }\n        }\n        \n        newDocument.write(to: tempURL)\n    }\n    \n    private func processJPEG(from url: URL, to tempURL: URL, settings: CompressionSettings) throws {\n        guard let image = NSImage(contentsOf: url) else {\n            throw CompressionError.conversionFailed\n        }\n        \n        guard let compressedData = compressImage(image, format: .jpeg, settings: settings) else {\n            throw CompressionError.compressionFailed\n        }\n        \n        try compressedData.write(to: tempURL)\n    }\n    \n    private func processPNG(from url: URL, to tempURL: URL, settings: CompressionSettings) throws {\n        guard let image = NSImage(contentsOf: url) else {\n            throw CompressionError.conversionFailed\n        }\n        \n        guard let compressedData = compressImage(image, format: .png, settings: settings) else {\n            throw CompressionError.compressionFailed\n        }\n        \n        try compressedData.write(to: tempURL)\n    }\n    \n    private func processHEIC(from url: URL, to tempURL: URL, settings: CompressionSettings) throws {\n        guard let image = NSImage(contentsOf: url) else {\n            throw CompressionError.conversionFailed\n        }\n        \n        guard let compressedData = compressImage(image, format: .jpeg, settings: settings) else {\n            throw CompressionError.compressionFailed\n        }\n        \n        let jpegURL = tempURL.deletingPathExtension().appendingPathExtension(\"jpg\")\n        try compressedData.write(to: jpegURL)\n    }\n    \n    private func processTIFF(from url: URL, to tempURL: URL, settings: CompressionSettings) throws {\n        guard let image = NSImage(contentsOf: url) else {\n            throw CompressionError.conversionFailed\n        }\n        \n        guard let compressedData = compressImage(image, format: .jpeg, settings: settings) else {\n            throw CompressionError.compressionFailed\n        }\n        \n        try compressedData.write(to: tempURL)\n    }\n    \n    private func processGIF(from url: URL, to tempURL: URL) throws {\n        guard let imageSource = CGImageSourceCreateWithURL(url as CFURL, nil) else {\n            throw CompressionError.conversionFailed\n        }\n        \n        let frameCount = CGImageSourceGetCount(imageSource)\n        \n        if frameCount > 1 {\n            try compressAnimatedGIF(imageSource, frameCount: frameCount, to: tempURL)\n        } else {\n            guard let image = NSImage(contentsOf: url) else {\n                throw CompressionError.conversionFailed\n            }\n            \n            let settings = CompressionSettings(\n                pngCompressionLevel: 6,\n                preserveMetadata: true,\n                maxDimension: 2048,\n                optimizeForWeb: true\n            )\n            \n            guard let compressedData = compressImage(image, format: .png, settings: settings) else {\n                throw CompressionError.compressionFailed\n            }\n            \n            let pngURL = tempURL.deletingPathExtension().appendingPathExtension(\"png\")\n            try compressedData.write(to: pngURL)\n        }\n    }\n    \n    private func processBMP(from url: URL, to tempURL: URL, settings: CompressionSettings) throws {\n        guard let image = NSImage(contentsOf: url) else {\n            throw CompressionError.conversionFailed\n        }\n        \n        guard let compressedData = compressImage(image, format: .png, settings: settings) else {\n            throw CompressionError.compressionFailed\n        }\n        \n        let pngURL = tempURL.deletingPathExtension().appendingPathExtension(\"png\")\n        try compressedData.write(to: pngURL)\n    }\n    \n    private func processWebP(from url: URL, to tempURL: URL, settings: CompressionSettings) throws {\n        guard let image = NSImage(contentsOf: url) else {\n            throw CompressionError.conversionFailed\n        }\n        \n        let hasAlpha = imageHasAlpha(image)\n        let format: NSBitmapImageRep.FileType = hasAlpha ? .png : .jpeg\n        \n        guard let compressedData = compressImage(image, format: format, settings: settings) else {\n            throw CompressionError.compressionFailed\n        }\n        \n        let newExt = hasAlpha ? \"png\" : \"jpg\"\n        let newURL = tempURL.deletingPathExtension().appendingPathExtension(newExt)\n        try compressedData.write(to: newURL)\n    }\n    \n    // MARK: - Private Methods - Helper Functions\n    private func compressPDFPage(_ page: PDFPage) throws -> PDFPage? {\n        let pageRect = page.bounds(for: .mediaBox)\n        \n        let image = NSImage(size: pageRect.size)\n        image.lockFocus()\n        if let context = NSGraphicsContext.current?.cgContext {\n            page.draw(with: .mediaBox, to: context)\n        }\n        image.unlockFocus()\n        \n        guard let tiffData = image.tiffRepresentation,\n              let bitmap = NSBitmapImageRep(data: tiffData),\n              let compressedData = bitmap.representation(\n                using: .jpeg,\n                properties: [.compressionFactor: 0.5]\n              ),\n              let compressedImage = NSImage(data: compressedData) else {\n            return nil\n        }\n        \n        return PDFPage(image: compressedImage)\n    }\n    \n    private func compressImage(_ image: NSImage, format: NSBitmapImageRep.FileType, settings: CompressionSettings) -> Data? {\n        \n        guard let cgImage = image.cgImage(forProposedRect: nil, context: nil, hints: nil) else {\n            \n            return nil\n        }\n        \n        let processedCGImage: CGImage\n        if let maxDimension = settings.maxDimension {\n            \n            if let resized = resizeImage(cgImage, maxDimension: maxDimension) {\n                processedCGImage = resized\n                \n            } else {\n                \n                processedCGImage = cgImage\n            }\n        } else {\n            \n            processedCGImage = cgImage\n        }\n        \n        let bitmapRep = NSBitmapImageRep(cgImage: processedCGImage)\n        \n        \n        var compressionProperties: [NSBitmapImageRep.PropertyKey: Any] = [:]\n        \n        switch format {\n        case .jpeg:\n            compressionProperties[.compressionFactor] = settings.quality\n        case .png:\n            compressionProperties[.compressionFactor] = 1.0\n        default:\n            break\n        }\n        \n        return bitmapRep.representation(using: format, properties: compressionProperties)\n    }\n    \n    private func imageHasAlpha(_ image: NSImage) -> Bool {\n        guard let cgImage = image.cgImage(forProposedRect: nil, context: nil, hints: nil) else {\n            return false\n        }\n        \n        let alphaInfo = cgImage.alphaInfo\n        return alphaInfo != .none && alphaInfo != .noneSkipLast && alphaInfo != .noneSkipFirst\n    }\n    \n    private func resizeImage(_ cgImage: CGImage, maxDimension: CGFloat) -> CGImage? {\n        let currentWidth = CGFloat(cgImage.width)\n        let currentHeight = CGFloat(cgImage.height)\n        \n        // Calculate scale factor to maintain aspect ratio\n        let scaleFactor = min(maxDimension / currentWidth, maxDimension / currentHeight)\n        \n        // Only resize if the image is larger than maxDimension\n        if scaleFactor >= 1.0 {\n            return cgImage\n        }\n        \n        let newWidth = Int(currentWidth * scaleFactor)\n        let newHeight = Int(currentHeight * scaleFactor)\n        \n        // Create a bitmap context with the proper color space and bitmap info\n        let bitmapInfo: UInt32\n        if cgImage.alphaInfo == .none {\n            bitmapInfo = CGImageAlphaInfo.noneSkipLast.rawValue\n        } else {\n            bitmapInfo = CGImageAlphaInfo.premultipliedLast.rawValue\n        }\n        \n        guard let context = CGContext(\n            data: nil,\n            width: newWidth,\n            height: newHeight,\n            bitsPerComponent: 8,\n            bytesPerRow: 0,\n            space: CGColorSpace(name: CGColorSpace.sRGB)!,\n            bitmapInfo: bitmapInfo\n        ) else {\n            print(\"Debug - Failed to create context\")\n            return nil\n        }\n        \n        // Set high quality image interpolation\n        context.interpolationQuality = .high\n        \n        // Draw the image in the new size\n        let newRect = CGRect(x: 0, y: 0, width: newWidth, height: newHeight)\n        context.draw(cgImage, in: newRect)\n        \n        // Get the resized image\n        guard let resizedImage = context.makeImage() else {\n            \n            return nil\n        }\n        \n        \n        return resizedImage\n    }\n    \n    private func compressAnimatedGIF(_ imageSource: CGImageSource, frameCount: Int, to url: URL) throws {\n        guard let destination = CGImageDestinationCreateWithURL(\n            url as CFURL,\n            UTType.gif.identifier as CFString,\n            frameCount,\n            nil\n        ) else {\n            throw CompressionError.compressionFailed\n        }\n        \n        if let properties = CGImageSourceCopyProperties(imageSource, nil) {\n            CGImageDestinationSetProperties(destination, properties)\n        }\n        \n        let options: [CFString: Any] = [\n            kCGImageSourceCreateThumbnailFromImageAlways: true,\n            kCGImageSourceCreateThumbnailWithTransform: true,\n            kCGImageSourceThumbnailMaxPixelSize: 1024\n        ]\n        \n        for i in 0..<frameCount {\n            autoreleasepool {\n                guard let frameProperties = CGImageSourceCopyPropertiesAtIndex(imageSource, i, nil) else {\n                    return\n                }\n                \n                // Extract GIF-specific properties\n                let gifProperties = (frameProperties as Dictionary)[kCGImagePropertyGIFDictionary] as? Dictionary<CFString, Any>\n                \n                // Get delay time for the frame\n                let defaultDelay = 0.1\n                let delayTime: Double\n                if let delay = gifProperties?[kCGImagePropertyGIFDelayTime] as? Double {\n                    delayTime = delay\n                } else {\n                    delayTime = defaultDelay\n                }\n                \n                // Create optimized frame\n                guard let frameImage = CGImageSourceCreateImageAtIndex(imageSource, i, options as CFDictionary) else {\n                    return\n                }\n                \n                // Prepare frame properties for destination\n                let destFrameProperties: [CFString: Any] = [\n                    kCGImagePropertyGIFDictionary: [\n                        kCGImagePropertyGIFDelayTime: delayTime\n                    ]\n                ]\n                \n                CGImageDestinationAddImage(destination, frameImage, destFrameProperties as CFDictionary)\n            }\n            \n            DispatchQueue.main.async {\n                self.progress = Double(i + 1) / Double(frameCount)\n            }\n        }\n        \n        if !CGImageDestinationFinalize(destination) {\n            throw CompressionError.compressionFailed\n        }\n    }\n    \n    // MARK: - Metadata Handling\n    private func extractMetadata(from imageSource: CGImageSource) -> [CFString: Any]? {\n        guard let properties = CGImageSourceCopyProperties(imageSource, nil) as? [CFString: Any] else {\n            return nil\n        }\n        \n        var metadata: [CFString: Any] = [:]\n        \n        // Extract relevant metadata while excluding unnecessary data\n        let keysToPreserve: [CFString] = [\n            kCGImagePropertyOrientation,\n            kCGImagePropertyDPIHeight,\n            kCGImagePropertyDPIWidth,\n            kCGImagePropertyPixelHeight,\n            kCGImagePropertyPixelWidth,\n            kCGImagePropertyProfileName\n        ]\n        \n        for key in keysToPreserve {\n            if let value = properties[key] {\n                metadata[key] = value\n            }\n        }\n        \n        return metadata\n    }\n    \n    // MARK: - Quality and Optimization\n    private func determineOptimalSettings(for image: NSImage, format: NSBitmapImageRep.FileType) -> CompressionSettings {\n        let size = image.size\n        let totalPixels = size.width * size.height\n        \n        // Base settings\n        var settings = CompressionSettings()\n        \n        // Adjust quality based on image size\n        if totalPixels > 4_000_000 { // 2000x2000 pixels\n            settings.maxDimension = 2048\n            settings.quality = 0.7\n        } else if totalPixels > 1_000_000 { // 1000x1000 pixels\n            settings.maxDimension = 1500\n            settings.quality = 0.8\n        } else {\n            settings.maxDimension = nil\n            settings.quality = 0.9\n        }\n        \n        // Format-specific adjustments\n        switch format {\n        case .jpeg:\n            // JPEG-specific optimizations\n            if totalPixels > 8_000_000 {\n                settings.quality = 0.6\n            }\n            settings.preserveMetadata = true\n            \n        case .png:\n            // PNG-specific optimizations\n            if imageHasAlpha(image) {\n                settings.pngCompressionLevel = 7\n            } else {\n                settings.pngCompressionLevel = 9\n            }\n            settings.preserveMetadata = true\n            \n        default:\n            settings.preserveMetadata = false\n        }\n        \n        return settings\n    }\n    \n    // MARK: - Progress Tracking\n    private func updateProgress(_ progress: Double) {\n        DispatchQueue.main.async {\n            self.progress = min(max(progress, 0), 1)\n        }\n    }\n}\n    \n    // MARK: - Extensions\n    extension FileProcessor {\n        enum FileType {\n            case pdf\n            case jpeg\n            case png\n            case heic\n            case gif\n            case tiff\n            case bmp\n            case webp\n            case svg\n            case raw\n            case ico\n            case mp4\n            case mov\n            case avi\n            case mpeg2\n            case quickTime\n            case mp3\n            case wav\n            case m4a\n            case aiff\n            case unknown\n            \n            init(url: URL) {\n                switch url.pathExtension.lowercased() {\n                case \"pdf\": self = .pdf\n                case \"jpg\", \"jpeg\": self = .jpeg\n                case \"png\": self = .png\n                case \"heic\": self = .heic\n                case \"gif\": self = .gif\n                case \"tiff\", \"tif\": self = .tiff\n                case \"bmp\": self = .bmp\n                case \"webp\": self = .webp\n                case \"svg\": self = .svg\n                case \"raw\", \"cr2\", \"nef\", \"arw\": self = .raw\n                case \"ico\": self = .ico\n                case \"mp4\": self = .mp4\n                case \"mov\": self = .mov\n                case \"avi\": self = .avi\n                case \"mpg\", \"mpeg\": self = .mpeg2\n                case \"qt\": self = .quickTime\n                case \"mp3\": self = .mp3\n                case \"wav\": self = .wav\n                case \"m4a\": self = .m4a\n                case \"aiff\", \"aif\": self = .aiff\n                default: self = .unknown\n                }\n            }\n            \n            var isAudio: Bool {\n                        switch self {\n                        case .mp3, .wav, .m4a, .aiff:\n                            return true\n                        default:\n                            return false\n                        }\n                    }\n            \n            var isVideo: Bool {\n                switch self {\n                case .mp4, .mov, .avi, .mpeg2, .quickTime:\n                    return true\n                default:\n                    return false\n                }\n            }\n            \n            var defaultOutputExtension: String {\n                switch self {\n                case .pdf: return \"pdf\"\n                case .jpeg: return \"jpg\"\n                case .png: return \"png\"\n                case .heic: return \"jpg\"\n                case .gif: return \"gif\"\n                case .tiff: return \"jpg\"\n                case .bmp: return \"png\"\n                case .webp: return \"jpg\"\n                case .svg: return \"svg\"\n                case .raw: return \"jpg\"\n                case .ico: return \"png\"\n                case .mp4, .mov, .avi, .mpeg2, .quickTime: return \"mp4\"\n                case .mp3: return \"mp3\"\n                case .wav: return \"wav\"\n                case .m4a: return \"m4a\"\n                case .aiff: return \"aiff\"\n                case .unknown: return \"\"\n                }\n            }\n        }\n    }\n"
  },
  {
    "path": "Achico/Processor/VideoProcessor.swift",
    "content": "import Foundation\nimport AVFoundation\n\n@available(macOS 12.0, *)\nactor VideoProcessor {\n    enum VideoError: LocalizedError {\n        case exportFailed\n        case invalidInput\n        case compressionFailed\n        \n        var errorDescription: String? {\n            switch self {\n            case .exportFailed: return \"Failed to export video\"\n            case .invalidInput: return \"Invalid input video\"\n            case .compressionFailed: return \"Video compression failed\"\n            }\n        }\n    }\n    \n    struct VideoCompressionSettings: Sendable {\n        let quality: Float\n        let maxWidth: Int?\n        let bitrateMultiplier: Float\n        let frameRate: Int?\n        let audioEnabled: Bool\n        \n        init(\n            quality: Float = 0.7,\n            maxWidth: Int? = nil,\n            bitrateMultiplier: Float = 0.7,\n            frameRate: Int? = 30,\n            audioEnabled: Bool = true\n        ) {\n            self.quality = quality\n            self.maxWidth = maxWidth\n            self.bitrateMultiplier = bitrateMultiplier\n            self.frameRate = frameRate\n            self.audioEnabled = audioEnabled\n        }\n    }\n    \n    func compressVideo(\n        inputURL: URL,\n        outputURL: URL,\n        settings: VideoCompressionSettings,\n        progressHandler: @Sendable @escaping (Float) -> Void\n    ) async throws {\n        let asset = AVAsset(url: inputURL)\n        \n        // Get video track\n        guard let videoTrack = try? await asset.loadTracks(withMediaType: .video).first else {\n            throw VideoError.invalidInput\n        }\n        \n        // Get original dimensions and apply size limit if needed\n        let originalSize = try await videoTrack.load(.naturalSize)\n        var targetSize = originalSize\n        \n        if let maxWidth = settings.maxWidth {\n            let scale = Float(maxWidth) / Float(originalSize.width)\n            if scale < 1.0 {\n                targetSize = CGSize(\n                    width: CGFloat(maxWidth),\n                    height: CGFloat(Float(originalSize.height) * scale)\n                )\n            }\n        }\n        \n        // Get original frame rate\n        let nominalFrameRate = try await videoTrack.load(.nominalFrameRate)\n        let targetFrameRate = Float(settings.frameRate ?? Int(nominalFrameRate))\n        \n        // Create export session\n        guard let exportSession = AVAssetExportSession(\n            asset: asset,\n            presetName: AVAssetExportPresetHighestQuality\n        ) else {\n            throw VideoError.exportFailed\n        }\n        \n        // Calculate bitrate\n        let originalBitrate = try await estimateBitrate(for: videoTrack)\n        let targetBitrate = Int(Float(originalBitrate) * settings.bitrateMultiplier)\n        \n        // Configure compression\n        let compressionProperties: [String: Any] = [\n            AVVideoAverageBitRateKey: targetBitrate,\n            AVVideoProfileLevelKey: AVVideoProfileLevelH264HighAutoLevel,\n            AVVideoH264EntropyModeKey: AVVideoH264EntropyModeCABAC\n        ]\n        \n        // Video settings\n        let videoSettings: [String: Any] = [\n            AVVideoCodecKey: AVVideoCodecType.h264,\n            AVVideoWidthKey: Int(targetSize.width),\n            AVVideoHeightKey: Int(targetSize.height),\n            AVVideoCompressionPropertiesKey: compressionProperties\n        ]\n        \n        // Create and configure video composition\n        let composition = AVMutableVideoComposition()\n        composition.renderSize = targetSize\n        composition.frameDuration = CMTime(value: 1, timescale: CMTimeScale(targetFrameRate))\n        \n        let instruction = AVMutableVideoCompositionInstruction()\n        instruction.timeRange = CMTimeRange(\n            start: .zero,\n            duration: try await asset.load(.duration)\n        )\n        \n        let layerInstruction = AVMutableVideoCompositionLayerInstruction(assetTrack: videoTrack)\n        \n        // Calculate transform for proper resizing\n        let originalTransform = try await videoTrack.load(.preferredTransform)\n        var finalTransform = originalTransform\n        \n        let scaleX = targetSize.width / originalSize.width\n        let scaleY = targetSize.height / originalSize.height\n        \n        // Apply scaling transform\n        finalTransform = finalTransform.concatenating(CGAffineTransform(scaleX: scaleX, y: scaleY))\n        \n        layerInstruction.setTransform(finalTransform, at: .zero)\n        instruction.layerInstructions = [layerInstruction]\n        composition.instructions = [instruction]\n        \n        // Configure export session\n        exportSession.videoComposition = composition\n        exportSession.outputURL = outputURL\n        exportSession.outputFileType = .mp4\n        \n        // Use Task for progress monitoring\n        let progressTask = Task { @MainActor in\n            repeat {\n                progressHandler(exportSession.progress)\n                try await Task.sleep(nanoseconds: 100_000_000) // 0.1 second\n            } while exportSession.status == .exporting\n        }\n        \n        // Start export and wait for completion\n        try await withCheckedThrowingContinuation { continuation in\n            exportSession.exportAsynchronously {\n                progressTask.cancel() // Stop progress monitoring\n                switch exportSession.status {\n                case .completed:\n                    continuation.resume()\n                case .failed:\n                    let error = exportSession.error ?? VideoError.exportFailed\n                    continuation.resume(throwing: error)\n                default:\n                    continuation.resume(throwing: VideoError.compressionFailed)\n                }\n            }\n        }\n    }\n    \n    private func estimateBitrate(for videoTrack: AVAssetTrack) async throws -> Int {\n        let duration = try await videoTrack.load(.timeRange).duration.seconds\n        let size = try await videoTrack.load(.totalSampleDataLength)\n        return Int(Double(size) * 8 / duration) // bits per second\n    }\n}\n"
  },
  {
    "path": "Achico/Resources/Assets.xcassets/AccentColor.colorset/Contents.json",
    "content": "{\n  \"colors\" : [\n    {\n      \"color\" : {\n        \"color-space\" : \"display-p3\",\n        \"components\" : {\n          \"alpha\" : \"1.000\",\n          \"blue\" : \"0.850\",\n          \"green\" : \"0.470\",\n          \"red\" : \"0.250\"\n        }\n      },\n      \"idiom\" : \"universal\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "Achico/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"filename\" : \"AppIcon-16.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"1x\",\n      \"size\" : \"16x16\"\n    },\n    {\n      \"filename\" : \"AppIcon-32 1.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"2x\",\n      \"size\" : \"16x16\"\n    },\n    {\n      \"filename\" : \"AppIcon-32.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"1x\",\n      \"size\" : \"32x32\"\n    },\n    {\n      \"filename\" : \"AppIcon-64.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"2x\",\n      \"size\" : \"32x32\"\n    },\n    {\n      \"filename\" : \"AppIcon-128.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"1x\",\n      \"size\" : \"128x128\"\n    },\n    {\n      \"filename\" : \"AppIcon-256 1.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"2x\",\n      \"size\" : \"128x128\"\n    },\n    {\n      \"filename\" : \"AppIcon-256.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"1x\",\n      \"size\" : \"256x256\"\n    },\n    {\n      \"filename\" : \"AppIcon-512 1.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"2x\",\n      \"size\" : \"256x256\"\n    },\n    {\n      \"filename\" : \"AppIcon-512.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"1x\",\n      \"size\" : \"512x512\"\n    },\n    {\n      \"filename\" : \"AppIcon-1024.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"2x\",\n      \"size\" : \"512x512\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "Achico/Resources/Assets.xcassets/Contents.json",
    "content": "{\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "Achico/Resources/CompressionSettings.swift",
    "content": "import Foundation\nimport CoreGraphics\n\npublic struct CompressionSettings {\n    var quality: CGFloat = 0.7        // JPEG/HEIC quality (0.0-1.0)\n    var pngCompressionLevel: Int = 6  // PNG compression (0-9)\n    var preserveMetadata: Bool = false\n    var maxDimension: CGFloat? = nil  // Downsample if larger\n    var optimizeForWeb: Bool = true   // Additional optimizations for web use\n    var audioBitRate: Int?        // In bits per second\n    var audioSampleRate: Double? // In Hertz\n    \n    public init(\n        quality: CGFloat = 0.7,\n        pngCompressionLevel: Int = 6,\n        preserveMetadata: Bool = false,\n        maxDimension: CGFloat? = nil,\n        optimizeForWeb: Bool = true\n    ) {\n        self.quality = quality\n        self.pngCompressionLevel = pngCompressionLevel\n        self.preserveMetadata = preserveMetadata\n        self.maxDimension = maxDimension\n        self.optimizeForWeb = optimizeForWeb\n    }\n}\n"
  },
  {
    "path": "Achico/Resources/FileDropDelegate.swift",
    "content": "import SwiftUI\nimport UniformTypeIdentifiers\nstruct FileDropDelegate: DropDelegate {\n    @Binding var isDragging: Bool\n    let supportedTypes: [UTType]\n    let handleDrop: ([NSItemProvider]) -> Void\n    \n    func validateDrop(info: DropInfo) -> Bool {\n        return info.hasItemsConforming(to: supportedTypes.map(\\.identifier))\n    }\n    \n    func dropEntered(info: DropInfo) {\n        isDragging = true\n    }\n    \n    func dropExited(info: DropInfo) {\n        isDragging = false\n    }\n    \n    func performDrop(info: DropInfo) -> Bool {\n        isDragging = false\n        let providers = info.itemProviders(for: supportedTypes.map(\\.identifier))\n        handleDrop(providers)\n        return true\n    }\n}\n"
  },
  {
    "path": "Achico/Resources/MenuBarController.swift",
    "content": "import SwiftUI\nimport AppKit\n\nclass MenuBarController: NSObject, ObservableObject {\n    @Published private(set) var updater = UpdateChecker()\n    private var statusItem: NSStatusItem!\n    \n    override init() {\n        super.init()\n        \n        // Initialize status item on main queue\n        DispatchQueue.main.async {\n            self.setupMenuBar()\n            self.updater.checkForUpdates()\n        }\n    }\n    \n    private func setupMenuBar() {\n        // Create the status item\n        statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)\n        \n        if let button = statusItem.button {\n            button.image = NSImage(systemSymbolName: \"checkmark.circle\", accessibilityDescription: \"Update Status\")\n            \n            // Create the menu\n            let menu = NSMenu()\n            menu.delegate = self\n            \n            // Set the menu\n            statusItem.menu = menu\n            \n            // Update the button image when the status changes\n            updater.onStatusChange = { [weak self] newIcon in\n                guard self != nil else { return }\n                DispatchQueue.main.async {\n                    button.image = NSImage(systemSymbolName: newIcon, accessibilityDescription: \"Update Status\")\n                }\n            }\n        }\n    }\n    \n    @objc private func checkForUpdates() {\n        updater.checkForUpdates()\n    }\n    \n    @objc private func downloadUpdate() {\n        if let url = updater.downloadURL {\n            NSWorkspace.shared.open(url)\n        }\n    }\n}\n\nextension MenuBarController: NSMenuDelegate {\n    func menuWillOpen(_ menu: NSMenu) {\n        // Clear existing items\n        menu.removeAllItems()\n        \n        // Add version\n        let versionItem = NSMenuItem(title: \"Achico v\\(Bundle.main.object(forInfoDictionaryKey: \"CFBundleShortVersionString\") as? String ?? \"1.0.0\")\", action: nil, keyEquivalent: \"\")\n        versionItem.isEnabled = false\n        menu.addItem(versionItem)\n        \n        menu.addItem(NSMenuItem.separator())\n        \n        // Add status\n        if updater.isChecking {\n            let checkingItem = NSMenuItem(title: \"Checking for updates...\", action: nil, keyEquivalent: \"\")\n            checkingItem.isEnabled = false\n            menu.addItem(checkingItem)\n        } else if updater.updateAvailable {\n            if let version = updater.latestVersion {\n                let availableItem = NSMenuItem(title: \"Version \\(version) Available\", action: nil, keyEquivalent: \"\")\n                availableItem.isEnabled = false\n                menu.addItem(availableItem)\n            }\n            let downloadItem = NSMenuItem(title: \"Download Update\", action: #selector(downloadUpdate), keyEquivalent: \"\")\n            downloadItem.target = self\n            menu.addItem(downloadItem)\n        } else {\n            let upToDateItem = NSMenuItem(title: \"App is up to date\", action: nil, keyEquivalent: \"\")\n            upToDateItem.isEnabled = false\n            menu.addItem(upToDateItem)\n        }\n        \n        menu.addItem(NSMenuItem.separator())\n        \n        // Add check for updates item\n        let checkItem = NSMenuItem(title: \"Check for Updates...\", action: #selector(checkForUpdates), keyEquivalent: \"u\")\n        checkItem.target = self\n        menu.addItem(checkItem)\n    }\n    \n    func menuDidClose(_ menu: NSMenu) {\n        // Optional: Handle menu closing\n    }\n    \n    func numberOfItems(in menu: NSMenu) -> Int {\n        // Let the menu build dynamically\n        return menu.numberOfItems\n    }\n}\n"
  },
  {
    "path": "Achico/Resources/UpdateChecker.swift",
    "content": "import Foundation\n\nstruct GitHubRelease: Codable {\n    let tagName: String\n    let name: String\n    let body: String\n    let htmlUrl: String\n    \n    enum CodingKeys: String, CodingKey {\n        case tagName = \"tag_name\"\n        case name\n        case body\n        case htmlUrl = \"html_url\"\n    }\n}\n\nclass UpdateChecker: ObservableObject {\n    @Published var updateAvailable = false\n    @Published var latestVersion: String?\n    @Published var releaseNotes: String?\n    @Published var downloadURL: URL?\n    @Published var isChecking = false\n    @Published var error: String?\n    @Published var statusIcon: String = \"checkmark.circle\"\n    \n    var onStatusChange: ((String) -> Void)?\n    var onUpdateAvailable: (() -> Void)?\n    \n    private let currentVersion: String\n    private let githubRepo: String\n    private var updateCheckTimer: Timer?\n    \n    init() {\n            self.currentVersion = Bundle.main.object(forInfoDictionaryKey: \"CFBundleShortVersionString\") as? String ?? \"1.0.0\"\n            self.githubRepo = \"nuance-dev/Achico\"\n            setupTimer()\n            updateStatusIcon()\n        }\n    \n    private func setupTimer() {\n        // Initial check after 2 seconds\n        DispatchQueue.main.asyncAfter(deadline: .now() + 2) { [weak self] in\n            self?.checkForUpdates()\n        }\n        \n        // Periodic check every 24 hours\n        updateCheckTimer = Timer.scheduledTimer(withTimeInterval: 24 * 60 * 60, repeats: true) { [weak self] _ in\n            self?.checkForUpdates()\n        }\n    }\n    \n    private func updateStatusIcon() {\n        DispatchQueue.main.async { [weak self] in\n            guard let self = self else { return }\n            if self.isChecking {\n                self.statusIcon = \"arrow.triangle.2.circlepath\"\n            } else {\n                self.statusIcon = self.updateAvailable ? \"exclamationmark.circle\" : \"checkmark.circle\"\n            }\n            self.onStatusChange?(self.statusIcon)\n        }\n    }\n    \n    func checkForUpdates() {\n        print(\"Checking for updates...\")\n        print(\"Current version: \\(currentVersion)\")\n        \n        isChecking = true\n        updateStatusIcon()\n        error = nil\n        \n        let baseURL = \"https://api.github.com/repos/\\(githubRepo)/releases/latest\"\n        guard let url = URL(string: baseURL) else {\n            error = \"Invalid GitHub repository URL\"\n            isChecking = false\n            updateStatusIcon()\n            return\n        }\n        \n        var request = URLRequest(url: url)\n        request.setValue(\"application/vnd.github.v3+json\", forHTTPHeaderField: \"Accept\")\n        request.setValue(\"Achico-App/\\(currentVersion)\", forHTTPHeaderField: \"User-Agent\")\n        \n        URLSession.shared.dataTask(with: request) { [weak self] data, response, error in\n            DispatchQueue.main.async {\n                self?.handleUpdateResponse(data: data, response: response as? HTTPURLResponse, error: error)\n            }\n        }.resume()\n    }\n    \n    private func handleUpdateResponse(data: Data?, response: HTTPURLResponse?, error: Error?) {\n        defer {\n            isChecking = false\n            updateStatusIcon()\n        }\n        \n        if let error = error {\n            print(\"Network error: \\(error)\")\n            self.error = \"Network error: \\(error.localizedDescription)\"\n            return\n        }\n        \n        guard let response = response else {\n            print(\"Invalid response\")\n            self.error = \"Invalid response from server\"\n            return\n        }\n        \n        print(\"Response status code: \\(response.statusCode)\")\n        \n        guard response.statusCode == 200 else {\n            self.error = \"Server error: \\(response.statusCode)\"\n            return\n        }\n        \n        guard let data = data else {\n            self.error = \"No data received\"\n            return\n        }\n        \n        do {\n            \n            let decoder = JSONDecoder()\n            let release = try decoder.decode(GitHubRelease.self, from: data)\n            \n            let cleanLatestVersion = release.tagName.replacingOccurrences(of: \"v\", with: \"\")\n            print(\"Latest version: \\(cleanLatestVersion)\")\n            print(\"Current version for comparison: \\(currentVersion)\")\n            \n            updateAvailable = compareVersions(current: currentVersion, latest: cleanLatestVersion)\n                        if updateAvailable {\n                            DispatchQueue.main.async {\n                                self.onUpdateAvailable?()\n                            }\n                        }\n            \n            latestVersion = cleanLatestVersion\n            releaseNotes = release.body\n            downloadURL = URL(string: release.htmlUrl)\n            \n            updateAvailable = compareVersions(current: currentVersion, latest: cleanLatestVersion)\n            print(\"Update available: \\(updateAvailable)\")\n            \n        } catch {\n            print(\"Parsing error: \\(error)\")\n            self.error = \"Failed to parse response: \\(error.localizedDescription)\"\n        }\n    }\n    \n    private func compareVersions(current: String, latest: String) -> Bool {\n        // Clean and split versions\n        let currentParts = current.replacingOccurrences(of: \"v\", with: \"\")\n            .split(separator: \".\")\n            .compactMap { Int($0) }\n        \n        let latestParts = latest.replacingOccurrences(of: \"v\", with: \"\")\n            .split(separator: \".\")\n            .compactMap { Int($0) }\n        \n        \n        // Ensure we have at least 3 components (major.minor.patch)\n        let paddedCurrent = currentParts + Array(repeating: 0, count: max(3 - currentParts.count, 0))\n        let paddedLatest = latestParts + Array(repeating: 0, count: max(3 - latestParts.count, 0))\n        \n        \n        // Compare each version component\n        for i in 0..<min(paddedCurrent.count, paddedLatest.count) {\n            if paddedLatest[i] > paddedCurrent[i] {\n                return true\n            } else if paddedLatest[i] < paddedCurrent[i] {\n                return false\n            }\n        }\n        \n        print(\"Versions are equal\")\n        return false\n    }\n    \n    deinit {\n        updateCheckTimer?.invalidate()\n    }\n}\n"
  },
  {
    "path": "Achico/UI Components/ButtonGroup.swift",
    "content": "import SwiftUI\n\nstruct ToolbarButton: View {\n    let title: String\n    let icon: String\n    let action: () -> Void\n    let isFirst: Bool\n    let isLast: Bool\n    \n    var body: some View {\n        Button(action: action) {\n            HStack(spacing: 6) {\n                Image(systemName: icon)\n                    .font(.system(size: 14))\n                Text(title)\n                    .font(.system(size: 13, weight: .medium))\n            }\n            .frame(height: 36)\n            .padding(.horizontal, 16)\n            .foregroundColor(.primary)\n            .background(Color.clear)\n            .contentShape(Rectangle())\n        }\n        .buttonStyle(PlainButtonStyle())\n    }\n}\n\nstruct ButtonDivider: View {\n    var body: some View {\n        Divider()\n            .frame(height: 24)\n    }\n}\n\nstruct ButtonGroup: View {\n    let buttons: [(title: String, icon: String, action: () -> Void)]\n    \n    var body: some View {\n        HStack(spacing: 0) {\n            ForEach(Array(buttons.enumerated()), id: \\.offset) { index, button in\n                if index > 0 {\n                    ButtonDivider()\n                }\n                \n                ToolbarButton(\n                    title: button.title,\n                    icon: button.icon,\n                    action: button.action,\n                    isFirst: index == 0,\n                    isLast: index == buttons.count - 1\n                )\n            }\n        }\n        .background(backgroundView)\n    }\n    \n    private var backgroundView: some View {\n        ZStack {\n            // Base background\n            RoundedRectangle(cornerRadius: 12)\n                .fill(Color(NSColor.windowBackgroundColor).opacity(0.5))\n            \n            // Subtle border\n            RoundedRectangle(cornerRadius: 12)\n                .strokeBorder(Color.primary.opacity(0.1), lineWidth: 1)\n            \n            // Glass effect overlay\n            RoundedRectangle(cornerRadius: 12)\n                .stroke(Color.white.opacity(0.1), lineWidth: 1)\n        }\n    }\n}\n"
  },
  {
    "path": "Achico/UI Components/GlassButtonStyle.swift",
    "content": "import SwiftUI\n\nstruct GlassButtonStyle: ButtonStyle {\n    func makeBody(configuration: Configuration) -> some View {\n        configuration.label\n            .padding(.horizontal, 20)\n            .padding(.vertical, 10)\n            .background(\n                RoundedRectangle(cornerRadius: 8)\n                    .fill(Color.primary.opacity(0.1))\n                    .overlay(\n                        RoundedRectangle(cornerRadius: 8)\n                            .stroke(Color.primary.opacity(0.2), lineWidth: 1)\n                    )\n            )\n            .opacity(configuration.isPressed ? 0.8 : 1.0)\n    }\n}\n"
  },
  {
    "path": "Achico/UI Components/TitleBarAccessory.swift",
    "content": "import SwiftUI\n\nstruct TitleBarAccessory: View {\n    @AppStorage(\"isDarkMode\") private var isDarkMode = false\n    \n    var body: some View {\n        Button(action: {\n            isDarkMode.toggle()\n        }) {\n            Image(systemName: isDarkMode ? \"sun.max.fill\" : \"moon.fill\")\n                .foregroundColor(.primary)\n        }\n        .buttonStyle(PlainButtonStyle())\n        .frame(width: 30, height: 30)\n    }\n}\n"
  },
  {
    "path": "Achico/UI Components/VisualEffectBlur.swift",
    "content": "import SwiftUI\n\nstruct VisualEffectBlur: NSViewRepresentable {\n    var material: NSVisualEffectView.Material\n    var blendingMode: NSVisualEffectView.BlendingMode\n    \n    func makeNSView(context: Context) -> NSVisualEffectView {\n        let view = NSVisualEffectView()\n        view.state = .active\n        view.material = material\n        view.blendingMode = blendingMode\n        view.alphaValue = 0.9\n        return view\n    }\n    \n    func updateNSView(_ nsView: NSVisualEffectView, context: Context) {}\n}\n"
  },
  {
    "path": "Achico/UI Components/WindowAccessor.swift",
    "content": "import SwiftUI\n\nstruct WindowAccessor: NSViewRepresentable {\n    func makeNSView(context: Context) -> NSView {\n        let nsView = NSView()\n        \n        DispatchQueue.main.async {\n            if let window = nsView.window {\n                let titleBarAccessory = NSTitlebarAccessoryViewController()\n                let hostingView = NSHostingView(rootView: TitleBarAccessory())\n                \n                hostingView.frame.size = hostingView.fittingSize\n                titleBarAccessory.view = hostingView\n                titleBarAccessory.layoutAttribute = .trailing\n                \n                window.addTitlebarAccessoryViewController(titleBarAccessory)\n            }\n        }\n        \n        return nsView\n    }\n    \n    func updateNSView(_ nsView: NSView, context: Context) {}\n}\n"
  },
  {
    "path": "Achico/Views/ContentView.swift",
    "content": "import SwiftUI\nimport UniformTypeIdentifiers\nimport AppKit\n\nstruct ContentView: View {\n    @StateObject private var processor = FileProcessor()\n    @StateObject private var multiProcessor = MultiFileProcessor()\n    @State private var isDragging = false\n    @State private var showAlert = false\n    @State private var alertMessage = \"\"\n    @State private var shouldResize = false\n    @State private var maxDimension = \"2048\"\n    \n    let supportedTypes: [UTType] = [\n        .pdf,      // PDF Documents\n        .jpeg,     // JPEG Images\n        .tiff,     // TIFF Images\n        .png,      // PNG Images\n        .heic,     // HEIC Images\n        .gif,      // GIF Images\n        .bmp,      // BMP Images\n        .webP,     // WebP Images\n        .svg,      // SVG Images\n        .rawImage, // RAW Images\n        .ico,      // ICO Images\n        .mpeg4Movie,    // MP4 Video\n        .movie,         // MOV\n        .avi,          // AVI\n        .mpeg2Video,   // MPEG-2\n        .quickTimeMovie, // QuickTime\n        .mpeg4Audio,     // MP4 Audio\n        .mp3,          // MP3 Audio\n        .wav,          // WAV Audio\n        .aiff,         // AIFF Audio\n    ]\n    \n    var body: some View {\n        ZStack {\n            VisualEffectBlur(material: .headerView, blendingMode: .behindWindow)\n                .ignoresSafeArea()\n            \n            VStack(spacing: 20) {\n                if processor.isProcessing {\n                    // Single file processing view\n                    VStack(spacing: 24) {\n                        // Progress Circle\n                        ZStack {\n                            Circle()\n                                .stroke(Color.secondary.opacity(0.2), lineWidth: 4)\n                                .frame(width: 60, height: 60)\n                            \n                            Circle()\n                                .trim(from: 0, to: processor.progress)\n                                .stroke(Color.accentColor, style: StrokeStyle(lineWidth: 4, lineCap: .round))\n                                .frame(width: 60, height: 60)\n                                .rotationEffect(.degrees(-90))\n                            \n                            Text(\"\\(Int(processor.progress * 100))%\")\n                                .font(.system(size: 14, weight: .medium))\n                        }\n                        \n                        VStack(spacing: 8) {\n                            Text(\"Compressing File\")\n                                .font(.system(size: 16, weight: .semibold))\n                            Text(\"This may take a moment...\")\n                                .font(.system(size: 14))\n                                .foregroundColor(.secondary)\n                        }\n                    }\n                    .frame(maxWidth: 320)\n                    .padding(32)\n                    .background(\n                        RoundedRectangle(cornerRadius: 16)\n                            .fill(Color(NSColor.windowBackgroundColor))\n                            .opacity(0.8)\n                            .shadow(color: Color.black.opacity(0.1), radius: 20, x: 0, y: 10)\n                    )\n                } else if let result = processor.processingResult {\n                    ResultView(result: result) {\n                        Task {\n                            await saveCompressedFile(url: result.compressedURL, originalName: result.fileName)\n                        }\n                    } onReset: {\n                        processor.cleanup()\n                    }\n                } else if !multiProcessor.files.isEmpty {\n                    MultiFileView(\n                        processor: multiProcessor,\n                        shouldResize: $shouldResize,\n                        maxDimension: $maxDimension,\n                        supportedTypes: supportedTypes\n                    )\n                } else {\n                    ZStack {\n                        DropZoneView(\n                            isDragging: $isDragging,\n                            shouldResize: $shouldResize,\n                            maxDimension: $maxDimension,\n                            onTap: selectFiles\n                        )\n                        \n                        Rectangle()\n                            .fill(Color.clear)\n                            .frame(maxWidth: .infinity, maxHeight: .infinity)\n                            .overlay(isDragging ? Color.accentColor.opacity(0.2) : Color.clear)\n                            .onDrop(of: supportedTypes, isTargeted: $isDragging) { providers in\n                                handleDrop(providers: providers)\n                                return true\n                            }\n                    }\n                }\n            }\n            .padding()\n        }\n        .frame(minWidth: 400, minHeight: 500)\n        .alert(\"Error\", isPresented: $showAlert) {\n            Button(\"OK\", role: .cancel) { }\n        } message: {\n            Text(alertMessage)\n        }\n    }\n    \n    private func selectFiles() {\n        let panel = NSOpenPanel()\n        panel.allowedContentTypes = supportedTypes\n        panel.allowsMultipleSelection = true\n        \n        if let window = NSApp.windows.first {\n            panel.beginSheetModal(for: window) { response in\n                if response == .OK {\n                    if panel.urls.count == 1, let url = panel.urls.first {\n                        print(\"📁 Selected single file: \\(url.path)\")\n                        print(\"📝 Original filename: \\(url.lastPathComponent)\")\n                        handleFileSelection(url: url, originalFilename: url.lastPathComponent)  // Pass originalFilename\n                    } else if panel.urls.count > 1 {\n                        print(\"📁 Selected multiple files: \\(panel.urls.map { $0.lastPathComponent })\")\n                        Task { @MainActor in\n                            multiProcessor.addFiles(panel.urls)\n                        }\n                    }\n                }\n            }\n        }\n    }\n        \n        private func handleDrop(providers: [NSItemProvider]) {\n            print(\"🔄 Handling drop with \\(providers.count) providers\")\n            if providers.count == 1 {\n                guard let provider = providers.first else { return }\n                handleSingleFileDrop(provider: provider)\n            } else {\n                handleMultiFileDrop(providers: providers)\n            }\n        }\n        \n    private func handleSingleFileDrop(provider: NSItemProvider) {\n        for type in supportedTypes {\n            if provider.hasItemConformingToTypeIdentifier(type.identifier) {\n                print(\"📥 Processing dropped file of type: \\(type.identifier)\")\n                provider.loadFileRepresentation(forTypeIdentifier: type.identifier) { url, error in\n                    guard let url = url else {\n                        print(\"❌ Failed to load dropped file URL\")\n                        Task { @MainActor in\n                            alertMessage = \"Failed to load file\"\n                            showAlert = true\n                        }\n                        return\n                    }\n                    \n                    print(\"📄 Original dropped file URL: \\(url.path)\")\n                    let originalFilename = url.lastPathComponent\n                    print(\"📝 Original dropped filename: \\(originalFilename)\")\n                    \n                    let tempURL = FileManager.default.temporaryDirectory\n                        .appendingPathComponent(UUID().uuidString)\n                        .appendingPathExtension(url.pathExtension)\n                    \n                    print(\"🔄 Creating temp file at: \\(tempURL.path)\")\n                    \n                    do {\n                        try FileManager.default.copyItem(at: url, to: tempURL)\n                        print(\"✅ Successfully copied to temp location\")\n                        \n                        Task { @MainActor in\n                            handleFileSelection(url: tempURL, originalFilename: originalFilename)  // Pass originalFilename\n                        }\n                    } catch {\n                        print(\"❌ Failed to copy dropped file: \\(error.localizedDescription)\")\n                        Task { @MainActor in\n                            alertMessage = \"Failed to process dropped file\"\n                            showAlert = true\n                        }\n                    }\n                }\n                return\n            }\n        }\n    }\n\n    private func handleFileSelection(url: URL, originalFilename: String? = nil) {\n        print(\"🔄 Processing file selection for URL: \\(url.path)\")\n        let filename = originalFilename ?? url.lastPathComponent\n        print(\"📝 Original filename: \\(filename)\")\n        \n        Task {\n            let dimensionValue = shouldResize ? Double(maxDimension) ?? 2048 : nil\n            let settings = CompressionSettings(\n                quality: 0.7,\n                pngCompressionLevel: 6,\n                preserveMetadata: true,\n                maxDimension: dimensionValue != nil ? CGFloat(dimensionValue!) : nil,\n                optimizeForWeb: true\n            )\n            \n            do {\n                try await processor.processFile(url: url, settings: settings, originalFileName: filename)\n            } catch {\n                print(\"❌ File processing error: \\(error.localizedDescription)\")\n                await MainActor.run {\n                    alertMessage = error.localizedDescription\n                    showAlert = true\n                }\n            }\n        }\n    }\n        \n        private func handleMultiFileDrop(providers: [NSItemProvider]) {\n            Task {\n                print(\"📥 Processing multiple dropped files\")\n                var urls: [URL] = []\n                \n                for (index, provider) in providers.enumerated() {\n                    for type in supportedTypes {\n                        if provider.hasItemConformingToTypeIdentifier(type.identifier) {\n                            do {\n                                let url = try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<URL, Error>) in\n                                    provider.loadFileRepresentation(forTypeIdentifier: type.identifier) { url, error in\n                                        if let error = error {\n                                            print(\"❌ Error loading file \\(index + 1): \\(error.localizedDescription)\")\n                                            continuation.resume(throwing: error)\n                                        } else if let url = url {\n                                            print(\"📄 Original file \\(index + 1) URL: \\(url.path)\")\n                                            print(\"📝 Original filename \\(index + 1): \\(url.lastPathComponent)\")\n                                            \n                                            let originalFileName = url.lastPathComponent\n                                            let tempURL = FileManager.default.temporaryDirectory\n                                                .appendingPathComponent(\"\\(UUID().uuidString)_\\(originalFileName)\")\n                                            \n                                            print(\"🔄 Creating temp file \\(index + 1) at: \\(tempURL.path)\")\n                                            \n                                            do {\n                                                try FileManager.default.copyItem(at: url, to: tempURL)\n                                                print(\"✅ Successfully copied file \\(index + 1) to temp location\")\n                                                continuation.resume(returning: tempURL)\n                                            } catch {\n                                                print(\"❌ Failed to copy file \\(index + 1): \\(error.localizedDescription)\")\n                                                continuation.resume(throwing: error)\n                                            }\n                                        } else {\n                                            print(\"❌ No URL available for file \\(index + 1)\")\n                                            continuation.resume(throwing: NSError(domain: \"\", code: -1, userInfo: [NSLocalizedDescriptionKey: \"Failed to load file\"]))\n                                        }\n                                    }\n                                }\n                                \n                                urls.append(url)\n                            } catch {\n                                print(\"❌ Failed to process dropped file \\(index + 1): \\(error.localizedDescription)\")\n                            }\n                            break\n                        }\n                    }\n                }\n                \n                if !urls.isEmpty {\n                    print(\"✅ Successfully processed \\(urls.count) files\")\n                    print(\"📁 Temp URLs: \\(urls.map { $0.path })\")\n                    await MainActor.run {\n                        multiProcessor.addFiles(urls)\n                    }\n                }\n            }\n        }\n        \n    private func handleFileSelection(url: URL) {\n        print(\"🔄 Processing file selection for URL: \\(url.path)\")\n        print(\"📝 Original filename: \\(url.lastPathComponent)\")\n        \n        Task {\n            let dimensionValue = shouldResize ? Double(maxDimension) ?? 2048 : nil\n            let settings = CompressionSettings(\n                quality: 0.7,\n                pngCompressionLevel: 6,\n                preserveMetadata: true,\n                maxDimension: dimensionValue != nil ? CGFloat(dimensionValue!) : nil,\n                optimizeForWeb: true\n            )\n            \n            do {\n                // Store original filename before processing\n                let originalFileName = url.lastPathComponent\n                try await processor.processFile(url: url, settings: settings, originalFileName: originalFileName)\n            } catch {\n                print(\"❌ File processing error: \\(error.localizedDescription)\")\n                await MainActor.run {\n                    alertMessage = error.localizedDescription\n                    showAlert = true\n                }\n            }\n        }\n    }\n        \n    @MainActor\n    func saveCompressedFile(url: URL, originalName: String) async {\n        print(\"💾 Saving compressed file\")\n        print(\"📝 Original name: \\(originalName)\")\n        print(\"📁 Compressed file URL: \\(url.path)\")\n        \n        let panel = NSSavePanel()\n        panel.canCreateDirectories = true\n        panel.showsTagField = false\n        \n        // Extract original filename without UUID\n        let originalURL = URL(fileURLWithPath: originalName)\n        let filenameWithoutExt = originalURL.deletingPathExtension().lastPathComponent\n            .replacingOccurrences(of: #\"[A-F0-9]{8}-[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{12}\\.\"#,\n                                 with: \"\",\n                                 options: .regularExpression)\n        let fileExtension = url.pathExtension\n        \n        let suggestedName = \"\\(filenameWithoutExt)_compressed.\\(fileExtension)\"\n        print(\"📝 Suggested save name: \\(suggestedName)\")\n        panel.nameFieldStringValue = suggestedName\n        \n        panel.allowedContentTypes = [UTType(filenameExtension: fileExtension)].compactMap { $0 }\n        panel.message = \"Choose where to save the compressed file\"\n        \n        guard let window = NSApp.windows.first else { return }\n        \n        let response = await panel.beginSheetModal(for: window)\n        \n        if response == .OK, let saveURL = panel.url {\n            print(\"📥 Saving to: \\(saveURL.path)\")\n            do {\n                // Check if file exists\n                if FileManager.default.fileExists(atPath: saveURL.path) {\n                    try FileManager.default.removeItem(at: saveURL)\n                }\n                \n                try FileManager.default.copyItem(at: url, to: saveURL)\n                print(\"✅ File saved successfully\")\n                processor.cleanup()\n            } catch {\n                print(\"❌ Save error: \\(error.localizedDescription)\")\n                alertMessage = \"Failed to save file: \\(error.localizedDescription)\"\n                showAlert = true\n            }\n        } else {\n            print(\"❌ Save cancelled or window not found\")\n        }\n    }\n}\n"
  },
  {
    "path": "Achico/Views/DropZoneView.swift",
    "content": "import SwiftUI\n\nstruct DropZoneView: View {\n    @Binding var isDragging: Bool\n    @Binding var shouldResize: Bool\n    @Binding var maxDimension: String\n    let onTap: () -> Void\n    \n    var body: some View {\n        Button(action: onTap) {\n            ZStack(alignment: .bottom) {\n                // Main drop zone content - centered\n                VStack(spacing: 12) {\n                    Image(systemName: \"doc.circle\")\n                        .font(.system(size: 32))\n                        .foregroundColor(.secondary)\n                    \n                    Text(\"Drop your file here\")\n                        .font(.system(size: 14))\n                        .foregroundColor(.secondary)\n                }\n                .frame(maxWidth: .infinity, maxHeight: .infinity)\n                \n                // Resize controls - bottom aligned\n                HStack(spacing: 12) {\n                    Toggle(\"Resize\", isOn: $shouldResize)\n                        .toggleStyle(.switch)\n                        .labelsHidden()\n                    \n                    Text(\"Resize\")\n                        .font(.system(size: 13))\n                        .foregroundColor(.secondary)\n                    \n                    if shouldResize {\n                        TextField(\"px\", text: $maxDimension)\n                            .frame(width: 60)\n                            .textFieldStyle(RoundedBorderTextFieldStyle())\n                            .font(.system(size: 13))\n                            .multilineTextAlignment(.trailing)\n                        \n                        Text(\"px\")\n                            .font(.system(size: 13))\n                            .foregroundColor(.secondary)\n                    }\n                }\n                .padding(.horizontal, 16)\n                .padding(.vertical, 8)\n                .background(\n                    RoundedRectangle(cornerRadius: 20)\n                        .fill(Color(NSColor.controlBackgroundColor).opacity(0.5))\n                )\n                .padding(.bottom, 24)\n            }\n            .frame(maxWidth: .infinity, maxHeight: .infinity)\n            .background(\n                RoundedRectangle(cornerRadius: 12)\n                    .strokeBorder(isDragging ? Color.accentColor : Color.secondary.opacity(0.2),\n                                style: StrokeStyle(lineWidth: 1))\n                    .background(Color.clear)\n            )\n            .padding()\n        }\n        .buttonStyle(PlainButtonStyle())\n    }\n}\n"
  },
  {
    "path": "Achico/Views/MenuBarView.swift",
    "content": "import SwiftUI\nimport AppKit\n\nstruct MenuBarView: View {\n    @ObservedObject var updater: UpdateChecker\n    @EnvironmentObject var menuBarController: MenuBarController\n    @Environment(\\.dismiss) var dismiss\n    \n    private var appIcon: NSImage {\n        if let bundleIcon = NSImage(named: NSImage.applicationIconName) {\n            return bundleIcon\n        }\n        return NSWorkspace.shared.icon(forFile: Bundle.main.bundlePath)\n    }\n    \n    var body: some View {\n        VStack(spacing: 16) {\n            // App Icon and Version\n            VStack(spacing: 8) {\n                Image(nsImage: appIcon)\n                    .resizable()\n                    .aspectRatio(contentMode: .fit)\n                    .frame(width: 64, height: 64)\n                \n                Text(\"Version \\(Bundle.main.object(forInfoDictionaryKey: \"CFBundleShortVersionString\") as? String ?? \"1.0.0\")\")\n                    .font(.subheadline)\n                    .foregroundColor(.secondary)\n            }\n            .padding(.top, 16)\n            \n            // Status Section\n            Group {\n                if updater.isChecking {\n                    VStack(spacing: 8) {\n                        ProgressView()\n                            .scaleEffect(1.2)\n                        Text(\"Checking for updates...\")\n                            .font(.headline)\n                            .foregroundColor(.secondary)\n                    }\n                } else if let error = updater.error {\n                    VStack(spacing: 8) {\n                        Image(systemName: \"xmark.circle.fill\")\n                            .font(.system(size: 28))\n                            .foregroundColor(.red)\n                        Text(error)\n                            .font(.subheadline)\n                            .foregroundColor(.secondary)\n                            .multilineTextAlignment(.center)\n                    }\n                } else if updater.updateAvailable {\n                    VStack(spacing: 12) {\n                        Image(systemName: \"arrow.down.circle.fill\")\n                            .font(.system(size: 28))\n                            .foregroundColor(.blue)\n                        \n                        if let version = updater.latestVersion {\n                            Text(\"Version \\(version) Available\")\n                                .font(.headline)\n                        }\n                        \n                        if let notes = updater.releaseNotes {\n                            ScrollView {\n                                Text(notes)\n                                    .font(.footnote)\n                                    .foregroundColor(.secondary)\n                                    .multilineTextAlignment(.center)\n                                    .padding(.horizontal)\n                            }\n                            .frame(maxHeight: 80)\n                        }\n                        \n                        Button {\n                            if let url = updater.downloadURL {\n                                NSWorkspace.shared.open(url)\n                                dismiss()\n                            }\n                        } label: {\n                            Text(\"Download Update\")\n                                .frame(maxWidth: 200)\n                        }\n                        .buttonStyle(.borderedProminent)\n                    }\n                } else {\n                    VStack(spacing: 8) {\n                        Image(systemName: \"checkmark.circle.fill\")\n                            .font(.system(size: 28))\n                            .foregroundColor(.green)\n                        Text(\"Achico is up to date\")\n                            .font(.headline)\n                    }\n                }\n            }\n            .frame(maxWidth: .infinity, alignment: .center)\n            .padding(.vertical, 8)\n            \n            Divider()\n            \n            // Bottom Buttons\n            HStack(spacing: 16) {\n                Button(\"Check Again\") {\n                    updater.checkForUpdates()\n                }\n                .buttonStyle(.plain)\n                .foregroundColor(.blue)\n                \n                Button(\"Close\") {\n                    dismiss()\n                }\n                .buttonStyle(.plain)\n                .foregroundColor(.secondary)\n            }\n            .padding(.bottom, 16)\n            \n            Text(\"Built by [Nuance](https://nuanc.me)\")\n                            .font(.footnote)\n                            .foregroundColor(.secondary)\n                            .padding(.bottom, 8)\n        }\n        .padding(.horizontal)\n        .frame(width: 300)\n        .fixedSize(horizontal: false, vertical: true)\n    }\n}\n"
  },
  {
    "path": "Achico/Views/MultiFileProcessingView.swift",
    "content": "import Foundation\nimport UniformTypeIdentifiers\nimport SwiftUI\n\nstruct FileProcessingState: Identifiable {\n    let id: UUID\n    let url: URL\n    let originalFileName: String\n    var progress: Double\n    var result: FileProcessor.ProcessingResult?\n    var isProcessing: Bool\n    var error: Error?\n    \n    // Add this computed property\n    var displayFileName: String {\n        // If the filename contains UUID prefix, remove it\n        let filename = url.lastPathComponent\n        if let range = filename.range(of: \"_\") {\n            return String(filename[range.upperBound...])\n        }\n        return originalFileName\n    }\n    \n    init(url: URL) {\n        self.id = UUID()\n        self.url = url\n        self.originalFileName = url.lastPathComponent\n        self.progress = 0\n        self.result = nil\n        self.isProcessing = false\n        self.error = nil\n    }\n}\n\n@MainActor\nclass MultiFileProcessor: ObservableObject {\n    @Published private(set) var files: [FileProcessingState] = []\n    @Published private(set) var isProcessingMultiple = false\n    private var processingTasks: [UUID: Task<Void, Never>] = [:]\n    \n    func addFiles(_ urls: [URL]) {\n        let newFiles = urls.map { FileProcessingState(url: $0) }\n        files.append(contentsOf: newFiles)\n        \n        // Process each new file individually\n        for file in newFiles {\n            processFile(with: file.id)\n        }\n    }\n    \n    func removeFile(at index: Int) {\n        guard index < files.count else { return }\n        let fileId = files[index].id\n        processingTasks[fileId]?.cancel()\n        processingTasks.removeValue(forKey: fileId)\n        files.remove(at: index)\n    }\n    \n    func clearFiles() {\n        // Cancel all ongoing processing tasks\n        for task in processingTasks.values {\n            task.cancel()\n        }\n        processingTasks.removeAll()\n        files.removeAll()\n    }\n    \n    private func processFile(with id: UUID) {\n        let task = Task {\n            await processFileInternal(with: id)\n        }\n        processingTasks[id] = task\n    }\n    \n    func saveAllFilesToFolder() async {\n        let panel = NSOpenPanel()\n        panel.canCreateDirectories = true\n        panel.canChooseFiles = false\n        panel.canChooseDirectories = true\n        panel.message = \"Choose where to save all compressed files\"\n        panel.prompt = \"Select Folder\"\n        \n        guard let window = NSApp.windows.first else { return }\n        let response = await panel.beginSheetModal(for: window)\n        \n        if response == .OK, let folderURL = panel.url {\n            for file in files {\n                if let result = file.result {\n                    do {\n                        // Use the original filename instead of the result filename\n                        let originalURL = URL(fileURLWithPath: file.originalFileName)\n                        let filenameWithoutExt = originalURL.deletingPathExtension().lastPathComponent\n                        let fileExtension = originalURL.pathExtension\n                        let newFileName = \"\\(filenameWithoutExt)_compressed.\\(fileExtension)\"\n                        let destinationURL = folderURL.appendingPathComponent(newFileName)\n                        \n                        try FileManager.default.copyItem(at: result.compressedURL, to: destinationURL)\n                    } catch {\n                        print(\"Failed to save file \\(file.originalFileName): \\(error.localizedDescription)\")\n                    }\n                }\n            }\n        }\n    }\n    \n    private func processFileInternal(with id: UUID) async {\n        guard let index = files.firstIndex(where: { $0.id == id }) else { return }\n        guard index < files.count else { return }\n        \n        let processor = FileProcessor()\n        \n        // Update the processing state\n        files[index].isProcessing = true\n        \n        do {\n            let settings = CompressionSettings(\n                quality: 0.7,\n                pngCompressionLevel: 6,\n                preserveMetadata: true,\n                optimizeForWeb: true\n            )\n            \n            try await processor.processFile(url: files[index].url, settings: settings)\n            \n            guard index < files.count, files[index].id == id else { return }\n            \n            if let processingResult = processor.processingResult {\n                files[index].result = processingResult\n                files[index].isProcessing = false\n            }\n        } catch {\n            guard index < files.count, files[index].id == id else { return }\n            files[index].error = error\n            files[index].isProcessing = false\n        }\n        \n        // Clean up the task\n        processingTasks.removeValue(forKey: id)\n    }\n    \n    func saveCompressedFile(url: URL, originalName: String) async {\n        let panel = NSSavePanel()\n        panel.canCreateDirectories = true\n        panel.showsTagField = false\n        \n        // Use originalName directly instead of extracting from URL\n        let originalURL = URL(fileURLWithPath: originalName)\n        let filenameWithoutExt = originalURL.deletingPathExtension().lastPathComponent\n        let fileExtension = originalURL.pathExtension\n        panel.nameFieldStringValue = \"\\(filenameWithoutExt)_compressed.\\(fileExtension)\"\n        \n        panel.allowedContentTypes = [UTType(filenameExtension: url.pathExtension)].compactMap { $0 }\n        panel.message = \"Choose where to save the compressed file\"\n        \n        guard let window = NSApp.windows.first else { return }\n        \n        let response = await panel.beginSheetModal(for: window)\n        \n        if response == .OK, let saveURL = panel.url {\n            do {\n                // Check if file exists\n                if FileManager.default.fileExists(atPath: saveURL.path) {\n                    try FileManager.default.removeItem(at: saveURL)\n                }\n                \n                try FileManager.default.copyItem(at: url, to: saveURL)\n            } catch {\n                print(\"Failed to save file: \\(error.localizedDescription)\")\n            }\n        }\n    }\n    \n    func downloadAllFiles() async {\n        for file in files {\n            if let result = file.result {\n                await saveCompressedFile(url: result.compressedURL, originalName: file.originalFileName)\n            }\n        }\n    }\n}\n\nstruct MultiFileView: View {\n    @ObservedObject var processor: MultiFileProcessor\n    @Binding var shouldResize: Bool\n    @Binding var maxDimension: String\n    let supportedTypes: [UTType]\n    @State private var hoveredFileID: UUID?\n    \n    init(processor: MultiFileProcessor, shouldResize: Binding<Bool>, maxDimension: Binding<String>, supportedTypes: [UTType]) {\n        self._processor = ObservedObject(wrappedValue: processor)\n        self._shouldResize = shouldResize\n        self._maxDimension = maxDimension\n        self.supportedTypes = supportedTypes\n    }\n    \n    var body: some View {\n        VStack(spacing: 20) {\n            // Header section with ButtonGroup\n            HStack {\n                Text(\"Files\")\n                    .font(.title2)\n                    .fontWeight(.semibold)\n                \n                Spacer()\n                \n                if !processor.files.isEmpty {\n                    ButtonGroup(buttons: [\n                        (\n                            title: \"Save All\",\n                            icon: \"folder.fill.badge.plus\",\n                            action: {\n                                Task {\n                                    await processor.saveAllFilesToFolder()\n                                }\n                            }\n                        ),\n                        (\n                            title: \"Clear All\",\n                            icon: \"trash.fill\",\n                            action: {\n                                processor.clearFiles()\n                            }\n                        )\n                    ])\n                }\n            }\n            .padding(.horizontal)\n            \n            // File list\n            VStack(spacing: 0) { // Wrapper for consistent padding\n                ScrollView {\n                    LazyVStack(spacing: 8) {\n                        ForEach(Array(processor.files.enumerated()), id: \\.element.id) { index, file in\n                            FileRow(\n                                file: file,\n                                isHovered: hoveredFileID == file.id,\n                                onSave: {\n                                    if let result = file.result {\n                                        Task {\n                                            await processor.saveCompressedFile(\n                                                url: result.compressedURL,\n                                                originalName: file.originalFileName\n                                            )\n                                        }\n                                    }\n                                },\n                                onRemove: {\n                                    processor.removeFile(at: index)\n                                }\n                            )\n                            .onHover { isHovered in\n                                hoveredFileID = isHovered ? file.id : nil\n                            }\n                        }\n                    }\n                    .padding(.horizontal)\n                    .padding(.vertical) // Add vertical padding inside scroll view\n                }\n            }\n            .frame(maxWidth: .infinity, maxHeight: .infinity)\n            .background(Color(NSColor.controlBackgroundColor).opacity(0.5))\n            .clipShape(RoundedRectangle(cornerRadius: 12))\n        }\n        .padding()\n    }\n}\n\nstruct FileRow: View {\n    let file: FileProcessingState\n    let isHovered: Bool\n    let onSave: () -> Void\n    let onRemove: () -> Void\n    \n    var body: some View {\n        HStack(spacing: 16) {\n            // File icon with extension badge\n            ZStack(alignment: .bottomTrailing) {\n                getFileIcon(for: file.url.pathExtension.lowercased())\n                    .font(.system(size: 28))\n                \n                Text(file.url.pathExtension.uppercased())\n                    .font(.system(size: 8, weight: .bold))\n                    .padding(.horizontal, 4)\n                    .padding(.vertical, 2)\n                    .background(.ultraThinMaterial)\n                    .clipShape(RoundedRectangle(cornerRadius: 4))\n            }\n            \n            // File info\n            VStack(alignment: .leading, spacing: 4) {\n                Text(file.displayFileName)\n                    .font(.headline)\n                    .lineLimit(1)\n                \n                Group {\n                    if let result = file.result {\n                        Label(\n                            \"Reduced by \\(result.savedPercentage)%\",\n                            systemImage: \"arrow.down.circle.fill\"\n                        )\n                        .foregroundStyle(.green)\n                    } else if let error = file.error {\n                        Label(\n                            error.localizedDescription,\n                            systemImage: \"exclamationmark.circle.fill\"\n                        )\n                        .foregroundStyle(.red)\n                    } else if file.isProcessing {\n                        Label(\n                            \"Processing...\",\n                            systemImage: \"arrow.triangle.2.circlepath\"\n                        )\n                        .foregroundStyle(.blue)\n                    }\n                }\n                .font(.subheadline)\n            }\n            \n            Spacer()\n            \n            // Actions\n            HStack(spacing: 12) {\n                if file.isProcessing {\n                    ProgressView()\n                        .controlSize(.small)\n                } else if let _ = file.result {\n                    Button(action: onSave) {\n                        Label(\"Download\", systemImage: \"square.and.arrow.down.fill\")\n                    }\n                    .buttonStyle(GlassButtonStyle())\n                }\n                \n                Button(action: onRemove) {\n                    Image(systemName: \"trash.fill\")\n                        .foregroundStyle(.red.opacity(0.8))\n                }\n                .buttonStyle(PlainButtonStyle())\n            }\n            .opacity(isHovered ? 1 : 0.7)\n        }\n        .padding()\n        .background(\n            RoundedRectangle(cornerRadius: 12)\n                .fill(isHovered ? Color(NSColor.controlBackgroundColor).opacity(0.7) : Color.clear)\n                .overlay(\n                    RoundedRectangle(cornerRadius: 12)\n                        .stroke(Color.primary.opacity(0.1), lineWidth: 1)\n                )\n        )\n        .contentShape(Rectangle())\n    }\n    \n    @ViewBuilder\n    private func getFileIcon(for extension: String) -> some View {\n        switch `extension` {\n        case \"jpg\", \"jpeg\", \"png\", \"heic\", \"webp\":\n            Image(systemName: \"photo.fill\")\n                .foregroundStyle(.blue)\n        case \"mp4\", \"mov\", \"avi\":\n            Image(systemName: \"video.fill\")\n                .foregroundStyle(.purple)\n        case \"mp3\", \"wav\", \"aiff\":\n            Image(systemName: \"music.note\")\n                .foregroundStyle(.pink)\n        case \"pdf\":\n            Image(systemName: \"doc.fill\")\n                .foregroundStyle(.red)\n        default:\n            Image(systemName: \"doc.fill\")\n                .foregroundStyle(.secondary)\n        }\n    }\n}\n"
  },
  {
    "path": "Achico/Views/ResultView.swift",
    "content": "import SwiftUI\n\nstruct ResultView: View {\n    let result: FileProcessor.ProcessingResult\n    let onDownload: () -> Void\n    let onReset: () -> Void\n    \n    var body: some View {\n        VStack(spacing: 24) {\n            // Status Icon\n            ZStack {\n                Circle()\n                    .fill(Color(NSColor.windowBackgroundColor))\n                    .frame(width: 64, height: 64)\n                    .shadow(color: Color.black.opacity(0.1), radius: 10, x: 0, y: 5)\n                \n                Image(systemName: result.savedPercentage > 0 ? \"checkmark\" : \"exclamationmark\")\n                    .font(.system(size: 24, weight: .medium))\n                    .foregroundColor(result.savedPercentage > 0 ? .green : .orange)\n            }\n            \n            // Status Text\n            VStack(spacing: 8) {\n                Text(result.savedPercentage > 0 ? \"Compression Complete\" : \"Already Optimized\")\n                    .font(.system(size: 16, weight: .semibold))\n                \n                if result.savedPercentage > 0 {\n                    Text(\"File size reduced by \\(result.savedPercentage)%\")\n                        .font(.system(size: 14))\n                        .foregroundColor(.secondary)\n                } else {\n                    Text(\"No further compression needed\")\n                        .font(.system(size: 14))\n                        .foregroundColor(.secondary)\n                }\n            }\n            \n            // File Size Info\n            HStack(spacing: 32) {\n                VStack(spacing: 4) {\n                    Text(\"Original\")\n                        .font(.system(size: 12))\n                        .foregroundColor(.secondary)\n                    Text(formatFileSize(result.originalSize))\n                        .font(.system(size: 14, weight: .medium))\n                }\n                \n                VStack(spacing: 4) {\n                    Text(\"Compressed\")\n                        .font(.system(size: 12))\n                        .foregroundColor(.secondary)\n                    Text(formatFileSize(result.compressedSize))\n                        .font(.system(size: 14, weight: .medium))\n                }\n            }\n            .padding(.vertical, 16)\n            .padding(.horizontal, 24)\n            .background(\n                RoundedRectangle(cornerRadius: 12)\n                    .fill(Color(NSColor.controlBackgroundColor))\n                    .opacity(0.5)\n            )\n            \n            // Action Buttons\n            HStack(spacing: 12) {\n                Button(action: onReset) {\n                    Text(\"New File\")\n                        .font(.system(size: 14, weight: .medium))\n                        .frame(maxWidth: .infinity)\n                        .frame(height: 36)\n                }\n                .buttonStyle(SecondaryButtonStyle())\n                \n                Button(action: onDownload) {\n                    Text(\"Save\")\n                        .font(.system(size: 14, weight: .medium))\n                        .frame(maxWidth: .infinity)\n                        .frame(height: 36)\n                }\n                .buttonStyle(PrimaryButtonStyle())\n            }\n            .padding(.top, 8)\n        }\n        .padding(32)\n        .frame(maxWidth: 320)\n        .background(\n            RoundedRectangle(cornerRadius: 16)\n                .fill(Color(NSColor.windowBackgroundColor))\n                .opacity(0.8)\n                .shadow(color: Color.black.opacity(0.1), radius: 20, x: 0, y: 10)\n        )\n    }\n    \n    private func formatFileSize(_ size: Int64) -> String {\n        let formatter = ByteCountFormatter()\n        formatter.allowedUnits = [.useMB]\n        formatter.countStyle = .file\n        return formatter.string(fromByteCount: size)\n    }\n}\n\n// Add new custom button styles\nstruct PrimaryButtonStyle: ButtonStyle {\n    func makeBody(configuration: Configuration) -> some View {\n        configuration.label\n            .foregroundColor(.white)\n            .background(Color.accentColor)\n            .cornerRadius(8)\n            .opacity(configuration.isPressed ? 0.9 : 1.0)\n    }\n}\n\nstruct SecondaryButtonStyle: ButtonStyle {\n    func makeBody(configuration: Configuration) -> some View {\n        configuration.label\n            .foregroundColor(.primary)\n            .background(Color(NSColor.controlBackgroundColor).opacity(0.5))\n            .cornerRadius(8)\n            .overlay(\n                RoundedRectangle(cornerRadius: 8)\n                    .strokeBorder(Color.primary.opacity(0.1), lineWidth: 1)\n            )\n            .opacity(configuration.isPressed ? 0.9 : 1.0)\n    }\n}\n"
  },
  {
    "path": "Achico.xcodeproj/project.pbxproj",
    "content": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 77;\n\tobjects = {\n\n/* Begin PBXFileReference section */\n\t\tC82F044E2CCB3DD20012C07B /* Achico.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Achico.app; sourceTree = BUILT_PRODUCTS_DIR; };\n/* End PBXFileReference section */\n\n/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */\n\t\tC8A16E302CE0FC6D00F427B2 /* Exceptions for \"Achico\" folder in \"Achico\" target */ = {\n\t\t\tisa = PBXFileSystemSynchronizedBuildFileExceptionSet;\n\t\t\tmembershipExceptions = (\n\t\t\t\tInfo.plist,\n\t\t\t);\n\t\t\ttarget = C82F044D2CCB3DD20012C07B /* Achico */;\n\t\t};\n/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */\n\n/* Begin PBXFileSystemSynchronizedRootGroup section */\n\t\tC82F04502CCB3DD20012C07B /* Achico */ = {\n\t\t\tisa = PBXFileSystemSynchronizedRootGroup;\n\t\t\texceptions = (\n\t\t\t\tC8A16E302CE0FC6D00F427B2 /* Exceptions for \"Achico\" folder in \"Achico\" target */,\n\t\t\t);\n\t\t\tpath = Achico;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXFileSystemSynchronizedRootGroup section */\n\n/* Begin PBXFrameworksBuildPhase section */\n\t\tC82F044B2CCB3DD20012C07B /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXFrameworksBuildPhase section */\n\n/* Begin PBXGroup section */\n\t\tC82F04452CCB3DD20012C07B = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tC82F04502CCB3DD20012C07B /* Achico */,\n\t\t\t\tC82F044F2CCB3DD20012C07B /* Products */,\n\t\t\t);\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tC82F044F2CCB3DD20012C07B /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tC82F044E2CCB3DD20012C07B /* Achico.app */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXGroup section */\n\n/* Begin PBXNativeTarget section */\n\t\tC82F044D2CCB3DD20012C07B /* Achico */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = C82F045D2CCB3DD40012C07B /* Build configuration list for PBXNativeTarget \"Achico\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tC82F044A2CCB3DD20012C07B /* Sources */,\n\t\t\t\tC82F044B2CCB3DD20012C07B /* Frameworks */,\n\t\t\t\tC82F044C2CCB3DD20012C07B /* Resources */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tfileSystemSynchronizedGroups = (\n\t\t\t\tC82F04502CCB3DD20012C07B /* Achico */,\n\t\t\t);\n\t\t\tname = Achico;\n\t\t\tpackageProductDependencies = (\n\t\t\t);\n\t\t\tproductName = Achico;\n\t\t\tproductReference = C82F044E2CCB3DD20012C07B /* Achico.app */;\n\t\t\tproductType = \"com.apple.product-type.application\";\n\t\t};\n/* End PBXNativeTarget section */\n\n/* Begin PBXProject section */\n\t\tC82F04462CCB3DD20012C07B /* Project object */ = {\n\t\t\tisa = PBXProject;\n\t\t\tattributes = {\n\t\t\t\tBuildIndependentTargetsInParallel = 1;\n\t\t\t\tLastSwiftUpdateCheck = 1600;\n\t\t\t\tLastUpgradeCheck = 1610;\n\t\t\t\tTargetAttributes = {\n\t\t\t\t\tC82F044D2CCB3DD20012C07B = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 16.0;\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t};\n\t\t\tbuildConfigurationList = C82F04492CCB3DD20012C07B /* Build configuration list for PBXProject \"Achico\" */;\n\t\t\tdevelopmentRegion = en;\n\t\t\thasScannedForEncodings = 0;\n\t\t\tknownRegions = (\n\t\t\t\ten,\n\t\t\t\tBase,\n\t\t\t);\n\t\t\tmainGroup = C82F04452CCB3DD20012C07B;\n\t\t\tminimizedProjectReferenceProxies = 1;\n\t\t\tpreferredProjectObjectVersion = 77;\n\t\t\tproductRefGroup = C82F044F2CCB3DD20012C07B /* Products */;\n\t\t\tprojectDirPath = \"\";\n\t\t\tprojectRoot = \"\";\n\t\t\ttargets = (\n\t\t\t\tC82F044D2CCB3DD20012C07B /* Achico */,\n\t\t\t);\n\t\t};\n/* End PBXProject section */\n\n/* Begin PBXResourcesBuildPhase section */\n\t\tC82F044C2CCB3DD20012C07B /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXResourcesBuildPhase section */\n\n/* Begin PBXSourcesBuildPhase section */\n\t\tC82F044A2CCB3DD20012C07B /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXSourcesBuildPhase section */\n\n/* Begin XCBuildConfiguration section */\n\t\tC82F045B2CCB3DD40012C07B /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++20\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEAD_CODE_STRIPPING = YES;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tENABLE_USER_SCRIPT_SANDBOXING = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu17;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tLOCALIZATION_PREFERS_STRING_CATALOGS = YES;\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 15.0;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;\n\t\t\t\tMTL_FAST_MATH = YES;\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tSDKROOT = macosx;\n\t\t\t\tSWIFT_ACTIVE_COMPILATION_CONDITIONS = \"DEBUG $(inherited)\";\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tC82F045C2CCB3DD40012C07B /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++20\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEAD_CODE_STRIPPING = YES;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_USER_SCRIPT_SANDBOXING = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu17;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tLOCALIZATION_PREFERS_STRING_CATALOGS = YES;\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 15.0;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tMTL_FAST_MATH = YES;\n\t\t\t\tSDKROOT = macosx;\n\t\t\t\tSWIFT_COMPILATION_MODE = wholemodule;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tC82F045E2CCB3DD40012C07B /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;\n\t\t\t\tASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES;\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = Achico/Achico.entitlements;\n\t\t\t\t\"CODE_SIGN_IDENTITY[sdk=macosx*]\" = \"Apple Development\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tCURRENT_PROJECT_VERSION = 26;\n\t\t\t\tDEAD_CODE_STRIPPING = YES;\n\t\t\t\tDEVELOPMENT_ASSET_PATHS = \"\\\"Achico/Preview Content\\\"\";\n\t\t\t\tDEVELOPMENT_TEAM = YYMLDY74QZ;\n\t\t\t\tENABLE_HARDENED_RUNTIME = YES;\n\t\t\t\tENABLE_PREVIEWS = YES;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tINFOPLIST_FILE = Achico/Info.plist;\n\t\t\t\tINFOPLIST_KEY_LSApplicationCategoryType = \"public.app-category.productivity\";\n\t\t\t\tINFOPLIST_KEY_NSHumanReadableCopyright = \"\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/../Frameworks\",\n\t\t\t\t);\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 14.0;\n\t\t\t\tMARKETING_VERSION = 3.2.1;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = Minimal.Achico;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = YES;\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tC82F045F2CCB3DD40012C07B /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;\n\t\t\t\tASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES;\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = Achico/Achico.entitlements;\n\t\t\t\t\"CODE_SIGN_IDENTITY[sdk=macosx*]\" = \"Apple Development\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tCURRENT_PROJECT_VERSION = 26;\n\t\t\t\tDEAD_CODE_STRIPPING = YES;\n\t\t\t\tDEVELOPMENT_ASSET_PATHS = \"\\\"Achico/Preview Content\\\"\";\n\t\t\t\tDEVELOPMENT_TEAM = YYMLDY74QZ;\n\t\t\t\tENABLE_HARDENED_RUNTIME = YES;\n\t\t\t\tENABLE_PREVIEWS = YES;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tINFOPLIST_FILE = Achico/Info.plist;\n\t\t\t\tINFOPLIST_KEY_LSApplicationCategoryType = \"public.app-category.productivity\";\n\t\t\t\tINFOPLIST_KEY_NSHumanReadableCopyright = \"\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/../Frameworks\",\n\t\t\t\t);\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 14.0;\n\t\t\t\tMARKETING_VERSION = 3.2.1;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = Minimal.Achico;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = YES;\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n/* End XCBuildConfiguration section */\n\n/* Begin XCConfigurationList section */\n\t\tC82F04492CCB3DD20012C07B /* Build configuration list for PBXProject \"Achico\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tC82F045B2CCB3DD40012C07B /* Debug */,\n\t\t\t\tC82F045C2CCB3DD40012C07B /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tC82F045D2CCB3DD40012C07B /* Build configuration list for PBXNativeTarget \"Achico\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tC82F045E2CCB3DD40012C07B /* Debug */,\n\t\t\t\tC82F045F2CCB3DD40012C07B /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n/* End XCConfigurationList section */\n\t};\n\trootObject = C82F04462CCB3DD20012C07B /* Project object */;\n}\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2024 nuance-dev\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# Achico - A Free MacOS Native File Compression App\n\nA lightweight, native macOS app that intelligently compresses files while maintaining quality. Support for PDF, images, videos, and more! Simple, fast, and efficient!\n\n![image](https://github.com/user-attachments/assets/4e10b8a7-decc-4e0b-8b56-f88198e75ec9)\n\n## Features\n\n### File Support\n\n- **PDFs**: Smart compression while preserving readability\n- **Images**: Support for JPEG, PNG, HEIC, TIFF, GIF, BMP, WebP, SVG, RAW, and ICO\n- **Videos**: MP4, MOV, AVI, and other common formats\n- **Audio**: M4V, WAV, MP3, AIFF\n- **File Resizing**: Optionally resize images and videos while compressing\n\n### Core Features\n\n- **Multiple Input Methods**: Drag & drop or click to select files\n- **Real-time Progress**: Watch your files being compressed with a clean progress indicator\n- **Compression Stats**: See how much space you've saved instantly\n- **Dark and Light modes**: Seamlessly integrates with your system preferences\n- **Native Performance**: Built with SwiftUI for optimal macOS integration\n\n### Compression Options\n\n- **Quality Control**: Adjust compression levels to balance size and quality\n- **Size Limits**: Set maximum dimensions for images and videos\n- **Format Conversion**: Automatic conversion of less efficient formats\n- **Metadata Handling**: Option to preserve or strip metadata\n\n![compression-demo](https://github.com/user-attachments/assets/e494937d-7e52-4d6c-9046-d6b0d577c67e)\n\n## 💻 Get Started\n\nDownload from the [releases](https://github.com/nuance-dev/Achico/releases/) page.\n\n## ⚡️ How it Works\n\n1. Drop or select your files\n2. Adjust compression settings (optional)\n3. Watch the magic happen\n4. Get your compressed files\n5. That's it!\n6. Update: you can now resize your images and videos\n7. Update: you can now drop multiple files\n   ![42630](https://github.com/user-attachments/assets/6def2137-fd12-4f7d-b59a-4476ae506331)\n\n## 🛠 Technical Details\n\n- Built natively for macOS using SwiftUI\n- Uses specialized frameworks for each file type:\n  - PDFKit for PDF compression\n  - AVFoundation for video processing\n  - Core Graphics for image optimization\n- Efficient memory management for handling large files\n- Clean, modern interface following Apple's design guidelines\n- Parallel processing for better performance\n\n## 🔮 Features Coming Soon\n\n- Batch processing\n- Folder monitoring\n- Quick Look integration\n- Custom presets for different use cases\n- Additional file format support\n- Advanced compression options\n- Progress notifications\n\n## 🤝 Contributing\n\nWe welcome contributions! Here's how you can help:\n\n1. Clone the repository\n2. Create your feature branch (`git checkout -b feature/AmazingFeature`)\n3. Commit your changes (`git commit -m 'Add some AmazingFeature'`)\n4. Push to the branch (`git push origin feature/AmazingFeature`)\n5. Open a Pull Request\n\nPlease ensure your PR:\n\n- Follows the existing code style\n- Includes appropriate tests if applicable\n- Updates documentation as needed\n\n## 📝 License\n\nThis project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.\n\n## 🔗 Links\n\n- Website: [Nuance](https://nuanc.me)\n- Report issues: [GitHub Issues](https://github.com/nuance-dev/Achico/issues)\n- Follow updates: [@NuanceDev](https://twitter.com/Nuancedev)\n\n## Requirements\n\n- macOS 14.0 or later\n\n## Supported File Formats\n\n### Images\n\n- JPEG/JPG\n- PNG\n- HEIC\n- TIFF/TIF\n- GIF (including animated)\n- BMP\n- WebP\n- SVG\n- RAW (CR2, NEF, ARW)\n- ICO\n\n### Videos\n\n- MP4\n- MOV\n- AVI\n- MPEG/MPG\n\n### Documents\n\n- PDF\n"
  }
]