mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-02 10:51:33 +00:00
fix(macos): keep permissions reachable on short screens (#112507)
* fix(macos): keep permissions reachable on short screens * fix(macos): preserve onboarding i18n inventory
This commit is contained in:
committed by
GitHub
parent
735a6acd56
commit
9231bcb38f
@@ -19,7 +19,7 @@ extension OnboardingView {
|
||||
case 4:
|
||||
self.memoryImportPage(contentHeight: contentHeight)
|
||||
case 5:
|
||||
self.permissionsPage(contentHeight: contentHeight)
|
||||
self.permissionsPage()
|
||||
case 9:
|
||||
self.readyPage()
|
||||
default:
|
||||
@@ -712,41 +712,41 @@ extension OnboardingView {
|
||||
.buttonStyle(.plain)
|
||||
}
|
||||
|
||||
func permissionsPage(contentHeight: CGFloat) -> some View {
|
||||
// Fixed layout (no ScrollView): sorted by importance and sized so all
|
||||
// permissions stay visible at once — no scrollbars during onboarding.
|
||||
VStack(spacing: 12) {
|
||||
HStack(spacing: 8) {
|
||||
Text("Grant permissions")
|
||||
.font(.largeTitle.weight(.semibold))
|
||||
if self.isRequesting {
|
||||
ProgressView()
|
||||
.controlSize(.small)
|
||||
func permissionsPage() -> some View {
|
||||
onboardingPage {
|
||||
VStack(spacing: 12) {
|
||||
// Keep intro and rows in one document so short windows can reveal every permission.
|
||||
HStack(spacing: 8) {
|
||||
Text("Grant permissions")
|
||||
.font(.largeTitle.weight(.semibold))
|
||||
if self.isRequesting {
|
||||
ProgressView()
|
||||
.controlSize(.small)
|
||||
}
|
||||
}
|
||||
}
|
||||
Text(
|
||||
"These macOS permissions let OpenClaw automate apps and capture context on this Mac. " +
|
||||
"Status updates automatically.")
|
||||
.font(.body)
|
||||
.foregroundStyle(.secondary)
|
||||
.multilineTextAlignment(.center)
|
||||
.frame(maxWidth: 520)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
Text(
|
||||
"These macOS permissions let OpenClaw automate apps and capture context on this Mac. " +
|
||||
"Status updates automatically.")
|
||||
.font(.body)
|
||||
.foregroundStyle(.secondary)
|
||||
.multilineTextAlignment(.center)
|
||||
.frame(maxWidth: 520)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
|
||||
self.onboardingCard(spacing: 4, padding: 12) {
|
||||
ForEach(Capability.importanceOrdered, id: \.self) { cap in
|
||||
PermissionRow(
|
||||
capability: cap,
|
||||
status: self.permissionMonitor.status[cap] ?? .notGranted,
|
||||
compact: true)
|
||||
{
|
||||
Task { await self.request(cap) }
|
||||
self.onboardingCard(spacing: 4, padding: 12) {
|
||||
ForEach(Capability.importanceOrdered, id: \.self) { cap in
|
||||
PermissionRow(
|
||||
capability: cap,
|
||||
status: self.permissionMonitor.status[cap] ?? .notGranted,
|
||||
compact: true)
|
||||
{
|
||||
Task { await self.request(cap) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding(.horizontal, 28)
|
||||
.frame(width: self.pageWidth, height: contentHeight, alignment: .top)
|
||||
// The root onboarding layout keeps navigation outside this scrollable document.
|
||||
}
|
||||
|
||||
func cliPage() -> some View {
|
||||
|
||||
@@ -40,7 +40,7 @@ extension OnboardingView {
|
||||
_ = view.connectionPage()
|
||||
_ = view.aiSetupPage(contentHeight: contentHeight)
|
||||
_ = view.memoryImportPage(contentHeight: contentHeight)
|
||||
_ = view.permissionsPage(contentHeight: contentHeight)
|
||||
_ = view.permissionsPage()
|
||||
_ = view.cliPage()
|
||||
_ = view.readyPage()
|
||||
|
||||
|
||||
@@ -73,6 +73,25 @@ struct OnboardingViewSmokeTests {
|
||||
#expect(short < preferred)
|
||||
}
|
||||
|
||||
@Test func `permissions page scrolls when the onboarding window is short`() throws {
|
||||
let state = AppState(preview: true)
|
||||
let view = OnboardingView(state: state)
|
||||
let hosting = NSHostingView(rootView: view.permissionsPage())
|
||||
let contentHeight = OnboardingView.contentHeight(
|
||||
for: OnboardingView.minimumWindowHeight,
|
||||
usesCompactHero: false)
|
||||
hosting.frame = NSRect(
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: OnboardingView.windowWidth,
|
||||
height: contentHeight)
|
||||
hosting.layoutSubtreeIfNeeded()
|
||||
|
||||
let scrollView = try #require(Self.firstDescendant(of: NSScrollView.self, in: hosting))
|
||||
#expect(contentHeight == 303)
|
||||
#expect(scrollView.documentView != nil)
|
||||
}
|
||||
|
||||
@Test func `local page order includes memory import only while eligible`() {
|
||||
let configuredOrder = OnboardingView.pageOrder(
|
||||
for: .local,
|
||||
@@ -589,4 +608,12 @@ struct OnboardingViewSmokeTests {
|
||||
== [.appleScript, .accessibility, .screenRecording])
|
||||
#expect(Capability.importanceOrdered.last == Capability.location)
|
||||
}
|
||||
|
||||
private static func firstDescendant<T: NSView>(of type: T.Type, in view: NSView) -> T? {
|
||||
if let match = view as? T { return match }
|
||||
for child in view.subviews {
|
||||
if let match = self.firstDescendant(of: type, in: child) { return match }
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user