Skip to content

Commit bf89552

Browse files
Solvely-ColinCopilotjoshavant
authored
Improve iPad and iPhone control surfaces (#91557)
* feat(ios): expand iPad layout support Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: improve iPad and iPhone control surfaces * fix: preserve workboard dispatch compatibility * fix: keep Talk reachable on iPad * fix: add universal iPad app icons * fix: address ready-review iOS feedback * fix: avoid workboard board id shadowing * fix ios sidebar separators --------- Co-authored-by: Solvely-Colin <211764741+Solvely-Colin@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: joshavant <830519+joshavant@users.noreply.github.com>
1 parent a8d33f2 commit bf89552

50 files changed

Lines changed: 8836 additions & 402 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
5.83 KB
Loading
6.44 KB
Loading
1.46 KB
Loading
2.9 KB
Loading
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"images":[{"size":"60x60","expected-size":"180","filename":"180.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"40x40","expected-size":"80","filename":"80.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"40x40","expected-size":"120","filename":"120.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"60x60","expected-size":"120","filename":"120.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"57x57","expected-size":"57","filename":"57.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"1x"},{"size":"29x29","expected-size":"58","filename":"58.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"29x29","expected-size":"29","filename":"29.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"1x"},{"size":"29x29","expected-size":"87","filename":"87.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"57x57","expected-size":"114","filename":"114.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"20x20","expected-size":"40","filename":"40.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"20x20","expected-size":"60","filename":"60.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"1024x1024","filename":"1024.png","expected-size":"1024","idiom":"ios-marketing","folder":"Assets.xcassets/AppIcon.appiconset/","scale":"1x"},{"idiom":"watch","filename":"172.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"38mm","scale":"2x","size":"86x86","expected-size":"172","role":"quickLook"},{"idiom":"watch","filename":"80.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"38mm","scale":"2x","size":"40x40","expected-size":"80","role":"appLauncher"},{"idiom":"watch","filename":"88.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"40mm","scale":"2x","size":"44x44","expected-size":"88","role":"appLauncher"},{"idiom":"watch","filename":"102.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"45mm","scale":"2x","size":"51x51","expected-size":"102","role":"appLauncher"},{"idiom":"watch","filename":"108.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"49mm","scale":"2x","size":"54x54","expected-size":"108","role":"appLauncher"},{"idiom":"watch","filename":"92.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"41mm","scale":"2x","size":"46x46","expected-size":"92","role":"appLauncher"},{"idiom":"watch","filename":"100.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"44mm","scale":"2x","size":"50x50","expected-size":"100","role":"appLauncher"},{"idiom":"watch","filename":"196.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"42mm","scale":"2x","size":"98x98","expected-size":"196","role":"quickLook"},{"idiom":"watch","filename":"216.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"44mm","scale":"2x","size":"108x108","expected-size":"216","role":"quickLook"},{"idiom":"watch","filename":"234.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"45mm","scale":"2x","size":"117x117","expected-size":"234","role":"quickLook"},{"idiom":"watch","filename":"258.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"49mm","scale":"2x","size":"129x129","expected-size":"258","role":"quickLook"},{"idiom":"watch","filename":"48.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"38mm","scale":"2x","size":"24x24","expected-size":"48","role":"notificationCenter"},{"idiom":"watch","filename":"55.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"42mm","scale":"2x","size":"27.5x27.5","expected-size":"55","role":"notificationCenter"},{"idiom":"watch","filename":"66.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"45mm","scale":"2x","size":"33x33","expected-size":"66","role":"notificationCenter"},{"size":"29x29","expected-size":"87","filename":"87.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"watch","role":"companionSettings","scale":"3x"},{"size":"29x29","expected-size":"58","filename":"58.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"watch","role":"companionSettings","scale":"2x"},{"size":"1024x1024","expected-size":"1024","filename":"1024.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"watch-marketing","scale":"1x"}]}
1+
{"images":[{"size":"60x60","expected-size":"180","filename":"180.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"40x40","expected-size":"80","filename":"80.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"40x40","expected-size":"120","filename":"120.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"60x60","expected-size":"120","filename":"120.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"57x57","expected-size":"57","filename":"57.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"1x"},{"size":"29x29","expected-size":"58","filename":"58.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"29x29","expected-size":"29","filename":"29.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"1x"},{"size":"29x29","expected-size":"87","filename":"87.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"57x57","expected-size":"114","filename":"114.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"20x20","expected-size":"40","filename":"40.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"20x20","expected-size":"60","filename":"60.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"20x20","expected-size":"20","filename":"20.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"20x20","expected-size":"40","filename":"40.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"29x29","expected-size":"29","filename":"29.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"29x29","expected-size":"58","filename":"58.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"40x40","expected-size":"40","filename":"40.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"40x40","expected-size":"80","filename":"80.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"76x76","expected-size":"76","filename":"76.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"76x76","expected-size":"152","filename":"152.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"83.5x83.5","expected-size":"167","filename":"167.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"1024x1024","filename":"1024.png","expected-size":"1024","idiom":"ios-marketing","folder":"Assets.xcassets/AppIcon.appiconset/","scale":"1x"},{"idiom":"watch","filename":"172.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"38mm","scale":"2x","size":"86x86","expected-size":"172","role":"quickLook"},{"idiom":"watch","filename":"80.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"38mm","scale":"2x","size":"40x40","expected-size":"80","role":"appLauncher"},{"idiom":"watch","filename":"88.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"40mm","scale":"2x","size":"44x44","expected-size":"88","role":"appLauncher"},{"idiom":"watch","filename":"102.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"45mm","scale":"2x","size":"51x51","expected-size":"102","role":"appLauncher"},{"idiom":"watch","filename":"108.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"49mm","scale":"2x","size":"54x54","expected-size":"108","role":"appLauncher"},{"idiom":"watch","filename":"92.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"41mm","scale":"2x","size":"46x46","expected-size":"92","role":"appLauncher"},{"idiom":"watch","filename":"100.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"44mm","scale":"2x","size":"50x50","expected-size":"100","role":"appLauncher"},{"idiom":"watch","filename":"196.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"42mm","scale":"2x","size":"98x98","expected-size":"196","role":"quickLook"},{"idiom":"watch","filename":"216.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"44mm","scale":"2x","size":"108x108","expected-size":"216","role":"quickLook"},{"idiom":"watch","filename":"234.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"45mm","scale":"2x","size":"117x117","expected-size":"234","role":"quickLook"},{"idiom":"watch","filename":"258.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"49mm","scale":"2x","size":"129x129","expected-size":"258","role":"quickLook"},{"idiom":"watch","filename":"48.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"38mm","scale":"2x","size":"24x24","expected-size":"48","role":"notificationCenter"},{"idiom":"watch","filename":"55.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"42mm","scale":"2x","size":"27.5x27.5","expected-size":"55","role":"notificationCenter"},{"idiom":"watch","filename":"66.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"45mm","scale":"2x","size":"33x33","expected-size":"66","role":"notificationCenter"},{"size":"29x29","expected-size":"87","filename":"87.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"watch","role":"companionSettings","scale":"3x"},{"size":"29x29","expected-size":"58","filename":"58.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"watch","role":"companionSettings","scale":"2x"},{"size":"1024x1024","expected-size":"1024","filename":"1024.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"watch-marketing","scale":"1x"}]}

apps/ios/Sources/Design/AgentProDreamingDestination.swift

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import SwiftUI
44

55
struct AgentProDreamingDestination: View {
66
@Environment(NodeAppModel.self) private var appModel
7+
let headerLeadingAction: OpenClawSidebarHeaderAction?
78
let overview: AgentOverviewSnapshot?
89
let gatewayConnected: Bool
910
let overviewLoading: Bool
@@ -20,6 +21,7 @@ struct AgentProDreamingDestination: View {
2021
OpenClawProBackground()
2122
ScrollView {
2223
VStack(alignment: .leading, spacing: 16) {
24+
self.header
2325
self.detailSummaryCard(
2426
icon: "moon",
2527
title: "Dreaming",
@@ -57,6 +59,23 @@ struct AgentProDreamingDestination: View {
5759
.navigationBarTitleDisplayMode(.inline)
5860
}
5961

62+
@ViewBuilder
63+
private var header: some View {
64+
if let headerLeadingAction {
65+
OpenClawAdaptiveHeaderRow(
66+
title: "Dreaming",
67+
subtitle: self.dreamingDetail,
68+
titleFont: .title3.weight(.semibold),
69+
subtitleFont: .callout)
70+
{
71+
OpenClawSidebarHeaderLeadingSlot(action: headerLeadingAction)
72+
} accessory: {
73+
EmptyView()
74+
}
75+
.padding(.horizontal, OpenClawProMetric.pagePadding)
76+
}
77+
}
78+
6079
private enum DreamAction: String, CaseIterable, Identifiable {
6180
case backfill
6281
case repair

apps/ios/Sources/Design/AgentProNodesDestination.swift

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import SwiftUI
33
import UIKit
44

55
struct AgentProNodesDestination: View {
6+
let headerLeadingAction: OpenClawSidebarHeaderAction?
67
let overview: AgentOverviewSnapshot?
78
let gatewayConnected: Bool
89
let agentCount: Int
@@ -16,6 +17,7 @@ struct AgentProNodesDestination: View {
1617
OpenClawProBackground()
1718
ScrollView {
1819
VStack(alignment: .leading, spacing: 16) {
20+
self.header
1921
self.summaryCard
2022
self.totalsCard
2123
self.nodesList
@@ -27,16 +29,33 @@ struct AgentProNodesDestination: View {
2729
}
2830
.safeAreaPadding(.bottom, OpenClawProMetric.bottomScrollInset)
2931
}
30-
.navigationTitle("Nodes")
32+
.navigationTitle("Instances")
3133
.navigationBarTitleDisplayMode(.inline)
3234
}
3335

36+
@ViewBuilder
37+
private var header: some View {
38+
if let headerLeadingAction {
39+
OpenClawAdaptiveHeaderRow(
40+
title: "Instances",
41+
subtitle: self.instancesDetail,
42+
titleFont: .title3.weight(.semibold),
43+
subtitleFont: .callout)
44+
{
45+
OpenClawSidebarHeaderLeadingSlot(action: headerLeadingAction)
46+
} accessory: {
47+
EmptyView()
48+
}
49+
.padding(.horizontal, OpenClawProMetric.pagePadding)
50+
}
51+
}
52+
3453
private var summaryCard: some View {
3554
ProCard {
3655
HStack(spacing: 12) {
3756
ProIconBadge(systemName: "display", color: self.instancesColor)
3857
VStack(alignment: .leading, spacing: 3) {
39-
Text("Nodes")
58+
Text("Instances")
4059
.font(.headline)
4160
Text(self.instancesDetail)
4261
.font(.caption)
@@ -70,16 +89,16 @@ struct AgentProNodesDestination: View {
7089

7190
private var nodesList: some View {
7291
VStack(alignment: .leading, spacing: 8) {
73-
ProSectionHeader(title: "Connected Nodes")
92+
ProSectionHeader(title: "Connected Instances")
7493
ProCard(padding: 0) {
7594
let nodes = self.sortedPresenceEntries
7695
if nodes.isEmpty {
7796
self.emptyRow(
7897
icon: "display",
79-
title: self.gatewayConnected ? "No nodes connected" : "Nodes unavailable",
98+
title: self.gatewayConnected ? "No instances connected" : "Instances unavailable",
8099
detail: self.gatewayConnected
81100
? "The gateway did not report any system presence entries."
82-
: "Connect a gateway to inspect connected nodes.")
101+
: "Connect a gateway to inspect connected instances.")
83102
.padding(14)
84103
} else {
85104
VStack(spacing: 0) {
@@ -114,7 +133,7 @@ struct AgentProNodesDestination: View {
114133
HStack(alignment: .top, spacing: 12) {
115134
ProIconBadge(systemName: Self.presenceIcon(entry), color: Self.presenceColor(entry))
116135
VStack(alignment: .leading, spacing: 4) {
117-
Text(Self.presenceLabel(entry) ?? "Node")
136+
Text(Self.presenceLabel(entry) ?? "Instance")
118137
.font(.subheadline.weight(.semibold))
119138
.lineLimit(1)
120139
Text(Self.presenceDetail(entry))
@@ -153,7 +172,7 @@ struct AgentProNodesDestination: View {
153172
HStack(spacing: 12) {
154173
ProIconBadge(systemName: Self.presenceIcon(entry), color: Self.presenceColor(entry))
155174
VStack(alignment: .leading, spacing: 3) {
156-
Text(Self.presenceLabel(entry) ?? "Node")
175+
Text(Self.presenceLabel(entry) ?? "Instance")
157176
.font(.headline)
158177
Text(Self.presenceDetail(entry))
159178
.font(.caption)
@@ -192,7 +211,7 @@ struct AgentProNodesDestination: View {
192211
}
193212
.safeAreaPadding(.bottom, OpenClawProMetric.bottomScrollInset)
194213
}
195-
.navigationTitle(Self.presenceLabel(entry) ?? "Node")
214+
.navigationTitle(Self.presenceLabel(entry) ?? "Instance")
196215
.navigationBarTitleDisplayMode(.inline)
197216
}
198217

apps/ios/Sources/Design/AgentProTab+Destinations.swift

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@ extension AgentProTab {
66
@ViewBuilder
77
func destination(for route: AgentRoute) -> some View {
88
switch route {
9+
case .agents:
10+
self.agentsDestination
911
case .skills:
1012
self.skillsDestination
11-
case .nodes:
12-
self.nodesDestination
13+
case .instances:
14+
self.instancesDestination
1315
case .cron:
1416
self.cronDestination
1517
case .usage:
@@ -19,6 +21,26 @@ extension AgentProTab {
1921
}
2022
}
2123

24+
var agentsDestination: some View {
25+
ZStack {
26+
OpenClawProBackground()
27+
ScrollView {
28+
VStack(alignment: .leading, spacing: 16) {
29+
self.rosterHeader
30+
self.agentFilters
31+
self.agentsSection
32+
}
33+
.padding(.vertical, 18)
34+
}
35+
.refreshable {
36+
await self.refreshOverview(force: true)
37+
}
38+
.safeAreaPadding(.bottom, OpenClawProMetric.bottomScrollInset)
39+
}
40+
.navigationTitle("Agents")
41+
.navigationBarTitleDisplayMode(.inline)
42+
}
43+
2244
var skillsDestination: some View {
2345
ZStack {
2446
OpenClawProBackground()
@@ -46,8 +68,9 @@ extension AgentProTab {
4668
.navigationBarTitleDisplayMode(.inline)
4769
}
4870

49-
var nodesDestination: some View {
71+
var instancesDestination: some View {
5072
AgentProNodesDestination(
73+
headerLeadingAction: self.directHeaderLeadingAction(for: .instances),
5174
overview: self.overview,
5275
gatewayConnected: self.gatewayConnected,
5376
agentCount: self.appModel.gatewayAgents.count,
@@ -64,6 +87,10 @@ extension AgentProTab {
6487
OpenClawProBackground()
6588
ScrollView {
6689
VStack(alignment: .leading, spacing: 16) {
90+
self.directHeader(
91+
for: .cron,
92+
title: "Cron Jobs",
93+
subtitle: self.cronDetail)
6794
self.detailSummaryCard(
6895
icon: "clock.arrow.circlepath",
6996
title: "Cron Jobs",
@@ -89,6 +116,10 @@ extension AgentProTab {
89116
OpenClawProBackground()
90117
ScrollView {
91118
VStack(alignment: .leading, spacing: 16) {
119+
self.directHeader(
120+
for: .usage,
121+
title: "Usage",
122+
subtitle: self.usageDetail)
92123
self.detailSummaryCard(
93124
icon: "chart.line.uptrend.xyaxis",
94125
title: "Usage",
@@ -111,6 +142,7 @@ extension AgentProTab {
111142

112143
var dreamingDestination: some View {
113144
AgentProDreamingDestination(
145+
headerLeadingAction: self.directHeaderLeadingAction(for: .dreaming),
114146
overview: self.overview,
115147
gatewayConnected: self.gatewayConnected,
116148
overviewLoading: self.overviewLoading,
@@ -122,6 +154,27 @@ extension AgentProTab {
122154
})
123155
}
124156

157+
@ViewBuilder
158+
func directHeader(for route: AgentRoute, title: String, subtitle: String) -> some View {
159+
if let headerLeadingAction = self.directHeaderLeadingAction(for: route) {
160+
OpenClawAdaptiveHeaderRow(
161+
title: title,
162+
subtitle: subtitle,
163+
titleFont: .title3.weight(.semibold),
164+
subtitleFont: .callout)
165+
{
166+
OpenClawSidebarHeaderLeadingSlot(action: headerLeadingAction)
167+
} accessory: {
168+
EmptyView()
169+
}
170+
.padding(.horizontal, OpenClawProMetric.pagePadding)
171+
}
172+
}
173+
174+
func directHeaderLeadingAction(for route: AgentRoute) -> OpenClawSidebarHeaderAction? {
175+
self.directRoute == route ? self.headerLeadingAction : nil
176+
}
177+
125178
func detailSummaryCard(
126179
icon: String,
127180
title: String,

0 commit comments

Comments
 (0)