4 Commits

Author SHA1 Message Date
alexandrev-tibco 33f48edc11 Sandbox the Finder extension so macOS actually loads it
The context menu never appeared because the extension wasn't registered at all:
pluginkit didn't list it, and it says nothing about why. macOS only loads Finder
Sync extensions that are sandboxed, and the target had App Sandbox off, so the
appex was silently ignored.

It now ships with its own entitlements — sandbox plus read-only access to the
items picked in Finder, which is all it needs to read the selection and open a
kotej:// URL. The containing app stays unsandboxed, since comparing arbitrary
folders needs unrestricted file access.

Verified: pluginkit lists it as enabled and the extension is loaded by Finder.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UfhDYRLTpGJSKGP1m3LVJN
2026-07-31 13:14:15 +02:00
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