diff --git a/App/Assets.xcassets/AppIcon.appiconset/Contents.json b/App/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..74b3caf --- /dev/null +++ b/App/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { "idiom" : "mac", "scale" : "1x", "size" : "16x16", "filename" : "icon_16.png" }, + { "idiom" : "mac", "scale" : "2x", "size" : "16x16", "filename" : "icon_32.png" }, + { "idiom" : "mac", "scale" : "1x", "size" : "32x32", "filename" : "icon_32.png" }, + { "idiom" : "mac", "scale" : "2x", "size" : "32x32", "filename" : "icon_64.png" }, + { "idiom" : "mac", "scale" : "1x", "size" : "128x128", "filename" : "icon_128.png" }, + { "idiom" : "mac", "scale" : "2x", "size" : "128x128", "filename" : "icon_256.png" }, + { "idiom" : "mac", "scale" : "1x", "size" : "256x256", "filename" : "icon_256.png" }, + { "idiom" : "mac", "scale" : "2x", "size" : "256x256", "filename" : "icon_512.png" }, + { "idiom" : "mac", "scale" : "1x", "size" : "512x512", "filename" : "icon_512.png" }, + { "idiom" : "mac", "scale" : "2x", "size" : "512x512", "filename" : "icon_1024.png" } + ], + "info" : { "author" : "xcode", "version" : 1 } +} diff --git a/App/Assets.xcassets/AppIcon.appiconset/icon_1024.png b/App/Assets.xcassets/AppIcon.appiconset/icon_1024.png new file mode 100644 index 0000000..1421585 Binary files /dev/null and b/App/Assets.xcassets/AppIcon.appiconset/icon_1024.png differ diff --git a/App/Assets.xcassets/AppIcon.appiconset/icon_128.png b/App/Assets.xcassets/AppIcon.appiconset/icon_128.png new file mode 100644 index 0000000..6650428 Binary files /dev/null and b/App/Assets.xcassets/AppIcon.appiconset/icon_128.png differ diff --git a/App/Assets.xcassets/AppIcon.appiconset/icon_16.png b/App/Assets.xcassets/AppIcon.appiconset/icon_16.png new file mode 100644 index 0000000..510c0bd Binary files /dev/null and b/App/Assets.xcassets/AppIcon.appiconset/icon_16.png differ diff --git a/App/Assets.xcassets/AppIcon.appiconset/icon_256.png b/App/Assets.xcassets/AppIcon.appiconset/icon_256.png new file mode 100644 index 0000000..05bcade Binary files /dev/null and b/App/Assets.xcassets/AppIcon.appiconset/icon_256.png differ diff --git a/App/Assets.xcassets/AppIcon.appiconset/icon_32.png b/App/Assets.xcassets/AppIcon.appiconset/icon_32.png new file mode 100644 index 0000000..46d0069 Binary files /dev/null and b/App/Assets.xcassets/AppIcon.appiconset/icon_32.png differ diff --git a/App/Assets.xcassets/AppIcon.appiconset/icon_512.png b/App/Assets.xcassets/AppIcon.appiconset/icon_512.png new file mode 100644 index 0000000..7ee5d5b Binary files /dev/null and b/App/Assets.xcassets/AppIcon.appiconset/icon_512.png differ diff --git a/App/Assets.xcassets/AppIcon.appiconset/icon_64.png b/App/Assets.xcassets/AppIcon.appiconset/icon_64.png new file mode 100644 index 0000000..3315151 Binary files /dev/null and b/App/Assets.xcassets/AppIcon.appiconset/icon_64.png differ diff --git a/App/Assets.xcassets/Contents.json b/App/Assets.xcassets/Contents.json new file mode 100644 index 0000000..46cba7d --- /dev/null +++ b/App/Assets.xcassets/Contents.json @@ -0,0 +1 @@ +{ "info" : { "author" : "xcode", "version" : 1 } } diff --git a/App/ComparisonModel.swift b/App/ComparisonModel.swift index 7309060..6bfff2b 100644 --- a/App/ComparisonModel.swift +++ b/App/ComparisonModel.swift @@ -1,6 +1,6 @@ import Foundation import Observation -import CotejoEngine +import KotejEngine /// Drives one comparison: what's on each side, the resulting tree, and the /// options. Scanning runs off the main actor so big trees don't freeze the UI. diff --git a/App/ContentView.swift b/App/ContentView.swift index 99e7bd8..923aaee 100644 --- a/App/ContentView.swift +++ b/App/ContentView.swift @@ -1,6 +1,6 @@ import SwiftUI import UniformTypeIdentifiers -import CotejoEngine +import KotejEngine struct ContentView: View { @Bindable var model: ComparisonModel diff --git a/App/DiffTreeView.swift b/App/DiffTreeView.swift index 6c2fe04..5cfa571 100644 --- a/App/DiffTreeView.swift +++ b/App/DiffTreeView.swift @@ -1,5 +1,5 @@ import SwiftUI -import CotejoEngine +import KotejEngine /// A row ready to render: the filter is applied up front so the outline can use a /// plain key path for its children. diff --git a/App/FileDiffView.swift b/App/FileDiffView.swift index 4069139..f58bee5 100644 --- a/App/FileDiffView.swift +++ b/App/FileDiffView.swift @@ -1,5 +1,5 @@ import SwiftUI -import CotejoEngine +import KotejEngine /// Side-by-side contents of the selected row. Text (and JSON/XML) is aligned line /// by line; binaries just report their verdict, since a hex view would be noise. diff --git a/App/Info.plist b/App/Info.plist index 42c7102..35eb2d0 100644 --- a/App/Info.plist +++ b/App/Info.plist @@ -5,7 +5,7 @@ CFBundleDevelopmentRegion en CFBundleDisplayName - Cotejo + Kotej CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier @@ -27,16 +27,16 @@ CFBundleURLName - com.alexandrev.cotejo + com.alexandrev.kotej CFBundleURLSchemes - cotejo + kotej CFBundleVersion $(CURRENT_PROJECT_VERSION) NSHumanReadableCopyright - Cotejo + Kotej diff --git a/App/CotejoApp.swift b/App/KotejApp.swift similarity index 95% rename from App/CotejoApp.swift rename to App/KotejApp.swift index 0b4018a..fce59f2 100644 --- a/App/CotejoApp.swift +++ b/App/KotejApp.swift @@ -1,15 +1,15 @@ import SwiftUI -import CotejoEngine +import KotejEngine @main -struct CotejoApp: App { +struct KotejApp: App { @State private var tabs = TabsModel() var body: some Scene { WindowGroup { RootView(tabs: tabs) .frame(minWidth: 900, minHeight: 560) - // cotejo://compare?left=…&right=… — how the Finder extension asks + // kotej://compare?left=…&right=… — how the Finder extension asks // for a comparison; it always lands in a tab. .onOpenURL { url in handle(url) } } @@ -34,7 +34,7 @@ struct CotejoApp: App { } private func handle(_ url: URL) { - guard url.scheme == "cotejo", url.host == "compare", + guard url.scheme == "kotej", url.host == "compare", let items = URLComponents(url: url, resolvingAgainstBaseURL: false)?.queryItems else { return } func path(_ name: String) -> URL? { items.first { $0.name == name }?.value.map { URL(fileURLWithPath: $0) } diff --git a/FinderExtension/FinderMenu.swift b/FinderExtension/FinderMenu.swift index 1c1c9b2..7a56bcf 100644 --- a/FinderExtension/FinderMenu.swift +++ b/FinderExtension/FinderMenu.swift @@ -1,18 +1,18 @@ import Cocoa import FinderSync -/// Adds Cotejo to Finder's contextual menu. +/// Adds Kotej to Finder's contextual menu. /// /// Two flows, matching how comparison tools are normally used: /// - select two items → "Comparar los 2 elementos" /// - select one item → "Seleccionar para comparar", then on another item /// "Comparar con " /// -/// The app is asked to do the work through its `cotejo://` URL scheme, so the +/// The app is asked to do the work through its `kotej://` URL scheme, so the /// extension stays tiny and holds no comparison logic. final class FinderMenu: FIFinderSync { /// Where the first pick is remembered between two right-clicks. - private static let pendingKey = "cotejo.pendingLeft" + private static let pendingKey = "kotej.pendingLeft" private var pendingLeft: URL? { get { @@ -44,10 +44,10 @@ final class FinderMenu: FIFinderSync { let selected = FIFinderSyncController.default().selectedItemURLs() ?? [] guard !selected.isEmpty else { return nil } - let menu = NSMenu(title: "Cotejo") + let menu = NSMenu(title: "Kotej") if selected.count >= 2 { - let item = NSMenuItem(title: String(localized: "Compare the 2 items with Cotejo"), + let item = NSMenuItem(title: String(localized: "Compare the 2 items with Kotej"), action: #selector(compareSelection(_:)), keyEquivalent: "") item.target = self menu.addItem(item) @@ -98,7 +98,7 @@ final class FinderMenu: FIFinderSync { private func open(left: URL, right: URL) { var components = URLComponents() - components.scheme = "cotejo" + components.scheme = "kotej" components.host = "compare" components.queryItems = [ URLQueryItem(name: "left", value: left.path), diff --git a/FinderExtension/Info.plist b/FinderExtension/Info.plist index 4ac5e49..2076c76 100644 --- a/FinderExtension/Info.plist +++ b/FinderExtension/Info.plist @@ -5,7 +5,7 @@ CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleDisplayName - Cotejo + Kotej CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier @@ -25,7 +25,7 @@ NSExtensionPointIdentifier com.apple.FinderSync NSExtensionPrincipalClass - CotejoFinder.FinderMenu + KotejFinder.FinderMenu diff --git a/FinderExtension/Resources/en.lproj/Localizable.strings b/FinderExtension/Resources/en.lproj/Localizable.strings index bbadbd7..bd891f3 100644 --- a/FinderExtension/Resources/en.lproj/Localizable.strings +++ b/FinderExtension/Resources/en.lproj/Localizable.strings @@ -1,4 +1,4 @@ -"Compare the 2 items with Cotejo" = "Compare the 2 items with Cotejo"; +"Compare the 2 items with Kotej" = "Compare the 2 items with Kotej"; "Select for comparison" = "Select for comparison"; "Compare with “%@”" = "Compare with “%@”"; "Forget “%@”" = "Forget “%@”"; diff --git a/FinderExtension/Resources/es.lproj/Localizable.strings b/FinderExtension/Resources/es.lproj/Localizable.strings index 29f1750..8f8b0c6 100644 --- a/FinderExtension/Resources/es.lproj/Localizable.strings +++ b/FinderExtension/Resources/es.lproj/Localizable.strings @@ -1,4 +1,4 @@ -"Compare the 2 items with Cotejo" = "Comparar los 2 elementos con Cotejo"; +"Compare the 2 items with Kotej" = "Comparar los 2 elementos con Kotej"; "Select for comparison" = "Seleccionar para comparar"; "Compare with “%@”" = "Comparar con «%@»"; "Forget “%@”" = "Olvidar «%@»"; diff --git a/Package.swift b/Package.swift index c02c24a..f652c59 100644 --- a/Package.swift +++ b/Package.swift @@ -2,14 +2,14 @@ import PackageDescription let package = Package( - name: "Cotejo", + name: "Kotej", platforms: [.macOS(.v14)], products: [ // The engine has no UI dependencies so it can also back a CLI later. - .library(name: "CotejoEngine", targets: ["CotejoEngine"]), + .library(name: "KotejEngine", targets: ["KotejEngine"]), ], targets: [ - .target(name: "CotejoEngine"), - .testTarget(name: "CotejoEngineTests", dependencies: ["CotejoEngine"]), + .target(name: "KotejEngine"), + .testTarget(name: "KotejEngineTests", dependencies: ["KotejEngine"]), ] ) diff --git a/README.md b/README.md index b511ccf..521fed4 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Cotejo +# Kotej Comparador de ficheros, carpetas y **artefactos** (ZIP/JAR/EAR/WAR) para macOS. Ligero, sin dependencias externas y con el motor aislado del interfaz. @@ -28,13 +28,13 @@ Por defecto cada fichero usa el modo que le corresponde según su tipo ## Integración con Finder Con la app instalada, activa la extensión en **Ajustes del Sistema → General → -Elementos de inicio y extensiones → Extensiones de Finder → Cotejo**. Después, +Elementos de inicio y extensiones → Extensiones de Finder → Kotej**. Después, en el menú contextual del Finder: -- Selecciona **dos** elementos → *Comparar los 2 elementos con Cotejo* +- Selecciona **dos** elementos → *Comparar los 2 elementos con Kotej* - O selecciona uno → *Seleccionar para comparar*, y en otro → *Comparar con “…”* -También responde a `cotejo://compare?left=/ruta/a&right=/ruta/b`, que abre la +También responde a `kotej://compare?left=/ruta/a&right=/ruta/b`, que abre la comparación en una pestaña. ## Pestañas @@ -46,7 +46,7 @@ comparando. ## Estado -- ✅ **Motor** (`CotejoEngine`): lector ZIP propio, escaneo de árboles, archivos +- ✅ **Motor** (`KotejEngine`): lector ZIP propio, escaneo de árboles, archivos anidados, comparación por niveles, comparadores binario/texto/JSON/XML y diff de líneas. - ✅ **App** SwiftUI: **pestañas** (varias comparaciones en una ventana), diff --git a/Sources/CotejoEngine/ContentComparer.swift b/Sources/KotejEngine/ContentComparer.swift similarity index 100% rename from Sources/CotejoEngine/ContentComparer.swift rename to Sources/KotejEngine/ContentComparer.swift diff --git a/Sources/CotejoEngine/DirectoryComparer.swift b/Sources/KotejEngine/DirectoryComparer.swift similarity index 100% rename from Sources/CotejoEngine/DirectoryComparer.swift rename to Sources/KotejEngine/DirectoryComparer.swift diff --git a/Sources/CotejoEngine/TextDiff.swift b/Sources/KotejEngine/TextDiff.swift similarity index 100% rename from Sources/CotejoEngine/TextDiff.swift rename to Sources/KotejEngine/TextDiff.swift diff --git a/Sources/CotejoEngine/Tree.swift b/Sources/KotejEngine/Tree.swift similarity index 99% rename from Sources/CotejoEngine/Tree.swift rename to Sources/KotejEngine/Tree.swift index c555994..50aefef 100644 --- a/Sources/CotejoEngine/Tree.swift +++ b/Sources/KotejEngine/Tree.swift @@ -172,7 +172,7 @@ public enum TreeScanner { private static func extractNested(reader: ZipReader, entry: ZipReader.Entry, name: String) throws -> URL { let directory = URL(fileURLWithPath: NSTemporaryDirectory()) - .appendingPathComponent("cotejo-nested-\(UUID().uuidString)") + .appendingPathComponent("kotej-nested-\(UUID().uuidString)") try FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true) let url = directory.appendingPathComponent(name) try reader.data(for: entry).write(to: url) diff --git a/Sources/CotejoEngine/ZipReader.swift b/Sources/KotejEngine/ZipReader.swift similarity index 100% rename from Sources/CotejoEngine/ZipReader.swift rename to Sources/KotejEngine/ZipReader.swift diff --git a/Tests/CotejoEngineTests/ContentComparerTests.swift b/Tests/KotejEngineTests/ContentComparerTests.swift similarity index 99% rename from Tests/CotejoEngineTests/ContentComparerTests.swift rename to Tests/KotejEngineTests/ContentComparerTests.swift index 4c59ca7..9a44546 100644 --- a/Tests/CotejoEngineTests/ContentComparerTests.swift +++ b/Tests/KotejEngineTests/ContentComparerTests.swift @@ -1,5 +1,5 @@ import XCTest -@testable import CotejoEngine +@testable import KotejEngine final class ContentComparerTests: XCTestCase { private func data(_ text: String) -> Data { Data(text.utf8) } diff --git a/Tests/CotejoEngineTests/DirectoryComparerTests.swift b/Tests/KotejEngineTests/DirectoryComparerTests.swift similarity index 98% rename from Tests/CotejoEngineTests/DirectoryComparerTests.swift rename to Tests/KotejEngineTests/DirectoryComparerTests.swift index 124ccb3..b94e9c6 100644 --- a/Tests/CotejoEngineTests/DirectoryComparerTests.swift +++ b/Tests/KotejEngineTests/DirectoryComparerTests.swift @@ -1,12 +1,12 @@ import XCTest -@testable import CotejoEngine +@testable import KotejEngine final class DirectoryComparerTests: XCTestCase { private var scratch: URL! override func setUpWithError() throws { scratch = URL(fileURLWithPath: NSTemporaryDirectory()) - .appendingPathComponent("cotejo-\(UUID().uuidString)") + .appendingPathComponent("kotej-\(UUID().uuidString)") try FileManager.default.createDirectory(at: scratch, withIntermediateDirectories: true) } diff --git a/Tests/CotejoEngineTests/TextDiffTests.swift b/Tests/KotejEngineTests/TextDiffTests.swift similarity index 99% rename from Tests/CotejoEngineTests/TextDiffTests.swift rename to Tests/KotejEngineTests/TextDiffTests.swift index 8860493..33827d7 100644 --- a/Tests/CotejoEngineTests/TextDiffTests.swift +++ b/Tests/KotejEngineTests/TextDiffTests.swift @@ -1,5 +1,5 @@ import XCTest -@testable import CotejoEngine +@testable import KotejEngine final class TextDiffTests: XCTestCase { func testIdenticalTextIsAllEqual() { diff --git a/Tests/CotejoEngineTests/ZipReaderTests.swift b/Tests/KotejEngineTests/ZipReaderTests.swift similarity index 96% rename from Tests/CotejoEngineTests/ZipReaderTests.swift rename to Tests/KotejEngineTests/ZipReaderTests.swift index 678339d..11297a4 100644 --- a/Tests/CotejoEngineTests/ZipReaderTests.swift +++ b/Tests/KotejEngineTests/ZipReaderTests.swift @@ -1,12 +1,12 @@ import XCTest -@testable import CotejoEngine +@testable import KotejEngine final class ZipReaderTests: XCTestCase { /// Builds a real archive with the system `zip`, so the reader is tested /// against the format as produced in the wild rather than a hand-made blob. private func makeArchive(_ files: [String: String], name: String = "test.zip") throws -> URL { let root = URL(fileURLWithPath: NSTemporaryDirectory()) - .appendingPathComponent("cotejo-\(UUID().uuidString)") + .appendingPathComponent("kotej-\(UUID().uuidString)") let content = root.appendingPathComponent("content") try FileManager.default.createDirectory(at: content, withIntermediateDirectories: true) @@ -84,7 +84,7 @@ final class ZipReaderTests: XCTestCase { func testRejectsNonZip() throws { let url = URL(fileURLWithPath: NSTemporaryDirectory()) - .appendingPathComponent("cotejo-\(UUID().uuidString).zip") + .appendingPathComponent("kotej-\(UUID().uuidString).zip") try Data("definitely not a zip".utf8).write(to: url) XCTAssertThrowsError(try ZipReader(url: url)) } diff --git a/icon-1024.png b/icon-1024.png new file mode 100644 index 0000000..1421585 Binary files /dev/null and b/icon-1024.png differ diff --git a/project.yml b/project.yml index d7d09dc..6f70557 100644 --- a/project.yml +++ b/project.yml @@ -1,4 +1,4 @@ -name: Cotejo +name: Kotej options: bundleIdPrefix: com.alexandrev createIntermediateGroups: true @@ -13,41 +13,43 @@ settings: DEVELOPMENT_TEAM: "2825Q76T7H" CODE_SIGN_STYLE: Automatic packages: - CotejoEngine: + KotejEngine: path: . targets: - Cotejo: + Kotej: type: application platform: macOS deploymentTarget: "14.0" sources: - App + - path: App/Assets.xcassets + buildPhase: resources - path: Resources buildPhase: resources dependencies: - - package: CotejoEngine - product: CotejoEngine - - target: CotejoFinder + - package: KotejEngine + product: KotejEngine + - target: KotejFinder settings: base: - PRODUCT_BUNDLE_IDENTIFIER: com.alexandrev.cotejo + PRODUCT_BUNDLE_IDENTIFIER: com.alexandrev.kotej ASSETCATALOG_COMPILER_APPICON_NAME: AppIcon ENABLE_APP_SANDBOX: NO ENABLE_HARDENED_RUNTIME: YES info: path: App/Info.plist properties: - CFBundleDisplayName: Cotejo + CFBundleDisplayName: Kotej CFBundleShortVersionString: "$(MARKETING_VERSION)" CFBundleVersion: "$(CURRENT_PROJECT_VERSION)" CFBundleDevelopmentRegion: en CFBundleLocalizations: [en, es] - NSHumanReadableCopyright: "Cotejo" + NSHumanReadableCopyright: "Kotej" CFBundleURLTypes: - - CFBundleURLName: com.alexandrev.cotejo - CFBundleURLSchemes: [cotejo] + - CFBundleURLName: com.alexandrev.kotej + CFBundleURLSchemes: [kotej] - CotejoFinder: + KotejFinder: type: app-extension platform: macOS deploymentTarget: "14.0" @@ -58,15 +60,15 @@ targets: buildPhase: resources settings: base: - PRODUCT_BUNDLE_IDENTIFIER: com.alexandrev.cotejo.finder + PRODUCT_BUNDLE_IDENTIFIER: com.alexandrev.kotej.finder ENABLE_APP_SANDBOX: NO ENABLE_HARDENED_RUNTIME: YES info: path: FinderExtension/Info.plist properties: - CFBundleDisplayName: Cotejo + CFBundleDisplayName: Kotej CFBundleShortVersionString: "$(MARKETING_VERSION)" CFBundleVersion: "$(CURRENT_PROJECT_VERSION)" NSExtension: NSExtensionPointIdentifier: com.apple.FinderSync - NSExtensionPrincipalClass: CotejoFinder.FinderMenu + NSExtensionPrincipalClass: KotejFinder.FinderMenu