Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions Sources/CodexBar/CodexLocalProjectUsageInspectorSelection.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import Foundation
import Observation

/// Persistent Workspaces-window destination selection. The synthetic
/// All Workspaces destination and concrete project destinations survive window
/// reopen without coupling the raw usage snapshot to presentation state.
@MainActor
@Observable
final class CodexLocalProjectUsageInspectorSelection {
private let defaults: UserDefaults
private let projectKey = "codexLocalProjectUsageWindowSelectedProjectId"

var selectedProjectID: String? {
didSet {
if let selectedProjectID, !selectedProjectID.isEmpty {
self.defaults.set(selectedProjectID, forKey: self.projectKey)
} else {
self.defaults.removeObject(forKey: self.projectKey)
}
}
}

init(defaults: UserDefaults = .standard) {
self.defaults = defaults
self.selectedProjectID = defaults.string(forKey: self.projectKey)
}
}
402 changes: 402 additions & 0 deletions Sources/CodexBar/CodexLocalProjectUsageSubmenuView.swift

Large diffs are not rendered by default.

73 changes: 73 additions & 0 deletions Sources/CodexBar/CodexLocalProjectUsageWindowController.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import AppKit
import SwiftUI

@MainActor
final class CodexLocalProjectUsageWindowController: NSWindowController {
static let selectedTabKey = "codexLocalProjectUsageWindowSelectedTab"
static let frameAutosaveName = "CodexLocalProjectUsageWindow"
static let preferredWidthMigrationKey = "codexLocalProjectUsageWindowPreferredWidthV2"

private let store: UsageStore

init(
store: UsageStore,
settings: SettingsStore,
selection: CodexLocalProjectUsageInspectorSelection)
{
self.store = store
let content = CodexLocalProjectUsageWindowView(store: store, settings: settings, selection: selection)
let window = NSWindow(
contentRect: NSRect(origin: .zero, size: CodexLocalProjectUsageWindowLayout.idealSize),
styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView],
backing: .buffered,
defer: false)
window.title = L("codex_workspaces_window_title")
window.titleVisibility = .visible
window.titlebarAppearsTransparent = true
window.titlebarSeparatorStyle = .none
window.minSize = CodexLocalProjectUsageWindowLayout.minimumSize
window.contentViewController = NSHostingController(rootView: content)
window.isReleasedWhenClosed = false
window.setFrameAutosaveName(Self.frameAutosaveName)
Self.applyPreferredWidthIfNeeded(to: window)
super.init(window: window)
}

@available(*, unavailable)
required init?(coder: NSCoder) {
nil
}

func showAndRefresh() {
self.showWindow(nil)
guard let window = self.window else { return }
window.centerIfNeeded()
window.makeKeyAndOrderFront(nil)
NSApp.activate(ignoringOtherApps: true)
self.store.scheduleCodexLocalProjectUsageRefreshIfNeeded()
}

func showSessions() {
UserDefaults.standard.set(CodexLocalProjectUsageWindowTab.sessions.rawValue, forKey: Self.selectedTabKey)
self.showAndRefresh()
}

private static func applyPreferredWidthIfNeeded(to window: NSWindow) {
let defaults = UserDefaults.standard
guard !defaults.bool(forKey: Self.preferredWidthMigrationKey) else { return }
defaults.set(true, forKey: Self.preferredWidthMigrationKey)

let currentSize = window.contentLayoutRect.size
guard currentSize.width < CodexLocalProjectUsageWindowLayout.idealSize.width else { return }
window.setContentSize(NSSize(
width: CodexLocalProjectUsageWindowLayout.idealSize.width,
height: max(currentSize.height, CodexLocalProjectUsageWindowLayout.idealSize.height)))
}
}

extension NSWindow {
fileprivate func centerIfNeeded() {
guard self.frame.origin == .zero else { return }
self.center()
}
}
Loading