Simple App to help @jaspermayone make it through COMP1050 with a professor who won't use version control.
1# CLAUDE.md
2
3This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
5## Project Overview
6
7ZipMerge is a macOS SwiftUI application that compares a local project directory with a teacher's submitted zip file. It's designed to help students merge changes from assignments when version control isn't used by instructors.
8
9## Building and Running
10
11```bash
12# Build the project
13xcodebuild -project ZipMerge.xcodeproj -scheme ZipMerge build
14
15# Run the app (after building in Xcode)
16open ZipMerge.xcodeproj
17# Then press Cmd+R in Xcode to run
18```
19
20The project uses Swift 5.0 and requires macOS with SwiftUI support.
21
22## Code Signing and Installation
23
24The project is configured with:
25- **Bundle Identifier**: com.singlefeather.ZipMerge
26- **Development Team**: M67B42LX8D
27- **Code Sign Style**: Automatic
28- **Code Sign Identity**: Currently ad-hoc (for local development)
29
30### Building for Local Development
31
32```bash
33# Build the project
34xcodebuild -project ZipMerge.xcodeproj -scheme ZipMerge -configuration Release clean build
35
36# The built app will be in:
37# ./build/Release/ZipMerge.app
38
39# Copy to Applications folder (optional)
40cp -r build/Release/ZipMerge.app /Applications/
41```
42
43### Release Process
44
45**Use the local release script for simplicity:**
46
47```bash
48./release.sh v1.0.0
49```
50
51This automated script handles everything:
521. Builds with Developer ID signing
532. Notarizes with Apple
543. Creates GitHub release with signed .zip
554. Calculates SHA256 hash
565. Updates Homebrew cask automatically
576. Commits and pushes cask update
58
59**Prerequisites:**
60- `gh` CLI installed and authenticated
61- Developer ID Application certificate in Keychain
62- Apple ID app-specific password (prompted during script)
63
64The script is fully automated and requires no manual steps. See RELEASING.md for details.
65
66### Distributing via Homebrew
67
68The Homebrew cask is located at `/Users/jsp/dev/projects/homebrew-tap/Casks/zipmerge.rb`.
69
70After each release, update the SHA256 hash in the cask file with the value from the release notes.
71
72**Users can install via:**
73```bash
74brew tap jaspermayone/tap
75brew install --cask zipmerge
76```
77
78### Manual Distribution (Without CI)
79
80If you need to build manually:
81
82```bash
83# Build with Developer ID signing
84xcodebuild -project ZipMerge.xcodeproj -scheme ZipMerge -configuration Release \
85 CODE_SIGN_IDENTITY="Developer ID Application" clean build
86
87# Notarize
88xcrun notarytool submit build/Release/ZipMerge.app.zip \
89 --apple-id your-email@example.com \
90 --team-id M67B42LX8D \
91 --password app-specific-password \
92 --wait
93
94# Staple and package
95xcrun stapler staple build/Release/ZipMerge.app
96cd build/Release && zip -r ZipMerge.zip ZipMerge.app
97```
98
99## Architecture
100
101### Core Components
102
103**Models.swift** - Defines the data model:
104- `FileChangeType`: Enum for tracking file states (added, modified, deleted, unchanged)
105- `MergeDecision`: User decision per file (keepMine, takeTheirs, pending)
106- `ComparedFile`: Represents a single file comparison with diff hunks
107- `DiffHunk` and `DiffLine`: Structures for granular diff display (not yet fully implemented)
108- `ComparisonResult`: Contains all compared files and directories
109
110**FileComparer.swift** - File operations and comparison logic:
111- `extractZip()`: Uses system `unzip` command to extract archives
112- `findRootDirectory()`: Handles zips with wrapper folders
113- `compare()`: Main comparison engine that walks both directory trees and categorizes changes
114- `applyChanges()`: Applies user merge decisions to the local directory
115- `computeHunks()`: Placeholder for future hunk-level merging (currently returns empty array)
116
117**ContentView.swift** - Main UI orchestration:
118- Left panel: Directory picker, zip drop zone, and file list
119- Right panel: Diff view for selected file
120- State management for comparison results and merge decisions
121- Git integration: Detects git repos and offers commit creation after merge
122- File cleanup: Removes temp directories and zip files after successful merge
123
124**DiffView.swift** - File diff visualization:
125- Side-by-side view for modified files
126- Single pane for added/deleted files with color-coded backgrounds
127- Line-by-line display with monospaced font
128
129### Data Flow
130
1311. User selects project directory and drops teacher's zip file
1322. `FileComparer.extractZip()` extracts to temp directory
1333. `FileComparer.compare()` walks both trees and generates `ComparisonResult`
1344. UI displays changed files with color-coded icons
1355. User reviews diffs and makes keepMine/takeTheirs decisions per file
1366. `FileComparer.applyChanges()` applies decisions
1377. If git repo detected, optionally creates commit
1388. Cleanup removes temp files and zip
139
140### Important Implementation Details
141
142- Zip extraction uses system `/usr/bin/unzip` command via Process
143- Comparison is byte-level for binaries, line-level diffs for text (when viewing)
144- Temp directories are created in system temp folder with UUID names
145- Git commits use `/usr/bin/git` directly (not libgit2 or similar)
146- All file operations happen on background queue, UI updates on main thread
147
148## Known Limitations
149
150- Hunk-level merging (`computeHunks()` and `applySelectedHunks()`) is stubbed but not implemented
151- Currently only supports whole-file merge decisions
152- No conflict resolution UI for line-level merging
153- Binary files show as "(binary or unreadable)" in diff view