Skip to content

Commit d6c9cd0

Browse files
will-osborneclaude
andcommitted
Redesign inspector file changes section with prominent diff viewer button
Replace the subtle file history icon with a full "Files Changed" section showing file-type icons, shortened paths, and a prominent "Open Diff Viewer" button. Also fix notifications for continuation runs and persist run_complete for all runs. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 446420f commit d6c9cd0

7 files changed

Lines changed: 400 additions & 44 deletions

File tree

Formula/orbitor.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,12 @@ def install
4242

4343
def post_install
4444
(Pathname.new(ENV["HOME"]) / ".orbitor").mkpath
45+
# Kill any existing orbitor server processes so the port is freed before
46+
# the service restarts. Without this, the new server can't bind because
47+
# the old process (possibly started outside brew services) still holds it.
48+
quiet_system "pkill", "-f", "orbitor server"
49+
quiet_system "pkill", "-f", "orbitor.*server"
50+
sleep 2
4551
# Restart the background service after upgrade so the new binary is used.
4652
# quiet_system avoids errors when the service isn't running yet.
4753
quiet_system "brew", "services", "restart", "orbitor"

desktop/Orbitor/Orbitor.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
A1B2C3D4E5F60001000A0017 /* CommandPaletteView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1B2C3D4E5F60002000A0017 /* CommandPaletteView.swift */; };
3434
A1B2C3D4E5F60001000A0018 /* RunRecord.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1B2C3D4E5F60002000A0018 /* RunRecord.swift */; };
3535
A1B2C3D4E5F60001000A0019 /* RunHistoryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1B2C3D4E5F60002000A0019 /* RunHistoryView.swift */; };
36+
A1B2C3D4E5F60001000A001A /* SideBySideDiffView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1B2C3D4E5F60002000A001A /* SideBySideDiffView.swift */; };
3637
/* End PBXBuildFile section */
3738

3839
/* Begin PBXFileReference section */
@@ -64,6 +65,7 @@
6465
A1B2C3D4E5F60002000A0017 /* CommandPaletteView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommandPaletteView.swift; sourceTree = "<group>"; };
6566
A1B2C3D4E5F60002000A0018 /* RunRecord.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunRecord.swift; sourceTree = "<group>"; };
6667
A1B2C3D4E5F60002000A0019 /* RunHistoryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunHistoryView.swift; sourceTree = "<group>"; };
68+
A1B2C3D4E5F60002000A001A /* SideBySideDiffView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SideBySideDiffView.swift; sourceTree = "<group>"; };
6769
A1B2C3D4E5F60003000A0001 /* Orbitor.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Orbitor.app; sourceTree = BUILT_PRODUCTS_DIR; };
6870
/* End PBXFileReference section */
6971

@@ -180,6 +182,7 @@
180182
A1B2C3D4E5F60002000A000F /* CodeBlockView.swift */,
181183
A1B2C3D4E5F60002000A0016 /* DiffView.swift */,
182184
A1B2C3D4E5F60002000A0015 /* HoverEffects.swift */,
185+
A1B2C3D4E5F60002000A001A /* SideBySideDiffView.swift */,
183186
A1B2C3D4E5F60002000A0010 /* StatusBadge.swift */,
184187
);
185188
path = Shared;
@@ -311,6 +314,7 @@
311314
A1B2C3D4E5F60001000A0017 /* CommandPaletteView.swift in Sources */,
312315
A1B2C3D4E5F60001000A0018 /* RunRecord.swift in Sources */,
313316
A1B2C3D4E5F60001000A0019 /* RunHistoryView.swift in Sources */,
317+
A1B2C3D4E5F60001000A001A /* SideBySideDiffView.swift in Sources */,
314318
);
315319
runOnlyForDeploymentPostprocessing = 0;
316320
};

desktop/Orbitor/Orbitor/OrbitorApp.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,16 @@ struct OrbitorApp: App {
3737
AppCommands(appState: appState)
3838
}
3939

40+
WindowGroup("File History", id: "file-history", for: String.self) { $sessionID in
41+
if let sessionID {
42+
RunHistoryView(sessionID: sessionID)
43+
.environment(appState)
44+
.environment(\.theme, appState.currentTheme)
45+
}
46+
}
47+
.defaultSize(width: 1100, height: 700)
48+
.windowStyle(.titleBar)
49+
4050
Settings {
4151
SettingsView()
4252
.environment(appState)

desktop/Orbitor/Orbitor/Views/Inspector/InspectorView.swift

Lines changed: 105 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ struct InspectorView: View {
44
@Environment(AppState.self) private var appState
55
@Environment(\.theme) private var theme
66
@State private var gitBranch: String? = nil
7-
@State private var showHistory = false
7+
@Environment(\.openWindow) private var openWindow
88

99
var body: some View {
1010
if let session = appState.sessionList.selectedSession {
@@ -21,15 +21,6 @@ struct InspectorView: View {
2121
.font(.caption)
2222
.foregroundStyle(theme.red)
2323
}
24-
Button {
25-
showHistory = true
26-
} label: {
27-
Image(systemName: "clock.arrow.trianglehead.counterclockwise.rotate.90")
28-
.font(.caption)
29-
.foregroundStyle(theme.muted)
30-
}
31-
.buttonStyle(.plain)
32-
.help("File History")
3324
}
3425

3526
// Session info grid
@@ -127,26 +118,78 @@ struct InspectorView: View {
127118
}
128119
}
129120

130-
// Files touched this session
131-
if !appState.chat.filesTouched.isEmpty {
132-
DetailSection(title: "Files Changed (\(appState.chat.filesTouched.count))") {
133-
ForEach(appState.chat.filesTouched.prefix(10), id: \.self) { path in
134-
HStack(spacing: 4) {
135-
Image(systemName: "pencil.circle.fill")
136-
.font(.system(size: 9))
137-
.foregroundStyle(theme.cyan)
138-
Text(path)
139-
.font(.system(size: 10, design: .monospaced))
140-
.foregroundStyle(theme.text)
141-
.lineLimit(1)
142-
.truncationMode(.head)
121+
// Files changed — prominent section with diff access
122+
VStack(alignment: .leading, spacing: 8) {
123+
HStack {
124+
Image(systemName: "doc.text.magnifyingglass")
125+
.font(.system(size: 12))
126+
.foregroundStyle(theme.accent)
127+
Text("FILES CHANGED")
128+
.font(.caption2.bold())
129+
.foregroundStyle(theme.muted)
130+
Spacer()
131+
if !appState.chat.filesTouched.isEmpty {
132+
Text("\(appState.chat.filesTouched.count)")
133+
.font(.caption2.bold().monospacedDigit())
134+
.foregroundStyle(theme.panel)
135+
.padding(.horizontal, 6)
136+
.padding(.vertical, 2)
137+
.background(theme.accent, in: Capsule())
138+
}
139+
}
140+
141+
if appState.chat.filesTouched.isEmpty {
142+
Text("No files changed yet")
143+
.font(.caption)
144+
.foregroundStyle(theme.muted.opacity(0.7))
145+
.frame(maxWidth: .infinity, alignment: .center)
146+
.padding(.vertical, 8)
147+
} else {
148+
VStack(spacing: 0) {
149+
ForEach(appState.chat.filesTouched.prefix(15), id: \.self) { path in
150+
HStack(spacing: 6) {
151+
Image(systemName: fileIcon(for: path))
152+
.font(.system(size: 10))
153+
.foregroundStyle(fileColor(for: path))
154+
.frame(width: 14)
155+
Text(shortenPath(path))
156+
.font(.system(size: 10, design: .monospaced))
157+
.foregroundStyle(theme.text)
158+
.lineLimit(1)
159+
.truncationMode(.middle)
160+
Spacer()
161+
}
162+
.padding(.vertical, 3)
163+
.padding(.horizontal, 6)
164+
}
165+
if appState.chat.filesTouched.count > 15 {
166+
Text("+ \(appState.chat.filesTouched.count - 15) more")
167+
.font(.caption2)
168+
.foregroundStyle(theme.muted)
169+
.padding(.vertical, 3)
170+
.padding(.horizontal, 6)
143171
}
144172
}
145-
if appState.chat.filesTouched.count > 10 {
146-
Text("…and \(appState.chat.filesTouched.count - 10) more")
147-
.font(.caption2)
148-
.foregroundStyle(theme.muted)
173+
.background(theme.panel.opacity(0.5))
174+
.clipShape(RoundedRectangle(cornerRadius: 6))
175+
176+
// Prominent button to open full diff viewer
177+
Button {
178+
openWindow(id: "file-history", value: session.id)
179+
} label: {
180+
HStack(spacing: 6) {
181+
Image(systemName: "arrow.up.left.and.arrow.down.right")
182+
.font(.system(size: 11, weight: .medium))
183+
Text("Open Diff Viewer")
184+
.font(.system(size: 12, weight: .medium))
185+
}
186+
.frame(maxWidth: .infinity)
187+
.padding(.vertical, 7)
188+
.foregroundStyle(theme.panel)
189+
.background(theme.accent, in: RoundedRectangle(cornerRadius: 6))
149190
}
191+
.buttonStyle(.plain)
192+
.help("Open side-by-side diffs in a separate window")
150193
}
151194
}
152195

@@ -197,11 +240,6 @@ struct InspectorView: View {
197240
.background(theme.panel)
198241
.onAppear { loadGitBranch(for: session) }
199242
.onChange(of: session.id) { _, _ in loadGitBranch(for: session) }
200-
.sheet(isPresented: $showHistory) {
201-
RunHistoryView(sessionID: session.id)
202-
.environment(appState)
203-
.environment(\.theme, theme)
204-
}
205243
} else {
206244
VStack {
207245
Text("No session")
@@ -232,6 +270,41 @@ struct InspectorView: View {
232270
if total < 60 { return "\(total)s" }
233271
return "\(total / 60)m \(total % 60)s"
234272
}
273+
274+
private func fileIcon(for path: String) -> String {
275+
let ext = (path as NSString).pathExtension.lowercased()
276+
switch ext {
277+
case "swift": return "swift"
278+
case "go": return "chevron.left.forwardslash.chevron.right"
279+
case "ts", "tsx", "js", "jsx": return "j.square"
280+
case "py": return "p.square"
281+
case "json", "yaml", "yml", "toml": return "doc.text"
282+
case "md": return "doc.richtext"
283+
case "css", "scss": return "paintbrush"
284+
case "html": return "globe"
285+
case "sql": return "cylinder"
286+
case "sh", "bash", "zsh", "fish": return "terminal"
287+
default: return "doc"
288+
}
289+
}
290+
291+
private func fileColor(for path: String) -> Color {
292+
let ext = (path as NSString).pathExtension.lowercased()
293+
switch ext {
294+
case "swift": return theme.orange
295+
case "go": return theme.cyan
296+
case "ts", "tsx", "js", "jsx": return theme.yellow
297+
case "py": return theme.green
298+
case "json", "yaml", "yml", "toml": return theme.muted
299+
default: return theme.cyan
300+
}
301+
}
302+
303+
private func shortenPath(_ path: String) -> String {
304+
let components = path.components(separatedBy: "/")
305+
if components.count <= 3 { return path }
306+
return components.suffix(3).joined(separator: "/")
307+
}
235308
}
236309

237310
// MARK: - Detail helpers

desktop/Orbitor/Orbitor/Views/Inspector/RunHistoryView.swift

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@ struct RunHistoryView: View {
44
let sessionID: String
55
@Environment(AppState.self) private var appState
66
@Environment(\.theme) private var theme
7-
@Environment(\.dismiss) private var dismiss
8-
97
@State private var runs: [RunRecord] = []
108
@State private var isLoading = false
119
@State private var selectedRunID: String?
@@ -28,13 +26,6 @@ struct RunHistoryView: View {
2826
if isLoading {
2927
ProgressView().controlSize(.small)
3028
}
31-
Button {
32-
dismiss()
33-
} label: {
34-
Image(systemName: "xmark.circle.fill")
35-
.foregroundStyle(theme.muted)
36-
}
37-
.buttonStyle(.plain)
3829
}
3930
.padding(.horizontal, 16)
4031
.padding(.vertical, 12)
@@ -185,7 +176,7 @@ struct RunHistoryView: View {
185176

186177
Divider().background(theme.sep)
187178

188-
FileDiffView(before: file.before, after: file.after)
179+
SideBySideDiffView(before: file.before, after: file.after)
189180
} else {
190181
VStack {
191182
Text("Select a file to view diff")

0 commit comments

Comments
 (0)