Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ import CmuxNextCompat
/// Gets each push for the page (the view sends it through the bridge) and a completion to call
/// once the page has run it (it paces the next push).
public var deliver: (@MainActor (AgentPaneTransportEvent, _ done: @escaping @MainActor @Sendable () -> Void) -> Void)?
/// Runs right before anything reaches the page: a push here, a script call or a picture
/// (``AgentPaneView/callPageScript(_:arguments:)``, ``AgentPaneView/pageSnapshot()``). A hidden page
/// that froze (the app's hibernation) thaws first, so the work runs now instead of waiting on a
/// page that runs no script (a push's completion paces the next one).
public var beforePageWork: (@MainActor () -> Void)?
/// Counts pushes, so a late completion of a previous connection's push is ignored.
private var deliveries = 0
private var pacerStorage: any AgentPaneTransportPacer
Expand Down Expand Up @@ -406,6 +411,7 @@ import CmuxNextCompat
guard let deliver else { return AgentPaneFlush(delivered: false, more: more) }
deliveries += 1
let delivery = deliveries
beforePageWork?()
deliver(event) { [weak self] in
guard let self, self.deliveries == delivery else { return }
self.pacer.delivered()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,15 @@ extension AgentPaneView {
/// The Chromium page that shows the chat now; nil on WebKit.
public var chromiumPage: (any PageChromiumPage)? { page?.chromiumPage }

/// The page shows in Chromium once its browser is made (``chromiumPage`` is nil until then).
public var waitsForChromium: Bool {
guard let page else { return false }
return page.engineDecision.engine == .cef && !page.showsChromium
}

/// A picture of the page as it draws now; nil when there is none.
public func pageSnapshot() async -> NSImage? {
model.transport.beforePageWork?()
if let page { return await page.snapshot() }
return try? await webView.takeSnapshot(configuration: nil)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ extension AgentPaneView {
/// Runs `body` as an async function body in the chat's page, in whichever engine shows it
/// (``PageWebView/callPageScript(_:arguments:)``).
public func callPageScript(_ body: String, arguments: [String: Any] = [:]) async throws -> Any? {
model.transport.beforePageWork?()
if let page { return try await page.callPageScript(body, arguments: arguments) }
return try await webView.callAsyncJavaScript(body, arguments: arguments, in: nil, contentWorld: .page)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,8 @@ extension AgentTabStore {
tabs?.localHostName = name
return AgentPaneModel.rememberLocalMachineName(AgentTabStore.displayName(name) ?? name)
}
tabs.onPageBuilt = { [weak services] key in services?.cache.hibernation?.agentPages?.pageBuilt(key) }
tabs.onCollectingPages = { [weak services] held in services?.cache.hibernation?.agentPages?.holdAwake(held) }
services.madeAgentTabs = tabs
return tabs
}
Expand Down
16 changes: 14 additions & 2 deletions Packages/macOS/CmuxNext/Sources/CmuxNextApp/AgentTabs.swift
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,11 @@ final class AgentTabStore {
var recycle: ((AgentPaneView) -> Bool)?
/// The prewarmed New Tab page, for a first page that has no view yet (NewTabSparePool).
var takeSpare: (() -> AgentPaneView?)?
/// Tab `key` got a page (built or adopted from the spare), drawn or not: a page built while its
/// tab does not draw starts its hidden clock (`AgentPageHibernation.pageBuilt`).
var onPageBuilt: ((String) -> Void)?
/// Every page is asked what it holds now (true), or that ended (false) (`AgentPageHibernation.holdAwake`).
var onCollectingPages: ((Bool) -> Void)?
/// Closed pages leave the view tree at once; their teardown waits (R81).
let retirer = AgentPageRetirer()
/// The session each tab's page reported, ahead of the store's echo of the bind and across a
Expand Down Expand Up @@ -215,8 +220,13 @@ final class AgentTabStore {
var wakeStates: [String: AgentPaneWakeState] = [:]
/// What the chat pages held at the last quit, for each tab's first page at launch (``AgentPaneRelaunchWake``).
var relaunchWake = AgentPaneRelaunchWake(file: nil)
/// What every chat holds for the next launch (the quit's window step).
func relaunchWakeStates() async -> [String: AgentPaneWakeState] { await relaunchWake.collect(held: wakeStates, pages: views.map { (resolve($0.key), $0.value) }, live: isAgentTab) }
/// What every chat holds for the next launch (the quit's window step). Pages that froze while
/// hidden thaw first and stay awake until it is collected: a frozen page answers nothing.
func relaunchWakeStates() async -> [String: AgentPaneWakeState] {
onCollectingPages?(true)
defer { onCollectingPages?(false) }
return await relaunchWake.collect(held: wakeStates, pages: views.map { (resolve($0.key), $0.value) }, live: isAgentTab)
}
/// The git link of tab `key`: the session host of the tab's own machine (AgentPaneGitReads.swift),
/// so a Cloud or SSH chat reads its folder there; nil answers the page `native.not_connected`. Move to Floating Chat puts a session in Start Agent's panel.
/// The upload for a chat tab's dropped or pasted files, nil when it takes none (``CloudUploads``).
Expand Down Expand Up @@ -389,6 +399,7 @@ final class AgentTabStore {
seed?.holdsOnlyFolder ?? true, wakeStates[key] == nil, !relaunchWake.mayHold(key), let spare = takeSpare?() {
adoptSpare(spare, key: key, page: newTab.page)
track(key, in: store)
onPageBuilt?(key)
return spare
}
let model = AgentPaneModel(
Expand Down Expand Up @@ -421,6 +432,7 @@ final class AgentTabStore {
}
views[key] = view
track(key, in: store)
onPageBuilt?(key)
return view
}

Expand Down
Loading
Loading