4 Commits

Author SHA1 Message Date
alexandrev-tibco f85c746af0 Rename to Kotej and add the app icon
"Cotejo" means nothing outside Spanish and is hard to pronounce elsewhere, so the
project is now Kotej: same root, but it reads and sounds the same in English and
Spanish. Renamed throughout — engine, targets, bundle ids and the URL scheme,
which is now kotej://compare.

Icon generated with Codex: two facing panels split by a seam, content lines on
each side and one amber line marking a difference — the tool's job at a glance,
still legible at 16pt.

34 tests still green; kotej:// verified end to end against the demo JARs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UfhDYRLTpGJSKGP1m3LVJN
2026-07-27 17:24:05 +02:00
alexandrev-tibco c8d0af0783 Tabs, and follow the system language (English + Spanish)
- Tabs: TabsModel owns one ComparisonModel per tab, so each keeps its own scan,
  selection and options. A comparison arriving from Finder reuses the current tab
  only while it's still empty, otherwise it opens another — an in-progress
  comparison is never overwritten. Tab strip with close buttons, plus cmd-T and
  cmd-W.
- Localization: UI strings are now English keys with en/es .strings for both the
  app and the Finder extension, so it follows the OS language. Care was needed
  where SwiftUI wouldn't localise: Text(someString) renders verbatim, so titles
  became LocalizedStringKey and file names are explicitly Text(verbatim:) — data
  must not be translated. Comparison mode labels are localised in the app because
  the engine stays UI-free and English.

Verified: both .lproj ship inside the app and the appex (40 keys each, "Empty
comparison" -> "Comparación vacía"), and the app launches under es and en.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UfhDYRLTpGJSKGP1m3LVJN
2026-07-27 15:05:37 +02:00
alexandrev-tibco 9941a71ddd SwiftUI app and Finder extension
- TextDiff: line alignment for the file view. Common prefix/suffix are matched
  first, so one change in a 20k-line file costs almost nothing; one-sided runs
  are paired into "changed" rows instead of a delete plus an insert, and very
  large differing sections fall back to positional pairing rather than hanging.
- App: empty comparison with drop wells, drag & drop or pick per side, mode
  picker (automatic per file type, or force binary/text/semantic), side-by-side
  tree with archives as folders, "only differences" filter, per-file diff and a
  totals bar. Scanning runs off the main actor.
- Finder extension: "Comparar los 2 elementos", or the two-step "Seleccionar
  para comparar" then "Comparar con …". It holds no logic — it just opens
  cotejo://compare, which the app handles.
- Engine target raised to macOS 14 for @Observable; Totals/status made public.

Verified on a real pair of JARs: reordered JSON keys and reformatted XML come out
"equivalent", genuine edits "different", and added/removed files as only-left or
only-right.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UfhDYRLTpGJSKGP1m3LVJN
2026-07-27 14:52:28 +02:00
alexandrev-tibco a8eda5c0a2 Comparison engine: ZIP-aware trees, layered diffing, semantic JSON/XML
First slice of Cotejo, a Beyond Compare-style tool for macOS. The engine has no
UI dependencies so the same code can back a CLI later.

- ZipReader: own central-directory parser (no third-party zip), inflating via the
  system Compression framework and handling ZIP64. Because the directory already
  carries每 entry's CRC32 and size, two archives can be compared entry by entry
  without decompressing anything; bytes are only inflated when an entry is opened.
- TreeScanner: folders and archives scan into one tree type, so JAR/EAR/WAR are
  browsed as directories — nested archives included (EAR -> JAR -> classes).
- DirectoryComparer: layered so large trees stay fast. Cheap checks (size, CRC)
  can prove files identical but never that they differ, since JSON/XML with other
  bytes may still be equivalent; whatever they can't settle is read, or left
  pending under a size limit and resolved on demand.
- ContentComparer: byte-for-byte, text with configurable tolerances, and semantic
  equality for JSON (key order irrelevant, array order significant, true != 1)
  and XML (attribute order and whitespace irrelevant, element order significant).

25 tests, including an EAR containing a JAR.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UfhDYRLTpGJSKGP1m3LVJN
2026-07-27 12:28:51 +02:00