Skip to content

Commit d979d96

Browse files
authored
Merge pull request #5 from mherrera53/feat/syncghviews
Feat/Syncghviews
2 parents d361cc3 + 2aacf22 commit d979d96

14 files changed

Lines changed: 733 additions & 115 deletions

File tree

GitMac.xcodeproj/project.pbxproj

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,8 @@
293293
D31155F217037D5B7777EED0 /* LinearContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A72B4B428ACA0A1D3C01558D /* LinearContentView.swift */; };
294294
D36FC421805A89519D5C8731 /* ResetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E42F45A779174FB0192AA60 /* ResetView.swift */; };
295295
D37C7945444AF7F89EE1F35A /* GitService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D9B988D57B4EBA758747115 /* GitService.swift */; };
296+
BRANCHPR0001BUILD000000001 /* BranchPRTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = BRANCHPR0001FILE0000000001 /* BranchPRTracker.swift */; };
297+
GHSYNCMG0001BUILD000000001 /* GitHubSyncManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = GHSYNCMG0001FILE0000000001 /* GitHubSyncManager.swift */; };
296298
D49227F9CCA29A4F6DC6129E /* DiffToolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8EC18843562D7D622FA0E10 /* DiffToolbar.swift */; };
297299
D4C8907702C129E8CBFB0870 /* DSPanel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96298B0967612ED6114D608E /* DSPanel.swift */; };
298300
D586D60CC063E011026505D0 /* TaigaTicketsPanel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32798DDE1B043BCB72AFEDC5 /* TaigaTicketsPanel.swift */; };
@@ -419,6 +421,8 @@
419421
1BE7FD2660507EDB36634450 /* RemoteOperationTracker.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = GitMac/Core/Services/RemoteOperationTracker.swift; sourceTree = SOURCE_ROOT; };
420422
1C7B6E6EE3D71DBE737B4C1D /* DiffLineRenderers.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = DiffLineRenderers.swift; path = ../Features/Diff/Renderers/DiffLineRenderers.swift; sourceTree = "<group>"; };
421423
1D9B988D57B4EBA758747115 /* GitService.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = GitService.swift; path = Core/Services/GitService.swift; sourceTree = "<group>"; };
424+
BRANCHPR0001FILE0000000001 /* BranchPRTracker.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BranchPRTracker.swift; path = Core/Services/BranchPRTracker.swift; sourceTree = "<group>"; };
425+
GHSYNCMG0001FILE0000000001 /* GitHubSyncManager.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = GitHubSyncManager.swift; path = Core/Services/GitHubSyncManager.swift; sourceTree = "<group>"; };
422426
1E722C9BE39DB145F515EE7A /* AWSCodeBuildService.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = GitMac/Features/CICD/AWSCodeBuildService.swift; sourceTree = SOURCE_ROOT; };
423427
1F038DA5BD2F2882EEDC31EA /* DiffOptions.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = DiffOptions.swift; path = ../Features/Diff/DiffOptions.swift; sourceTree = "<group>"; };
424428
202778629A618B9E881C961C /* DiffMinimap.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = DiffMinimap.swift; path = ../Features/Diff/Renderers/DiffMinimap.swift; sourceTree = "<group>"; };
@@ -1649,6 +1653,8 @@
16491653
1D9B988D57B4EBA758747115 /* GitService.swift */,
16501654
99AA1FB5A8306B98A36E3C86 /* AIService.swift */,
16511655
E284BF9CDF10EDB4C57C2244 /* GitHubService.swift */,
1656+
BRANCHPR0001FILE0000000001 /* BranchPRTracker.swift */,
1657+
GHSYNCMG0001FILE0000000001 /* GitHubSyncManager.swift */,
16521658
B04DFEA202E551F0C5CE9419 /* WorkspaceSettings.swift */,
16531659
F7B82AC93A83839A10C7CDCC /* MicrosoftPlannerService.swift */,
16541660
68995E0B81A9B7E42741AE47 /* GitSubmoduleService.swift */,
@@ -2439,6 +2445,8 @@
24392445
338609A9890C174FAC467CA4 /* GitSubmodule.swift in Sources */,
24402446
16FE878B5150049FCF623EDD /* GitHooksService.swift in Sources */,
24412447
D37C7945444AF7F89EE1F35A /* GitService.swift in Sources */,
2448+
BRANCHPR0001BUILD000000001 /* BranchPRTracker.swift in Sources */,
2449+
GHSYNCMG0001BUILD000000001 /* GitHubSyncManager.swift in Sources */,
24422450
E31D0AE2FBC3898B195BDCC4 /* AIService.swift in Sources */,
24432451
F33F3ED30023FB4F63F59B08 /* GitHubService.swift in Sources */,
24442452
42EE92CC8919B6BC223491BA /* WorkspaceSettings.swift in Sources */,

GitMac/App/ContentView.swift

Lines changed: 79 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -313,13 +313,19 @@ struct MainLayout: View {
313313
.frame(maxWidth: .infinity, maxHeight: .infinity)
314314

315315
// Bottom Panel (Terminal/Logs) - Resizable
316-
if bottomPanelManager.isPanelVisible {
317-
UnifiedBottomPanel(panelManager: bottomPanelManager)
318-
.frame(height: bottomPanelManager.panelHeight)
319-
.transition(.move(edge: .bottom))
320-
// Force recreation of the panel when repository changes to ensure
321-
// correct context (Terminal sessions, Integration tabs, etc.)
322-
.id(appState.currentRepository?.id.uuidString ?? "no-repo")
316+
// Always show UnifiedBottomPanel - it handles both collapsed (28px tab bar) and expanded states
317+
// When fully hidden (no tabs), show minimal CollapsedBottomPanelBar
318+
if bottomPanelManager.openTabs.isEmpty && !bottomPanelManager.isPanelVisible {
319+
// No tabs open and panel hidden - show minimal bar to open terminal
320+
CollapsedBottomPanelBar(panelManager: bottomPanelManager)
321+
} else {
322+
// Has tabs or panel is visible - show full UnifiedBottomPanel
323+
// UnifiedBottomPanel handles its own height (28px collapsed, panelHeight expanded)
324+
UnifiedBottomPanel(panelManager: bottomPanelManager)
325+
.transition(.move(edge: .bottom))
326+
// Force recreation of the panel when repository changes to ensure
327+
// correct context (Terminal sessions, Integration tabs, etc.)
328+
.id(appState.currentRepository?.id.uuidString ?? "no-repo")
323329
}
324330
}
325331
.contextMenu { toolbarConfigurationMenu }
@@ -542,3 +548,69 @@ struct MainLayout: View {
542548
}
543549
}
544550
}
551+
552+
// MARK: - Collapsed Bottom Panel Bar
553+
/// Minimal bar shown when bottom panel is hidden - click to expand
554+
struct CollapsedBottomPanelBar: View {
555+
@ObservedObject var panelManager: BottomPanelManager
556+
@State private var isHovered = false
557+
558+
var body: some View {
559+
HStack(spacing: 6) {
560+
// Drag handle indicator
561+
Image(systemName: "chevron.up")
562+
.font(.system(size: 8, weight: .bold))
563+
.foregroundColor(AppTheme.textMuted)
564+
565+
// Show open tabs icons
566+
if !panelManager.openTabs.isEmpty {
567+
ForEach(panelManager.openTabs.prefix(4)) { tab in
568+
Image(systemName: tab.type.icon)
569+
.font(.system(size: 9))
570+
.foregroundColor(panelManager.activeTabId == tab.id ? AppTheme.accent : AppTheme.textMuted)
571+
}
572+
if panelManager.openTabs.count > 4 {
573+
Text("+\(panelManager.openTabs.count - 4)")
574+
.font(.system(size: 9))
575+
.foregroundColor(AppTheme.textMuted)
576+
}
577+
} else {
578+
Text("Terminal")
579+
.font(.system(size: 10))
580+
.foregroundColor(AppTheme.textMuted)
581+
}
582+
583+
Spacer()
584+
585+
// Quick action buttons
586+
Button {
587+
panelManager.openTab(type: .terminal)
588+
} label: {
589+
Image(systemName: "terminal")
590+
.font(.system(size: 9))
591+
.foregroundColor(AppTheme.textMuted)
592+
}
593+
.buttonStyle(.plain)
594+
}
595+
.padding(.horizontal, 12)
596+
.frame(height: 22)
597+
.background(isHovered ? AppTheme.hover : AppTheme.backgroundSecondary)
598+
.overlay(
599+
Rectangle()
600+
.fill(AppTheme.border)
601+
.frame(height: 1),
602+
alignment: .top
603+
)
604+
.contentShape(Rectangle())
605+
.onTapGesture {
606+
withAnimation(.spring(response: 0.3)) {
607+
if panelManager.openTabs.isEmpty {
608+
panelManager.openTab(type: .terminal)
609+
} else {
610+
panelManager.isPanelVisible = true
611+
}
612+
}
613+
}
614+
.onHover { isHovered = $0 }
615+
}
616+
}

GitMac/App/GitMacApp.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,12 @@ struct GitMacApp: App {
4646
window.titleVisibility = .hidden
4747
window.title = ""
4848
window.appearance = ThemeManager.shared.appearance
49+
50+
// Set minimum window size - center panel compresses first
51+
window.minSize = NSSize(
52+
width: DesignTokens.Layout.Window.minWidth,
53+
height: DesignTokens.Layout.Window.minHeight
54+
)
4955
}
5056

5157
// Ensure the app icon is properly set
@@ -651,6 +657,7 @@ extension Notification.Name {
651657
static let showWorktrees = Notification.Name("show_worktrees")
652658
static let toggleSidebar = Notification.Name("toggle_sidebar")
653659
static let repositoryDidRefresh = Notification.Name("repositoryDidRefresh")
660+
static let branchDidCheckout = Notification.Name("branchDidCheckout")
654661

655662
// CI/CD Triggers
656663
static let gitPushCompleted = Notification.Name("gitPushCompleted")

GitMac/App/Panels/Right/StashComponents.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,11 @@ class StashDetailViewModel: ObservableObject {
2929

3030
func getDiff(for file: StashFile, stash: Stash, at path: String) async -> FileDiff? {
3131
let shell = ShellExecutor()
32+
// Use git diff between stash parent and stash for specific file
33+
// Syntax: git diff stash@{n}^ stash@{n} -- file.path
3234
let result = await shell.execute(
3335
"git",
34-
arguments: ["stash", "show", "-p", stash.reference, "--", file.path],
36+
arguments: ["diff", "\(stash.reference)^", stash.reference, "--", file.path],
3537
workingDirectory: path
3638
)
3739

GitMac/App/Sheets/CreateBranchSheet.swift

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,14 @@ struct CreateBranchSheet: View {
2121
appState.currentRepository?.branches ?? []
2222
}
2323

24+
var currentBranchName: String {
25+
appState.currentRepository?.currentBranch?.name ?? "HEAD"
26+
}
27+
28+
var repoName: String {
29+
appState.currentRepository?.name ?? "Unknown"
30+
}
31+
2432
var body: some View {
2533
VStack(spacing: 0) {
2634
// Header
@@ -41,6 +49,34 @@ struct CreateBranchSheet: View {
4149
.padding(16)
4250
.background(AppTheme.backgroundSecondary)
4351

52+
// Repository context info
53+
HStack(spacing: 12) {
54+
// Repository
55+
HStack(spacing: 4) {
56+
Image(systemName: "folder.fill")
57+
.font(.system(size: 10))
58+
.foregroundColor(AppTheme.accent)
59+
Text(repoName)
60+
.font(.system(size: 11, weight: .medium))
61+
.foregroundColor(AppTheme.textPrimary)
62+
}
63+
64+
// Current branch
65+
HStack(spacing: 4) {
66+
Image(systemName: "arrow.triangle.branch")
67+
.font(.system(size: 10))
68+
.foregroundColor(AppTheme.success)
69+
Text(currentBranchName)
70+
.font(.system(size: 11, weight: .medium))
71+
.foregroundColor(AppTheme.textPrimary)
72+
}
73+
74+
Spacer()
75+
}
76+
.padding(.horizontal, 16)
77+
.padding(.vertical, 8)
78+
.background(AppTheme.backgroundTertiary.opacity(0.5))
79+
4480
Rectangle().fill(AppTheme.border).frame(height: 1)
4581

4682
// Content
@@ -125,22 +161,36 @@ struct CreateBranchSheet: View {
125161
}
126162
.padding(16)
127163
}
128-
.frame(width: 380, height: 320)
164+
.frame(width: 380, height: 350)
129165
.background(AppTheme.backgroundSecondary)
130166
}
131167

132168
private func createBranch() {
169+
guard let repoPath = appState.currentRepository?.path else {
170+
errorMessage = "No repository selected"
171+
return
172+
}
173+
133174
isCreating = true
134175
errorMessage = nil
135176

136177
Task {
137178
do {
138-
_ = try await appState.gitService.createBranch(
179+
let engine = GitEngine()
180+
_ = try await engine.createBranch(
139181
named: branchName,
140182
from: baseBranch,
141-
checkout: checkoutAfterCreate
183+
checkout: checkoutAfterCreate,
184+
at: repoPath
142185
)
143186
await appState.refresh()
187+
188+
// Post notifications for UI sync
189+
if checkoutAfterCreate {
190+
NotificationCenter.default.post(name: .branchDidCheckout, object: branchName)
191+
}
192+
NotificationCenter.default.post(name: .repositoryDidRefresh, object: repoPath)
193+
144194
await MainActor.run {
145195
isPresented = false
146196
}

0 commit comments

Comments
 (0)