The TidyFlow project implements a cross-platform Git layer with a shared protocol model (TidyFlowShared) and platform-specific implementations (macOS AppKit, iOS UIKit). The architecture follows a Redux-like state management pattern using GitWorkspaceStateDriver for shared state mutations and separate platforms for UI rendering and effect handling.
Located in: app/TidyFlowShared/Protocol/GitProtocolModels.swift
-
GitDiffResult: Server response for git diff requests
- Fields:
project,workspace,path,code(status),format,text,isBinary,truncated,mode - Used for: working tree and staged diffs
- Fields:
-
DiffCache: Client-side caching wrapper
- Fields:
text,parsedLines: [DiffLine],isLoading,error,isBinary,truncated,code,updatedAt - Expiry: 30 seconds
- Parsed asynchronously on background thread to avoid UI blocking
- Fields:
-
DiffLine: Single parsed line from unified diff
- Kinds:
.header,.hunk,.context,.add,.del - Fields:
id,kind,oldLineNumber?,newLineNumber?,text,isNavigable
- Kinds:
-
DiffViewMode:
unified | split -
SplitBuilder: Converts unified diff lines to split view rows
- Max lines for split: 5000 lines
-
DiffDescriptor: Cross-platform diff cache key
- Key:
"project:workspace:path:mode" - Ensures macOS/iOS use identical keying scheme
- Key:
-
GitStatusItem: Individual file status
- Fields:
id: String,path,status(e.g., "M", "A", "D", "??"),additions,deletions,isStaged
- Fields:
-
GitStatusResult: Server response for git status
- Fields:
items,error?,isGitRepo,hasStagedChanges,stagedCount,currentBranch?,defaultBranch?,aheadBy?,behindBy?,comparedBranch?
- Fields:
-
GitStatusCache: Client cache with expiry (60 seconds)
-
GitPanelSemanticSnapshot: Unified semantic snapshot for both platforms
- Classifies files into:
stagedItems,trackedUnstagedItems,untrackedItems - Derived properties:
hasStagedChanges,hasUntrackedChanges, etc.
- Classifies files into:
-
GitBranchItem: Single branch metadata
-
GitBranchesResult & GitBranchCache: Branch list management
-
GitLogEntry: Single commit in history
- Fields:
id,sha,message,author,date,relativeDate,refs: [String] - Max entries: 50 (default limit)
- Fields:
-
GitLogResult & GitLogCache: Commit history caching
- Includes "HEAD" detection for current branch highlighting
-
GitShowResult: Single commit details (file changes)
- Fields:
sha,message,author,files: [GitShowFileEntry]
- Fields:
-
GitShowFileEntry: File changed in a commit
- Fields:
path,status(M/A/D/R/C),additions,deletions
- Fields:
-
GitOpState enum:
normal | rebasing | merging -
GitRebaseResult: Rebase operation result with conflict detection
-
GitOpStatusResult: Operation status with conflict files
-
GitOpStatusCache: Client cache for operation state
-
IntegrationState enum:
idle | conflict | rebasing | merging | rebaseConflict -
GitMergeToDefaultResult: Merge to default branch result
-
GitIntegrationStatusResult: Integration worktree status
-
GitIntegrationStatusCache: Project-level integration state
-
ConflictFileEntry: Single conflict file
- Fields:
path,conflictType(content|add_add|delete_modify|modify_delete),staged(resolved?)
- Fields:
-
ConflictSnapshot: Complete conflict context
- Fields:
context(workspace|integration),files: [ConflictFileEntry],allResolved
- Fields:
-
GitConflictDetailResult: Four-way conflict diff
- Fields:
baseContent?,oursContent?,theirsContent?,currentContent,conflictMarkersCount,isBinary - Used by both macOS GitConflictWizardView and iOS GitConflictWizardSheet
- Fields:
-
GitConflictDetailResultCache: Cache wrapper for conflict details
-
ConflictWizardCache: Combined state for conflict wizard
- Fields:
snapshot?,selectedFilePath?,currentDetail?,isLoading,updatedAt - macOS caches by
"project:workspace"or"project:integration" - iOS uses same keying scheme
- Fields:
- GitStashEntry: Stash metadata
- GitStashFileEntry: File in stash with stats
- GitStashListResult & GitStashListCache: Stash list management
- GitStashShowResult & GitStashShowCache: Stash diff details
- GitStashOpResult: Apply/pop/drop operation results
Located in: app/TidyFlowShared/Presentation/GitWorkspaceStateDriver.swift
Pure functional state machine for Git workspace state:
- No platform dependencies (no SwiftUI/AppKit/UIKit)
- No network dependencies (no WSClient references)
- All state mutations described as effects instead of direct network calls
- Completely Sendable for thread-safe concurrency
GitWorkspaceContext: Identifies workspace scope
public struct GitWorkspaceContext: Equatable, Hashable, Sendable {
public let projectName: String
public let workspaceName: String
public let globalKey: String // "project:workspace"
}GitWorkspaceInput: All possible state mutations
- User intents:
refreshStatus,stage,unstage,discard,commit,switchBranch,createBranch - Server results:
gitStatusResult,gitBranchesResult,gitOpResult,gitCommitResult - Notifications:
gitStatusChanged - Environment:
connectionChanged
GitWorkspaceEffect: Describes side effects (network requests)
requestStatus,requestBranches,requestStage,requestUnstage,requestDiscardrequestCommit,requestSwitchBranch,requestCreateBranch- Used by platform layer to translate to WSClient calls
GitWorkspaceState: Single workspace Git state
statusCache: Current file status fromgit statusbranchCache: Available branches fromgit branchopsInFlight: Set of stage/unstage/discard operations in progressbranchSwitchInFlight?: Branch name if switch in progressbranchCreateInFlight?: Branch name if create in progresscommitInFlight: Whether commit is in progresscommitMessage: Current commit message (UI-managed)commitResult?: Last commit result messagehasResolvedStatus: Whether status fetched at least once
GitWorkspaceStateDriver.reduce(): Pure function
public static func reduce(
state: GitWorkspaceState,
input: GitWorkspaceInput,
context: GitWorkspaceContext
) -> (GitWorkspaceState, [GitWorkspaceEffect])- State-only on success: Operations only remove from
opsInFlighton success - Auto-refresh: Successful stage/unstage/discard triggers status refresh
- Branch switches trigger both:
requestStatus+requestBrancheson success - Canary checks:
canCommit,canSwitchBranch,canCreateBranchcomputed properties prevent invalid state - Disconnection handling: Clears all in-flight operations, preserves last snapshot
- No message clearing on error: Keep error messages visible
Located in: app/TidyFlowShared/Networking/WSClient+Send.swift
requestGitDiff(project, workspace, path, mode, cacheMode)
requestGitStatus(project, workspace, cacheMode)
requestGitLog(project, workspace, limit, cacheMode)
requestGitShow(project, workspace, sha, cacheMode)
requestGitBranches(project, workspace, cacheMode)
requestGitOpStatus(project, workspace, cacheMode)
requestGitIntegrationStatus(project, cacheMode)
requestGitCheckBranchUpToDate(project, workspace, cacheMode)// Stage/Unstage
requestGitStage(project, workspace, path?, scope)
requestGitUnstage(project, workspace, path?, scope)
requestGitDiscard(project, workspace, path?, scope, includeUntracked?)
// Branches
requestGitSwitchBranch(project, workspace, branch)
requestGitCreateBranch(project, workspace, branch)
// Commit
requestGitCommit(project, workspace, message)
requestEvoAutoCommit(project, workspace)
requestGitAIMerge(project, workspace, aiAgent?, defaultBranch?)
// Rebase
requestGitFetch(project, workspace)
requestGitRebase(project, workspace, ontoBranch)
requestGitRebaseContinue(project, workspace)
requestGitRebaseAbort(project, workspace)
// Merge to default (integration)
requestGitMergeToDefault(project, workspace, defaultBranch)
requestGitMergeContinue(project)
requestGitMergeAbort(project)
// Rebase onto default (integration)
requestGitRebaseOntoDefault(project, workspace, defaultBranch)
requestGitRebaseOntoDefaultContinue(project)
requestGitRebaseOntoDefaultAbort(project)
requestGitResetIntegrationWorktree(project)
// Conflict resolution
requestGitConflictDetail(project, workspace, path, context, cacheMode)
requestGitConflictAcceptOurs(project, workspace, path, context)
requestGitConflictAcceptTheirs(project, workspace, path, context)
requestGitConflictAcceptBoth(project, workspace, path, context)
requestGitConflictMarkResolved(project, workspace, path, context)
// Stash
requestGitStashList(project, workspace, cacheMode)
requestGitStashShow(project, workspace, stashId, cacheMode)
requestGitStashSave(project, workspace, message?, includeUntracked?, keepIndex?, paths?)
requestGitStashApply(project, workspace, stashId)
requestGitStashPop(project, workspace, stashId)
requestGitStashDrop(project, workspace, stashId)
requestGitStashRestorePaths(project, workspace, stashId, paths)GitStageRequest // action: "git_stage"
GitUnstageRequest // action: "git_unstage"
GitDiscardRequest // action: "git_discard"
GitBranchRequest // action: "git_switch_branch" or "git_create_branch"
GitCommitRequest // action: "git_commit"
GitAIMergeRequest // action: "git_ai_merge"
GitOntoBranchRequest // action: "git_rebase" or "git_rebase_onto_default"
GitDefaultBranchRequest // action: "git_merge_to_default" or "git_rebase_onto_default"
GitStashSaveRequest // action: "git_stash_save"
GitStashIdRequest // action: "git_stash_apply" etc.
ProjectWorkspacePathContextTypedWSRequest // action: conflict resolution opsLocated in: app/TidyFlow/Views/GitCacheState+Operations.swift (650 lines+)
Platform-specific cache management for Git operations on macOS with diff/log/show support.
gitStatusCache[key: String] // By workspace
gitBranchesCache[key: String] // By workspace
gitLogCache[key: String] // By workspace
gitShowCache[key: String] // By SHA
diffCache[key: String] // By "project:workspace:path:mode"
gitOpStatusCache[key: String] // By workspace
gitIntegrationStatusCache[key: String] // By project
conflictWizardCache[key: String] // By "project:workspace" or "project:integration"
stashListCache[key: String] // By "project:workspace"
stashShowCache[key: String] // By "project:workspace:stash:stashId"
Diff Management
func handleGitDiffResult(_ result: GitDiffResult)
// Parses diff on background thread (userInitiated)
// Updates cache + triggers UI refresh
func fetchGitDiff(workspaceKey, path, mode, cacheMode)
// Validates connection, sets loading state, sends request
func isFileDeleted(workspaceKey, path, mode) -> Bool
// Helper: checks if file status code starts with "D"Status & Branches
func handleGitStatusResult(_ result: GitStatusResult)
// Drives applyGitInput(.gitStatusResult)
func fetchGitStatus(workspaceKey, cacheMode)
// Triggers shared driver via applyGitInput(.refreshStatus)
func getGitStatusIndex(workspaceKey) -> GitStatusIndex
// Derived index with counts (cached)History & Details
func handleGitLogResult(_ result: GitLogResult)
// Stores commit list, cache expires in 120 seconds
func fetchGitLog(workspaceKey, limit=50, cacheMode)
// Loads recent commits
func handleGitShowResult(_ result: GitShowResult)
// Caches single commit details (no expiry once fetched)
func fetchGitShow(workspaceKey, sha, cacheMode)
// Loads diff for specific commitRebase Operations
func handleGitRebaseResult(_ result: GitRebaseResult)
// Updates opStatus cache, syncs conflictWizardCache on conflict
func gitRebase(workspaceKey, ontoBranch)
// Sets rebaseInFlight flag, sends requestMerge to Default (Integration)
func handleGitMergeToDefaultResult(_ result: GitMergeToDefaultResult)
// Sets integrationStatusCache state, syncs conflict wizard
func gitMergeToDefault(workspaceKey, defaultBranch)
// Project-level merge operationConflict Wizard
func handleGitConflictDetailResult(_ result: GitConflictDetailResult)
// Stores current conflict file details + content
func handleGitConflictActionResult(_ result: GitConflictActionResult)
// Updates snapshot after accept/mark-resolved action
func fetchConflictDetail(project, workspace, path, context)
// Loads four-way diff for conflict file
func conflictAcceptOurs/Theirs/Both(project, workspace, path, context)
// Sends conflict resolution requests
func conflictMarkResolved(project, workspace, path, context)
// Marks file as resolved- Shared driver state (
workspaceGitState[key]): Pure, effects-based - macOS-specific caches:
- Diff cache (parsed + unparsed)
- Log/Show cache
- Op status cache
- Conflict wizard cache (shared with iOS)
- Stash caches
The shared driver focuses on status/branch/staging, while macOS maintains deep caches for diff/history/conflicts.
Located in: app/TidyFlow/Views/GitConflictWizardView.swift (546 lines)
- Replaces normal Git panel when conflicts detected
- Two-pane layout: file list (left) + detail (right)
- Header shows progress (e.g., "1 of 5 resolved")
- Footer with Abort/Continue buttons
- Header: Orange warning banner with conflict count + refresh button
- File List: Left sidebar showing all conflict files
- Icons: ✓ (resolved) |
⚠️ (unresolved) - Badges: AA (add-add), DM (delete-modify), MD (modify-delete), UU (default)
- Icons: ✓ (resolved) |
- Detail Panel: Right side with four tabs
- Tabs: Current | Ours | Theirs | Base
- Buttons: Accept Ours/Theirs/Both | Mark Resolved | Open Editor
- All Resolved: Placeholder view with checkmark when done
private var wizard: ConflictWizardCache
private var conflictFiles: [ConflictFileEntry]
private var resolvedCount: Int // staged files
private var totalCount: Int
private var canContinue: Bool // resolvedCount == totalCount
private var isContinueInFlight: BoolselectFile(_ file) // Load conflict detail
acceptOurs/Theirs/Both() // Resolve using strategy
markResolved() // Manual mark resolved
continueOperation() // git rebase/merge continue
abortOperation() // git rebase/merge abort
refreshWizard() // Re-fetch status- Detects context: "workspace" (rebase) vs "integration" (merge-to-default)
- Adapts continue/abort semantics based on context
- Syncs with opStatus cache for merge/rebase state
Located: app/TidyFlow/Views/GitGraphViews.swift (323 lines)
-
GitGraphSection: Collapsible history section
- Shows recent commits (50 limit)
- Expandable/collapsible
- Auto-loads commit details on expand
-
GitLogRow: Single commit line
- Chevron (expand/collapse)
- Circle dot (head marker for current)
- Message (one line)
- Relative date
- Hover triggers floating panel after 2 seconds
- Expandable to show file list
-
CommitFilesView: Nested file list under expanded commit
- Status indicators (M/A/D/R/C)
- File icons
- Directory paths
-
CommitDetailPanelManager: Floating panel on hover
- Shows SHA (copyable), message, author, refs
- Colored badges for branch/tag/HEAD
- Non-activating NSPanel (doesn't steal focus)
- 2-second hover delay before show
- 250ms delay before hide
Located: app/TidyFlow/Views/NativeGitPanelView.swift (574 lines)
- Top Fixed: Panel header + commit input box
- Middle Scrollable:
- Staged changes section (collapsible)
- Unstaged changes section (collapsible)
- Stash section (collapsible)
- Bottom Fixed: History/Graph section (collapsible)
- Normal mode: Shows staged/unstaged/history
- Conflict mode: Switches to GitConflictWizardView automatically
- Restores normal panel after conflicts resolved
- Appears after staged changes
- Shows loading state while analyzing
- Links to review session
Located in: app/TidyFlow-iOS/MobileAppState.swift (350+ lines for Git)
iOS-specific state management using shared driver but with mobile-tailored caching.
- No deep diff parsing: iOS caches entire diff text
- Flat cache structure: Fewer nested caches
- Shared conflict wizard state: Uses same cache keys as macOS
- Simplified branch handling: No local branch create UI
- Task system integration: Maps Git operations to task lifecycle
@Published var workspaceGitState: [String: GitWorkspaceState]
// Shared driver state (same as macOS)
@Published var workspaceGitDetailState: [String: MobileWorkspaceGitDetailState]
// Mobile-specific: branches, staged, unstaged items
@Published var workspaceDiffCache: [String: DiffCache]
// Simple diff cache by "project:workspace:path:mode"
@Published var conflictWizardCache: [String: ConflictWizardCache]
// Shared with macOS!
@Published var stashListCache: [String: GitStashListCache]
// Stash support (v1.50+)struct MobileWorkspaceGitDetailState {
var currentBranch: String?
var defaultBranch: String?
var branches: [GitBranchItem]
var stagedItems: [GitStatusItem]
var unstagedItems: [GitStatusItem]
var isGitRepo: Bool
var aheadBy: Int?
var behindBy: Int?
var isCommitting: Bool
var commitResult: String?
// Produces GitPanelSemanticSnapshot (macOS-compatible)
var semanticSnapshot: GitPanelSemanticSnapshot
}Located in: app/TidyFlow-iOS/MobileAppState+HandlerMethods.swift (945 lines)
func handleGitStatusResult(_ result: GitStatusResult)
// Routes to applyGitInput(.gitStatusResult)
func handleGitBranchesResult(_ result: GitBranchesResult)
// Routes to applyGitInput(.gitBranchesResult)
func handleGitCommitResult(_ result: GitCommitResult)
// Routes to applyGitInput(.gitCommitResult)
func handleGitOpResult(_ result: GitOpResult)
// Routes to applyGitInput(.gitOpResult)
func handleGitAIMergeResult(_ result: GitAIMergeResult)
// Creates/updates task with status/message
func handleGitMergeToDefaultResult(_ result: GitMergeToDefaultResult)
// Updates task, syncs conflictWizardCache
func handleGitStatusChanged(_ notification: GitStatusChangedNotification)
// Routes to applyGitInput(.gitStatusChanged)func handleGitConflictDetailResult(_ result: GitConflictDetailResult)
// Updates conflictWizardCache[key].currentDetail
// key = "project:integration" or "project:workspace"
func handleGitConflictActionResult(_ result: GitConflictActionResult)
// Updates snapshot after resolution action
func fetchGitDetailForWorkspace(project, workspace)
// Loads full Git state for workspacefunc handleGitStashListResult(_ result: GitStashListResult)
// Updates stashListCache + auto-selects first
func handleGitStashShowResult(_ result: GitStashShowResult)
// Stores stash diff details
func handleGitStashOpResult(_ result: GitStashOpResult)
// Updates stashOpInFlight, handles conflicts, refreshes statusUnlike macOS which uses GitCacheState as intermediary:
appState.wsClient.requestGitStage(project, workspace, path, scope)
appState.wsClient.requestGitUnstage(project, workspace, path, scope)
appState.wsClient.requestGitConflictAcceptOurs(project, workspace, path, context)Located: app/TidyFlow-iOS/Views/GitConflictWizardSheet.swift (418 lines)
- Full-screen sheet (not sidebar)
- Navigation stack for detail navigation
- List for conflict file browsing
- Segmented picker for tab selection
- Bottom safeAreaInset for action buttons
- NavigationStack wrapper
- List of conflict files (NavigationLinks to detail)
- ConflictFileDetailView: Nested detail page
- Horizontal scroll of action buttons
- Segmented picker for tabs
- ScrollView for content
- Bottom action bar (Abort | Continue)
- No floating panels
- Horizontal scroll for buttons (mobile-friendly)
- Segmented picker instead of tabs
- Navigation-based instead of split view
- Full-screen modal instead of sidebar replacement
conflictActionButton(label, icon, color)
// Wrapped in horizontal scroll for small screens
acceptOurs/Theirs/Both()
markResolved()
// Direct WS calls via appState.wsClientLocated: app/TidyFlow-iOS/Views/WorkspaceGitView.swift (996 lines)
- Conflict banner (if active)
- Branch section (picker)
- Stash section (if has stashes)
- Staged section
- Tracked unstaged section
- Untracked section
- Commit input
- Branch picker sheet (MobileBranchListSheet)
- Stash detail sheet (showStashDetail)
- Stash save form (showStashSaveForm)
- Message input
- Include untracked toggle
- Keep index toggle
- Confirmation dialogs for discard operations
iOS is MISSING:
- Commit history (git log)
- Commit details (git show)
- Floating panels
- Any visual graph representation
This is a significant UX gap compared to macOS.
Both platforms produce identical snapshots:
public struct GitPanelSemanticSnapshot: Equatable, Sendable {
public let stagedItems: [GitStatusItem]
public let trackedUnstagedItems: [GitStatusItem]
public let untrackedItems: [GitStatusItem]
public let isGitRepo: Bool
public let isLoading: Bool
public let currentBranch: String?
public let defaultBranch: String?
public let aheadBy: Int?
public let behindBy: Int?
public let semanticSnapshot: GitPanelSemanticSnapshot // Self-reference for recursive projection
public var hasStagedChanges: Bool
public var hasUntrackedChanges: Bool
public var isEmpty: Bool
}Semantic Projection (GitWorkspaceProjectionSemantics):
func make(
workspaceKey: String,
snapshot: GitPanelSemanticSnapshot,
isStageAllInFlight: Bool,
hasResolvedStatus: Bool
) -> GitWorkspaceProjectionKey: "project:workspace" (workspace context) or "project:integration" (integration context)
public struct ConflictWizardCache: Equatable {
public var snapshot: ConflictSnapshot? // Current files list
public var selectedFilePath: String? // Active file being viewed
public var currentDetail: GitConflictDetailResultCache? // Four-way diff
public var isLoading: Bool
public var updatedAt: Date
public var hasActiveConflicts: Bool // !snapshot?.allResolved
public var conflictFileCount: Int
}Both macOS and iOS:
- Fetch snapshot via applyGitInput or wsClient
- Fetch detail via fetchConflictDetail
- Resolve via conflict action methods
- Update cache immediately
- GitWorkspaceStateDriver: Pure reducer pattern
- GitWorkspaceEffect: Describes side effects
- Platform layer converts effects → WS requests
- Benefits: Testable, Sendable, cross-platform
- Shared driver: status, branches (short-lived)
- Platform-specific: diff, log, show (long-lived)
- Conflict wizard: Unified across platforms (key="project:workspace" or "project:integration")
- Reads (status, diff, log, show, branches): HTTP with caching fallback
- Writes (stage, commit, branch ops, conflict resolution): WS only
- Rationale: HTTP reads scale better, WS writes need real-time ordering
- Workspace context: Rebase/merge in single workspace
- Integration context: Merge-to-default in integration worktree
- Same handler methods, context passed through protocol
| Feature | macOS | iOS | Status |
|---|---|---|---|
| Git History (log) | ✓ | ✗ | Missing |
| Commit Details (show) | ✓ | ✗ | Missing |
| Floating Panels | ✓ | ✗ | N/A |
| Split Diff View | ✓ | ✗ | Missing |
| Branch Create | ✓ | ~ Partial | Manual input only |
| Stash Support | ✓ | ✓ | v1.50+ |
| Conflict Wizard | ✓ | ✓ | Shared (v1.40+) |
| Rebase Support | ✓ | ✗ | Missing |
| Model | Purpose | Platform Scope |
|---|---|---|
| GitDiffResult | Server response for diff | Shared |
| DiffCache | Client diff cache | Shared structure, platform storage |
| GitStatusResult | Server git status | Shared |
| GitStatusCache | Client status cache | Shared structure |
| GitPanelSemanticSnapshot | Unified status snapshot | Shared (both platforms) |
| GitLogEntry | Single commit | Shared |
| GitLogCache | Commit history cache | macOS specific |
| GitShowResult | Commit details | Shared |
| GitShowCache | Commit details cache | macOS specific |
| ConflictFileEntry | Conflict file metadata | Shared |
| ConflictSnapshot | Complete conflict state | Shared |
| GitConflictDetailResult | Four-way diff | Shared |
| ConflictWizardCache | Conflict UI state | Shared between platforms |
| GitStashEntry | Stash metadata | Shared |
| GitStashListCache | Stash list cache | Shared structure |
| GitWorkspaceState | Shared driver state | Shared (both platforms) |
-
Add Git History View
- Reuse GitLogEntry/GitLogCache models
- Simple list (no floating panels needed)
- Tap to show commit details sheet
-
Add Commit Details Sheet
- Show files changed in commit
- Link to file diffs if possible
-
Add Rebase UI
- Select onto-branch
- Conflict wizard integration
- Continue/abort actions
-
Implement Split Diff (optional)
- Horizontal scroll for small screens
- Show left/right columns
- Centralize Diff Caching: Move to shared layer with descriptor keys
- Sendable Protocol Models: Ensure all models are Sendable for actor isolation
- WebSocket Batching: Combine multiple operations in single message
- Conflict Wizard Testing: Snapshot-based tests for state transitions
app/
├── TidyFlowShared/
│ ├── Protocol/
│ │ └── GitProtocolModels.swift (2000+ lines)
│ ├── Presentation/
│ │ └── GitWorkspaceStateDriver.swift (330 lines)
│ └── Networking/
│ └── WSClient+Send.swift (2000+ lines)
│
├── TidyFlow/ (macOS)
│ └── Views/
│ ├── GitCacheState+Operations.swift (550+ lines)
│ ├── GitCacheState+DiffStatus.swift (250+ lines)
│ ├── GitConflictWizardView.swift (546 lines)
│ ├── GitGraphViews.swift (323 lines)
│ ├── NativeGitPanelView.swift (574 lines)
│ └── FloatingPanelController.swift (298 lines)
│
└── TidyFlow-iOS/
├── MobileAppState.swift
├── MobileAppState+HandlerMethods.swift (945 lines)
└── Views/
├── GitConflictWizardSheet.swift (418 lines)
└── WorkspaceGitView.swift (996 lines)