mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-25 03:45:46 -06:00
e5fca42952
* fix(apps): open session desktop on its machine * fix(ui): scope the desktop session lookup to the key's own agent `sessions.list` has no exact-key filter, so the viewer resolves a `session=` parameter by searching for the key and matching it exactly in the response. A key that prefixes longer ones — `agent:main:main` alongside user-named sessions that start the same way — could push the exact row outside a five-row page and silently fall back to the picker. Session keys encode their agent and the list API accepts `agentId`, so scope the search to that agent and widen the page. * refactor(ui): stop parking a session key in the desktop environment id Document-mode inventory failures stashed the requested session key in `environmentId` purely so the Retry button's non-null guard would pass, even though document-mode retry refreshes the inventory and never reads the value. A session key only names a machine once the inventory loads, so it now stays out of `environmentId`, and the retry branch that ignores it runs before the guard. Adds E2E coverage for recovering a session-preselected desktop. * fix(ui): resolve the desktop session with an exact-key lookup The session-preselect path searched `sessions.list` and scoped the search to the key's own agent, but a bounded search cannot rule a key out: 25 newer same-agent sessions sharing the requested key's prefix would push the exact row off the page, and the viewer would report the source as unavailable for a session that exists. `sessions.describe` is the exact-key operation and already projects placement, so the panel calls it directly and the app-root resolver plumbing goes away with it. * refactor(ui): move the desktop document keyboard bridge into its own controller `desktop-panel.ts` crossed the 700-line cap. The mobile keyboard bridge — the padded sentinel, the value diffing that turns composed input into backspaces and text, and the field focus/reset helpers — is a self-contained concern, so it moves into a `DesktopMobileKeyboard` controller alongside the existing fullscreen controller instead of taking a `max-lines` suppression. * test(ios): drain text fields instead of assuming a delete burst lands `testReleaseChatScreenshot` typed a 5-character probe, sent 5 deletes in one `typeText`, then asserted the field was empty. CI dropped one synthetic keystroke under simulator load and the assertion failed with a leftover "f". XCUITest makes no lossless-burst guarantee, so clearing now re-sends against whatever the field actually still holds, bounded. The two gateway-setup fields that overtyped through the same burst use the helper for the same reason. * fix(ui): keep the chat placement owner out of the startup chunk `desktop-document-mode.ts` is imported by bootstrap, so importing the chat placement owner from it pulled the chat page's dependency tree into the startup bundle and pushed startup JS past its gzip budget (331075 B against a 330507 B allowance). The route module now only parses the URL; resolving a session to its machine moves next to the lazily loaded desktop panel, which is the only caller. Startup JS is back to 329710 B.
86 lines
3.3 KiB
Swift
86 lines
3.3 KiB
Swift
import OpenClawKit
|
|
import SwiftUI
|
|
|
|
/// Session-scoped dashboard rendered by the gateway Control UI.
|
|
struct SessionDashboardScreen: View {
|
|
@Environment(NodeAppModel.self) private var appModel
|
|
@Environment(\.dismiss) private var dismiss
|
|
@State private var showsDesktop = false
|
|
let sessionKey: String
|
|
|
|
var body: some View {
|
|
let config = self.appModel.activeGatewayConnectConfig
|
|
let storedOperatorToken = AuthenticatedControlUI.storedOperatorToken(config: config)
|
|
ZStack {
|
|
OpenClawProBackground()
|
|
if let url = Self.dashboardURL(config: config, sessionKey: self.sessionKey) {
|
|
AuthenticatedControlUIWebView(
|
|
url: url,
|
|
authScript: AuthenticatedControlUI.authUserScript(
|
|
config: config,
|
|
pageURL: url,
|
|
storedOperatorToken: storedOperatorToken),
|
|
tls: config?.tls)
|
|
.id(AuthenticatedControlUI.webContentIdentity(
|
|
config: config,
|
|
storedOperatorToken: storedOperatorToken))
|
|
.ignoresSafeArea(.container, edges: .bottom)
|
|
} else {
|
|
self.unavailableCard
|
|
}
|
|
}
|
|
.navigationTitle("Dashboard")
|
|
.navigationBarTitleDisplayMode(.inline)
|
|
.toolbar {
|
|
if self.appModel.isDesktopObserveAvailable {
|
|
ToolbarItem(placement: .topBarTrailing) {
|
|
Button {
|
|
self.showsDesktop = true
|
|
} label: {
|
|
Image(systemName: "display")
|
|
.font(OpenClawType.subheadSemiBold)
|
|
}
|
|
.accessibilityLabel("Open Desktop")
|
|
.accessibilityIdentifier("SessionDashboard.Desktop")
|
|
}
|
|
}
|
|
ToolbarItem(placement: .topBarTrailing) {
|
|
Button {
|
|
self.dismiss()
|
|
} label: {
|
|
Text("Done")
|
|
.font(OpenClawType.subheadSemiBold)
|
|
}
|
|
}
|
|
}
|
|
.navigationDestination(isPresented: self.$showsDesktop) {
|
|
DesktopHubScreen(session: self.sessionKey, usesNativeNavigationChrome: true)
|
|
}
|
|
}
|
|
|
|
private var unavailableCard: some View {
|
|
VStack(spacing: 12) {
|
|
ProIconBadge(systemName: "rectangle.grid.2x2", color: OpenClawBrand.accent)
|
|
Text("Dashboard needs a connected gateway")
|
|
.font(OpenClawType.subheadSemiBold)
|
|
Text("Connect to your gateway to open this session dashboard.")
|
|
.font(OpenClawType.caption)
|
|
.foregroundStyle(.secondary)
|
|
.multilineTextAlignment(.center)
|
|
}
|
|
.padding(24)
|
|
}
|
|
|
|
/// Converts the active gateway WebSocket endpoint into the one-shot
|
|
/// authenticated Control UI route. Credentials stay in the startup script.
|
|
static func dashboardURL(config: GatewayConnectConfig?, sessionKey: String) -> URL? {
|
|
AuthenticatedControlUI.pageURL(
|
|
config: config,
|
|
path: "/chat",
|
|
queryItems: [
|
|
URLQueryItem(name: "session", value: sessionKey),
|
|
URLQueryItem(name: "face", value: "dashboard"),
|
|
])
|
|
}
|
|
}
|