[
  {
    "path": ".github/FUNDING.yml",
    "content": "ko_fi: nsantoine\n"
  },
  {
    "path": ".github/workflows/main.yml",
    "content": "name: Xcode - Build and Analyze\n\non:\n  push:\n    branches: [ \"main\" ]\n  pull_request:\n    branches: [ \"main\" ]\n\njobs:\n  build:\n    name: Build and analyse default scheme using xcodebuild command\n    runs-on: macos-latest\n\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v3\n      - name: Build\n        run: |\n          xcodebuild build -scheme Samra -project Samra.xcodeproj -configuration Release CODE_SIGN_IDENTITY=\"\" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO BUILD_DIR=${{ github.workspace }}/xcodebuild\n          xcodebuild build -scheme extractutil -project Samra.xcodeproj -configuration Release CODE_SIGN_IDENTITY=\"\" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO BUILD_DIR=${{ github.workspace }}/xcodebuild\n          \n          mkdir -p ${{ github.workspace }}/product\n          cp -R ${{ github.workspace }}/xcodebuild/Release/Samra.app ${{ github.workspace }}/product\n          \n          mv  ${{ github.workspace }}/xcodebuild/Release/extractutil ${{ github.workspace }}/product/Samra.app/Contents/MacOS\n          \n          cd ${{ github.workspace }}/product\n          zip -r ${{ github.workspace }}/Samra.zip .\n          \n      - name: Upload app to artifacts\n        uses: actions/upload-artifact@v3\n        with:\n          name: Samra\n          path: ${{ github.workspace }}/Samra.zip\n"
  },
  {
    "path": ".gitignore",
    "content": ".DS_Store\nPackage.resolved\n*.xcuserdatad\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2024 Antoine\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": "# Samra\n\nA macOS Application to explore and edit Asset Catalog (.car) files on macOS, with a nicer native, modern-feeling UI that does not crash every couple of seconds.\n\n<img width=\"800\" alt=\"image\" src=\"https://user-images.githubusercontent.com/48022799/222929188-7eb62314-8433-42c6-baf5-5d851b679998.png\">\n\n## Why Samra?\nThere are a few existing asset catalog viewer applications for macOS, however, none felt feature complete, Samra offers the following:\n- Browse through the Asset Catalog file\n- Show all types of objects (renditions) in it, not just images (colors, pdfs, image sets, etc)\n- Ability to edit icons/images & colors\n- Search in Asset Catalog for renditions by name\n- View detailed information about each rendition, such as lookup name, width, height, appearance (if it's meant for dark mode or light mode) and other type-specific information (ie, RGB values for colors).\n\n## What versions does this support?\nmacOS 10.15.1+\n\n## How can I download this?\nDownload the .app from the Releases\n\n## Preview\n<img width=\"933\" alt=\"image\" src=\"https://github.com/user-attachments/assets/a5fe1256-955e-4b10-964e-78d8588bebcc\">\n<img width=\"1099\" alt=\"image\" src=\"https://user-images.githubusercontent.com/48022799/222929119-4f9e043c-fdbf-4e39-9137-f344fc693da4.png\">\n<img width=\"493\" alt=\"image\" src=\"https://user-images.githubusercontent.com/48022799/222929132-4e5ee546-18b5-492f-a4a6-5599c1b76a20.png\">\n<img width=\"753\" alt=\"image\" src=\"https://user-images.githubusercontent.com/48022799/222929151-6355f60a-757e-4b17-bc4a-cb25213e8e8c.png\">\n"
  },
  {
    "path": "Samra/AppDelegate.swift",
    "content": "//\n//  AppDelegate.swift\n//  Samra\n//\n//  Created by Serena on 18/02/2023.\n// \n\nimport Cocoa\nimport AssetCatalogWrapper\n\n@main\nclass AppDelegate: NSObject, NSApplicationDelegate {\n    \n    var showWelcomeViewController: Bool = false\n    \n    static func main() {\n        let instance = AppDelegate()\n        NSApplication.shared.delegate = instance\n        NSApplication.shared.run()\n    }\n    \n    func applicationWillFinishLaunching(_ notification: Notification) {}\n    \n    @objc\n    func openMenuItemClicked() {\n        URLHandler.shared.presentArchiveChooserPanel(insertToRecentItems: true, senderView: nil)\n    }\n    \n    func applicationDidFinishLaunching(_ aNotification: Notification) {\n        // Insert code here to initialize your application        \n        if Preferences.showWelcomeVCOnLaunch {\n            WindowController(kind: .welcome).showWindow(self)\n        }\n        \n        let items = RenditionType.allCases.map { type in\n            let item = NSMenuItem(title: type.description, action: #selector(TypesListViewController.goToSection(menuItemSender:)))\n            item.tag = type.rawValue\n            item.isEnabled = false\n            return item\n        }\n        \n        NSApplication.shared.mainMenu = NSMenu(items: [\n            NSMenuItem(submenuTitle: \"App\", items: [\n                NSMenuItem(title: \"About Samra\",\n                           action: #selector(openAboutPanel),\n                           keyEquivalent: \"\"),\n                NSMenuItem.separator(),\n                NSMenuItem(title: \"Hide Others\", action: #selector(NSApplication.hideOtherApplications(_:)), keyEquivalent: \"h\", keyEquivalentModifierMask: [.command, .option]),\n                NSMenuItem(title: \"Show All\", action: #selector(NSApplication.unhideAllApplications(_:))),\n                NSMenuItem.separator(),\n                NSMenuItem(title: \"Quit Samra\", action: #selector(NSApplication.terminate(_:)), keyEquivalent: \"q\"),\n            ]),\n            NSMenuItem(submenuTitle: \"File\", items: [\n                NSMenuItem(title: \"Open...\", action: #selector(openMenuItemClicked), keyEquivalent: \"o\"),\n                NSMenuItem(title: \"Export to...\", action: #selector(RenditionListViewController.exportCatalog)),\n                NSMenuItem.separator(),\n                NSMenuItem(title: \"Diff Asset Catalogs\", action: #selector(openDiffViewController), keyEquivalent: \"d\"),\n                NSMenuItem.separator(),\n                NSMenuItem(title: \"Close\", action: #selector(NSWindow.performClose(_:)), keyEquivalent: \"w\")\n            ]),\n            \n            NSMenuItem(submenuTitle: \"Edit\", items: [\n                NSMenuItem(title: \"Undo\", action: Selector((\"undo:\")), keyEquivalent: \"z\"),\n                NSMenuItem(title: \"Redo\", action: Selector((\"redo:\")), keyEquivalent: \"Z\"),\n                NSMenuItem.separator(),\n                NSMenuItem(title: \"Cut\", action: #selector(NSText.cut(_:)), keyEquivalent: \"x\"),\n                NSMenuItem(title: \"Copy\", action: #selector(NSText.copy(_:)), keyEquivalent: \"c\"),\n                NSMenuItem(title: \"Paste\", action: #selector(NSText.paste(_:)), keyEquivalent: \"v\"),\n                NSMenuItem(title: \"Paste and Match Style\", action: #selector(NSText.paste(_:)), keyEquivalent: \"V\", keyEquivalentModifierMask: [.command, .option]),\n                NSMenuItem(title: \"Delete\", action: #selector(NSText.delete(_:))),\n                NSMenuItem(title: \"Select All\", action: #selector(NSText.selectAll(_:)), keyEquivalent: \"a\"),\n                NSMenuItem.separator(),\n                NSMenuItem(\n                    submenuTitle: \"Find\",\n                    items: [\n                        NSMenuItem(title: \"Find…\", action: #selector(NSResponder.performTextFinderAction(_:)), keyEquivalent: \"f\", tag: NSTextFinder.Action.showFindInterface.rawValue),\n                        NSMenuItem(title: \"Find and Replace…\", action: #selector(NSResponder.performTextFinderAction(_:)), keyEquivalent: \"f\", keyEquivalentModifierMask: [.command, .option], tag: NSTextFinder.Action.replaceAndFind.rawValue),\n                        NSMenuItem(title: \"Find Next\", action: #selector(NSResponder.performTextFinderAction(_:)), keyEquivalent: \"g\", tag: NSTextFinder.Action.nextMatch.rawValue),\n                        NSMenuItem(title: \"Find Previous\", action: #selector(NSResponder.performTextFinderAction(_:)), keyEquivalent: \"G\", tag: NSTextFinder.Action.previousMatch.rawValue),\n                        NSMenuItem(title: \"Use Selection for Find\", action: #selector(NSResponder.performTextFinderAction(_:)), keyEquivalent: \"e\", tag: NSTextFinder.Action.setSearchString.rawValue),\n                        NSMenuItem(title: \"Jump to Selection\", action: #selector(NSResponder.centerSelectionInVisibleArea(_:)), keyEquivalent: \"j\"),\n                    ]),\n                NSMenuItem(\n                    submenuTitle: \"Spelling and Grammar\",\n                    items: [\n                        NSMenuItem(title: \"Show Spelling and Grammar\", action: #selector(NSTextCheckingController.showGuessPanel(_:)), keyEquivalent: \":\"),\n                        NSMenuItem(title: \"Check Document Now\", action: #selector(NSTextCheckingController.checkSpelling(_:)), keyEquivalent: \";\"),\n                        NSMenuItem(title: \"Check Spelling While Typing\", action: #selector(NSTextView.toggleContinuousSpellChecking(_:))),\n                        NSMenuItem(title: \"Correct Spelling Automatically\", action: #selector(NSTextView.toggleAutomaticSpellingCorrection(_:))),\n                    ]),\n                NSMenuItem(\n                    submenuTitle: \"Substitutions\",\n                    items: [\n                        NSMenuItem(title: \"Show Substitutions\", action: #selector(NSTextCheckingController.orderFrontSubstitutionsPanel(_:))),\n                        NSMenuItem.separator(),\n                        NSMenuItem(title: \"Smart Copy/Paste\", action: #selector(NSTextView.toggleSmartInsertDelete(_:))),\n                        NSMenuItem(title: \"Smart Quotes\", action: #selector(NSTextView.toggleAutomaticQuoteSubstitution(_:))),\n                        NSMenuItem(title: \"Smart Dashes\", action: #selector(NSTextView.toggleAutomaticDashSubstitution(_:))),\n                        NSMenuItem(title: \"Smart Links\", action: #selector(NSTextView.toggleAutomaticLinkDetection(_:))),\n                        NSMenuItem(title: \"Data Detectors\", action: #selector(NSTextView.toggleAutomaticDataDetection(_:))),\n                        NSMenuItem(title: \"Text Replacement\", action: #selector(NSTextView.toggleAutomaticTextReplacement(_:))),\n                    ]),\n                NSMenuItem(\n                    submenuTitle: \"Transformations\",\n                    items: [\n                        NSMenuItem(title: \"Make Upper Case\", action: #selector(NSResponder.uppercaseWord(_:))),\n                        NSMenuItem(title: \"Make Lower Case\", action: #selector(NSResponder.lowercaseWord(_:))),\n                        NSMenuItem(title: \"Capitalize\", action: #selector(NSResponder.capitalizeWord(_:))),\n                    ]),\n                NSMenuItem(\n                    submenuTitle: \"Speech\",\n                    items: [\n                        NSMenuItem(title: \"Start Speaking\", action: #selector(NSSpeechSynthesizer.startSpeaking(_:))),\n                        NSMenuItem(title: \"Stop Speaking\", action: #selector(NSTextView.stopSpeaking(_:))),\n                    ]),\n            ]),\n            NSMenuItem(submenuTitle: \"Sections\", items: items),\n            NSMenuItem(submenuTitle: \"Help\", items: [\n                NSMenuItem(title: \"Help\", action: #selector(NSApplication.showHelp(_:)), keyEquivalent: \"?\")\n            ]),\n        ])\n    }\n    \n    func applicationWillTerminate(_ aNotification: Notification) {\n        // Insert code here to tear down your application\n    }\n    \n    func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool {\n        return true\n    }\n    \n    \n    func applicationShouldHandleReopen(_ sender: NSApplication, hasVisibleWindows flag: Bool) -> Bool {\n        guard !flag else {\n            return true\n        }\n        \n        if Preferences.showWelcomeVCOnLaunch {\n            WindowController(kind: .welcome).showWindow(self)\n        } else {\n            URLHandler.shared.presentArchiveChooserPanel(insertToRecentItems: true, senderView: nil)\n        }\n        \n        return false\n    }\n    \n    @objc\n    func openAboutPanel() {\n        WindowController(kind: .aboutPanel).showWindow(self)\n    }\n    \n    @objc\n    func openDiffViewController() {\n        WindowController(kind: .diffSelection).showWindow(self)\n    }\n    \n    func applicationDockMenu(_ sender: NSApplication) -> NSMenu? {\n        let items = Preferences.recentlyOpenedFilePaths\n        guard !items.isEmpty else {\n            return nil\n        }\n        \n        let parentMenu = NSMenu()\n        let submenu = NSMenu()\n        let submenuItem = NSMenuItem()\n        submenuItem.title = \"Recents\"\n        submenuItem.submenu = submenu\n        \n        for (index, item) in items.enumerated() {\n            let menuItem = NSMenuItem(title: URL(fileURLWithPath: item).lastPathComponent,\n                                      action: #selector(openItemFromDockMenu(sender:)),\n                                      keyEquivalent: \"\")\n            menuItem.tag = index\n            submenu.addItem(menuItem)\n        }\n        \n        parentMenu.addItem(submenuItem)\n        return parentMenu\n    }\n    \n    @objc\n    func openItemFromDockMenu(sender: NSMenuItem) {\n        let item = Preferences.recentlyOpenedFilePaths[sender.tag]\n        URLHandler.shared.handleURLChosen(urlChosen: URL(fileURLWithPath: item),\n                                          senderView: nil, insertToRecentItems: true)\n    }\n    \n    func application(_ application: NSApplication, open urls: [URL]) {\n        for url in urls {\n            URLHandler.shared.handleURLChosen(urlChosen: url,\n                                              senderView: nil,\n                                              insertToRecentItems: true,\n                                              openWelcomeScreenUponError: true)\n        }\n    }\n}\n\n"
  },
  {
    "path": "Samra/Assets.xcassets/AccentColor.colorset/Contents.json",
    "content": "{\n  \"colors\" : [\n    {\n      \"idiom\" : \"universal\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "Samra/Assets.xcassets/AppIcon.appiconset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"size\" : \"16x16\",\n      \"idiom\" : \"mac\",\n      \"filename\" : \"icon_16x16.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"16x16\",\n      \"idiom\" : \"mac\",\n      \"filename\" : \"icon_16x16@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"32x32\",\n      \"idiom\" : \"mac\",\n      \"filename\" : \"icon_32x32.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"32x32\",\n      \"idiom\" : \"mac\",\n      \"filename\" : \"icon_32x32@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"128x128\",\n      \"idiom\" : \"mac\",\n      \"filename\" : \"icon_128x128.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"128x128\",\n      \"idiom\" : \"mac\",\n      \"filename\" : \"icon_128x128@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"256x256\",\n      \"idiom\" : \"mac\",\n      \"filename\" : \"icon_256x256.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"256x256\",\n      \"idiom\" : \"mac\",\n      \"filename\" : \"icon_256x256@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"512x512\",\n      \"idiom\" : \"mac\",\n      \"filename\" : \"icon_512x512.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"512x512\",\n      \"idiom\" : \"mac\",\n      \"filename\" : \"icon_512x512@2x.png\",\n      \"scale\" : \"2x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"iconfly\"\n  }\n}"
  },
  {
    "path": "Samra/Assets.xcassets/Contents.json",
    "content": "{\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "Samra/Backend/AppKitPrivates/AppKitPrivates.h",
    "content": "//\n//  AppKitPrivates.h\n//  Samra\n//\n//  Created by Serena on 22/02/2023.\n// \n\n#ifndef AppKitPrivates_h\n#define AppKitPrivates_h\n\n#import <AppKit/NSSplitViewController.h>\n\n// Never go Eric Benét\n@interface NSSplitViewController (PrivateForWhateverReason)\n- (void)splitViewItem:(NSSplitViewItem * _Nonnull)item didChangeCollapsed:(BOOL)didCollapse animated:(BOOL)animated;\n@end\n\n#endif /* AppKitPrivates_h */\n"
  },
  {
    "path": "Samra/Backend/AppKitPrivates/module.modulemap",
    "content": "module AppKitPrivates {\n    header \"AppKitPrivates.h\"\n}\n"
  },
  {
    "path": "Samra/Backend/AssetCatalogInput.swift",
    "content": "//\n//  AssetCatalogInput.swift\n//  Samra\n//\n//  Created by Serena on 06/03/2023.\n// \n\nimport AssetCatalogWrapper\n\nstruct AssetCatalogInput {\n    let fileURL: URL\n    let catalog: CUICatalog\n    let collection: RenditionCollection\n    \n    init(fileURL: URL, catalog: CUICatalog, collection: RenditionCollection) {\n        self.fileURL = fileURL\n        self.catalog = catalog\n        self.collection = collection\n    }\n    \n    init(fileURL: URL) throws {\n        let (catalog, collection) = try AssetCatalogWrapper.shared.renditions(forCarArchive: fileURL)\n        self.catalog = catalog\n        self.collection = collection\n        self.fileURL = fileURL\n    }\n}\n"
  },
  {
    "path": "Samra/Backend/ClosureMenuItem.swift",
    "content": "//\n//  ClosureMenuItem.swift\n//  Samra\n//\n//  Created by Serena on 02/03/2023.\n// \n\nimport Cocoa\n\nclass ClosureMenuItem: NSMenuItem {\n    var closure: (() -> Void)\n    \n    @objc\n    func performClosure() {\n        closure()\n    }\n    \n    init(title: String, closure: @escaping (() -> Void)) {\n        self.closure = closure\n        super.init(title: title, action: #selector(performClosure), keyEquivalent: \"\")\n        self.target = self\n    }\n    \n    required init(coder: NSCoder) {\n        fatalError(\"init(coder:) has not been implemented\")\n    }\n}\n"
  },
  {
    "path": "Samra/Backend/DetailItem.swift",
    "content": "//\n//  DetailItem.swift\n//  Samra\n//\n//  Created by Serena on 21/02/2023.\n// \n\nimport Cocoa\nimport AssetCatalogWrapper\n\nstruct DetailItem: Hashable {\n    /// The Primary Text, such as \"Height\"\n    let primaryText: String\n    \n    /// The Secondary Text, such as the height itself in String form\n    let secondaryText: String\n    \n    init(primaryText: String, secondaryText: String) {\n        self.primaryText = primaryText\n        self.secondaryText = secondaryText\n    }\n    \n    init<T: CustomStringConvertible>(primaryText: String, secondaryText: T?, fallback: String = \"Unknown\") {\n        self.primaryText = primaryText\n        self.secondaryText = secondaryText?.description ?? fallback\n    }\n}\n\nstruct DetailItemSection: Hashable {\n    let sectionHeader: String\n    let items: [DetailItem]\n    \n    static func from(assetStorage: CUICommonAssetStorage) -> [DetailItemSection] {\n        let toolSection = DetailItemSection(sectionHeader: \"Authoring Tool\", items: [\n            DetailItem(primaryText: \"Tool\", secondaryText: assetStorage.authoringTool()),\n            DetailItem(primaryText: \"Version\", secondaryText: String(cString: assetStorage.versionString())),\n        ])\n        \n        let argumentsSection = DetailItemSection(sectionHeader: \"Arguments\", items: [\n            DetailItem(primaryText: \"Thinning Arguments\", secondaryText: assetStorage.thinningArguments())\n        ])\n        \n        let dateFormatter = DateFormatter()\n        dateFormatter.dateFormat = \"E, d MMM yyyy h:mm a\"\n        let date = Date(timeIntervalSince1970: TimeInterval(assetStorage.storageTimestamp()))\n        \n        let dateSection = DetailItemSection(sectionHeader: \"Date\", items: [\n            DetailItem(primaryText: \"Date\", secondaryText: dateFormatter.string(from: date)),\n            DetailItem(primaryText: \"UNIX Timestamp\", secondaryText: assetStorage.storageTimestamp())\n        ])\n        \n        let coreUIVersionText = assetStorage.responds(to: #selector(CUICommonAssetStorage.coreuiVersion)) ? assetStorage.coreuiVersion().description : \"Unknown\"\n        let coreUISection = DetailItemSection(sectionHeader: \"Other\", items: [\n            DetailItem(primaryText: \"CoreUI Version\", secondaryText: coreUIVersionText),\n            DetailItem(primaryText: \"Schema Version\", secondaryText: assetStorage.schemaVersion()),\n        ])\n        \n        return [toolSection, argumentsSection, dateSection, coreUISection]\n    }\n    \n    static func from(rendition: Rendition) -> [DetailItemSection] {\n        let cuiRend = rendition.cuiRend\n        let namedLookup = rendition.namedLookup\n        \n        let formatter = ByteCountFormatter()\n        formatter.countStyle = .memory\n        formatter.includesActualByteCount = true\n        \n        let diskSize = formatter.string(fromByteCount: Int64(cuiRend.srcData.count))\n        \n        let sizeOnDisk = DetailItem(primaryText: \"Size On Disk\", secondaryText: diskSize)\n        var items: [DetailItemSection] = []\n\n        switch rendition.type {\n        case .rawData:\n            items.append(DetailItemSection(sectionHeader: \"Base Attributes\", items: [\n                DetailItem(primaryText: \"Name\", secondaryText: namedLookup.name),\n                sizeOnDisk,\n                DetailItem(primaryText: \"Compression\", secondaryText:cuiRend.bitmapEncoding())\n            ]))\n            var details : [DetailItem] = []\n            if let data = cuiRend.data() {\n                let size = formatter.string(fromByteCount: Int64(data.count))\n                details.append(DetailItem(primaryText: \"Data Length\", secondaryText:size))\n\n            }\n            if let uti = cuiRend.utiType() {\n                details.append(DetailItem(primaryText: \"UTI\", secondaryText:uti))\n            }\n            items.append(DetailItemSection(sectionHeader: \"Data Attributes\", items: details))\n\n        case .color:\n            items.append(DetailItemSection(sectionHeader: \"Base Attributes\", items: [\n                DetailItem(primaryText: \"Name\", secondaryText: cuiRend.name()),\n                sizeOnDisk,\n            ]))\n            let cgColor = cuiRend.cgColor().takeUnretainedValue()\n            let nsColor = NSColor(cgColor:cgColor)?.usingColorSpace(.deviceRGB)\n            var red: CGFloat = 0\n            var green: CGFloat = 0\n            var blue: CGFloat = 0\n            var alpha: CGFloat = 0\n            nsColor?.getRed(&red, green: &green, blue: &blue, alpha: &alpha)\n\n            items.append(DetailItemSection(sectionHeader: \"Color Attributes\", items: [\n                DetailItem(primaryText: \"Red\", secondaryText: Int(red * 255)),\n                DetailItem(primaryText: \"Green\", secondaryText: Int(green * 255)),\n                DetailItem(primaryText: \"Blue\", secondaryText: Int(blue * 255)),\n            ]))\n\n        case .svg, .pdf:\n            items.append(DetailItemSection(sectionHeader: \"Base Attributes\", items: [\n                DetailItem(primaryText: \"Rendition Name\", secondaryText: cuiRend.name()),\n                DetailItem(primaryText: \"Lookup Name\", secondaryText: namedLookup.name),\n                sizeOnDisk,\n            ]))\n            var size = CGSizeZero\n            switch rendition.type {\n            case .svg:\n                if let svgDoc = cuiRend.svgDocument() {\n                    size = CGSVGDocumentGetCanvasSize(svgDoc)\n                }\n            case .pdf:\n                if let pdfDoc = cuiRend.pdfDocument()?.takeUnretainedValue(), let page = pdfDoc.page(at:1) {\n                    size = page.getBoxRect(.artBox).size\n                }\n            default:\n                break\n            }\n            items.append(DetailItemSection(sectionHeader: \"Dimensions\", items: [\n                DetailItem(primaryText: \"Width\", secondaryText: size.width),\n                DetailItem(primaryText: \"Height\", secondaryText: size.height),\n            ]))\n\n        default:\n            items.append(DetailItemSection(sectionHeader: \"Base Attributes\", items: [\n                DetailItem(primaryText: \"Rendition Name\", secondaryText: cuiRend.name()),\n                DetailItem(primaryText: \"Lookup Name\", secondaryText: namedLookup.name),\n                sizeOnDisk,\n                DetailItem(primaryText: \"Compression\", secondaryText:cuiRend.bitmapEncoding())\n            ]))\n            let size = cuiRend.unslicedSize()\n            items.append(DetailItemSection(sectionHeader: \"Dimensions\", items: [\n                DetailItem(primaryText: \"Width\", secondaryText: size.width),\n                DetailItem(primaryText: \"Height\", secondaryText: size.height),\n                DetailItem(primaryText: \"Scale\", secondaryText: cuiRend.scale())\n            ]))\n        }\n        \n        let key = namedLookup.key\n        items.append(DetailItemSection(sectionHeader: \"Rendition Information\", items: [\n            DetailItem(primaryText: \"Display Gamut\", secondaryText: Rendition.DisplayGamut(key)),\n            DetailItem(primaryText: \"Appearance\", secondaryText: namedLookup.appearance),\n            DetailItem(primaryText: \"Idiom\", secondaryText: Rendition.Idiom(key))\n        ]))\n        \n        return items\n    }\n}\n"
  },
  {
    "path": "Samra/Backend/Extensions.swift",
    "content": "//\n//  Extensions.swift\n//  Samra\n//\n//  Created by Serena on 18/02/2023.\n// \n\nimport Cocoa\nimport AssetCatalogWrapper\nimport UniformTypeIdentifiers\n\n@available(macOS 11, *)\nextension UTType {\n    static var carFile: UTType = UTType(filenameExtension: \"car\")!\n}\n\nextension NSUserInterfaceItemIdentifier: ExpressibleByStringLiteral {\n    public init(stringLiteral value: StringLiteralType) {\n        self.init(value)\n    }\n}\n\nextension NSToolbarItem.Identifier {\n    static let searchBar = NSToolbarItem.Identifier(\"SearchBar\")\n}\n\nextension NSMenu {\n    convenience init(title: String? = nil, items: [NSMenuItem]?) {\n        defer {\n            items.flatMap {\n                self.items = $0\n            }\n        }\n        guard let title = title else {\n            self.init()\n            return\n        }\n        self.init(title: title)\n    }\n}\n\nextension NSMenuItem {\n    convenience init(submenuTitle: String, items: [NSMenuItem]?) {\n        self.init(title: submenuTitle, action: nil, keyEquivalent: \"\")\n        submenu = NSMenu(title: submenuTitle, items: items)\n    }\n    \n    convenience init(title: String, action: Selector? = nil, keyEquivalent: String = \"\", keyEquivalentModifierMask: NSEvent.ModifierFlags? = nil, tag: Int? = nil) {\n        self.init(title: title, action: action, keyEquivalent: keyEquivalent)\n        keyEquivalentModifierMask.flatMap {\n            self.keyEquivalentModifierMask = $0\n        }\n        tag.flatMap {\n            self.tag = $0\n        }\n    }\n}\n\nextension CGImage {\n    var size: CGSize {\n        return CGSize(width: width, height: height)\n    }\n}\n\nextension NSAlert {\n    convenience init(title: String, message: String? = nil) {\n        self.init()\n        self.messageText = title\n        self.informativeText = message ?? self.informativeText\n    }\n}\n\nextension NSWindow {\n    /// Makes the title bar of the NSWindow transparent and removes the window's ability to be resized\n    func makeTitleBarTransparentAndUnresizable() {\n        styleMask.remove(.resizable)\n        titleVisibility = .hidden\n        titlebarAppearsTransparent = true\n    }\n}\n\nextension NSColor {\n    static func _makeStandardWindowBg(appearance: NSAppearance) -> NSColor {\n        switch appearance.name {\n        case .aqua, .vibrantLight, .accessibilityHighContrastAqua, .accessibilityHighContrastVibrantLight: // light\n            return .white\n        case .darkAqua, .accessibilityHighContrastVibrantDark, .accessibilityHighContrastDarkAqua, .vibrantDark: // dark\n            return NSColor(red: 0.19, green: 0.19, blue: 0.19, alpha: 1)\n        default:\n            fatalError()\n        }\n    }\n    \n    static var standardWindowBackgroundColor: NSColor {\n        return NSColor(name: nil, dynamicProvider: _makeStandardWindowBg(appearance:))\n    }\n}\n\nextension NSImage {\n    convenience init?(systemName: String) {\n        if #available(macOS 11, *) {\n            self.init(systemSymbolName: systemName, accessibilityDescription: nil)\n        } else {\n            return nil\n        }\n    }\n}\n"
  },
  {
    "path": "Samra/Backend/Preferences.swift",
    "content": "//\n//  Preferences.swift\n//  Samra\n//\n//  Created by Serena on 18/02/2023.\n// \n\nimport Foundation\n\n@propertyWrapper\nstruct Storage<T> {\n    let key: String\n    var defaultValue: T\n    \n    var wrappedValue: T {\n        get {\n            UserDefaults.standard.object(forKey: key) as? T ?? defaultValue\n        }\n        \n        set {\n            UserDefaults.standard.set(newValue, forKey: key)\n        }\n    }\n}\n\nenum Preferences {\n    @Storage(key: \"RecentlyOpenedPaths\", defaultValue: [])\n    static var recentlyOpenedFilePaths: [String]\n    \n    @Storage(key: \"ShowWelcomeViewControllerOnLaunch\", defaultValue: true)\n    static var showWelcomeVCOnLaunch: Bool\n    \n    /*\n    static var recentlyOpenedFilePaths: [String] {\n        get {\n            let arr = UserDefaults.standard.stringArray(forKey: \"RecentlyOpenedPaths\") ?? []\n            return arr\n        }\n        \n        set {\n            UserDefaults.standard.set(newValue, forKey: \"RecentlyOpenedPaths\")\n        }\n    }\n     */\n}\n"
  },
  {
    "path": "Samra/Backend/RenditionDiff.swift",
    "content": "//\n//  DiffKind.swift\n//  Samra\n//\n//  Created by Serena on 07/03/2023.\n// \n\nimport AssetCatalogWrapper\n\nstruct RenditionDiff {\n    let rend: Rendition\n    let kind: Kind\n    \n    enum Kind: CustomStringConvertible {\n        case added\n        case removed\n        \n        var description: String {\n            switch self {\n            case .added:\n                return \"Added\"\n            case .removed:\n                return \"Removed\"\n            }\n        }\n    }\n}\n\nenum DiffSide: Int {\n\tcase left = 1\n\tcase right = 2\n}\n"
  },
  {
    "path": "Samra/Backend/UI Support/AssetCatalogDocument.swift",
    "content": "//\n//  AssetCatalogDocument.swift\n//  Samra\n//\n//  Created by Serena on 02/03/2023.\n// \n\nimport Cocoa\nimport AssetCatalogWrapper\n\n// this NSDocument subclass is from https://github.com/insidegui/AssetCatalogTinkerer\n// (because this app is my first attempt at AppKit and I didn't really know how to do NSDocument)..\n// but adjusted for Samra\nclass AssetCatalogDocument: NSDocument {\n    override func read(from url: URL, ofType typeName: String) throws {\n        // close the welcome view controller if opened\n        for window in NSApplication.shared.windows {\n            if window.contentViewController is WelcomeViewController {\n                window.close()\n            }\n        }\n        \n        let windowController = WindowController(kind: .assetCatalog(try AssetCatalogInput(fileURL: url)))\n        addWindowController(windowController)\n        windowController.showWindow(nil)\n    }\n}\n"
  },
  {
    "path": "Samra/Backend/UI Support/BasicLayoutAnchorsHolding.swift",
    "content": "//\n//  BasicLayoutAnchorsHolding.swift\n//  Samra\n//\n//  Created by Serena on 18/02/2023.\n// \n\n#if canImport(AppKit)\nimport AppKit\n#elseif canImport(UIKit)\nimport UIKit\n#endif\n\nprotocol BasicLayoutAnchorsHolding {\n    var topAnchor: NSLayoutYAxisAnchor { get }\n    var bottomAnchor: NSLayoutYAxisAnchor { get }\n    var leadingAnchor: NSLayoutXAxisAnchor { get }\n    var trailingAnchor: NSLayoutXAxisAnchor { get }\n    var centerXAnchor: NSLayoutXAxisAnchor { get }\n    var centerYAnchor: NSLayoutYAxisAnchor { get }\n}\n\nextension BasicLayoutAnchorsHolding {\n    /// Activate constraints to cover the target with the current item.\n    func constraintCompletely<Target: BasicLayoutAnchorsHolding>(to target: Target) {\n        NSLayoutConstraint.activate([\n            leadingAnchor.constraint(equalTo: target.leadingAnchor),\n            trailingAnchor.constraint(equalTo: target.trailingAnchor),\n            topAnchor.constraint(equalTo: target.topAnchor),\n            bottomAnchor.constraint(equalTo: target.bottomAnchor)\n        ])\n    }\n    \n    /// Activate constraints to center the target with the current item.\n    func centerConstraints<Target: BasicLayoutAnchorsHolding>(to target: Target) {\n        NSLayoutConstraint.activate([\n            centerXAnchor.constraint(equalTo: target.centerXAnchor),\n            centerYAnchor.constraint(equalTo: target.centerYAnchor)\n        ])\n    }\n}\n\n#if canImport(UIKit)\nextension UIView: BasicLayoutAnchorsHolding {}\nextension UILayoutGuide: BasicLayoutAnchorsHolding {}\n#else\nextension NSView: BasicLayoutAnchorsHolding {}\nextension NSLayoutGuide: BasicLayoutAnchorsHolding {}\n#endif\n"
  },
  {
    "path": "Samra/Backend/UI Support/QuickLooKPreviewSource.swift",
    "content": "//\n//  QuickLooKPreviewSource.swift\n//  Samra\n//\n//  Created by Serena on 03/03/2023.\n// \n\nimport Cocoa\nimport QuickLookUI\n\nclass QuickLookPreviewSource: NSObject, QLPreviewPanelDataSource {\n    let fileURL: URL\n    \n    init(fileURL: URL) {\n        self.fileURL = fileURL\n    }\n    \n    func numberOfPreviewItems(in panel: QLPreviewPanel!) -> Int {\n        return 1\n    }\n    \n    func previewPanel(_ panel: QLPreviewPanel!, previewItemAt index: Int) -> QLPreviewItem! {\n        return fileURL as QLPreviewItem\n    }\n}\n"
  },
  {
    "path": "Samra/Backend/UI Support/URLHandler.swift",
    "content": "//\n//  URLHandler.swift\n//  Samra\n//\n//  Created by Serena on 20/02/2023.\n// \n\nimport Cocoa\nimport AssetCatalogWrapper\n\nclass URLHandler {\n    \n    static let shared = URLHandler()\n    \n    private init() {}\n    \n    @objc\n    func presentArchiveChooserPanel(insertToRecentItems: Bool = false, senderView: NSView?, handler: ((URL) -> Void)? = nil) {\n        let panel = NSOpenPanel()\n        let button = ClosureBasedButton(checkboxWithTitle: \"Treat Bundles as directories\", target: nil, action: nil)\n        button.allowsMixedState = false\n        button.setAction {\n            switch button.state {\n            case .on:\n                panel.treatsFilePackagesAsDirectories = true\n            case .off:\n                panel.treatsFilePackagesAsDirectories = false\n            default:\n                print(\"Not supposed to be here\")\n            }\n        }\n        \n        panel.accessoryView = button\n        panel.accessoryView?.frame.size.height += 18\n        panel.canChooseDirectories = false\n        panel.allowsMultipleSelection = false\n        if #available(macOS 11, *) {\n            panel.allowedContentTypes = [.carFile, .application]\n        } else {\n            panel.allowedFileTypes = [\"car\", \"app\"]\n        }\n        \n        if panel.runModal() == .OK {\n            if let handler {\n                handler(panel.urls[0])\n            } else {\n                handleURLChosen(urlChosen: panel.urls[0], senderView: senderView, insertToRecentItems: insertToRecentItems)\n            }\n        }\n    }\n    \n    \n    func handleURLChosen(urlChosen: URL,\n                         senderView: NSView?,\n                         insertToRecentItems: Bool = false,\n                         openWelcomeScreenUponError: Bool = false) {\n        \n        let urlToOpen: URL\n        switch urlChosen.pathExtension {\n        case \"car\":\n            // in case the URL was opened through the samra:// URL scheme,\n            // let's init with URL(fileURLWithPath:),\n            // to make sure that we have the file:// URL scheme\n            urlToOpen = URL(fileURLWithPath: urlChosen.path)\n        case \"app\":\n            // find Assets.car file for the application\n            // and make sure it exists\n            urlToOpen = URL(fileURLWithPath: urlChosen.path) // set to file URL\n                .appendingPathComponent(\"Contents/Resources/Assets.car\")\n            guard FileManager.default.fileExists(atPath: urlToOpen.path) else {\n                NSAlert(title: \"Assets.car file does not exist for Application \\(urlChosen.path)\").runModal()\n                return\n            }\n            \n        default:\n            NSAlert(title: \"File has unrecognized extension \\\"\\(urlChosen.pathExtension)\\\"\").runModal()\n            return\n        }\n        \n        do {\n            let input = try AssetCatalogInput(fileURL: urlToOpen)\n            // open new window & view controller for it\n            WindowController(kind: .assetCatalog(input)).showWindow(self)\n            if insertToRecentItems {\n                var copy = Preferences.recentlyOpenedFilePaths\n                copy.removeAll { $0 == urlChosen.path }\n                copy.append(urlChosen.path)\n                Preferences.recentlyOpenedFilePaths = copy\n            }\n            senderView?.window?.close()\n        } catch {\n            if openWelcomeScreenUponError {\n                WindowController(kind: .welcome).showWindow(NSApplication.shared.delegate)\n            }\n            \n            let alert = NSAlert()\n            alert.messageText = \"Unable to load Assets file\"\n            alert.informativeText = \"Error: \\(error.localizedDescription)\"\n            alert.runModal()\n        }\n    }\n}\n"
  },
  {
    "path": "Samra/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\t<key>CFBundleDocumentTypes</key>\n\t<array>\n\t\t<dict>\n\t\t\t<key>CFBundleTypeExtensions</key>\n\t\t\t<array>\n\t\t\t\t<string>car</string>\n\t\t\t</array>\n\t\t\t<key>CFBundleTypeIconFile</key>\n\t\t\t<string></string>\n\t\t\t<key>CFBundleTypeName</key>\n\t\t\t<string>Asset Catalog</string>\n\t\t\t<key>CFBundleTypeIconSystemGenerated</key>\n\t\t\t<integer>1</integer>\n\t\t\t<key>CFBundleTypeRole</key>\n\t\t\t<string>Viewer</string>\n\t\t\t<key>LSItemContentTypes</key>\n\t\t\t<array>\n\t\t\t\t<string>com.apple.assetcatalog</string>\n\t\t\t</array>\n\t\t\t<key>LSTypeIsPackage</key>\n\t\t\t<integer>0</integer>\n\t\t\t<key>NSDocumentClass</key>\n\t\t\t<string>$(PRODUCT_MODULE_NAME).AssetCatalogDocument</string>\n\t\t</dict>\n\t</array>\n\t<key>UTExportedTypeDeclarations</key>\n\t<array>\n\t\t<dict>\n\t\t\t<key>UTTypeConformsTo</key>\n\t\t\t<array>\n\t\t\t\t<string>public.data</string>\n\t\t\t</array>\n\t\t\t<key>UTTypeDescription</key>\n\t\t\t<string>Asset Catalog</string>\n\t\t\t<key>UTTypeIdentifier</key>\n\t\t\t<string>com.apple.assetcatalog</string>\n\t\t\t<key>UTTypeIconFile</key>\n\t\t\t<string></string>\n\t\t\t<key>UTTypeIcons</key>\n\t\t\t<dict>\n\t\t\t\t<key>UTTypeIconBackgroundName</key>\n\t\t\t\t<string>AssetCatalog</string>\n\t\t\t\t<key>UTTypeIconBadgeName</key>\n\t\t\t\t<string></string>\n\t\t\t\t<key>UTTypeIconText</key>\n\t\t\t\t<string></string>\n\t\t\t</dict>\n\t\t\t<key>UTTypeTagSpecification</key>\n\t\t\t<dict>\n\t\t\t\t<key>public.filename-extension</key>\n\t\t\t\t<array>\n\t\t\t\t\t<string>car</string>\n\t\t\t\t</array>\n\t\t\t</dict>\n\t\t</dict>\n\t</array>\n\t<key>CFBundleURLTypes</key>\n\t<array>\n\t\t<dict>\n\t\t\t<key>CFBundleTypeRole</key>\n\t\t\t<string>Editor</string>\n\t\t\t<key>CFBundleURLName</key>\n\t\t\t<string>com.serena.samra.openfile</string>\n\t\t\t<key>CFBundleURLSchemes</key>\n\t\t\t<array>\n\t\t\t\t<string>samra</string>\n\t\t\t</array>\n\t\t</dict>\n\t</array>\n</dict>\n</plist>\n"
  },
  {
    "path": "Samra/Samra.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</plist>\n"
  },
  {
    "path": "Samra/UI/AboutViewController.swift",
    "content": "//\n//  AboutViewController.swift\n//  Samra\n//\n//  Created by Serena on 28/02/2023.\n// \n\nimport Cocoa\n\nclass AboutViewController: NSViewController {\n    override func loadView() {\n        view = NSView()\n        view.frame.size = CGSize(width: 530.0, height:219.0)\n    }\n    \n    override func viewDidLoad() {\n        super.viewDidLoad()\n        \n        \n        let imageView = NSImageView(image: NSApplication.shared.applicationIconImage)\n        \n        let titleLabel = NSTextField(labelWithString: \"Samra\")\n        titleLabel.font = .systemFont(ofSize: 38)\n        \n        let version = Bundle.main.infoDictionary?[\"CFBundleVersion\"] as! String\n        let versionLabel = NSTextField(labelWithString: \"Version \\(version)\")\n        versionLabel.textColor = .secondaryLabelColor\n        \n        let explanation = \"An open source macOS Application to browse and edit Asset Catalog files, created by Antoine\"\n        let explanationLabel = NSTextField(wrappingLabelWithString: explanation)\n        \n        explanationLabel.textColor = NSColor(red: 0.60, green: 0.60, blue: 0.60, alpha: 1.00)\n        if #available(macOS 11, *) {\n            explanationLabel.font = .preferredFont(forTextStyle: .footnote)\n        } else {\n            explanationLabel.font = .systemFont(ofSize: 10)\n        }\n        \n        imageView.translatesAutoresizingMaskIntoConstraints = false\n        titleLabel.translatesAutoresizingMaskIntoConstraints = false\n        versionLabel.translatesAutoresizingMaskIntoConstraints = false\n        explanationLabel.translatesAutoresizingMaskIntoConstraints = false\n        \n        view.addSubview(imageView)\n        view.addSubview(titleLabel)\n        view.addSubview(versionLabel)\n        view.addSubview(explanationLabel)\n        \n        NSLayoutConstraint.activate([\n            imageView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 40),\n            imageView.centerYAnchor.constraint(equalTo: view.centerYAnchor),\n            \n            titleLabel.leadingAnchor.constraint(equalTo: imageView.trailingAnchor, constant: 15),\n            titleLabel.centerYAnchor.constraint(equalTo: imageView.topAnchor, constant: 32),\n            \n            versionLabel.leadingAnchor.constraint(equalTo: titleLabel.leadingAnchor),\n            versionLabel.centerYAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 5),\n            \n            explanationLabel.leadingAnchor.constraint(equalTo: versionLabel.leadingAnchor),\n            explanationLabel.trailingAnchor.constraint(lessThanOrEqualTo: view.trailingAnchor),\n            explanationLabel.centerYAnchor.constraint(equalTo: versionLabel.bottomAnchor, constant: 20)\n        ])\n        \n        let twitterButton = NSButton(title: \"Twitter\",\n                                     target: self, action: #selector(openTwitter))\n        let sourceCodeButton = NSButton(title: \"Source Code\",\n                                        target: self, action: #selector(openSourceCode))\n        \n        twitterButton.bezelStyle = .rounded\n        sourceCodeButton.bezelStyle = .rounded\n        \n        let buttonsStackView = NSStackView(views: [twitterButton, sourceCodeButton])\n        buttonsStackView.translatesAutoresizingMaskIntoConstraints = false\n        \n        view.addSubview(buttonsStackView)\n        NSLayoutConstraint.activate([\n            buttonsStackView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -26.4),\n            buttonsStackView.centerYAnchor.constraint(equalTo: view.bottomAnchor, constant: -25),\n            \n//            twitterButton.widthAnchor.constraint(equalToConstant: 154),\n//            sourceCodeButton.widthAnchor.constraint(equalToConstant: 160)\n        ])\n    }\n    \n    @objc\n    func openSourceCode() {\n        NSWorkspace.shared.open(URL(string: \"https://github.com/NSAntoine/Samra\")!)\n    }\n    \n    @objc\n    func openTwitter() {\n        NSWorkspace.shared.open(URL(string: \"https://twitter.com/NSAntoine\")!)\n    }\n    \n    override func viewDidAppear() {\n        super.viewDidAppear()\n        \n        guard let window = view.window else { return }\n        window.backgroundColor = .standardWindowBackgroundColor\n        window.standardWindowButton(.miniaturizeButton)?.isEnabled = false\n        window.standardWindowButton(.zoomButton)?.isEnabled = false\n    }\n}\n"
  },
  {
    "path": "Samra/UI/ClosureBasedButton.swift",
    "content": "//\n//  ClosureBasedButton.swift\n//  Samra\n//\n//  Created by Serena on 05/03/2024.\n//  \n\nimport Cocoa\n\nclass ClosureBasedButton: NSButton {\n    var closureAction: (() -> Void)?\n    \n    @objc\n    func performClosureAction() {\n        closureAction?()\n    }\n    \n    func setAction(_ action: @escaping () -> Void) {\n        self.closureAction = action\n        self.action = #selector(performClosureAction)\n        self.target = self\n    }\n}\n"
  },
  {
    "path": "Samra/UI/CollapseNotifierSplitViewController.swift",
    "content": "//\n//  CollapseNotifierSplitViewController.swift\n//  Samra\n//\n//  Created by Serena on 22/02/2023.\n// \n\nimport Cocoa\nimport AppKitPrivates\n\n/// A NSSPlitViewController subclass that notifies it's reciever\n/// when a collapse status changes\nclass CollapseNotifierSplitViewController: NSSplitViewController {\n    typealias Handler = (_ item: NSSplitViewItem, _ didCollapse: Bool, _ animated: Bool) -> Void\n    \n    var handler: Handler? = nil\n    \n    /// Whether or not the view controller should focus on the search bar\n    /// when the cmd+f combo is clicked\n    var shouldFocusOnSearchBar: Bool = false\n    \n    override func splitViewItem(_ item: NSSplitViewItem, didChangeCollapsed didCollapse: Bool, animated: Bool) {\n        super.splitViewItem(item, didChangeCollapsed: didCollapse, animated: animated)\n        handler?(item, didCollapse, animated)\n    }\n}\n"
  },
  {
    "path": "Samra/UI/Diff/AssetCatalogDiffSelectionViewController.swift",
    "content": "//\n//  AssetCatalogDiffSelectionViewController.swift\n//  Samra\n//\n//  Created by Serena on 06/03/2023.\n// \n\nimport Cocoa\nimport AssetCatalogWrapper\n\n/// A View Controller to select 2 files to diff them.\nclass AssetCatalogDiffSelectionViewController: NSViewController {\n    override func loadView() {\n        view = NSView()\n        view.frame.size = CGSize(width: 577, height: 208)\n    }\n    \n    typealias DataSource = NSCollectionViewDiffableDataSource<RenditionDiff.Kind, Rendition>\n    var dataSource: DataSource!\n    \n    var leftCatalogInput: AssetCatalogInput?\n    var rightCatalogInput: AssetCatalogInput?\n    \n    var leftCatalogPathLabel: NSTextField!\n    var rightCatalogPathLabel: NSTextField!\n    \n    var leftCatalogPreview: DiffFilePreviewView!\n    var rightCatalogPreview: DiffFilePreviewView!\n    \n    var diffCatalogsButton: NSButton!\n    \n    override func viewDidLoad() {\n        super.viewDidLoad()\n        \n        let leftButton = NSButton(title: \"Left...\",\n                                  target: self, action: #selector(leftOrRightButtonClicked(sender:)))\n        leftButton.tag = DiffSide.left.rawValue\n        \n        let rightButton = NSButton(title: \"Right...\",\n                                   target: self, action: #selector(leftOrRightButtonClicked(sender:)))\n        rightButton.tag = DiffSide.right.rawValue\n        \n        leftButton.translatesAutoresizingMaskIntoConstraints = false\n        rightButton.translatesAutoresizingMaskIntoConstraints = false\n        view.addSubview(leftButton)\n        view.addSubview(rightButton)\n        \n        leftCatalogPathLabel = NSTextField(labelWithString: \"\")\n        rightCatalogPathLabel = NSTextField(labelWithString: \"\")\n        \n        leftCatalogPathLabel.translatesAutoresizingMaskIntoConstraints = false\n        rightCatalogPathLabel.translatesAutoresizingMaskIntoConstraints = false\n        \n        view.addSubview(leftCatalogPathLabel)\n        view.addSubview(rightCatalogPathLabel)\n        \n        diffCatalogsButton = NSButton(title: \"Start Diff\", target: self, action: #selector(diffButtonPressed))\n        diffCatalogsButton.translatesAutoresizingMaskIntoConstraints = false\n        diffCatalogsButton.isEnabled = false\n        \n        view.addSubview(diffCatalogsButton)\n        \n        let previewBackgroundColor = NSColor(red: 0.22, green: 0.21, blue: 0.21, alpha: 1.00)\n\t\tleftCatalogPreview = makePreview(color: previewBackgroundColor, side: .left)\n        leftCatalogPreview.translatesAutoresizingMaskIntoConstraints = false\n        view.addSubview(leftCatalogPreview)\n        \n        let leftCatalogPreviewLabel = NSTextField(labelWithString: \"Left\")\n        leftCatalogPreviewLabel.translatesAutoresizingMaskIntoConstraints = false\n        leftCatalogPreviewLabel.alignment = .center\n        view.addSubview(leftCatalogPreviewLabel)\n        \n\t\trightCatalogPreview = makePreview(color: previewBackgroundColor, side: .right)\n        rightCatalogPreview.translatesAutoresizingMaskIntoConstraints = false\n        view.addSubview(rightCatalogPreview)\n        \n        let rightCatalogPreviewLabel = NSTextField(labelWithString: \"Right\")\n        rightCatalogPreviewLabel.translatesAutoresizingMaskIntoConstraints = false\n        rightCatalogPreviewLabel.alignment = .center\n        view.addSubview(rightCatalogPreviewLabel)\n        \n        NSLayoutConstraint.activate([\n            leftButton.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: -10),\n            leftButton.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),\n            leftButton.widthAnchor.constraint(equalToConstant: 80),\n            \n            rightButton.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: 25),\n            rightButton.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),\n            rightButton.widthAnchor.constraint(equalToConstant: 80),\n            \n            rightCatalogPreview.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -13),\n            rightCatalogPreview.widthAnchor.constraint(equalToConstant: 55),\n            rightCatalogPreview.topAnchor.constraint(equalTo: leftButton.topAnchor),\n            rightCatalogPreview.bottomAnchor.constraint(equalTo: rightButton.bottomAnchor),\n            \n            rightCatalogPreviewLabel.topAnchor.constraint(equalTo: rightCatalogPreview.topAnchor, constant: -20),\n            rightCatalogPreviewLabel.leadingAnchor.constraint(equalTo: rightCatalogPreview.leadingAnchor),\n            rightCatalogPreviewLabel.trailingAnchor.constraint(equalTo: rightCatalogPreview.trailingAnchor),\n            \n            leftCatalogPreview.trailingAnchor.constraint(equalTo: rightCatalogPreviewLabel.leadingAnchor, constant: -20),\n            leftCatalogPreview.widthAnchor.constraint(equalToConstant: 55),\n            leftCatalogPreview.topAnchor.constraint(equalTo: leftButton.topAnchor),\n            leftCatalogPreview.bottomAnchor.constraint(equalTo: rightButton.bottomAnchor),\n            \n            leftCatalogPathLabel.centerYAnchor.constraint(equalTo: leftButton.centerYAnchor),\n            leftCatalogPathLabel.leadingAnchor.constraint(equalTo: leftButton.trailingAnchor, constant: 10),\n            leftCatalogPathLabel.trailingAnchor.constraint(equalTo: leftCatalogPreview.leadingAnchor, constant: -20),\n            \n            leftCatalogPreviewLabel.topAnchor.constraint(equalTo: leftCatalogPreview.topAnchor, constant: -20),\n            leftCatalogPreviewLabel.leadingAnchor.constraint(equalTo: leftCatalogPreview.leadingAnchor),\n            leftCatalogPreviewLabel.trailingAnchor.constraint(equalTo: leftCatalogPreview.trailingAnchor),\n            \n            rightCatalogPathLabel.centerYAnchor.constraint(equalTo: rightButton.centerYAnchor),\n            rightCatalogPathLabel.leadingAnchor.constraint(equalTo: rightButton.trailingAnchor, constant: 10),\n            rightCatalogPathLabel.trailingAnchor.constraint(equalTo: leftCatalogPreview.leadingAnchor, constant: -20),\n            \n            diffCatalogsButton.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -26.4),\n            diffCatalogsButton.centerYAnchor.constraint(equalTo: view.bottomAnchor, constant: -25),\n        ])\n    }\n    \n    override func viewDidAppear() {\n        super.viewDidAppear()\n        \n        view.window?.title = \"Diff Catalogs\"\n        view.window?.styleMask.remove(.resizable)\n    }\n    \n\tfunc makePreview(color: NSColor, side: DiffSide) -> DiffFilePreviewView {\n        let preview = DiffFilePreviewView(side: side)\n\t\tpreview.delegate = self\n\t\treturn preview\n    }\n    \n    @objc\n    func diffButtonPressed() {\n        guard let left = leftCatalogInput, let right = rightCatalogInput else { return }\n        let rightCollection = right.collection.flatMap(\\.renditions)\n        let leftCollection = left.collection.flatMap(\\.renditions)\n        \n        let diff = leftCollection.difference(from: rightCollection) { rend1, rend2 in\n            return rend1.namedLookup.name == rend2.namedLookup.name\n        }\n        \n        var finalDiffs: [RenditionDiff] = []\n        for meow in diff {\n            switch meow {\n            case .insert(_, let element, _):\n                finalDiffs.append(RenditionDiff(rend: element, kind: .added))\n            case .remove(_, let element, _):\n                finalDiffs.append(RenditionDiff(rend: element, kind: .removed))\n            }\n        }\n        \n        WindowController(kind: .diffShow(finalDiffs, left.catalog, left.fileURL)).showWindow(nil)\n    }\n    \n    @objc\n    func leftOrRightButtonClicked(sender: NSButton) {\n        URLHandler.shared.presentArchiveChooserPanel(senderView: nil) { [unowned self] url in\n            validateAndProcessURL(url, forSide: DiffSide(rawValue: sender.tag)!)\n        }\n    }\n    \n    func validateAndProcessURL(_ url: URL, forSide side: DiffSide) {\n        // if it's an .app, point to it's .car file\n        let urlToChoose = url.pathExtension == \"app\" ? url.appendingPathComponent(\"Contents/Resources/Assets.car\") : url\n        guard FileManager.default.fileExists(atPath: urlToChoose.path) else {\n            NSAlert(title: \"Asset Catalog file \\(urlToChoose.path) doesn't exist\").runModal()\n            return\n        }\n        \n        do {\n            switch side {\n            case .left:\n                leftCatalogInput = try AssetCatalogInput(fileURL: urlToChoose)\n                leftCatalogPathLabel.stringValue = urlToChoose.path\n\t\t\t\tleftCatalogPreview.imageView.image = NSWorkspace.shared.icon(forFile: url.path)\n            case .right:\n                rightCatalogInput = try AssetCatalogInput(fileURL: urlToChoose)\n                rightCatalogPathLabel.stringValue = urlToChoose.path\n\t\t\t\trightCatalogPreview.imageView.image = NSWorkspace.shared.icon(forFile: url.path)\n            }\n   \n            diffCatalogsButton.isEnabled = rightCatalogInput != nil && leftCatalogInput != nil\n        } catch {\n            NSAlert(title: \"Unable to open Asset Catalog file \\(urlToChoose.path)\")\n                .runModal()\n        }\n    }\n    \n    func setImageViewForPreview(url: URL, side: DiffSide) {\n\t\tswitch side {\n\t\tcase .left:\n\t\t\tleftCatalogPreview.imageView.image = NSWorkspace.shared.icon(forFile: url.path)\n\t\tcase .right:\n\t\t\trightCatalogPreview.imageView.image = NSWorkspace.shared.icon(forFile: url.path)\n\t\t}\n    }\n}\n\nextension AssetCatalogDiffSelectionViewController: DiffFilePreviewDelegate {\n\tfunc diffFilePreview(_ view: DiffFilePreviewView, didGetURLDragged urlRecieved: URL) {\n\t\tvalidateAndProcessURL(urlRecieved, forSide: view.side)\n\t}\n}\n"
  },
  {
    "path": "Samra/UI/Diff/DiffFilePreviewView.swift",
    "content": "//\n//  DiffFilePreviewView.swift\n//  Samra\n//\n//  Created by Serena on 03/05/2023.\n//\n\nimport Cocoa\n\nclass DiffFilePreviewView: NSView {\n\tlet side: DiffSide\n\tlet imageView = NSImageView()\n\t\n\tweak var delegate: DiffFilePreviewDelegate?\n\t\n\tinit(side: DiffSide) {\n\t\tself.side = side\n\t\tsuper.init(frame: .zero)\n\t\tcommonInit()\n\t}\n\t\n\trequired init?(coder: NSCoder) {\n\t\tfatalError(\"init(coder:) has not been implemented\")\n\t}\n\t\n\t// setup the view\n\tfunc commonInit() {\n\t\tlet previewBackgroundColor = NSColor(red: 0.22, green: 0.21, blue: 0.21, alpha: 1.00)\n\t\t\n\t\tlet previewLayer = CALayer()\n\t\tpreviewLayer.backgroundColor = previewBackgroundColor.cgColor\n\t\tpreviewLayer.borderColor = NSColor.lightGray.cgColor\n\t\tpreviewLayer.borderWidth = 1.34\n\t\tpreviewLayer.cornerRadius = 8\n\t\tlayer = previewLayer\n\t\twantsLayer = true\n\t\t\n\t\tregisterForDraggedTypes([.fileURL])\n\t\t\n\t\timageView.translatesAutoresizingMaskIntoConstraints = false\n\t\taddSubview(imageView)\n\t\t\n\t\tNSLayoutConstraint.activate([\n\t\t\timageView.heightAnchor.constraint(equalTo: heightAnchor),\n\t\t\timageView.centerYAnchor.constraint(equalTo: centerYAnchor),\n\t\t\timageView.centerXAnchor.constraint(equalTo: centerXAnchor)\n\t\t])\n\t}\n\t\n\toverride func draggingEntered(_ sender: NSDraggingInfo) -> NSDragOperation {\n\t\treturn .generic\n\t}\n\t\n\toverride func draggingEnded(_ sender: NSDraggingInfo) {\n\t\tsender.enumerateDraggingItems(for: nil, classes: [NSURL.self]) { [unowned self] item, t, ptr in\n\t\t\tlet asURL = item.item as! URL\n\t\t\tdelegate?.diffFilePreview(self, didGetURLDragged: asURL)\n\t\t}\n\t}\n}\n\nprotocol DiffFilePreviewDelegate: AnyObject {\n\tfunc diffFilePreview(_ view: DiffFilePreviewView, didGetURLDragged: URL)\n}\n"
  },
  {
    "path": "Samra/UI/Diff/DiffListViewController.swift",
    "content": "//\n//  DiffListViewController.swift\n//  Samra\n//\n//  Created by Serena on 07/03/2023.\n// \n\nimport Cocoa\nimport class SwiftUI.NSHostingController\nimport AssetCatalogWrapper\n\nclass DiffListViewController: NSViewController {\n    \n    typealias DataSource = NSCollectionViewDiffableDataSource<RenditionDiff.Kind, Rendition>\n    var dataSource: DataSource!\n    \n    let diffs: [RenditionDiff]\n    var catalog: CUICatalog\n    var fileURL: URL\n    \n    init(diffs: [RenditionDiff], catalog: CUICatalog, fileURL: URL) {\n        self.diffs = diffs\n        self.catalog = catalog\n        self.fileURL = fileURL\n        \n        super.init(nibName: nil, bundle: nil)\n    }\n    \n    required init?(coder: NSCoder) {\n        fatalError(\"init(coder:) has not been implemented\")\n    }\n    \n    override func loadView() {\n        let collectionView = NSCollectionView()\n        collectionView.translatesAutoresizingMaskIntoConstraints = false\n        collectionView.collectionViewLayout = RenditionListViewController.makeLayout(layout: .horizontal)\n        \n        dataSource = DataSource(collectionView: collectionView) { collectionView, indexPath, rendition in\n            let cell = collectionView.makeItem(withIdentifier: RenditionCollectionViewItem.reuseIdentifier,\n                                               for: indexPath) as! RenditionCollectionViewItem\n            cell.configure(rendition: rendition)\n            return cell\n        }\n        \n        dataSource.supplementaryViewProvider = { [unowned self] collectionView, kind, indexPath in\n            guard kind == NSCollectionView.elementKindSectionHeader else {\n                return nil\n            }\n            \n            let header = collectionView.makeSupplementaryView(\n                ofKind: kind,\n                withIdentifier: RenditionTypeHeaderView.identifier,\n                for: indexPath) as! RenditionTypeHeaderView\n            let snapshot = dataSource.snapshot()\n            let section = snapshot.sectionIdentifiers[indexPath.section]\n            header.configure(typeLabelText: section.description, numberOfItems: snapshot.numberOfItems(inSection: section))\n            return header\n        }\n        \n        collectionView.delegate = self\n        collectionView.allowsMultipleSelection = false\n        collectionView.isSelectable = true\n        collectionView.register(RenditionCollectionViewItem.self,\n                                forItemWithIdentifier: RenditionCollectionViewItem.reuseIdentifier)\n        collectionView.register(RenditionTypeHeaderView.self,\n                                forSupplementaryViewOfKind: NSCollectionView.elementKindSectionHeader,\n                                withIdentifier: RenditionTypeHeaderView.identifier)\n        \n        addSnapshot(diffs: diffs)\n        let scrollView = NSScrollView()\n        scrollView.verticalScroller = nil\n        scrollView.documentView = collectionView\n        scrollView.hasHorizontalScroller = false\n        view = scrollView\n        view.frame.size = CGSize(width: 724, height: 676)\n    }\n    \n    func addSnapshot(diffs: [RenditionDiff]) {\n        var snapshot = NSDiffableDataSourceSnapshot<RenditionDiff.Kind, Rendition>()\n        // i want to cuddle a femboyyyy 🥺\n        let justSections = Set(diffs.map(\\.kind)) // remove duplicates\n        snapshot.appendSections(Array(justSections))\n        for diff in diffs {\n            snapshot.appendItems([diff.rend], toSection: diff.kind)\n        }\n        dataSource.apply(snapshot)\n    }\n    \n    override func performTextFinderAction(_ sender: Any?) {\n        for item in view.window?.toolbar?.items ?? [] {\n            if let search = item.view as? NSSearchField {\n                search.becomeFirstResponder()\n                break\n            }\n        }\n    }\n    \n}\n\nextension DiffListViewController: NSCollectionViewDelegate {\n    func collectionView(_ collectionView: NSCollectionView, shouldSelectItemsAt indexPaths: Set<IndexPath>) -> Set<IndexPath> {\n        return [indexPaths.first!]\n    }\n    \n    func collectionView(_ collectionView: NSCollectionView, didSelectItemsAt indexPaths: Set<IndexPath>) {\n        guard let first = indexPaths.first,\n              let item = dataSource.itemIdentifier(for: first) else {\n            return\n        }\n        \n        let view = RenditionInformationView(rendition: item, catalog: catalog, fileURL: fileURL, canEdit: false, canDelete: false, changeCallback: nil) { [unowned self] in\n            // done callback\n            guard let currentlyBeingPresented = presentedViewControllers?.first else { return }\n            dismiss(currentlyBeingPresented)\n        }\n        let controller = NSHostingController(rootView: view)\n        controller.view.frame.size = CGSize(width: 650, height: 500)\n        presentAsSheet(controller)\n    }\n}\nextension DiffListViewController: NSSearchFieldDelegate {\n    func controlTextDidChange(_ obj: Notification) {\n        guard let searchText = (obj.object as? NSSearchField)?.stringValue else { return }\n        if searchText.isEmpty {\n            addSnapshot(diffs: diffs)\n            return\n        }\n        \n        let new = diffs.filter { diff in\n            return diff.rend.name.localizedCaseInsensitiveContains(searchText)\n        }\n        addSnapshot(diffs: new)\n    }\n}\n"
  },
  {
    "path": "Samra/UI/MenuableCollectionView.swift",
    "content": "//\n//  MenuableCollectionView.swift\n//  Samra\n//\n//  Created by Serena on 02/03/2023.\n// \n\nimport Cocoa\n\nclass CollectionViewWithMenu: NSCollectionView {\n    weak var menuProvider: MenuProvider?\n    \n    override func menu(for event: NSEvent) -> NSMenu? {\n        guard event.type == .rightMouseDown,\n              let indexPath = indexPathForItem(\n                at: convert(event.locationInWindow, from: nil)\n              ) else {\n            return nil\n        }\n        \n        return menuProvider?.collectionView(self, menuForItemAt: indexPath)\n    }\n}\n\nprotocol MenuProvider: AnyObject {\n    func collectionView(_ collectionView: NSCollectionView, menuForItemAt: IndexPath) -> NSMenu?\n}\n"
  },
  {
    "path": "Samra/UI/PastFilesListViewController.swift",
    "content": "//\n//  PastFilesListViewController.swift\n//  Samra\n//\n//  Created by Serena on 18/02/2023.\n// \n\nimport Cocoa\nimport QuickLookUI\nimport AssetCatalogWrapper\n\n/// A View Controller showing the past files opened\nclass PastFilesListViewController: NSViewController {\n    var paths: [String] = Preferences.recentlyOpenedFilePaths.reversed()\n    var tableView: NSTableView!\n    \n    override func loadView() {\n        tableView = NSTableView()\n        tableView.dataSource = self\n        tableView.delegate = self\n        tableView.headerView = nil\n        tableView.doubleAction = #selector(doubeClickedItem)\n        \n        let col = NSTableColumn(identifier: \"Column\")\n        tableView.addTableColumn(col)\n        \n        let menu = NSMenu()\n        menu.delegate = self\n        menu.addItem(withTitle: \"Show in Finder\", action: #selector(showInFinder), keyEquivalent: \"\")\n        menu.addItem(withTitle: \"Remove\", action: #selector(deleteItem), keyEquivalent: \"\")\n        menu.autoenablesItems = false\n        tableView.menu = menu\n        \n        let scrollView = NSScrollView()\n        scrollView.documentView = tableView\n        scrollView.hasHorizontalScroller = false\n        view = scrollView\n        view.frame.size = CGSize(width: 250, height: 0)\n    }\n}\n\nextension PastFilesListViewController {\n    // Menu item actions\n    @objc\n    func deleteItem() {\n        guard tableView.clickedRow >= 0 else { return }\n        paths.remove(at: tableView.clickedRow)\n        Preferences.recentlyOpenedFilePaths = paths.reversed()\n        tableView.removeRows(at: [tableView.clickedRow], withAnimation: [.slideRight])\n    }\n    \n    @objc\n    func showInFinder() {\n        guard tableView.clickedRow >= 0 else { return }\n        \n        let item = paths[tableView.clickedRow]\n        NSWorkspace.shared.activateFileViewerSelecting([URL(fileURLWithPath: item)])\n    }\n}\n\nextension PastFilesListViewController: NSMenuDelegate {\n    func menuNeedsUpdate(_ menu: NSMenu) {\n        // if no item is selected, then disable the menu items\n        let keepItemsEnabled = tableView.clickedRow >= 0\n        \n        for item in menu.items {\n            item.isEnabled = keepItemsEnabled\n        }\n    }\n    \n    override func keyDown(with event: NSEvent) {\n        guard tableView.selectedRow != -1 else { return }\n        super.keyDown(with: event)\n        \n        // space, show QuickLook\n        if event.characters == \" \" {\n            if let sharedPanel = QLPreviewPanel.shared() {\n                let url = URL(fileURLWithPath: paths[tableView.selectedRow])\n                let source = QuickLookPreviewSource(fileURL: url)\n                sharedPanel.dataSource = source\n                sharedPanel.makeKeyAndOrderFront(nil)\n            }\n        }\n        \n        // carriage return, open up the item\n        if event.characters == \"\\r\" {\n            doubeClickedItem()\n        }\n    }\n}\n\nextension PastFilesListViewController: NSTableViewDataSource, NSTableViewDelegate {\n    func numberOfRows(in tableView: NSTableView) -> Int {\n        return paths.count\n    }\n    \n    func tableView(_ tableView: NSTableView, rowViewForRow row: Int) -> NSTableRowView? {\n        let rowView = NSTableRowView()\n        rowView.isEmphasized = false\n        return rowView\n    }\n    \n    func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {\n        let item = URL(fileURLWithPath: paths[row])\n        \n        let cell = NSTableCellView()\n        let imageView = NSImageView(image: NSWorkspace.shared.icon(forFile: item.path))\n        \n        let text = NSTextField(labelWithString: item.lastPathComponent)\n        let subtitleText = NSTextField(labelWithString: item.deletingLastPathComponent().path)\n        if #available(macOS 11, *) {\n            subtitleText.font = .preferredFont(forTextStyle: .subheadline)\n        } else {\n            subtitleText.font = .systemFont(ofSize: 11)\n        }\n        \n        subtitleText.lineBreakMode = .byTruncatingMiddle\n        \n        subtitleText.textColor = .secondaryLabelColor\n        \n        let titlesStackView = NSStackView(views: [text, subtitleText])\n        titlesStackView.alignment = .left\n        titlesStackView.distribution = .equalCentering\n        titlesStackView.orientation = .vertical\n        titlesStackView.spacing = 0\n        \n        let stackView = NSStackView(views: [imageView, titlesStackView])\n        stackView.translatesAutoresizingMaskIntoConstraints = false\n        cell.addSubview(stackView)\n        NSLayoutConstraint.activate([\n            stackView.leadingAnchor.constraint(equalTo: cell.leadingAnchor),\n            stackView.trailingAnchor.constraint(equalTo: cell.trailingAnchor),\n            stackView.centerYAnchor.constraint(equalTo: cell.centerYAnchor),\n        ])\n        \n        return cell\n    }\n    \n    func tableView(_ tableView: NSTableView, heightOfRow row: Int) -> CGFloat {\n        return 40\n    }\n    \n    @objc\n    func doubeClickedItem() {\n        guard tableView.selectedRow != -1 else { return }\n        var copy = Preferences.recentlyOpenedFilePaths\n        let item = paths[tableView.selectedRow]\n        copy.removeAll { $0 == item } // remove if exists\n        copy.append(item) // add item to amke it most recent\n        Preferences.recentlyOpenedFilePaths = copy\n        paths = Array(copy)\n        URLHandler.shared.handleURLChosen(urlChosen: URL(fileURLWithPath: item), senderView: view)\n    }\n}\n"
  },
  {
    "path": "Samra/UI/Rendition/AssetCatalogDetailsView.swift",
    "content": "//\n//  AssetCatalogDetailsView.swift\n//  Samra\n//\n//  Created by Serena on 27/02/2023.\n// \n\nimport SwiftUI\nimport AssetCatalogWrapper\n\n/// Shows information about a given asset catalog.\nstruct AssetCatalogDetailsView: View {\n    var assetStorage: CUICommonAssetStorage\n    var doneCallback: () -> Void\n    \n    var body: some View {\n        mainView\n            .frame(width: 630, height: 450)\n    }\n    \n    @ViewBuilder\n    var mainView: some View {\n        List(DetailItemSection.from(assetStorage: assetStorage), id: \\.self) { section in\n            Section(header: Text(section.sectionHeader)) {\n                ForEach(section.items, id: \\.self) { item in\n                    HStack {\n                        Text(item.primaryText)\n                            .foregroundColor(Color(NSColor.secondaryLabelColor))\n                        Spacer()\n                        Text(item.secondaryText)\n                            .multilineTextAlignment(.center)\n                    }\n                    .contextMenu {\n                        Button(\"Copy\") {\n                            NSPasteboard.general.declareTypes([.string], owner: nil)\n                            NSPasteboard.general.setString(item.secondaryText, forType: .string)\n                        }\n                    }\n                }\n            }\n        }\n        \n        Spacer()\n        Divider()\n        Button(\"Done\", action: doneCallback)\n            .frame(height: 35, alignment: .center)\n    }\n}\n"
  },
  {
    "path": "Samra/UI/Rendition/RenditionCollectionViewItem.swift",
    "content": "//\n//  RenditionCollectionViewItem.swift\n//  Samra\n//\n//  Created by Serena on 18/02/2023.\n// \n\nimport Cocoa\nimport AssetCatalogWrapper\n\nclass RenditionCollectionViewItem: NSCollectionViewItem {\n    \n    static let reuseIdentifier = NSUserInterfaceItemIdentifier(\"RenditionCollectionViewItem\")\n    var nameLabel: NSTextField!\n    var representationPreview: NSView!\n    \n    override func loadView() {\n        view = NSView()\n    }\n    \n    func configure(rendition: Rendition) {\n        nameLabel = NSTextField(labelWithString: rendition.name)\n        nameLabel.translatesAutoresizingMaskIntoConstraints = false\n        nameLabel.maximumNumberOfLines = 0\n        nameLabel.alignment = .center\n        nameLabel.lineBreakMode = .byCharWrapping\n        \n        switch rendition.representation {\n        case .color(let cGColor):\n            let circleView = NSView()\n            circleView.translatesAutoresizingMaskIntoConstraints = false\n            \n            let layer = CALayer()\n            layer.cornerRadius = 20\n            layer.cornerCurve = .circular\n            layer.backgroundColor = cGColor\n            circleView.wantsLayer = false\n            circleView.layer = layer\n            \n            view.addSubview(circleView)\n            NSLayoutConstraint.activate([\n                circleView.centerXAnchor.constraint(equalTo: view.centerXAnchor),\n                circleView.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: -10),\n                circleView.heightAnchor.constraint(equalToConstant: 40),\n                circleView.widthAnchor.constraint(equalToConstant: 40)\n            ])\n            \n            representationPreview = circleView\n        case .image(let cGImage):\n            let imageView = NSImageView()\n            imageView.image = NSImage(cgImage: cGImage, size: cGImage.size)\n            imageView.translatesAutoresizingMaskIntoConstraints = false\n            view.addSubview(imageView)\n            \n            imageView.imageScaling = .scaleProportionallyUpOrDown\n            imageView.imageAlignment = .alignCenter\n            \n//            imageView.centerConstraints(to: view)\n            NSLayoutConstraint.activate([\n                imageView.centerXAnchor.constraint(equalTo: view.centerXAnchor),\n                imageView.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: -12.34),\n            ])\n            \n            /*\n            if #available(macOS 11, *) {\n                NSLayoutConstraint.activate([\n                    imageView.widthAnchor.constraint(equalTo: view.layoutMarginsGuide.widthAnchor),\n                    imageView.heightAnchor.constraint(equalTo: view.layoutMarginsGuide.heightAnchor)\n                ])\n            } else {*/\n            NSLayoutConstraint.activate([\n                imageView.widthAnchor.constraint(equalTo: view.widthAnchor, constant: -20),\n                imageView.heightAnchor.constraint(equalTo: view.heightAnchor, constant: -34)\n            ])\n            /*}*/\n            \n            representationPreview = imageView\n\n        case .rawData(let data):\n            var visibleString = \"No Preview Available\"\n            if let string = String(data:data, encoding:.utf8) {\n                visibleString = string.count > 500 ? String(string.prefix(500)) : string\n            }\n            let textField = NSTextField(wrappingLabelWithString:visibleString)\n            textField.isBordered = true\n            textField.isEditable = false\n            textField.isSelectable = false\n            textField.translatesAutoresizingMaskIntoConstraints = false\n            textField.maximumNumberOfLines = 5\n            view.addSubview(textField)\n            representationPreview = textField\n            NSLayoutConstraint.activate([\n                textField.centerXAnchor.constraint(equalTo: view.centerXAnchor),\n                textField.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: -12.34),\n                textField.widthAnchor.constraint(equalTo: view.widthAnchor, constant: -20),\n                textField.heightAnchor.constraint(equalTo: view.heightAnchor, constant: -34)\n            ])\n\n        case nil:\n            representationPreview = .init()\n        }\n        \n        view.addSubview(nameLabel)\n        NSLayoutConstraint.activate([\n            nameLabel.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -8),\n            nameLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10),\n            nameLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -10)\n        ])\n        \n        let layer = CALayer()\n        layer.borderWidth = 1.87\n        layer.cornerRadius = 10\n        layer.cornerCurve = .continuous\n        layer.masksToBounds = true\n        layer.borderColor = NSColor.systemGray.cgColor\n        view.layer = layer\n    }\n    \n    override func prepareForReuse() {\n        super.prepareForReuse()\n        \n        nameLabel.stringValue = \"\"\n        representationPreview.removeFromSuperview()\n        representationPreview = nil\n    }\n}\n"
  },
  {
    "path": "Samra/UI/Rendition/RenditionInformationView.swift",
    "content": "//\n//  RenditionInformationView.swift\n//  Samra\n//\n//  Created by Serena on 21/02/2023.\n// \n\nimport SwiftUI\nimport UniformTypeIdentifiers\nimport AssetCatalogWrapper\n\nstruct RenditionInformationView: View {\n    \n    @State var showDeleteAlert: Bool = false\n    \n    var rendition: Rendition\n    var catalog: CUICatalog\n    var fileURL: URL\n    var canEdit: Bool\n    var canDelete: Bool\n    var changeCallback: ((Change) -> Void)?\n    \n    var doneButtonCallback: (() -> Void)?\n    \n    var body: some View {\n        switch rendition.representation {\n        case .image(let cgImage):\n            //            GeometryReader { proxy in\n            Image(cgImage, scale: NSScreen.main!.backingScaleFactor, label: Text(\"\"))\n                .resizable()\n                .aspectRatio(contentMode: .fit)\n            //                    .frame(width: proxy.size.width,\n            //                           height: proxy.size.height, alignment: .center)\n            //            }\n            \n                .frame(alignment: .center)\n                .contextMenu {\n                    Button(\"Copy Image\") {\n                        NSPasteboard.general.declareTypes([.tiff], owner: nil)\n                        NSPasteboard.general.setData(NSImage(cgImage: cgImage, size: cgImage.size).tiffRepresentation,\n                                                     forType: .tiff)\n                    }\n                    \n                    Button(\"Save Image As..\") {\n                        let panel = NSSavePanel()\n                        panel.nameFieldStringValue = rendition.cuiRend.name()\n                        \n                        if panel.runModal() == .OK, let chosenURL = panel.url {\n                            let rep = NSBitmapImageRep(cgImage: cgImage)\n                            guard let data = rep.representation(using: .png, properties: [.compressionFactor: 1]) else {\n                                NSAlert(title: \"Unable to generate png data for image\").runModal()\n                                return\n                            }\n                            \n                            do {\n                                try data.write(to: chosenURL, options: .atomic)\n                            } catch {\n                                NSAlert(title: \"Unable to write image data to \\(chosenURL.path)\",\n                                        message: error.localizedDescription).runModal()\n                            }\n                        }\n                    }\n                }\n            /*\n             .onDrag {\n             }\n             */\n        case .color(let cgColor):\n            Circle()\n                .fill(Color(NSColor(cgColor: cgColor)!))\n                .frame(width: 130, height: 230, alignment: .center)\n\n        case .rawData(let data):\n            if let string = String(data:data, encoding:.utf8) {\n                Text(String(string.prefix(1024)))\n                    .font(.body)\n                    .padding(5)\n            } else {\n                Text(\"No Preview Available\")\n                    .font(.title.italic())\n                    .padding(30)\n            }\n\n\n        default:\n            Text(\"No Preview Available.\")\n                .font(.title.italic())\n                .frame(width: 130, height: 230)\n        }\n        \n        HStack {\n            if rendition.type == .rawData, rendition.cuiRend.responds(to: #selector(CUIThemeRendition.data)) {\n                Button(\"Export Data to...\") {\n                    guard let data = rendition.cuiRend.data() else { \n                        NSAlert(title: \"Failed to export data\", message: \"Unable to get data (rendition.cuiRend.data() returned null)\")\n                            .runModal()\n                        return\n                    }\n                    \n                    let savePanel = NSSavePanel()\n                    savePanel.nameFieldStringValue = rendition.name\n                    if savePanel.runModal() == .OK, let url = savePanel.url {\n                        do {\n                            try data.write(to: url)\n                        } catch {\n                            NSAlert(title: \"Error trying to write data to file \\(url)\", message: error.localizedDescription)\n                                .runModal()\n                        }\n                    }\n                }\n            }\n            \n            Button(\"Edit\") {\n                switch rendition.representation {\n                case .color(let cgColor):\n                    let colorPanel = CallbackableColorPanel()\n                    colorPanel.color = NSColor(cgColor: cgColor) ?? colorPanel.color\n                    colorPanel.isContinuous = false\n                    colorPanel.makeKeyAndOrderFront(nil)\n                    \n                    colorPanel.callback = { nsColor in\n                        do {\n                            try catalog.editItem(rendition, fileURL: fileURL, to: .color(nsColor.cgColor))\n                            changeCallback?(.edit)\n                        } catch {\n                            NSAlert(title: \"Failed to edit item\", message: error.localizedDescription)\n                                .runModal()\n                        }\n                    }\n                    \n                case .image(_):\n                    let panel = NSOpenPanel()\n                    panel.canChooseDirectories = false\n                    panel.canChooseFiles = true\n                    if #available(macOS 11, *) {\n                        panel.allowedContentTypes = [.image]\n                    } else {\n                        panel.allowedFileTypes = [kUTTypeImage as String]\n                    }\n                    \n                    if panel.runModal() == .OK, let chosenURL = panel.url {\n                        guard let cgImage = NSImage(contentsOf: chosenURL)?.cgImage(forProposedRect: nil, context: nil, hints: nil) else {\n                            NSAlert(title: \"Failed to edit item\", message: \"Unable to get image representation of the file selected\").runModal()\n                            return\n                        }\n                        \n                        do {\n                            try catalog.editItem(rendition, fileURL: fileURL, to: .image(cgImage))\n                            changeCallback?(.edit)\n                        } catch {\n                            NSAlert(title: \"Failed to edit item\", message: error.localizedDescription)\n                                .runModal()\n                        }\n                    }\n                default:\n                    break // never supposed to get here\n                }\n            }\n            .disabled(!canEdit || !rendition.type.isEditable)\n            \n            if let doneButtonCallback {\n                Button(\"Done\", action: doneButtonCallback)\n            }\n            \n            Button {\n                showDeleteAlert = true\n            } label: {\n                Text(\"Delete\")\n                    .foregroundColor(.red)\n            }\n            .disabled(!canDelete)\n        }\n        \n        mainView\n            .frame(maxWidth: .infinity, maxHeight: .infinity)\n            .alert(isPresented: $showDeleteAlert) {\n                let deleteButton: Alert.Button = .destructive(Text(\"Delete\")) {\n                    do {\n                        try catalog.removeItem(rendition, fileURL: fileURL)\n                        changeCallback?(.delete)\n                    } catch {\n                        NSAlert(title: \"Error encountered while trying to delete \\(rendition.name)\",\n                                message: error.localizedDescription).runModal()\n                    }\n                }\n                \n                return Alert(title: Text(\"Are you sure you want to delete \\(rendition.name)?\"),\n                             message: Text(\"This action cannot be undone\"), primaryButton: deleteButton, secondaryButton: .cancel())\n            }\n    }\n    \n    @ViewBuilder\n    var mainView: some View {\n        List(DetailItemSection.from(rendition: rendition), id: \\.self) { section in\n            Section(header: Text(section.sectionHeader)) {\n                ForEach(section.items, id: \\.self) { item in\n                    HStack {\n                        Text(item.primaryText)\n                        Spacer()\n                        Text(item.secondaryText)\n                            .multilineTextAlignment(.trailing)\n                    }\n                    .contextMenu {\n                        Button(\"Copy\") {\n                            NSPasteboard.general.declareTypes([.string], owner: nil)\n                            NSPasteboard.general.setString(item.secondaryText, forType: .string)\n                        }\n                    }\n                }\n            }\n        }\n    }\n    \n    enum Change {\n        /// item was deleted\n        case delete\n        /// item was edited\n        case edit\n    }\n}\n\nclass CallbackableColorPanel: NSColorPanel, NSWindowDelegate {\n    var callback: ((NSColor) -> Void)? = nil\n    \n    override init(contentRect: NSRect, styleMask style: NSWindow.StyleMask, backing backingStoreType: NSWindow.BackingStoreType, defer flag: Bool) {\n        super.init(contentRect: contentRect, styleMask: style, backing: backingStoreType, defer: flag)\n        self.delegate = self\n    }\n    \n    func windowWillClose(_ notification: Notification) {\n        callback?(color)\n    }\n}\n"
  },
  {
    "path": "Samra/UI/Rendition/RenditionListViewController.swift",
    "content": "//\n//  RenditionListViewController.swift\n//  Samra\n//\n//  Created by Serena on 18/02/2023.\n// \n\nimport Cocoa\nimport AppKitPrivates\nimport class SwiftUI.NSHostingController\nimport AssetCatalogWrapper\nimport SVGWrapper\n\n/// A View Controller displaying all the renditions of a given Asset Catalog.\nclass RenditionListViewController: NSViewController {\n    \n    static let titleHeaderIdentifier = \"Identifier\"\n    \n    typealias DataSource = NSCollectionViewDiffableDataSource<RenditionType, Rendition>\n    var dataSource: DataSource!\n    var collectionView: CollectionViewWithMenu!\n    lazy var allItemsSnapshot = addSnapshot(collectionToAdd: collection)\n    \n    var itemToDeleteIndexPath: IndexPath? = nil\n    \n    var catalog: CUICatalog\n    var collection: RenditionCollection\n    let fileURL: URL\n    \n    private var scrollObserver: NSObjectProtocol?\n    \n    init(catalog: CUICatalog, collection: RenditionCollection, fileURL: URL) {\n        self.catalog = catalog\n        self.collection = collection\n        self.fileURL = fileURL\n        super.init(nibName: nil, bundle: nil)\n    }\n    \n    var splitViewParent: CollapseNotifierSplitViewController? {\n        parent as? CollapseNotifierSplitViewController\n    }\n    \n    required init?(coder: NSCoder) {\n        fatalError(\"init(coder:) has not been implemented\")\n    }\n    \n    override func loadView() {\n        collectionView = CollectionViewWithMenu()\n        \n        dataSource = DataSource(collectionView: collectionView) { collectionView, indexPath, rendition in\n            let cell = collectionView.makeItem(withIdentifier: RenditionCollectionViewItem.reuseIdentifier,\n                                               for: indexPath) as! RenditionCollectionViewItem\n            cell.configure(rendition: rendition)\n            return cell\n        }\n        \n#warning(\"Add footers for explanations for multisizeImageSet\")\n        dataSource.supplementaryViewProvider = { [unowned self] collectionView, kind, indexPath in\n            guard kind == NSCollectionView.elementKindSectionHeader else {\n                return nil\n            }\n            \n            let header = collectionView.makeSupplementaryView(\n                ofKind: kind,\n                withIdentifier: RenditionTypeHeaderView.identifier,\n                for: indexPath) as! RenditionTypeHeaderView\n            let snapshot = dataSource.snapshot()\n            let section = snapshot.sectionIdentifiers[indexPath.section]\n            header.configure(typeLabelText: section.description, numberOfItems: snapshot.numberOfItems(inSection: section))\n            return header\n        }\n        \n        collectionView.allowsMultipleSelection = false\n        collectionView.isSelectable = true\n        collectionView.delegate = self\n        collectionView.menuProvider = self\n        collectionView.collectionViewLayout = Self.makeLayout(layout: .horizontal)\n        collectionView.identifier = \"HorizLayout\"\n        \n        collectionView.register(RenditionCollectionViewItem.self,\n                                forItemWithIdentifier: RenditionCollectionViewItem.reuseIdentifier)\n        collectionView.register(RenditionTypeHeaderView.self,\n                                forSupplementaryViewOfKind: NSCollectionView.elementKindSectionHeader,\n                                withIdentifier: RenditionTypeHeaderView.identifier)\n        addSnapshot(collectionToAdd: collection)\n        \n        splitViewParent?.handler = { [unowned self] item, didCollapse, _ in\n            guard item.viewController.identifier == \"RenditionInfo\" else { return }\n            collectionView.collectionViewLayout = Self.makeLayout(\n                layout: didCollapse ? .horizontal : .vertical\n            )\n            \n            collectionView.identifier = didCollapse ? \"HorizLayout\" : \"VerticalLayout\"\n        }\n        \n        let scrollView = NSScrollView()\n        scrollView.verticalScroller = nil\n        scrollView.documentView = collectionView\n        scrollView.hasHorizontalScroller = false\n        \n        view = scrollView\n        view.frame.size = CGSize(width: 724, height: 676)\n        \n        let observer = NotificationCenter.default.addObserver(forName: NSScrollView.didEndLiveScrollNotification, object: scrollView, queue: nil) { [weak self] _ in\n            guard let self = self else { return }\n            let vc = self.splitViewParent?.splitViewItems[0].viewController as? TypesListViewController\n            guard let vc, let currentSection = self.collectionView.indexPathsForVisibleItems().first?.section else {\n                return\n            }\n            \n            vc.ignoreChanges = true\n            vc.tableView.deselectRow(vc.tableView.selectedRow)\n            vc.tableView.selectRowIndexes([currentSection], byExtendingSelection: true)\n            vc.ignoreChanges = false\n        }\n        \n        self.scrollObserver = observer\n        \n        collectionView.registerForDraggedTypes(NSImage.imageTypes.map { .init($0) })\n        collectionView.setDraggingSourceOperationMask(.every, forLocal: true)\n        collectionView.setDraggingSourceOperationMask(.every, forLocal: false)\n    }\n    \n    func collectionView(_ collectionView: NSCollectionView, canDragItemsAt indexPaths: Set<IndexPath>, with event: NSEvent) -> Bool {\n        return true\n    }\n    \n    func collectionView(_ collectionView: NSCollectionView, pasteboardWriterForItemAt indexPath: IndexPath) -> NSPasteboardWriting? {\n//        return dataSource.itemIdentifier(for: indexPath)?.name as? NSString\n        switch dataSource.itemIdentifier(for: indexPath)?.representation {\n        case .image(let cgImage):\n            return NSImage(cgImage: cgImage, size: cgImage.size)\n        default:\n            return nil\n        }\n    }\n    \n    func collectionView(_ collectionView: NSCollectionView, draggingSession session: NSDraggingSession, endedAt screenPoint: NSPoint, dragOperation operation: NSDragOperation) {}\n    \n    @discardableResult\n    func addSnapshot(collectionToAdd: RenditionCollection) -> NSDiffableDataSourceSnapshot<RenditionType, Rendition> {\n        var snapshot = NSDiffableDataSourceSnapshot<RenditionType, Rendition>()\n        for item in collectionToAdd {\n            snapshot.appendSections([item.type])\n            snapshot.appendItems(item.renditions, toSection: item.type)\n        }\n        \n        dataSource.apply(snapshot)\n        return snapshot\n    }\n    \n    @discardableResult\n    func refreshAssetCatalog() -> Bool {\n        do {\n            let (newCatalog, newCollection) = try AssetCatalogWrapper.shared.renditions(forCarArchive: fileURL)\n            self.catalog = newCatalog\n            self.collection = newCollection\n            addSnapshot(collectionToAdd: collection)\n            return true\n        } catch {\n            NSAlert(title: \"Failed to refresh Asset Catalog\", message: error.localizedDescription)\n                .runModal()\n            return false\n        }\n    }\n    \n    deinit {\n        if let observer = scrollObserver {\n            NotificationCenter.default.removeObserver(observer)\n        }\n        print(\"And I'm tripping and falling..\")\n    }\n}\n\nextension RenditionListViewController {\n    static func makeLayout(layout: LayoutMode) -> NSCollectionViewCompositionalLayout {\n        let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),\n                                              heightDimension: .fractionalHeight(1.0))\n        let item = NSCollectionLayoutItem(layoutSize: itemSize)\n        let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),\n                                               heightDimension: .absolute(115))\n        \n        let group: NSCollectionLayoutGroup\n        switch layout {\n        case .vertical:\n            group = .vertical(layoutSize: groupSize, subitems: [item]/*, count: 3*/)\n        case .horizontal:\n            group = .horizontal(layoutSize: groupSize, subitem: item, count: 3)\n        }\n        \n        let spacing = CGFloat(15)\n        group.interItemSpacing = .fixed(spacing)\n        \n        let titleHeaderSize = NSCollectionLayoutSize(\n            widthDimension: .fractionalWidth(1.0),\n            heightDimension: .absolute(50)\n        )\n        \n        let titleSupplementary = NSCollectionLayoutBoundarySupplementaryItem(\n            layoutSize: titleHeaderSize,\n            elementKind: NSCollectionView.elementKindSectionHeader,\n            alignment: .topTrailing\n        )\n        \n        \n        let section = NSCollectionLayoutSection(group: group)\n        section.interGroupSpacing = spacing\n        section.contentInsets = NSDirectionalEdgeInsets(top: 20,\n                                                        leading: spacing,\n                                                        bottom: 20,\n                                                        trailing: spacing)\n        section.boundarySupplementaryItems = [titleSupplementary]\n        //section.orthogonalScrollingBehavior = .continuous\n        return NSCollectionViewCompositionalLayout(section: section)\n    }\n}\n\nextension RenditionListViewController: MenuProvider {\n    \n    static private func _promptToSaveImage(cgImage: CGImage, formatType: NSBitmapImageRep.FileType, defaultFileName: String, displayFormat: String) {\n        let savePanel = NSSavePanel()\n        savePanel.nameFieldStringValue = defaultFileName\n        guard savePanel.runModal() == .OK, let urlToSaveTo = savePanel.url else { return }\n        \n        guard let data = NSBitmapImageRep(cgImage: cgImage).representation(using: formatType, properties: [.compressionFactor: 1]) else {\n            NSAlert(title: \"Failed to save Image as \\(displayFormat)\", message: \"NSBitmapImageRep representation returned nil.\").runModal()\n            return\n        }\n        \n        do {\n            try data.write(to: urlToSaveTo)\n        } catch {\n            NSAlert(title: \"Failed to save Image as \\(displayFormat)\", message: error.localizedDescription).runModal()\n        }\n    }\n    \n    func collectionView(_ collectionView: NSCollectionView, menuForItemAt indexPath: IndexPath) -> NSMenu? {\n        guard let item = dataSource.itemIdentifier(for: indexPath) else { return nil }\n        let copyName = ClosureMenuItem(title: \"Copy Name\") {\n            NSPasteboard.general.declareTypes([.string], owner: nil)\n            NSPasteboard.general.setString(item.name, forType: .string)\n        }\n        \n        var items: [NSMenuItem] = [copyName]\n        \n        switch item.representation {\n        case .image(let cgImage):\n            let copyImage = ClosureMenuItem(title: \"Copy Image\") {\n                NSPasteboard.general.declareTypes([.tiff], owner: nil)\n                NSPasteboard.general.setData(NSImage(cgImage: cgImage, size: cgImage.size).tiffRepresentation, forType: .tiff)\n            }\n            items.append(copyImage)\n            \n            var saveImageAsItems = [\n                ClosureMenuItem(title: \"PNG\") {\n                    Self._promptToSaveImage(cgImage: cgImage, formatType: .png, defaultFileName: \"image.png\", displayFormat: \"PNG\")\n                },\n                \n                ClosureMenuItem(title: \"JPEG\") {\n                    Self._promptToSaveImage(cgImage: cgImage, formatType: .jpeg, defaultFileName: \"image.jpeg\", displayFormat: \"JPEG\")\n                }\n            ]\n            \n            if item.type == .svg, let svgDoc = item.cuiRend.svgDocument() {\n                let asSVG = ClosureMenuItem(title: \"SVG\") {\n                    let savePanel = NSSavePanel()\n                    savePanel.nameFieldStringValue = \"image.svg\"\n                    guard savePanel.runModal() == .OK, let urlToSaveTo = savePanel.url else { return }\n                    CGSVGDocumentWriteToURL(svgDoc, urlToSaveTo as CFURL, nil)\n                }\n                \n                saveImageAsItems.insert(asSVG, at: 0)\n            }\n            \n            let saveImageAs = NSMenuItem(submenuTitle: \"Save Image As...\", items: saveImageAsItems)\n            items.insert(saveImageAs, at: 0)\n            items.insert(.separator(), at: 1)\n        default:\n            break\n        }\n        \n        let deleteItem = ClosureMenuItem(title: \"Delete\") { [unowned self] in\n            let alert = NSAlert(title: \"Are you sure you want to delete \\(item.name)?\",\n                                message: \"This action cannot be undone\")\n            let deleteButton = alert.addButton(withTitle: \"Delete\")\n            deleteButton.target = self\n            deleteButton.action = #selector(deleteItem(sender:))\n            \n            if #available(macOS 11, *) {\n                deleteButton.hasDestructiveAction = true\n            }\n            \n            itemToDeleteIndexPath = indexPath\n            alert.addButton(withTitle: \"Cancel\")\n            alert.runModal()\n        }\n        \n        items.append(deleteItem)\n        return NSMenu(items: items)\n    }\n    \n    @objc\n    func deleteItem(sender: NSButton) {\n        guard let itemToDeleteIndexPath,\n                let item = dataSource.itemIdentifier(for: itemToDeleteIndexPath) else {\n            return\n        }\n        \n        do {\n            try catalog.removeItem(item, fileURL: fileURL)\n            NSApplication.shared.abortModal()\n            refreshAssetCatalog()\n        } catch {\n            NSAlert(title: \"Failed to remove \\(item.name)\", message: error.localizedDescription)\n                .runModal()\n            return\n        }\n    }\n}\n\nextension RenditionListViewController {\n    @objc\n    func infoButtonClicked(sender: NSButton) {\n        guard let ass = CUICommonAssetStorage(path: fileURL.path, forWriting: false) else {\n            NSAlert(\n                title: \"Failed to display details of Assets.car file\",\n                message: \"Failed to init CUICommonAssetStorage for \\(fileURL.path)\"\n            )\n            .runModal()\n            return\n        }\n        \n        /*\n        let popover = NSPopover()\n        popover.behavior = .transient\n        popover.contentSize = NSSize(width: 400, height: 200)\n         */\n        \n        let detailsView = AssetCatalogDetailsView(assetStorage: ass) { [unowned self] in\n            // Callback for 'Done' button\n            guard let currentlyPresenting = presentedViewControllers?.first else { return }\n            dismiss(currentlyPresenting)\n        }\n        \n        presentAsSheet(NSHostingController(rootView: detailsView))\n    }\n    \n    @objc\n    func exportCatalog() {\n        let panel = NSOpenPanel()\n        panel.title = \"Directory to export to\"\n        panel.canChooseDirectories = true\n        panel.canCreateDirectories = true\n        panel.canChooseFiles = false\n        \n        guard panel.runModal() == .OK, let destinationURL = panel.url else { return }\n        \n        do {\n            try AssetCatalogWrapper.shared.extract(collection: collection, to: destinationURL)\n            NSWorkspace.shared.activateFileViewerSelecting([destinationURL])\n        } catch {\n            NSAlert(title: \"Failed to export (some) items\", message: error.localizedDescription)\n                .runModal()\n        }\n    }\n}\n\nextension RenditionListViewController {\n    // MARK: - Layout\n    enum LayoutMode {\n        case vertical\n        case horizontal\n    }\n}\n\nextension RenditionListViewController: NSCollectionViewDelegate {\n    func collectionView(_ collectionView: NSCollectionView, shouldSelectItemsAt indexPaths: Set<IndexPath>) -> Set<IndexPath> {\n        return [indexPaths.first!]\n    }\n    \n    func collectionView(_ collectionView: NSCollectionView, didSelectItemsAt indexPaths: Set<IndexPath>) {\n        guard let firstIndexPath = indexPaths.first,\n              let item = dataSource.itemIdentifier(for: firstIndexPath),\n              let parent = splitViewParent else {\n            return\n        }\n        \n        let layer = collectionView.item(at: firstIndexPath)?.view.layer\n        layer?.borderColor = NSColor.controlAccentColor.cgColor\n        layer?.borderWidth = 3.5 // enlargen border width when selected\n        \n        // if we already have an existing info vc then remove it\n        if parent.splitViewItems.count == 3 {\n            parent.removeSplitViewItem(parent.splitViewItems[2])\n        }\n        \n        let view = RenditionInformationView(rendition: item, catalog: catalog, fileURL: fileURL, canEdit: true, canDelete: true) { [unowned self] change in\n            switch change {\n            case .delete:\n                refreshAssetCatalog()\n            case .edit:\n                if refreshAssetCatalog() {\n                    self.collectionView(collectionView, didSelectItemsAt: indexPaths)\n                }\n            }\n        }\n        \n        let renditionVC = NSHostingController(rootView: view)\n        renditionVC.identifier = \"RenditionInfo\"\n        let splitViewItem = NSSplitViewItem(contentListWithViewController: renditionVC)\n        splitViewItem.minimumThickness = 400\n        splitViewItem.canCollapse = true\n        splitViewItem.maximumThickness = 600\n        splitViewItem.automaticMaximumThickness = 600\n        splitViewItem.preferredThicknessFraction = 2\n        \n        parent.addSplitViewItem(splitViewItem)\n        \n        if collectionView.identifier == \"HorizLayout\" {\n            collectionView.collectionViewLayout = Self.makeLayout(layout: .vertical)\n            collectionView.identifier = \"VerticalLayout\"\n            // scroll back here because switching between layouts may cause the item to not be visible\n            // in the new layout\n            collectionView.scrollToItems(at: indexPaths,\n                                         scrollPosition: [.centeredVertically, .centeredHorizontally])\n        }\n    }\n    \n    func collectionView(_ collectionView: NSCollectionView, didDeselectItemsAt indexPaths: Set<IndexPath>) {\n        for indexPath in indexPaths {\n            let layer = collectionView.item(at: indexPath)?.view.layer\n            layer?.borderColor = NSColor.systemGray.cgColor\n            // item is no longer in focus, set it's border width to the standard amount \n            layer?.borderWidth = 1.87\n        }\n    }\n    \n    override func performTextFinderAction(_ sender: Any?) {\n        for item in view.window?.toolbar?.items ?? [] {\n            if let search = item.view as? NSSearchField {\n                search.becomeFirstResponder()\n                break\n            }\n        }\n    }\n    \n    /*\n    func collectionView(_ collectionView: NSCollectionView,\n                        canDragItemsAt indexPaths: Set<IndexPath>, with event: NSEvent) -> Bool {\n        return indexPaths.allSatisfy { [unowned self] indxPath in\n            switch dataSource.itemIdentifier(for: indxPath)?.type {\n            case .image, .icon:\n                return true\n            default:\n                return false\n            }\n        }\n    }\n    \n    func collectionView(_ collectionView: NSCollectionView, draggingSession session: NSDraggingSession, willBeginAt screenPoint: NSPoint, forItemsAt indexPaths: Set<IndexPath>) {\n        print(#function)\n    }\n     */\n}\n\nextension RenditionListViewController: NSSearchFieldDelegate {\n    \n    /// Set the types in the sidebar,\n    /// if nil, then this will default to all the types\n    func setSidebarTypes(_ types: [RenditionType]?) {\n        if let sidebar = splitViewParent?.splitViewItems[0].viewController as? TypesListViewController {\n            sidebar.types = types ?? sidebar.allTypes\n            sidebar.tableView.reloadData()\n        }\n    }\n    \n    func controlTextDidChange(_ obj: Notification) {\n        guard let searchText = (obj.object as? NSSearchField)?.stringValue else { return }\n        \n        if searchText.isEmpty {\n            dataSource.apply(allItemsSnapshot)\n            setSidebarTypes(nil)\n            return\n        }\n        \n        var newSidebarTypes: [RenditionType] = []\n        let newCollection: RenditionCollection = collection.compactMap { type, renditions in\n            // query by the renditions that have the search text in their name\n            let newRends = renditions.filter { rend in\n                return rend.name.localizedCaseInsensitiveContains(searchText)\n            }\n            \n            // Don't include the section if no items match the query\n            if newRends.isEmpty {\n                return nil\n            }\n            \n            // the section has renditions that match our description, add it to the sidebar\n            newSidebarTypes.append(type)\n            \n            return (type, newRends)\n        }\n        \n        addSnapshot(collectionToAdd: newCollection)\n        \n        setSidebarTypes(newSidebarTypes)\n        \n    }\n}\n"
  },
  {
    "path": "Samra/UI/Rendition/RenditionTypeHeaderView.swift",
    "content": "//\n//  RenditionTypeHeaderView.swift\n//  Samra\n//\n//  Created by Serena on 19/02/2023.\n// \n\nimport Cocoa\nimport AssetCatalogWrapper\n\nclass RenditionTypeHeaderView: NSView, NSCollectionViewElement {\n    \n    static let identifier = NSUserInterfaceItemIdentifier(\"RenditionTypeHeaderView\")\n    \n    var typeLabel: NSTextField!\n    var amountOfItemsLabel: NSTextField!\n    \n    func configure(typeLabelText: String, numberOfItems: Int) {\n        typeLabel = NSTextField(labelWithString: typeLabelText)\n        typeLabel.translatesAutoresizingMaskIntoConstraints = false\n        \n        addSubview(typeLabel)\n        \n        amountOfItemsLabel = NSTextField(labelWithString: \"\\(numberOfItems) Items\")\n        amountOfItemsLabel.translatesAutoresizingMaskIntoConstraints = false\n        addSubview(amountOfItemsLabel)\n        \n        if #available(macOS 11, *) {\n            typeLabel.font = .preferredFont(forTextStyle: .largeTitle)\n            amountOfItemsLabel.font = .preferredFont(forTextStyle: .caption1)\n        } else {\n            amountOfItemsLabel.font = .systemFont(ofSize: 10)\n            typeLabel.font = .systemFont(ofSize: 26)\n        }\n        \n        NSLayoutConstraint.activate([\n            typeLabel.leadingAnchor.constraint(equalTo: leadingAnchor),\n            typeLabel.topAnchor.constraint(equalTo: topAnchor),\n            \n            amountOfItemsLabel.leadingAnchor.constraint(equalTo: leadingAnchor),\n            amountOfItemsLabel.centerXAnchor.constraint(equalTo: typeLabel.centerXAnchor, constant: -30)\n        ])\n    }\n    \n    override func prepareForReuse() {\n        super.prepareForReuse()\n        \n        typeLabel.removeFromSuperview()\n        typeLabel = nil\n        \n        amountOfItemsLabel.removeFromSuperview()\n        amountOfItemsLabel = nil\n    }\n}\n"
  },
  {
    "path": "Samra/UI/Rendition/TypesListViewController.swift",
    "content": "//\n//  TypesListViewController.swift\n//  Samra\n//\n//  Created by Serena on 18/02/2023.\n// \n\nimport Cocoa\nimport AssetCatalogWrapper\n\nclass TypesListViewController: NSViewController {\n    typealias SectionClickedHandler = (RenditionType) -> Void\n    \n    let changeHandler: SectionClickedHandler\n    \n    let allTypes: [RenditionType]\n    // the types shown in the UI, if there is a search session, this may not be equal to allTypes\n    // depending on if the search result's types are less than allTypes\n    var types: [RenditionType]\n    \n    // for when manually doing select and deselectRow\n    var ignoreChanges: Bool = false\n    \n    var tableView: NSTableView!\n    \n    init(types: [RenditionType], changeHandler: @escaping SectionClickedHandler) {\n        self.types = types\n        self.allTypes = types\n        self.changeHandler = changeHandler\n        super.init(nibName: nil, bundle: nil)\n    }\n    \n    required init?(coder: NSCoder) {\n        fatalError(\"init(coder:) has not been implemented\")\n    }\n    \n    override func loadView() {\n        tableView = NSTableView()\n        tableView.dataSource = self\n        tableView.delegate = self\n        tableView.target = self\n        tableView.headerView = nil\n        \n        let col = NSTableColumn(identifier: \"Column\")\n        tableView.addTableColumn(col)\n        \n        let scrollView = NSScrollView()\n        scrollView.documentView = tableView\n        scrollView.hasHorizontalScroller = false\n        view = scrollView\n        view.frame.size = CGSize(width: 200, height: 0)\n        \n        setupMenuBarItems()\n    }\n    \n    override func viewDidDisappear() {\n        super.viewDidDisappear()\n        \n        // disable section items\n        for item in NSApplication.shared.mainMenu?.items ?? [] {\n            guard item.title == \"Sections\", let submenu = item.submenu else {\n                continue\n            }\n            \n            for item in submenu.items {\n                item.isEnabled = false\n                item.keyEquivalent = \"\"\n            }\n        }\n    }\n    \n    \n    override func performTextFinderAction(_ sender: Any?) {\n        for item in view.window?.toolbar?.items ?? [] {\n            if let search = item.view as? NSSearchField {\n                search.becomeFirstResponder()\n                break\n            }\n        }\n    }\n    \n    // Map this function to the main list vc exportCatalog function\n    @objc\n    func exportCatalog() {\n        for item in (parent as? NSSplitViewController)?.splitViewItems ?? [] {\n            if let list = item.viewController as? RenditionListViewController {\n                list.exportCatalog()\n                break\n            }\n        }\n    }\n    \n    func setupMenuBarItems() {\n        for item in NSApplication.shared.mainMenu?.items ?? [] {\n            // we just want to modify the \"Sections\" section\n            guard item.title == \"Sections\", let submenu = item.submenu else {\n                continue\n            }\n            \n            submenu.autoenablesItems = false\n            submenu.removeAllItems()\n            \n            // add only the types that we have\n            // to the section\n            for (index, item) in allTypes.enumerated() {\n                // make the keyEquivalent index + 1\n                // so that it's less confusing to the user,\n                // ie, if `Color` was the first section, this would make it cmd 1\n                // rather than cmd 0\n                let item =  NSMenuItem(title: item.description,\n                                       action: #selector(goToSection),\n                                       keyEquivalent: (index + 1).description, tag: index)\n                submenu.addItem(item)\n            }\n        }\n    }\n    \n    @objc\n    func goToSection(menuItemSender: NSMenuItem) {\n        changeSection(to: menuItemSender.tag)\n    }\n}\n\nextension TypesListViewController: NSTableViewDataSource, NSTableViewDelegate {\n    func numberOfRows(in tableView: NSTableView) -> Int {\n        return types.count\n    }\n    \n    func tableView(_ tableView: NSTableView, heightOfRow row: Int) -> CGFloat {\n        return 30\n    }\n    \n    func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {\n        let type = types[row]\n        let cell = NSTableCellView()\n        let imageIconView = NSImageView()\n        imageIconView.image = NSImage(systemName: type.displayIconName)\n        \n        let stackView = NSStackView(views: [imageIconView,\n                                            NSTextField(labelWithString: type.description)])\n        stackView.translatesAutoresizingMaskIntoConstraints = false\n        cell.addSubview(stackView)\n        \n        NSLayoutConstraint.activate([\n            stackView.leadingAnchor.constraint(equalTo: cell.leadingAnchor),\n            stackView.trailingAnchor.constraint(equalTo: cell.trailingAnchor),\n            stackView.centerYAnchor.constraint(equalTo: cell.centerYAnchor)\n        ])\n        return cell\n    }\n    \n    func tableView(_ tableView: NSTableView, shouldSelectRow row: Int) -> Bool {\n        return true\n    }\n    \n    func changeSection(to index: Int) {\n        if !ignoreChanges {\n            changeHandler(types[index])\n        }\n    }\n    \n    \n    func tableViewSelectionDidChange(_ notification: Notification) {\n        changeSection(to: tableView.selectedRow)\n    }\n}\n\nextension RenditionType {\n    var displayIconName: String {\n        switch self {\n        case .image, .svg:\n            return \"photo\"\n        case .icon:\n            return \"app\"\n        case .imageSet:\n            if #available(macOS 13, iOS 16, *) {\n                return \"photo.stack\"\n            }\n            \n            return \"rectangle.stack\"\n        case .multiSizeImageSet:\n            return \"cube.box\"\n        case .pdf:\n            return \"doc.richtext\"\n        case .color:\n            return \"paintbrush\"\n        case .rawData:\n            return \"text.quote\"\n        case .unknown:\n            return \"questionmark.app\"\n        }\n    }\n}\n"
  },
  {
    "path": "Samra/UI/WelcomeScreenOption.swift",
    "content": "//\n//  WelcomeScreenOption.swift\n//  Samra\n//\n//  Created by Serena on 21/02/2023.\n// \n\nimport Cocoa\n\n/// Represents an option on the main menu screen,\n/// similar to that of Xcode's.\nclass WelcomeScreenOption: NSView {\n    var actionClosure: () -> Void\n    \n    init(primaryText: String, secondaryText: String, image: NSImage?, action: @escaping () -> Void) {\n        self.actionClosure = action\n        \n        super.init(frame: .zero)\n        let finalImage: NSImage?\n        if #available(macOS 11, *) {\n            finalImage = image?\n                .withSymbolConfiguration(.init(pointSize: 30, weight: .regular))\n        } else {\n            finalImage = image\n        }\n        \n        let finalImageView = NSImageView()\n        finalImageView.image = finalImage\n        finalImageView.contentTintColor = .controlAccentColor\n        \n        let primaryTextLabel = NSTextField(labelWithString: primaryText)\n        let secondaryTextLabel = NSTextField(labelWithString: secondaryText)\n        secondaryTextLabel.textColor = .secondaryLabelColor\n        \n        if #available(macOS 11, *) {\n            primaryTextLabel.font = .preferredFont(forTextStyle: .headline)\n            secondaryTextLabel.font = .preferredFont(forTextStyle: .subheadline)\n        } else {\n            primaryTextLabel.font = .boldSystemFont(ofSize: 13)\n            secondaryTextLabel.font = .systemFont(ofSize: 11)\n        }\n        \n        let textLabelsStackView = NSStackView(views: [primaryTextLabel, secondaryTextLabel])\n        textLabelsStackView.alignment = .left\n        textLabelsStackView.spacing = 0.4\n        textLabelsStackView.orientation = .vertical\n        \n        let completeStackView = NSStackView(views: [finalImageView, textLabelsStackView])\n        completeStackView.addGestureRecognizer(NSClickGestureRecognizer(target: self, action: #selector(performAction)))\n        completeStackView.orientation = .horizontal\n        completeStackView.translatesAutoresizingMaskIntoConstraints = false\n        addSubview(completeStackView)\n        completeStackView.constraintCompletely(to: self)\n    }\n    \n    @objc func performAction() {\n        actionClosure()\n    }\n    \n    required init?(coder: NSCoder) {\n        fatalError(\"init(coder:) has not been implemented\")\n    }\n}\n"
  },
  {
    "path": "Samra/UI/WelcomeViewController.swift",
    "content": "//\n//  WelcomeViewController.swift\n//  Samra\n//\n//  Created by Serena on 18/02/2023.\n// \n\nimport Cocoa\nimport AssetCatalogWrapper\n\nclass WelcomeViewController: NSViewController {\n    \n    // override so that it doesn't try to load a fucking nib\n    override func loadView() {\n        view = NSView()\n        view.frame.size = CGSize(width: 570, height: 460)\n    }\n    \n    override func viewDidLoad() {\n        super.viewDidLoad()\n        \n        let appIcon = NSImageView(image: NSApplication.shared.applicationIconImage)\n        let welcomeTextLabel = NSTextField(labelWithString: \"Welcome to Samra\")\n        welcomeTextLabel.font = .systemFont(ofSize: 30)\n        \n        let subtitleLabel = NSTextField(labelWithString: \"Created by Antoine (formerly known as Serena)\")\n        subtitleLabel.textColor = .secondaryLabelColor\n        \n        let stackView = NSStackView(views: [appIcon, welcomeTextLabel, subtitleLabel])\n        stackView.orientation = .vertical\n        stackView.translatesAutoresizingMaskIntoConstraints = false\n        stackView.spacing = 0.3\n        view.addSubview(stackView)\n        \n        NSLayoutConstraint.activate([\n            stackView.centerXAnchor.constraint(equalTo: view.centerXAnchor),\n            stackView.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: -40)\n        ])\n        \n        let openFolderOption = WelcomeScreenOption(\n            primaryText: \"Open Assets File\",\n            secondaryText: \"Browse and Edit Assets Files on your Mac\",\n            image: NSImage(systemName: \"folder\")) { [unowned self] in\n                URLHandler.shared.presentArchiveChooserPanel(insertToRecentItems: true, senderView: view)\n            }\n        \n        openFolderOption.translatesAutoresizingMaskIntoConstraints = false\n        view.addSubview(openFolderOption)\n        \n        NSLayoutConstraint.activate([\n            openFolderOption.topAnchor.constraint(equalTo: stackView.bottomAnchor, constant: 40),\n            openFolderOption.centerXAnchor.constraint(equalTo: view.centerXAnchor)\n        ])\n        \n        let diffCatalogsOption = WelcomeScreenOption(primaryText: \"Diff Catalogs\", secondaryText: \"Diff 2 different Asset Catalogs on your Mac\", image: NSImage(systemName: \"doc.plaintext\")) {\n            WindowController(kind: .diffSelection).showWindow(nil)\n        }\n        \n        diffCatalogsOption.translatesAutoresizingMaskIntoConstraints = false\n        view.addSubview(diffCatalogsOption)\n        \n        NSLayoutConstraint.activate([\n            diffCatalogsOption.topAnchor.constraint(equalTo: openFolderOption.bottomAnchor, constant: 20),\n            diffCatalogsOption.centerXAnchor.constraint(equalTo: view.centerXAnchor)\n        ])\n        \n        let closeWindowButton = NSButton()\n        closeWindowButton.image = NSImage(systemName: \"xmark\")\n        closeWindowButton.action = #selector(closeWindowButtonClicked)\n        closeWindowButton.target = self\n        \n        closeWindowButton.showsBorderOnlyWhileMouseInside = true\n        closeWindowButton.bezelStyle = .roundRect\n        closeWindowButton.bezelColor = .gray\n        \n        closeWindowButton.translatesAutoresizingMaskIntoConstraints = false\n        view.addSubview(closeWindowButton)\n        \n        NSLayoutConstraint.activate([\n            closeWindowButton.topAnchor.constraint(equalTo: view.topAnchor, constant: 10),\n            closeWindowButton.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 8)\n        ])\n        \n        let showThisWindowButton = NSButton(title: \"Show this window when Samra launches\",\n                                            target: self,\n                                            action: #selector(showThisWindowOnLaunchButtonClicked(sender:)))\n        showThisWindowButton.setButtonType(.switch)\n        showThisWindowButton.state = Preferences.showWelcomeVCOnLaunch ? .on : .off\n        showThisWindowButton.translatesAutoresizingMaskIntoConstraints = false\n        view.addSubview(showThisWindowButton)\n        \n        NSLayoutConstraint.activate([\n            showThisWindowButton.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -10),\n            showThisWindowButton.centerXAnchor.constraint(equalTo: view.centerXAnchor)\n        ])\n        \n        // Register for when cursor is on Window\n        // if it's not, hide the closeWindowButton and the showThisWindowButton\n        // otherwise show it\n        NSEvent.addLocalMonitorForEvents(matching: [.mouseEntered, .mouseExited]) { event in\n            let newAlphaValue: CGFloat = (event.type == .mouseExited) ? 0 : 1\n            \n            NSAnimationContext.runAnimationGroup { context in\n                context.duration = 0.2\n                context.allowsImplicitAnimation = true\n                \n                closeWindowButton.animator().alphaValue = newAlphaValue\n                showThisWindowButton.animator().alphaValue = newAlphaValue\n            }\n            \n            return event\n        }\n    }\n    \n    @objc\n    func closeWindowButtonClicked() {\n        view.window?.close()\n    }\n    \n    @objc\n    func showThisWindowOnLaunchButtonClicked(sender: NSButton) {\n        var newValue = Preferences.showWelcomeVCOnLaunch\n        newValue.toggle()\n        Preferences.showWelcomeVCOnLaunch = newValue\n    }\n    \n    deinit {\n        print(\"Magna Carta.. Holy Grail.\")\n        print(\"deinit called for WelcomeViewController\")\n    }\n    \n    override func viewDidAppear() {\n        super.viewDidAppear()\n        \n        guard let window = view.window else { return }\n        window.backgroundColor = .standardWindowBackgroundColor\n        window.standardWindowButton(.closeButton)?.isHidden = true\n        window.standardWindowButton(.zoomButton)?.isHidden = true\n        window.standardWindowButton(.miniaturizeButton)?.isHidden = true\n    }\n}\n"
  },
  {
    "path": "Samra/WindowController.swift",
    "content": "//\n//  WindowController.swift\n//  Samra\n//\n//  Created by Serena on 18/02/2023.\n// \n\nimport Cocoa\nimport AssetCatalogWrapper\n\nclass WindowController: NSWindowController, NSWindowDelegate {\n    \n    enum Kind {\n        /// The 'Welcome to Samra' screen\n        case welcome\n        \n        /// The 'About Samra' Panel.\n        case aboutPanel\n        \n        /// A View Controller to select 2 AssetCatalogs to diff between them\n        case diffSelection\n        \n        /// A View Controller to show the diff between 2 asset catalogs\n        case diffShow([RenditionDiff], CUICatalog, URL)\n        \n        /// Show a View Controller of a rendition collection\n        case assetCatalog(AssetCatalogInput)\n    }\n    \n    convenience init(kind: Kind) {\n        let viewController: NSViewController\n        \n        switch kind {\n        case .welcome:\n            let splitViewController = CollapseNotifierSplitViewController()\n            let welcomeViewController = WelcomeViewController()\n            let list = PastFilesListViewController()\n            splitViewController.addSplitViewItem(NSSplitViewItem(viewController: welcomeViewController))\n            splitViewController.addSplitViewItem(NSSplitViewItem(sidebarWithViewController: list))\n            viewController = splitViewController\n        case .assetCatalog(let input):\n            let splitViewController = CollapseNotifierSplitViewController()\n            let renditionVC = RenditionListViewController(catalog: input.catalog, collection: input.collection,\n                                                          fileURL: input.fileURL)\n            let typesSidebar = TypesListViewController(types: input.collection.map(\\.type)) { type in\n                if let index = renditionVC.dataSource.snapshot().indexOfSection(type) {\n                    renditionVC.collectionView.scrollToItems(at: [IndexPath(item: 0, section: index)], scrollPosition: .top)\n                }\n            }\n            \n            splitViewController.addSplitViewItem(NSSplitViewItem(sidebarWithViewController: typesSidebar))\n            splitViewController.addSplitViewItem(NSSplitViewItem(viewController: renditionVC))\n            viewController = splitViewController\n        case .aboutPanel:\n            viewController = AboutViewController()\n        case .diffSelection:\n            viewController = AssetCatalogDiffSelectionViewController()\n        case .diffShow(let diffs, let catalog, let fileURL):\n            viewController = DiffListViewController(diffs: diffs, catalog: catalog, fileURL: fileURL)\n        }\n        \n        let window = NSWindow(contentViewController: viewController)\n        window.styleMask.insert(.fullSizeContentView)\n        self.init(window: window)\n        \n        switch kind {\n        case .assetCatalog(let input):\n            let toolbar = NSToolbar()\n            toolbar.delegate = self\n            window.toolbar = toolbar\n            toolbar.insertItem(withItemIdentifier: .flexibleSpace, at: 0)\n            toolbar.insertItem(withItemIdentifier: .searchBar, at: 1)\n            toolbar.insertItem(withItemIdentifier: .init(\"infoButton\"), at: 2)\n            window.toolbar?.centeredItemIdentifier = .searchBar\n            window.animationBehavior = .documentWindow\n            window.delegate = self\n            window.title = input.fileURL.lastPathComponent\n            if #available(macOS 11, *) {\n                window.subtitle = input.fileURL.deletingLastPathComponent().lastPathComponent\n            }\n        case .welcome:\n            window.makeTitleBarTransparentAndUnresizable()\n            window.animationBehavior = .utilityWindow\n            window.title = \"Samra\"\n        case .aboutPanel:\n            window.makeTitleBarTransparentAndUnresizable()\n            window.title = \"Samra\"\n        case .diffSelection:\n            window.title = \"Diff\"\n        case .diffShow(_, _, _):\n            let toolbar = NSToolbar()\n            toolbar.delegate = self\n            window.toolbar = toolbar\n            window.title = \"Diff\"\n            toolbar.insertItem(withItemIdentifier: .flexibleSpace, at: 0)\n            toolbar.insertItem(withItemIdentifier: .searchBar, at: 1)\n            window.animationBehavior = .documentWindow\n            window.delegate = self\n        }\n    }\n}\n\nextension WindowController: NSToolbarDelegate {\n    func toolbarDefaultItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {\n        return []\n    }\n    \n    func toolbarAllowedItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {\n        return []\n    }\n    \n    func toolbar(_ toolbar: NSToolbar, itemForItemIdentifier itemIdentifier: NSToolbarItem.Identifier, willBeInsertedIntoToolbar flag: Bool) -> NSToolbarItem? {\n        switch itemIdentifier {\n        case .searchBar:\n            let rendVC: NSSearchFieldDelegate?\n            \n            if let splitVC = contentViewController as? NSSplitViewController {\n                rendVC = splitVC.splitViewItems[1].viewController as? NSSearchFieldDelegate\n            } else {\n                rendVC = contentViewController as? NSSearchFieldDelegate\n            }\n            \n            /*\n            if #available(macOS 11, *) {\n                let item = NSSearchToolbarItem(itemIdentifier: .searchBar)\n                item.searchField.delegate = rendVC\n                return item\n            }\n             */\n            \n            let item = NSToolbarItem(itemIdentifier: .searchBar)\n            let searchField = NSSearchField()\n            searchField.delegate = rendVC\n            item.view = searchField\n            return item\n        case .init(\"infoButton\"):\n            let toolbarItem = NSToolbarItem(itemIdentifier: itemIdentifier)\n            let button = NSButton()\n            if #available(macOS 11, *) {\n                button.image = NSImage(systemSymbolName: \"info.circle\", accessibilityDescription: nil)?\n                    .withSymbolConfiguration(NSImage.SymbolConfiguration(pointSize: 18, weight: .regular))\n            } else {\n                button.title = \"Info\"\n            }\n            button.action = #selector(RenditionListViewController.infoButtonClicked(sender:))\n            button.target = (contentViewController as? NSSplitViewController)?.splitViewItems[1].viewController as? RenditionListViewController\n            button.setButtonType(.momentaryPushIn)\n            button.bezelStyle = .texturedRounded\n            toolbarItem.view = button\n            \n//            toolbarItem.action = #selector(RenditionListViewController.infoPopoverItemClicked(sender:))\n//            toolbarItem.target = (contentViewController as? NSSplitViewController)?.splitViewItems[1].viewController as? RenditionListViewController\n//            toolbarItem.image = NSImage(systemSymbolName: \"info.circle\", accessibilityDescription: nil)\n//            toolbarItem.isEnabled = true\n            return toolbarItem\n        case .init(\"flexSpace\"):\n            #warning(\"Fix this (want flexible space between search bar and sidebar)\")\n            let toolbarItem = NSToolbarItem(itemIdentifier: NSToolbarItem.Identifier(rawValue: \"flexSpace\"))\n            return toolbarItem\n        default:\n            return NSToolbarItem(itemIdentifier: itemIdentifier)\n        }\n    }\n    \n    func toolbar(_ toolbar: NSToolbar, itemIdentifier: NSToolbarItem.Identifier, canBeInsertedAt index: Int) -> Bool {\n        return true\n    }\n}\n"
  },
  {
    "path": "Samra.xcodeproj/project.pbxproj",
    "content": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 56;\n\tobjects = {\n\n/* Begin PBXBuildFile section */\n\t\tCE1126A929A556C0000AC770 /* RenditionInformationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE1126A829A556C0000AC770 /* RenditionInformationView.swift */; };\n\t\tCE15D4BF29A3E5D5001D66E6 /* URLHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE15D4BE29A3E5D5001D66E6 /* URLHandler.swift */; };\n\t\tCE1673DB29ACDF8100F94683 /* AssetCatalogDetailsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE1673DA29ACDF8100F94683 /* AssetCatalogDetailsView.swift */; };\n\t\tCE1F1D3429B0A4C1000B288C /* MenuableCollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE1F1D3329B0A4C1000B288C /* MenuableCollectionView.swift */; };\n\t\tCE1F1D3629B0ADCE000B288C /* ClosureMenuItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE1F1D3529B0ADCE000B288C /* ClosureMenuItem.swift */; };\n\t\tCE375E6429B65D3900CAC2F0 /* AssetCatalogDiffSelectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE375E6329B65D3900CAC2F0 /* AssetCatalogDiffSelectionViewController.swift */; };\n\t\tCE375E6629B6675900CAC2F0 /* AssetCatalogInput.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE375E6529B6675900CAC2F0 /* AssetCatalogInput.swift */; };\n\t\tCE3BC09829A0C626009823CF /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE3BC09729A0C626009823CF /* AppDelegate.swift */; };\n\t\tCE3BC09A29A0C626009823CF /* WindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE3BC09929A0C626009823CF /* WindowController.swift */; };\n\t\tCE3BC09C29A0C626009823CF /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CE3BC09B29A0C626009823CF /* Assets.xcassets */; };\n\t\tCE3BC0A729A0CC27009823CF /* WelcomeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE3BC0A629A0CC27009823CF /* WelcomeViewController.swift */; };\n\t\tCE3BC0A929A0D713009823CF /* PastFilesListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE3BC0A829A0D713009823CF /* PastFilesListViewController.swift */; };\n\t\tCE3BC0AF29A0E345009823CF /* BasicLayoutAnchorsHolding.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE3BC0AE29A0E345009823CF /* BasicLayoutAnchorsHolding.swift */; };\n\t\tCE3BC0B129A0E990009823CF /* Preferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE3BC0B029A0E990009823CF /* Preferences.swift */; };\n\t\tCE3F2D052A02DABC0026A9F9 /* DiffFilePreviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE3F2D042A02DABC0026A9F9 /* DiffFilePreviewView.swift */; };\n\t\tCE3F9C6E2CCA22C400662232 /* AssetCatalogWrapper in Frameworks */ = {isa = PBXBuildFile; productRef = CE3F9C6D2CCA22C400662232 /* AssetCatalogWrapper */; };\n\t\tCE45E09D29C24E1F00817359 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE45E09C29C24E1F00817359 /* main.swift */; };\n\t\tCE5AF1A829A2516500C675D8 /* RenditionTypeHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE5AF1A729A2516500C675D8 /* RenditionTypeHeaderView.swift */; };\n\t\tCE7D54A729A1238F00862873 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE7D54A629A1238F00862873 /* Extensions.swift */; };\n\t\tCE7D54AD29A1313000862873 /* TypesListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE7D54AC29A1313000862873 /* TypesListViewController.swift */; };\n\t\tCE7D54AF29A1370D00862873 /* RenditionListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE7D54AE29A1370D00862873 /* RenditionListViewController.swift */; };\n\t\tCE7D54B129A14F2200862873 /* RenditionCollectionViewItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE7D54B029A14F2200862873 /* RenditionCollectionViewItem.swift */; };\n\t\tCE7E5D9229B1D3AC0064B91B /* QuickLooKPreviewSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE7E5D9129B1D3AC0064B91B /* QuickLooKPreviewSource.swift */; };\n\t\tCE7E5D9829B1D5D30064B91B /* QuickLookUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CE7E5D9729B1D5D30064B91B /* QuickLookUI.framework */; };\n\t\tCEA6629029A4E5FF00215B08 /* WelcomeScreenOption.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEA6628F29A4E5FF00215B08 /* WelcomeScreenOption.swift */; };\n\t\tCEA71A5E29AE760900BEBE93 /* AboutViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEA71A5D29AE760900BEBE93 /* AboutViewController.swift */; };\n\t\tCEC3B27C29B14551007E853E /* AssetCatalogDocument.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEC3B27B29B14551007E853E /* AssetCatalogDocument.swift */; };\n\t\tCEC5EBC729B7CCD6009BA873 /* DiffListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEC5EBC629B7CCD6009BA873 /* DiffListViewController.swift */; };\n\t\tCED4DE3129A626D7008B2B8A /* CollapseNotifierSplitViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CED4DE3029A626D7008B2B8A /* CollapseNotifierSplitViewController.swift */; };\n\t\tCEE9FA3E2CCA245C00F3F356 /* AssetCatalogWrapper in Frameworks */ = {isa = PBXBuildFile; productRef = CEE9FA3D2CCA245C00F3F356 /* AssetCatalogWrapper */; };\n\t\tCEEA6AB429A515EA00B3CEA9 /* DetailItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEEA6AB329A515EA00B3CEA9 /* DetailItem.swift */; };\n\t\tCEEE131029B73B99009C1ACD /* RenditionDiff.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEEE130F29B73B99009C1ACD /* RenditionDiff.swift */; };\n\t\tCEF987032B974C53002177A2 /* ClosureBasedButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEF987022B974C53002177A2 /* ClosureBasedButton.swift */; };\n/* End PBXBuildFile section */\n\n/* Begin PBXCopyFilesBuildPhase section */\n\t\tCE45E09829C24E1F00817359 /* CopyFiles */ = {\n\t\t\tisa = PBXCopyFilesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tdstPath = /usr/share/man/man1/;\n\t\t\tdstSubfolderSpec = 0;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 1;\n\t\t};\n/* End PBXCopyFilesBuildPhase section */\n\n/* Begin PBXFileReference section */\n\t\tCE1126A829A556C0000AC770 /* RenditionInformationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RenditionInformationView.swift; sourceTree = \"<group>\"; };\n\t\tCE15D4BE29A3E5D5001D66E6 /* URLHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLHandler.swift; sourceTree = \"<group>\"; };\n\t\tCE1673DA29ACDF8100F94683 /* AssetCatalogDetailsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssetCatalogDetailsView.swift; sourceTree = \"<group>\"; };\n\t\tCE1F1D3329B0A4C1000B288C /* MenuableCollectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuableCollectionView.swift; sourceTree = \"<group>\"; };\n\t\tCE1F1D3529B0ADCE000B288C /* ClosureMenuItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClosureMenuItem.swift; sourceTree = \"<group>\"; };\n\t\tCE375E6329B65D3900CAC2F0 /* AssetCatalogDiffSelectionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssetCatalogDiffSelectionViewController.swift; sourceTree = \"<group>\"; };\n\t\tCE375E6529B6675900CAC2F0 /* AssetCatalogInput.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssetCatalogInput.swift; sourceTree = \"<group>\"; };\n\t\tCE3BC09429A0C626009823CF /* Samra.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Samra.app; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tCE3BC09729A0C626009823CF /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = \"<group>\"; };\n\t\tCE3BC09929A0C626009823CF /* WindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowController.swift; sourceTree = \"<group>\"; };\n\t\tCE3BC09B29A0C626009823CF /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = \"<group>\"; };\n\t\tCE3BC0A029A0C626009823CF /* Samra.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Samra.entitlements; sourceTree = \"<group>\"; };\n\t\tCE3BC0A629A0CC27009823CF /* WelcomeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeViewController.swift; sourceTree = \"<group>\"; };\n\t\tCE3BC0A829A0D713009823CF /* PastFilesListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PastFilesListViewController.swift; sourceTree = \"<group>\"; };\n\t\tCE3BC0AE29A0E345009823CF /* BasicLayoutAnchorsHolding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BasicLayoutAnchorsHolding.swift; sourceTree = \"<group>\"; };\n\t\tCE3BC0B029A0E990009823CF /* Preferences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Preferences.swift; sourceTree = \"<group>\"; };\n\t\tCE3F2D042A02DABC0026A9F9 /* DiffFilePreviewView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiffFilePreviewView.swift; sourceTree = \"<group>\"; };\n\t\tCE45E09A29C24E1F00817359 /* extractutil */ = {isa = PBXFileReference; explicitFileType = \"compiled.mach-o.executable\"; includeInIndex = 0; path = extractutil; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tCE45E09C29C24E1F00817359 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = \"<group>\"; };\n\t\tCE5AF1A729A2516500C675D8 /* RenditionTypeHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RenditionTypeHeaderView.swift; sourceTree = \"<group>\"; };\n\t\tCE7D54A629A1238F00862873 /* Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = \"<group>\"; };\n\t\tCE7D54A829A1243C00862873 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = \"<group>\"; };\n\t\tCE7D54AC29A1313000862873 /* TypesListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TypesListViewController.swift; sourceTree = \"<group>\"; };\n\t\tCE7D54AE29A1370D00862873 /* RenditionListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RenditionListViewController.swift; sourceTree = \"<group>\"; };\n\t\tCE7D54B029A14F2200862873 /* RenditionCollectionViewItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RenditionCollectionViewItem.swift; sourceTree = \"<group>\"; };\n\t\tCE7E5D9129B1D3AC0064B91B /* QuickLooKPreviewSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuickLooKPreviewSource.swift; sourceTree = \"<group>\"; };\n\t\tCE7E5D9529B1D5CC0064B91B /* QuickLookThumbnailing.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuickLookThumbnailing.framework; path = System/Library/PrivateFrameworks/QuickLookThumbnailing.framework; sourceTree = SDKROOT; };\n\t\tCE7E5D9729B1D5D30064B91B /* QuickLookUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuickLookUI.framework; path = System/Library/Frameworks/QuickLookUI.framework; sourceTree = SDKROOT; };\n\t\tCEA6628F29A4E5FF00215B08 /* WelcomeScreenOption.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeScreenOption.swift; sourceTree = \"<group>\"; };\n\t\tCEA71A5D29AE760900BEBE93 /* AboutViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutViewController.swift; sourceTree = \"<group>\"; };\n\t\tCEC3B27B29B14551007E853E /* AssetCatalogDocument.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssetCatalogDocument.swift; sourceTree = \"<group>\"; };\n\t\tCEC5EBC629B7CCD6009BA873 /* DiffListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiffListViewController.swift; sourceTree = \"<group>\"; };\n\t\tCED4DE2E29A62566008B2B8A /* AppKitPrivates.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppKitPrivates.h; sourceTree = \"<group>\"; };\n\t\tCED4DE2F29A62586008B2B8A /* module.modulemap */ = {isa = PBXFileReference; lastKnownFileType = \"sourcecode.module-map\"; path = module.modulemap; sourceTree = \"<group>\"; };\n\t\tCED4DE3029A626D7008B2B8A /* CollapseNotifierSplitViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollapseNotifierSplitViewController.swift; sourceTree = \"<group>\"; };\n\t\tCEEA6AB329A515EA00B3CEA9 /* DetailItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailItem.swift; sourceTree = \"<group>\"; };\n\t\tCEEE130F29B73B99009C1ACD /* RenditionDiff.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RenditionDiff.swift; sourceTree = \"<group>\"; };\n\t\tCEF987022B974C53002177A2 /* ClosureBasedButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClosureBasedButton.swift; sourceTree = \"<group>\"; };\n/* End PBXFileReference section */\n\n/* Begin PBXFrameworksBuildPhase section */\n\t\tCE3BC09129A0C625009823CF /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tCE7E5D9829B1D5D30064B91B /* QuickLookUI.framework in Frameworks */,\n\t\t\t\tCE3F9C6E2CCA22C400662232 /* AssetCatalogWrapper in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tCE45E09729C24E1F00817359 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tCEE9FA3E2CCA245C00F3F356 /* AssetCatalogWrapper in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXFrameworksBuildPhase section */\n\n/* Begin PBXGroup section */\n\t\tCE3BC08B29A0C625009823CF = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tCE3BC09629A0C626009823CF /* Samra */,\n\t\t\t\tCE45E09B29C24E1F00817359 /* extractutil */,\n\t\t\t\tCE3BC09529A0C626009823CF /* Products */,\n\t\t\t\tCE7E5D9429B1D5CC0064B91B /* Frameworks */,\n\t\t\t);\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tCE3BC09529A0C626009823CF /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tCE3BC09429A0C626009823CF /* Samra.app */,\n\t\t\t\tCE45E09A29C24E1F00817359 /* extractutil */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tCE3BC09629A0C626009823CF /* Samra */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tCE7D54A829A1243C00862873 /* Info.plist */,\n\t\t\t\tCE3BC09729A0C626009823CF /* AppDelegate.swift */,\n\t\t\t\tCE3BC09929A0C626009823CF /* WindowController.swift */,\n\t\t\t\tCE3BC0AA29A0E2E7009823CF /* UI */,\n\t\t\t\tCE3BC0AB29A0E2F6009823CF /* Backend */,\n\t\t\t\tCE3BC09B29A0C626009823CF /* Assets.xcassets */,\n\t\t\t\tCE3BC0A029A0C626009823CF /* Samra.entitlements */,\n\t\t\t);\n\t\t\tpath = Samra;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tCE3BC0AA29A0E2E7009823CF /* UI */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tCE3BC0A629A0CC27009823CF /* WelcomeViewController.swift */,\n\t\t\t\tCEA6628F29A4E5FF00215B08 /* WelcomeScreenOption.swift */,\n\t\t\t\tCE3BC0A829A0D713009823CF /* PastFilesListViewController.swift */,\n\t\t\t\tCED4DE3029A626D7008B2B8A /* CollapseNotifierSplitViewController.swift */,\n\t\t\t\tCEA71A5D29AE760900BEBE93 /* AboutViewController.swift */,\n\t\t\t\tCE1F1D3329B0A4C1000B288C /* MenuableCollectionView.swift */,\n\t\t\t\tCEF987022B974C53002177A2 /* ClosureBasedButton.swift */,\n\t\t\t\tCEC5EBC529B7CCCA009BA873 /* Diff */,\n\t\t\t\tCECA445029A23D80003222D0 /* Rendition */,\n\t\t\t);\n\t\t\tpath = UI;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tCE3BC0AB29A0E2F6009823CF /* Backend */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tCE7E5D9329B1D45B0064B91B /* UI Support */,\n\t\t\t\tCED4DE2D29A62558008B2B8A /* AppKitPrivates */,\n\t\t\t\tCEEA6AB329A515EA00B3CEA9 /* DetailItem.swift */,\n\t\t\t\tCEEE130F29B73B99009C1ACD /* RenditionDiff.swift */,\n\t\t\t\tCE3BC0B029A0E990009823CF /* Preferences.swift */,\n\t\t\t\tCE7D54A629A1238F00862873 /* Extensions.swift */,\n\t\t\t\tCE375E6529B6675900CAC2F0 /* AssetCatalogInput.swift */,\n\t\t\t\tCE1F1D3529B0ADCE000B288C /* ClosureMenuItem.swift */,\n\t\t\t);\n\t\t\tpath = Backend;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tCE45E09B29C24E1F00817359 /* extractutil */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tCE45E09C29C24E1F00817359 /* main.swift */,\n\t\t\t);\n\t\t\tpath = extractutil;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tCE7E5D9329B1D45B0064B91B /* UI Support */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tCE3BC0AE29A0E345009823CF /* BasicLayoutAnchorsHolding.swift */,\n\t\t\t\tCE15D4BE29A3E5D5001D66E6 /* URLHandler.swift */,\n\t\t\t\tCEC3B27B29B14551007E853E /* AssetCatalogDocument.swift */,\n\t\t\t\tCE7E5D9129B1D3AC0064B91B /* QuickLooKPreviewSource.swift */,\n\t\t\t);\n\t\t\tpath = \"UI Support\";\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tCE7E5D9429B1D5CC0064B91B /* Frameworks */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tCE7E5D9729B1D5D30064B91B /* QuickLookUI.framework */,\n\t\t\t\tCE7E5D9529B1D5CC0064B91B /* QuickLookThumbnailing.framework */,\n\t\t\t);\n\t\t\tname = Frameworks;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tCEC5EBC529B7CCCA009BA873 /* Diff */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tCE375E6329B65D3900CAC2F0 /* AssetCatalogDiffSelectionViewController.swift */,\n\t\t\t\tCE3F2D042A02DABC0026A9F9 /* DiffFilePreviewView.swift */,\n\t\t\t\tCEC5EBC629B7CCD6009BA873 /* DiffListViewController.swift */,\n\t\t\t);\n\t\t\tpath = Diff;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tCECA445029A23D80003222D0 /* Rendition */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tCE7D54AC29A1313000862873 /* TypesListViewController.swift */,\n\t\t\t\tCE5AF1A729A2516500C675D8 /* RenditionTypeHeaderView.swift */,\n\t\t\t\tCE7D54AE29A1370D00862873 /* RenditionListViewController.swift */,\n\t\t\t\tCE7D54B029A14F2200862873 /* RenditionCollectionViewItem.swift */,\n\t\t\t\tCE1126A829A556C0000AC770 /* RenditionInformationView.swift */,\n\t\t\t\tCE1673DA29ACDF8100F94683 /* AssetCatalogDetailsView.swift */,\n\t\t\t);\n\t\t\tpath = Rendition;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tCED4DE2D29A62558008B2B8A /* AppKitPrivates */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tCED4DE2E29A62566008B2B8A /* AppKitPrivates.h */,\n\t\t\t\tCED4DE2F29A62586008B2B8A /* module.modulemap */,\n\t\t\t);\n\t\t\tpath = AppKitPrivates;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXGroup section */\n\n/* Begin PBXNativeTarget section */\n\t\tCE3BC09329A0C625009823CF /* Samra */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = CE3BC0A329A0C626009823CF /* Build configuration list for PBXNativeTarget \"Samra\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tCE3BC09029A0C625009823CF /* Sources */,\n\t\t\t\tCE3BC09129A0C625009823CF /* Frameworks */,\n\t\t\t\tCE3BC09229A0C625009823CF /* Resources */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = Samra;\n\t\t\tpackageProductDependencies = (\n\t\t\t\tCE3F9C6D2CCA22C400662232 /* AssetCatalogWrapper */,\n\t\t\t);\n\t\t\tproductName = Samra;\n\t\t\tproductReference = CE3BC09429A0C626009823CF /* Samra.app */;\n\t\t\tproductType = \"com.apple.product-type.application\";\n\t\t};\n\t\tCE45E09929C24E1F00817359 /* extractutil */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = CE45E0A029C24E1F00817359 /* Build configuration list for PBXNativeTarget \"extractutil\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tCE45E09629C24E1F00817359 /* Sources */,\n\t\t\t\tCE45E09729C24E1F00817359 /* Frameworks */,\n\t\t\t\tCE45E09829C24E1F00817359 /* CopyFiles */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = extractutil;\n\t\t\tpackageProductDependencies = (\n\t\t\t\tCEE9FA3D2CCA245C00F3F356 /* AssetCatalogWrapper */,\n\t\t\t);\n\t\t\tproductName = extractutil;\n\t\t\tproductReference = CE45E09A29C24E1F00817359 /* extractutil */;\n\t\t\tproductType = \"com.apple.product-type.tool\";\n\t\t};\n/* End PBXNativeTarget section */\n\n/* Begin PBXProject section */\n\t\tCE3BC08C29A0C625009823CF /* Project object */ = {\n\t\t\tisa = PBXProject;\n\t\t\tattributes = {\n\t\t\t\tBuildIndependentTargetsInParallel = 1;\n\t\t\t\tLastSwiftUpdateCheck = 1420;\n\t\t\t\tLastUpgradeCheck = 1420;\n\t\t\t\tTargetAttributes = {\n\t\t\t\t\tCE3BC09329A0C625009823CF = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 14.2;\n\t\t\t\t\t};\n\t\t\t\t\tCE45E09929C24E1F00817359 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 14.2;\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t};\n\t\t\tbuildConfigurationList = CE3BC08F29A0C625009823CF /* Build configuration list for PBXProject \"Samra\" */;\n\t\t\tcompatibilityVersion = \"Xcode 14.0\";\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 = CE3BC08B29A0C625009823CF;\n\t\t\tpackageReferences = (\n\t\t\t\tCE3F9C6C2CCA22C400662232 /* XCRemoteSwiftPackageReference \"PrivateKits\" */,\n\t\t\t);\n\t\t\tproductRefGroup = CE3BC09529A0C626009823CF /* Products */;\n\t\t\tprojectDirPath = \"\";\n\t\t\tprojectRoot = \"\";\n\t\t\ttargets = (\n\t\t\t\tCE3BC09329A0C625009823CF /* Samra */,\n\t\t\t\tCE45E09929C24E1F00817359 /* extractutil */,\n\t\t\t);\n\t\t};\n/* End PBXProject section */\n\n/* Begin PBXResourcesBuildPhase section */\n\t\tCE3BC09229A0C625009823CF /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tCE3BC09C29A0C626009823CF /* Assets.xcassets in Resources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXResourcesBuildPhase section */\n\n/* Begin PBXSourcesBuildPhase section */\n\t\tCE3BC09029A0C625009823CF /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tCE3BC0A729A0CC27009823CF /* WelcomeViewController.swift in Sources */,\n\t\t\t\tCE3BC09A29A0C626009823CF /* WindowController.swift in Sources */,\n\t\t\t\tCE3BC0B129A0E990009823CF /* Preferences.swift in Sources */,\n\t\t\t\tCE3BC0A929A0D713009823CF /* PastFilesListViewController.swift in Sources */,\n\t\t\t\tCE3BC0AF29A0E345009823CF /* BasicLayoutAnchorsHolding.swift in Sources */,\n\t\t\t\tCE1126A929A556C0000AC770 /* RenditionInformationView.swift in Sources */,\n\t\t\t\tCE15D4BF29A3E5D5001D66E6 /* URLHandler.swift in Sources */,\n\t\t\t\tCE1673DB29ACDF8100F94683 /* AssetCatalogDetailsView.swift in Sources */,\n\t\t\t\tCE7D54AD29A1313000862873 /* TypesListViewController.swift in Sources */,\n\t\t\t\tCEF987032B974C53002177A2 /* ClosureBasedButton.swift in Sources */,\n\t\t\t\tCE1F1D3429B0A4C1000B288C /* MenuableCollectionView.swift in Sources */,\n\t\t\t\tCE375E6429B65D3900CAC2F0 /* AssetCatalogDiffSelectionViewController.swift in Sources */,\n\t\t\t\tCE7D54B129A14F2200862873 /* RenditionCollectionViewItem.swift in Sources */,\n\t\t\t\tCE3BC09829A0C626009823CF /* AppDelegate.swift in Sources */,\n\t\t\t\tCEC5EBC729B7CCD6009BA873 /* DiffListViewController.swift in Sources */,\n\t\t\t\tCE5AF1A829A2516500C675D8 /* RenditionTypeHeaderView.swift in Sources */,\n\t\t\t\tCE1F1D3629B0ADCE000B288C /* ClosureMenuItem.swift in Sources */,\n\t\t\t\tCEA71A5E29AE760900BEBE93 /* AboutViewController.swift in Sources */,\n\t\t\t\tCE7D54A729A1238F00862873 /* Extensions.swift in Sources */,\n\t\t\t\tCE3F2D052A02DABC0026A9F9 /* DiffFilePreviewView.swift in Sources */,\n\t\t\t\tCED4DE3129A626D7008B2B8A /* CollapseNotifierSplitViewController.swift in Sources */,\n\t\t\t\tCE375E6629B6675900CAC2F0 /* AssetCatalogInput.swift in Sources */,\n\t\t\t\tCEEA6AB429A515EA00B3CEA9 /* DetailItem.swift in Sources */,\n\t\t\t\tCE7D54AF29A1370D00862873 /* RenditionListViewController.swift in Sources */,\n\t\t\t\tCEA6629029A4E5FF00215B08 /* WelcomeScreenOption.swift in Sources */,\n\t\t\t\tCE7E5D9229B1D3AC0064B91B /* QuickLooKPreviewSource.swift in Sources */,\n\t\t\t\tCEEE131029B73B99009C1ACD /* RenditionDiff.swift in Sources */,\n\t\t\t\tCEC3B27C29B14551007E853E /* AssetCatalogDocument.swift in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tCE45E09629C24E1F00817359 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tCE45E09D29C24E1F00817359 /* main.swift in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXSourcesBuildPhase section */\n\n/* Begin XCBuildConfiguration section */\n\t\tCE3BC0A129A0C626009823CF /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\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\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\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\tMACOSX_DEPLOYMENT_TARGET = 13.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;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tCE3BC0A229A0C626009823CF /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\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\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\tGCC_C_LANGUAGE_STANDARD = gnu11;\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\tMACOSX_DEPLOYMENT_TARGET = 13.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\tSWIFT_OPTIMIZATION_LEVEL = \"-O\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tCE3BC0A429A0C626009823CF /* 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 = Samra/Samra.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 = 1.4;\n\t\t\t\tDEVELOPMENT_TEAM = L9735M962H;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tINFOPLIST_FILE = Samra/Info.plist;\n\t\t\t\tINFOPLIST_KEY_LSApplicationCategoryType = \"public.app-category.utilities\";\n\t\t\t\tINFOPLIST_KEY_NSHumanReadableCopyright = \"\";\n\t\t\t\tINFOPLIST_KEY_NSPrincipalClass = NSApplication;\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 = 10.15.1;\n\t\t\t\tMARKETING_VERSION = 1;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.serena.Samra;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = YES;\n\t\t\t\tSWIFT_INCLUDE_PATHS = \"$(SRCROOT)/Samra/Backend/AppKitPrivates\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tSYSTEM_FRAMEWORK_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"$(SDKROOT)$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks\",\n\t\t\t\t);\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tCE3BC0A529A0C626009823CF /* 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 = Samra/Samra.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 = 1.4;\n\t\t\t\tDEVELOPMENT_TEAM = L9735M962H;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tINFOPLIST_FILE = Samra/Info.plist;\n\t\t\t\tINFOPLIST_KEY_LSApplicationCategoryType = \"public.app-category.utilities\";\n\t\t\t\tINFOPLIST_KEY_NSHumanReadableCopyright = \"\";\n\t\t\t\tINFOPLIST_KEY_NSPrincipalClass = NSApplication;\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 = 10.15.1;\n\t\t\t\tMARKETING_VERSION = 1;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.serena.Samra;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = YES;\n\t\t\t\tSWIFT_INCLUDE_PATHS = \"$(SRCROOT)/Samra/Backend/AppKitPrivates\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tSYSTEM_FRAMEWORK_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"$(SDKROOT)$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks\",\n\t\t\t\t);\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tCE45E09E29C24E1F00817359 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tDEVELOPMENT_TEAM = L9735M962H;\n\t\t\t\tENABLE_HARDENED_RUNTIME = YES;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tCE45E09F29C24E1F00817359 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tDEVELOPMENT_TEAM = L9735M962H;\n\t\t\t\tENABLE_HARDENED_RUNTIME = YES;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\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\tCE3BC08F29A0C625009823CF /* Build configuration list for PBXProject \"Samra\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tCE3BC0A129A0C626009823CF /* Debug */,\n\t\t\t\tCE3BC0A229A0C626009823CF /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tCE3BC0A329A0C626009823CF /* Build configuration list for PBXNativeTarget \"Samra\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tCE3BC0A429A0C626009823CF /* Debug */,\n\t\t\t\tCE3BC0A529A0C626009823CF /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tCE45E0A029C24E1F00817359 /* Build configuration list for PBXNativeTarget \"extractutil\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tCE45E09E29C24E1F00817359 /* Debug */,\n\t\t\t\tCE45E09F29C24E1F00817359 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n/* End XCConfigurationList section */\n\n/* Begin XCRemoteSwiftPackageReference section */\n\t\tCE3F9C6C2CCA22C400662232 /* XCRemoteSwiftPackageReference \"PrivateKits\" */ = {\n\t\t\tisa = XCRemoteSwiftPackageReference;\n\t\t\trepositoryURL = \"https://github.com/SerenaKit/PrivateKits\";\n\t\t\trequirement = {\n\t\t\t\tbranch = main;\n\t\t\t\tkind = branch;\n\t\t\t};\n\t\t};\n/* End XCRemoteSwiftPackageReference section */\n\n/* Begin XCSwiftPackageProductDependency section */\n\t\tCE3F9C6D2CCA22C400662232 /* AssetCatalogWrapper */ = {\n\t\t\tisa = XCSwiftPackageProductDependency;\n\t\t\tpackage = CE3F9C6C2CCA22C400662232 /* XCRemoteSwiftPackageReference \"PrivateKits\" */;\n\t\t\tproductName = AssetCatalogWrapper;\n\t\t};\n\t\tCEE9FA3D2CCA245C00F3F356 /* AssetCatalogWrapper */ = {\n\t\t\tisa = XCSwiftPackageProductDependency;\n\t\t\tpackage = CE3F9C6C2CCA22C400662232 /* XCRemoteSwiftPackageReference \"PrivateKits\" */;\n\t\t\tproductName = AssetCatalogWrapper;\n\t\t};\n/* End XCSwiftPackageProductDependency section */\n\t};\n\trootObject = CE3BC08C29A0C625009823CF /* Project object */;\n}\n"
  },
  {
    "path": "Samra.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"self:\">\n   </FileRef>\n</Workspace>\n"
  },
  {
    "path": "Samra.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.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\t<key>IDEDidComputeMac32BitWarning</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "Samra.xcodeproj/xcshareddata/xcschemes/Samra.xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1420\"\n   version = \"1.3\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"CE3BC09329A0C625009823CF\"\n               BuildableName = \"Samra.app\"\n               BlueprintName = \"Samra\"\n               ReferencedContainer = \"container:Samra.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\">\n      <Testables>\n      </Testables>\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"CE3BC09329A0C625009823CF\"\n            BuildableName = \"Samra.app\"\n            BlueprintName = \"Samra\"\n            ReferencedContainer = \"container:Samra.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Release\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"CE3BC09329A0C625009823CF\"\n            BuildableName = \"Samra.app\"\n            BlueprintName = \"Samra\"\n            ReferencedContainer = \"container:Samra.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Release\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": "Samra.xcodeproj/xcshareddata/xcschemes/extractutil.xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1420\"\n   version = \"1.3\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"CE45E09929C24E1F00817359\"\n               BuildableName = \"extractutil\"\n               BlueprintName = \"extractutil\"\n               ReferencedContainer = \"container:Samra.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\">\n      <Testables>\n      </Testables>\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\"\n      viewDebuggingEnabled = \"No\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"CE45E09929C24E1F00817359\"\n            BuildableName = \"extractutil\"\n            BlueprintName = \"extractutil\"\n            ReferencedContainer = \"container:Samra.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Release\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"CE45E09929C24E1F00817359\"\n            BuildableName = \"extractutil\"\n            BlueprintName = \"extractutil\"\n            ReferencedContainer = \"container:Samra.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Release\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": "extractutil/main.swift",
    "content": "//\n//  main.swift\n//  extractutil\n//\n//  Created by Serena on 15/03/2023.\n// \n// smol CommandLine tool to just extract an asset catalog :3\n\nimport Foundation\nimport AssetCatalogWrapper\n\nguard CommandLine.arguments.count >= 3 else {\n    fatalError(\"usage: \\(CommandLine.arguments[0]) <catalog-url> <directory-to-extract-to>\")\n}\n\nlet catalogURL = URL(fileURLWithPath: CommandLine.arguments[1])\nlet destinationURL = URL(fileURLWithPath: CommandLine.arguments[2])\n\nlet rends: RenditionCollection\n\ndo {\n    rends = try AssetCatalogWrapper.shared.renditions(forCarArchive: catalogURL).1\n} catch {\n    fatalError(\"Failed to fetch Catalog from URL \\(catalogURL.path), error: \\(error.localizedDescription)\")\n}\n\n// try create the destination URL if it doesn't exist\nif !FileManager.default.fileExists(atPath: destinationURL.path) {\n    do {\n        print(\"destination URL \\(destinationURL.path) doesn't exist, will try to create\")\n        try FileManager.default.createDirectory(at: destinationURL, withIntermediateDirectories: true)\n    } catch {\n        fatalError(\"Failed to create \\(destinationURL.path), error: \\(error.localizedDescription)\")\n    }\n}\n\ndo {\n    try AssetCatalogWrapper.shared.extract(collection: rends, to: destinationURL)\n    print(\"Extracted catalog to \\(destinationURL.path)\")\n} catch {\n    fatalError(\"Failed to extract (some) items, error: \\(error.localizedDescription)\")\n}\n"
  }
]