Deduplicate high-frequency socket metadata updates

This commit is contained in:
Lawrence Chen 2026-02-20 23:30:59 -08:00
parent 021cc05cf3
commit 68cf29cd2d
3 changed files with 161 additions and 12 deletions

View file

@ -68,6 +68,41 @@ class TerminalController {
private init() {}
nonisolated static func shouldReplaceStatusEntry(
current: SidebarStatusEntry?,
key: String,
value: String,
icon: String?,
color: String?
) -> Bool {
guard let current else { return true }
return current.key != key || current.value != value || current.icon != icon || current.color != color
}
nonisolated static func shouldReplaceProgress(
current: SidebarProgressState?,
value: Double,
label: String?
) -> Bool {
guard let current else { return true }
return current.value != value || current.label != label
}
nonisolated static func shouldReplaceGitBranch(
current: SidebarGitBranchState?,
branch: String,
isDirty: Bool
) -> Bool {
guard let current else { return true }
return current.branch != branch || current.isDirty != isDirty
}
nonisolated static func shouldReplacePorts(current: [Int]?, next: [Int]) -> Bool {
let currentSorted = Array(Set(current ?? [])).sorted()
let nextSorted = Array(Set(next)).sorted()
return currentSorted != nextSorted
}
/// Update which window's TabManager receives socket commands.
/// This is used when the user switches between multiple terminal windows.
func setActiveTabManager(_ tabManager: TabManager?) {
@ -194,7 +229,14 @@ class TerminalController {
guard let workspace = tabManager.tabs.first(where: { $0.id == workspaceId }) else { return }
let validSurfaceIds = Set(workspace.panels.keys)
guard validSurfaceIds.contains(panelId) else { return }
workspace.surfaceListeningPorts[panelId] = ports.isEmpty ? nil : ports
let nextPorts = Array(Set(ports)).sorted()
let currentPorts = workspace.surfaceListeningPorts[panelId] ?? []
guard currentPorts != nextPorts else { return }
if nextPorts.isEmpty {
workspace.surfaceListeningPorts.removeValue(forKey: panelId)
} else {
workspace.surfaceListeningPorts[panelId] = nextPorts
}
workspace.recomputeListeningPorts()
}
}
@ -10421,12 +10463,22 @@ class TerminalController {
result = parsed.options["tab"] != nil ? "ERROR: Tab not found" : "ERROR: No tab selected"
return
}
guard Self.shouldReplaceStatusEntry(
current: tab.statusEntries[key],
key: key,
value: value,
icon: icon,
color: color
) else {
return
}
tab.statusEntries[key] = SidebarStatusEntry(
key: key,
value: value,
icon: icon,
color: color,
timestamp: Date())
timestamp: Date()
)
}
return result
}
@ -10569,6 +10621,9 @@ class TerminalController {
result = parsed.options["tab"] != nil ? "ERROR: Tab not found" : "ERROR: No tab selected"
return
}
guard Self.shouldReplaceProgress(current: tab.progress, value: clamped, label: label) else {
return
}
tab.progress = SidebarProgressState(value: clamped, label: label)
}
return result
@ -10581,7 +10636,9 @@ class TerminalController {
result = "ERROR: Tab not found"
return
}
tab.progress = nil
if tab.progress != nil {
tab.progress = nil
}
}
return result
}
@ -10599,6 +10656,9 @@ class TerminalController {
result = parsed.options["tab"] != nil ? "ERROR: Tab not found" : "ERROR: No tab selected"
return
}
guard Self.shouldReplaceGitBranch(current: tab.gitBranch, branch: branch, isDirty: isDirty) else {
return
}
tab.gitBranch = SidebarGitBranchState(branch: branch, isDirty: isDirty)
}
return result
@ -10611,7 +10671,9 @@ class TerminalController {
result = "ERROR: Tab not found"
return
}
tab.gitBranch = nil
if tab.gitBranch != nil {
tab.gitBranch = nil
}
}
return result
}
@ -10628,6 +10690,7 @@ class TerminalController {
}
ports.append(port)
}
let normalizedPorts = Array(Set(ports)).sorted()
var result = "OK"
DispatchQueue.main.sync {
@ -10664,7 +10727,11 @@ class TerminalController {
return
}
tab.surfaceListeningPorts[surfaceId] = ports
guard Self.shouldReplacePorts(current: tab.surfaceListeningPorts[surfaceId], next: normalizedPorts) else {
return
}
tab.surfaceListeningPorts[surfaceId] = normalizedPorts
tab.recomputeListeningPorts()
}
return result
@ -10744,11 +10811,15 @@ class TerminalController {
result = "ERROR: Panel not found '\(surfaceId.uuidString)'"
return
}
tab.surfaceListeningPorts.removeValue(forKey: surfaceId)
if tab.surfaceListeningPorts.removeValue(forKey: surfaceId) != nil {
tab.recomputeListeningPorts()
}
} else {
tab.surfaceListeningPorts.removeAll()
if !tab.surfaceListeningPorts.isEmpty {
tab.surfaceListeningPorts.removeAll()
tab.recomputeListeningPorts()
}
}
tab.recomputeListeningPorts()
}
return result
}
@ -10792,6 +10863,7 @@ class TerminalController {
return
}
guard tab.surfaceTTYNames[surfaceId] != ttyName else { return }
tab.surfaceTTYNames[surfaceId] = ttyName
PortScanner.shared.registerTTY(workspaceId: tab.id, panelId: surfaceId, ttyName: ttyName)
}
@ -10799,15 +10871,14 @@ class TerminalController {
}
private func portsKick(_ args: String) -> String {
let parsed = parseOptions(args)
var result = "OK"
DispatchQueue.main.sync {
guard let tab = resolveTabForReport(args) else {
let parsed = parseOptions(args)
result = parsed.options["tab"] != nil ? "ERROR: Tab not found" : "ERROR: No tab selected"
return
}
let parsed = parseOptions(args)
let panelArg = parsed.options["panel"] ?? parsed.options["surface"]
let surfaceId: UUID
if let panelArg {

View file

@ -559,7 +559,7 @@ final class Workspace: Identifiable, ObservableObject {
panelDirectories[panelId] = trimmed
}
// Update current directory if this is the focused panel
if panelId == focusedPanelId {
if panelId == focusedPanelId, currentDirectory != trimmed {
currentDirectory = trimmed
}
}
@ -616,7 +616,10 @@ final class Workspace: Identifiable, ObservableObject {
func recomputeListeningPorts() {
let unique = Set(surfaceListeningPorts.values.flatMap { $0 })
listeningPorts = unique.sorted()
let next = unique.sorted()
if listeningPorts != next {
listeningPorts = next
}
}
// MARK: - Panel Operations

View file

@ -3008,3 +3008,78 @@ final class BrowserHostWhitelistTests: XCTestCase {
XCTAssertTrue(BrowserLinkOpenSettings.hostMatchesWhitelist("xn--bcher-kva.example", defaults: defaults))
}
}
final class TerminalControllerSidebarDedupeTests: XCTestCase {
func testShouldReplaceStatusEntryReturnsFalseForUnchangedPayload() {
let current = SidebarStatusEntry(
key: "agent",
value: "idle",
icon: "bolt",
color: "#ffffff",
timestamp: Date(timeIntervalSince1970: 123)
)
XCTAssertFalse(
TerminalController.shouldReplaceStatusEntry(
current: current,
key: "agent",
value: "idle",
icon: "bolt",
color: "#ffffff"
)
)
}
func testShouldReplaceStatusEntryReturnsTrueWhenValueChanges() {
let current = SidebarStatusEntry(
key: "agent",
value: "idle",
icon: "bolt",
color: "#ffffff",
timestamp: Date(timeIntervalSince1970: 123)
)
XCTAssertTrue(
TerminalController.shouldReplaceStatusEntry(
current: current,
key: "agent",
value: "running",
icon: "bolt",
color: "#ffffff"
)
)
}
func testShouldReplaceProgressReturnsFalseForUnchangedPayload() {
XCTAssertFalse(
TerminalController.shouldReplaceProgress(
current: SidebarProgressState(value: 0.42, label: "indexing"),
value: 0.42,
label: "indexing"
)
)
}
func testShouldReplaceGitBranchReturnsFalseForUnchangedPayload() {
XCTAssertFalse(
TerminalController.shouldReplaceGitBranch(
current: SidebarGitBranchState(branch: "main", isDirty: true),
branch: "main",
isDirty: true
)
)
}
func testShouldReplacePortsIgnoresOrderAndDuplicates() {
XCTAssertFalse(
TerminalController.shouldReplacePorts(
current: [9229, 3000],
next: [3000, 9229, 3000]
)
)
XCTAssertTrue(
TerminalController.shouldReplacePorts(
current: [9229, 3000],
next: [3000]
)
)
}
}