|
1 | 1 | # Architecture Guide |
2 | 2 |
|
3 | | -Load this file before changing module ownership, repo state, review state, presentation surfaces, or large file layout. |
| 3 | +Load this file before changing crate ownership, module boundaries, or cross-shell contracts. For shell file layouts and conventions load [SwiftUI Shell Guide](swiftui.md) or [GPUI Shell Guide](gpui.md); for review marks and notes load [Review State Guide](review-state.md). |
4 | 4 |
|
5 | | -## MVVM |
6 | | - |
7 | | -```text |
8 | | -Rust Core -> UniFFI -> ViewModel -> SwiftUI/GPUI Views |
9 | | - ^ | | | |
10 | | - | v v v |
11 | | - business type bridge actions rendering |
12 | | -``` |
| 5 | +## Workspace Crates |
13 | 6 |
|
14 | | -- **Model** (`crates/jayjay-core/`): jj-lib wrapper, diff engine, tree-sitter syntax, review state, repo operations. Pure Rust, no platform code. |
15 | | -- **Bindings** (`crates/jayjay-uniffi/`): thin UniFFI layer. Convert types and expose core APIs; do not add business logic. |
16 | | -- **SwiftUI ViewModel** (`Repo/RepoViewModel.swift` and `RepoViewModel+Actions.swift`): owns `JayJayRepo`. jj operations go through here. Async work uses `Task.detached` then `MainActor.run`. |
17 | | -- **Views**: feature folders in SwiftUI and GPUI. Views render state and call callbacks; they should not know jj internals. |
18 | | - |
19 | | -## Core Modules |
| 7 | +| Crate / target | Responsibility | |
| 8 | +| --- | --- | |
| 9 | +| `jayjay-primitives` | jj-lib-free domain types: `Change`, `Bookmark`, `DiffHunk`, review types, hashing | |
| 10 | +| `jj-diff` | Diff engine: Histogram line diff, word diff, tree-sitter syntax, context collapse, wrapping, conflict display, canonical change groups | |
| 11 | +| `jayjay-review` | Local review store: marks, notes, reconciliation (uses `jj-diff`) | |
| 12 | +| `jayjay-network` | Shared blocking HTTP client helpers | |
| 13 | +| `jayjay-core` | jj-lib wrapper and repo operations; re-exports `jj-diff` as `jayjay_core::diff` | |
| 14 | +| `jayjay-uniffi` | UniFFI bindings for the SwiftUI shell only; no business logic | |
| 15 | +| `jayjay-cli` | `jayjay` binary: app launcher plus agent-facing `review` subcommands | |
| 16 | +| `jj-test` | Shared jj repo fixtures for integration and component tests | |
| 17 | +| `shell/gpui` | Cross-platform GPUI shell; links the Rust crates directly (no UniFFI) | |
| 18 | +| `shell/mac` | SwiftUI shell + the `JayJayDiffUI` Swift package (AppKit diff renderer) | |
20 | 19 |
|
21 | | -Keep core logic split by responsibility: |
| 20 | +Dependency direction (never invert): `primitives` and `jj-diff` are leaves → `jayjay-review` → `jayjay-core` → `jayjay-uniffi` / `jayjay-cli` / `shell/gpui`. |
22 | 21 |
|
23 | | -- `repo/mod.rs` |
24 | | -- `repo/log.rs` |
25 | | -- `repo/diff.rs` |
26 | | -- `repo/mutations.rs` |
27 | | -- `repo/bookmarks.rs` |
28 | | -- `repo/git.rs` |
29 | | -- `repo/working_copy.rs` |
30 | | -- `repo/config.rs` |
31 | | -- `repo/environment.rs` |
32 | | -- `repo/resolve.rs` |
33 | | -- `repo/conflicts.rs` |
34 | | -- `repo/annotate.rs` |
35 | | -- `diff/compute.rs` |
36 | | -- `review.rs` |
37 | | -- `hash.rs` |
| 22 | +- New shared types go in `jayjay-primitives`, not `jayjay-core`, so review/CLI code stays jj-lib-light. |
| 23 | +- Anything two surfaces must agree on (change groups, review identity, note reconciliation) lives at or below `jayjay-review`/`jj-diff` and is consumed by all surfaces. Do not re-implement a diff or identity computation per surface; the GUI and `jayjay review notes` must reconcile through the same provider or notes silently report stale. |
38 | 24 |
|
39 | | -## SwiftUI File Layout |
| 25 | +## MVVM |
40 | 26 |
|
41 | 27 | ```text |
42 | | -shell/mac/Sources/JayJay/ |
43 | | - App/ |
44 | | - Config/ AppSettings, AppearanceTypes, EditorTypes, TerminalTypes, AppSettingsTools, FontEnvironment, AppMetadata, JJEnvironment |
45 | | - Window/ RepoWindowManager, RepositoryCommands, RepositoryFocus, RepositoryActions |
46 | | - Watcher/ RepoFSWatcher |
47 | | - JayJayApp.swift, CLIInstaller.swift, DebugBadge.swift, LaunchArguments.swift, SparkleUpdater.swift |
48 | | - Repo/ RepoWindow, RepoSidebar, RepoViewModel, RepoViewModel+Actions, DAGView, DAGLayout, DAGRow, DAGRowViewModel, RepoPresentation, RepoToast, CommitBox, BookmarkPicker, UndoView |
49 | | - Detail/ DetailView, DetailHeader, FileColumn, FileListView, AnnotateView, FileHistoryView |
50 | | - Diff/ DiffSection, DiffColors, NativeDiffView, SideBySideDiffView |
51 | | - Onboarding/ OnboardingView, WelcomeView |
52 | | - Settings/ SettingsView, JJConfigView, AboutView, SettingsComponents |
53 | | - Shared/ ChangeActions, ErrorMessages, ReviewStore, SheetViews, CommandPalette |
| 28 | +Rust Core -> UniFFI -> ViewModel -> SwiftUI Views |
| 29 | + | |
| 30 | + +------------- direct link ---------> GPUI shell (view_model + window) |
54 | 31 | ``` |
55 | 32 |
|
56 | | -## Review State |
57 | | - |
58 | | -Review state is persistent across app restarts and local to the user. |
59 | | - |
60 | | -- Canonical implementation: `jayjay_core::review::ReviewStore`. |
61 | | -- SwiftUI still has `Shared/ReviewStore.swift` with the same shape; GPUI uses the Rust store. |
62 | | -- Identity is caller-supplied. The store is a pure keyed store with no disk access and no hashing. |
63 | | -- Review identity is computed in `jayjay_core::repo::diff::entry::compute_review_identity` from `MergedTreeValue` blob IDs. |
64 | | -- Keying: `(changeId, path) -> { identity, file_marked, hunks }`. |
65 | | -- Valid marks require the stored identity to match the current `hunk.review_identity`. |
66 | | -- Same blob IDs preserve review across rebases or amends that do not change file bytes. |
67 | | -- Any byte change in the old or new side invalidates only that file's review. |
68 | | -- `is_hunk_reviewed(idx)` is true when the file is marked or the hunk set contains `idx`. |
69 | | -- Marking every hunk promotes to file-level review. Unmarking a hunk drops the file flag. |
70 | | -- Persistence path: `~/Library/Application Support/dev.hewig.jayjay/review_store.json` for Rust and `UserDefaults` for Swift. |
71 | | - |
72 | | -## Presentation Surfaces |
| 33 | +- **Model** (`crates/`): all business logic. Pure Rust, no platform code. |
| 34 | +- **Bindings** (`crates/jayjay-uniffi/`): convert types and expose core APIs; do not add business logic. Bindings regenerate during `just build`. |
| 35 | +- **ViewModels** own the repo handle and all jj operations: `Repo/ViewModel/` in SwiftUI, `repo/view_model/` in GPUI. |
| 36 | +- **Views**: feature folders in SwiftUI and GPUI. Views render state and call callbacks; they should not know jj internals. |
73 | 37 |
|
74 | | -Use repo-level presentation types from `RepoPresentation.swift` instead of ad hoc booleans. |
| 38 | +Async conventions (both shells): heavy jj work runs off the UI thread (`Task.detached` → `MainActor.run` in Swift; `cx.background_spawn` → `this.update` in GPUI) and every in-flight result is guarded by a supersession check — a token, generation counter, or commit-id compare against current `@State`/VM state — so a slow stale result can never overwrite a newer one. Both shells also suppress the FS-watcher echo of their own writes (`lastInternalMutationAt` / `last_internal_mutation_at`). |
75 | 39 |
|
76 | | -- **Inline state**: pane-scoped no-data, first-run guidance, and recoverable section errors. |
77 | | -- **Toast** (`RepoOverlayState.toast` / `RepoToast`): non-blocking action feedback, success messages, conflict follow-up, and lightweight warnings. Keep it short and allow at most one direct action. |
78 | | -- **HUD** (`RepoOverlayState.loading`): temporary blocking busy states where further interaction would be misleading or unsafe. Prefer quiet refreshes. |
79 | | -- **Alert** (`RepoAlertState`): short blocking interruptions that need acknowledgement or a simple binary choice. No forms, long copy, or more than two meaningful actions. |
80 | | -- **Sheet** (`RepoModalState` + `SheetContainer`): forms, previews, richer explanations, multi-step flows, or confirmations needing more context than an alert. |
| 40 | +## Core Modules |
81 | 41 |
|
82 | | -Do not escalate inline states into alerts or sheets just because they are errors. Scope the surface to the problem. |
| 42 | +Keep `jayjay-core` logic split by responsibility under `repo/` (one file or folder per operation family: `log`, `diff/`, `mutations`, `bookmarks`, `git/`, `working_copy`, `resolve/`, `conflicts`, `annotate`, `evolog`, `diffedit/`, `stacked_pr/`, `pull_requests/`, `review_notes`, `undo`, `workspace`). Top-level modules (`dag`, `file_tree`, `fuzzy`, `palette`, `theme`, `commit_message`) are repo-free helpers. |
0 commit comments