Address PR review feedback

This commit is contained in:
austinpower1258 2026-03-05 23:09:45 -08:00
parent 1291776597
commit 0285cd1f51
3 changed files with 51 additions and 12 deletions

View file

@ -2590,6 +2590,11 @@ struct ContentView: View {
case completed(reason: String)
}
private enum BackgroundWorkspacePrimePolicy {
static let timeoutSeconds: TimeInterval = 2.0
static let pollIntervalNanoseconds: UInt64 = 50_000_000
}
private func primeBackgroundWorkspaceIfNeeded(workspaceId: UUID) async {
let shouldPrime = await MainActor.run {
tabManager.pendingBackgroundWorkspaceLoadIds.contains(workspaceId)
@ -2601,7 +2606,7 @@ struct ContentView: View {
dlog("workspace.backgroundPrime.start workspace=\(workspaceId.uuidString.prefix(5))")
#endif
let timeout = Date().addingTimeInterval(2.0)
let timeout = Date().addingTimeInterval(BackgroundWorkspacePrimePolicy.timeoutSeconds)
while !Task.isCancelled {
let state = await MainActor.run {
stepBackgroundWorkspacePrime(workspaceId: workspaceId)
@ -2609,7 +2614,7 @@ struct ContentView: View {
switch state {
case .pending:
if Date() < timeout {
try? await Task.sleep(nanoseconds: 50_000_000)
try? await Task.sleep(nanoseconds: BackgroundWorkspacePrimePolicy.pollIntervalNanoseconds)
continue
}
await MainActor.run {

View file

@ -635,6 +635,7 @@ class TabManager: ObservableObject {
qos: .utility
)
private var initialWorkspaceGitProbeGenerationByWorkspace: [UUID: UUID] = [:]
private var initialWorkspaceGitProbeTimersByWorkspace: [UUID: [DispatchSourceTimer]] = [:]
// Recent tab history for back/forward navigation (like browser history)
private var tabHistory: [UUID] = []
@ -870,6 +871,7 @@ class TabManager: ObservableObject {
) {
let normalizedDirectory = normalizeDirectory(directory)
let generation = UUID()
cancelInitialWorkspaceGitProbeTimers(workspaceId: workspaceId)
initialWorkspaceGitProbeGenerationByWorkspace[workspaceId] = generation
#if DEBUG
@ -880,9 +882,12 @@ class TabManager: ObservableObject {
#endif
let delays = Self.initialWorkspaceGitProbeDelays
var timers: [DispatchSourceTimer] = []
for (index, delay) in delays.enumerated() {
let isLastAttempt = index == delays.count - 1
initialWorkspaceGitProbeQueue.asyncAfter(deadline: .now() + delay) {
let timer = DispatchSource.makeTimerSource(queue: initialWorkspaceGitProbeQueue)
timer.schedule(deadline: .now() + delay, repeating: .never)
timer.setEventHandler { [weak self] in
let snapshot = Self.initialWorkspaceGitMetadataSnapshot(for: normalizedDirectory)
Task { @MainActor [weak self] in
self?.applyInitialWorkspaceGitMetadataSnapshot(
@ -895,7 +900,25 @@ class TabManager: ObservableObject {
)
}
}
timers.append(timer)
timer.resume()
}
initialWorkspaceGitProbeTimersByWorkspace[workspaceId] = timers
}
private func cancelInitialWorkspaceGitProbeTimers(workspaceId: UUID) {
guard let timers = initialWorkspaceGitProbeTimersByWorkspace.removeValue(forKey: workspaceId) else {
return
}
for timer in timers {
timer.setEventHandler {}
timer.cancel()
}
}
private func clearInitialWorkspaceGitProbe(workspaceId: UUID) {
initialWorkspaceGitProbeGenerationByWorkspace.removeValue(forKey: workspaceId)
cancelInitialWorkspaceGitProbeTimers(workspaceId: workspaceId)
}
private func applyInitialWorkspaceGitMetadataSnapshot(
@ -909,17 +932,17 @@ class TabManager: ObservableObject {
defer {
if isLastAttempt,
initialWorkspaceGitProbeGenerationByWorkspace[workspaceId] == generation {
initialWorkspaceGitProbeGenerationByWorkspace.removeValue(forKey: workspaceId)
clearInitialWorkspaceGitProbe(workspaceId: workspaceId)
}
}
guard initialWorkspaceGitProbeGenerationByWorkspace[workspaceId] == generation else { return }
guard let workspace = tabs.first(where: { $0.id == workspaceId }) else {
initialWorkspaceGitProbeGenerationByWorkspace.removeValue(forKey: workspaceId)
clearInitialWorkspaceGitProbe(workspaceId: workspaceId)
return
}
guard workspace.panels[panelId] != nil else {
initialWorkspaceGitProbeGenerationByWorkspace.removeValue(forKey: workspaceId)
clearInitialWorkspaceGitProbe(workspaceId: workspaceId)
return
}
@ -927,7 +950,7 @@ class TabManager: ObservableObject {
workspace.panelDirectories[panelId] ?? workspace.currentDirectory
)
if let currentDirectory, currentDirectory != expectedDirectory {
initialWorkspaceGitProbeGenerationByWorkspace.removeValue(forKey: workspaceId)
clearInitialWorkspaceGitProbe(workspaceId: workspaceId)
#if DEBUG
dlog(
"workspace.gitProbe.skip workspace=\(workspaceId.uuidString.prefix(5)) " +
@ -980,7 +1003,7 @@ class TabManager: ObservableObject {
process.executableURL = URL(fileURLWithPath: "/usr/bin/env")
process.arguments = ["git", "-C", directory] + arguments
process.standardOutput = stdout
process.standardError = Pipe()
process.standardError = FileHandle.nullDevice
do {
try process.run()
@ -988,12 +1011,13 @@ class TabManager: ObservableObject {
return nil
}
// Drain stdout while the subprocess is active so large repos cannot fill the pipe buffer.
let data = stdout.fileHandleForReading.readDataToEndOfFile()
process.waitUntilExit()
guard process.terminationStatus == 0 else {
return nil
}
let data = stdout.fileHandleForReading.readDataToEndOfFile()
return String(data: data, encoding: .utf8)
}
@ -1196,7 +1220,7 @@ class TabManager: ObservableObject {
guard tabs.count > 1 else { return }
guard let index = tabs.firstIndex(where: { $0.id == workspace.id }) else { return }
sentryBreadcrumb("workspace.close", data: ["tabCount": tabs.count - 1])
initialWorkspaceGitProbeGenerationByWorkspace.removeValue(forKey: workspace.id)
clearInitialWorkspaceGitProbe(workspaceId: workspace.id)
AppDelegate.shared?.notificationStore?.clearNotifications(forTabId: workspace.id)
unwireClosedBrowserTracking(for: workspace)
@ -1218,7 +1242,7 @@ class TabManager: ObservableObject {
@discardableResult
func detachWorkspace(tabId: UUID) -> Workspace? {
guard let index = tabs.firstIndex(where: { $0.id == tabId }) else { return nil }
initialWorkspaceGitProbeGenerationByWorkspace.removeValue(forKey: tabId)
clearInitialWorkspaceGitProbe(workspaceId: tabId)
let removed = tabs.remove(at: index)
unwireClosedBrowserTracking(for: removed)

View file

@ -5,6 +5,7 @@ from __future__ import annotations
import glob
import os
import re
import shutil
import subprocess
import sys
@ -16,7 +17,16 @@ sys.path.insert(0, str(Path(__file__).parent))
from cmux import cmux, cmuxError
SOCKET_PATH = os.environ.get("CMUX_SOCKET", "/tmp/cmux-debug.sock")
def _resolve_socket_path() -> str:
socket_path = os.environ.get("CMUX_SOCKET", "").strip()
if not socket_path:
raise cmuxError("CMUX_SOCKET is required (expected /tmp/cmux-debug-<tag>.sock)")
if not re.fullmatch(r"/tmp/cmux-debug-[^/]+\.sock", socket_path):
raise cmuxError(f"CMUX_SOCKET must be a tagged debug socket, got: {socket_path!r}")
return socket_path
SOCKET_PATH = _resolve_socket_path()
def _must(cond: bool, msg: str) -> None: