Add screenshot generator

This commit is contained in:
世界
2026-01-08 16:13:20 +08:00
parent 264c4eed81
commit 6b589cb771
57 changed files with 1931 additions and 109 deletions
+6 -1
View File
@@ -32,7 +32,12 @@ open class ApplicationDelegate: NSObject, NSApplicationDelegate, UNUserNotificat
let launchedAsLogInItem =
event?.eventID == kAEOpenApplication &&
event?.paramDescriptor(forKeyword: keyAEPropData)?.enumCodeValue == keyAELaunchedAsLogInItem
if SharedPreferences.inDebug || !launchedAsLogInItem || !SharedPreferences.showMenuBarExtra.getBlocking() || !SharedPreferences.menuBarExtraInBackground.getBlocking() {
let shouldShowWindow = Variant.screenshotMode ||
SharedPreferences.inDebug ||
!launchedAsLogInItem ||
!SharedPreferences.showMenuBarExtra.getBlocking() ||
!SharedPreferences.menuBarExtraInBackground.getBlocking()
if shouldShowWindow {
NSApp.setActivationPolicy(.regular)
NSApp.activate(ignoringOtherApps: true)
} else {
-14
View File
@@ -157,20 +157,6 @@ private class WindowState {
}
}
private struct WindowAccessor: NSViewRepresentable {
let callback: (NSWindow?) -> Void
func makeNSView(context _: Context) -> NSView {
let view = NSView()
DispatchQueue.main.async {
callback(view.window)
}
return view
}
func updateNSView(_: NSView, context _: Context) {}
}
private class WindowCloseDelegate: NSObject, NSWindowDelegate {
var allowClose = false
private let windowState: WindowState
+57 -3
View File
@@ -1,4 +1,6 @@
import AppKit
import ApplicationLibrary
import Foundation
import Library
import SwiftUI
@@ -6,17 +8,49 @@ import SwiftUI
public struct MainView: View {
@Environment(\.controlActiveState) private var controlActiveState
@EnvironmentObject private var environments: ExtensionEnvironments
@StateObject private var viewModel = MainViewModel()
@StateObject private var viewModel: MainViewModel
@State private var showCardManagement = false
@State private var cardConfigurationVersion = 0
@State private var settingsNavigationPath = NavigationPath()
@State private var pendingSettingsPage: SettingsPage?
@State private var didConfigureScreenshotWindow = false
@State private var pendingScreenshotSelection: NavigationPage?
private let profileEditor: (Binding<String>, Bool) -> AnyView = { text, isEditable in
AnyView(ProfileEditorWrapperView(text: text, isEditable: isEditable))
}
public init() {}
private let screenshotDefaultPixelHeight: CGFloat = 1000
public init() {
let initialSelection: NavigationPage = .dashboard
if Variant.screenshotMode,
let pageValue = ProcessInfo.processInfo.environment["SCREENSHOT_PAGE"],
let page = NavigationPage(snapshotValue: pageValue)
{
_pendingScreenshotSelection = State(initialValue: page)
} else {
_pendingScreenshotSelection = State(initialValue: nil)
}
_viewModel = StateObject(wrappedValue: MainViewModel(selection: initialSelection))
}
private func screenshotTargetHeight(baseHeight _: CGFloat, window: NSWindow) -> CGFloat {
let scale = max(window.backingScaleFactor, 1)
if let pixelOverride = ProcessInfo.processInfo.environment["SCREENSHOT_WINDOW_PIXEL_HEIGHT"] {
let trimmed = pixelOverride.trimmingCharacters(in: .whitespacesAndNewlines)
if let height = Double(trimmed), height > 0 {
return CGFloat(height) / scale
}
}
if let override = ProcessInfo.processInfo.environment["SCREENSHOT_WINDOW_HEIGHT"] {
let trimmed = override.trimmingCharacters(in: .whitespacesAndNewlines)
if let height = Double(trimmed), height > 0 {
return CGFloat(height)
}
}
return screenshotDefaultPixelHeight / scale
}
public var body: some View {
NavigationSplitView {
@@ -31,7 +65,27 @@ public struct MainView: View {
.environment(\.settingsNavigationPath, $settingsNavigationPath)
.navigationSplitViewColumnWidth(650)
}
.frame(minHeight: 500)
.frame(minHeight: Variant.screenshotMode ? 0 : 500)
.background(WindowAccessor { window in
guard Variant.screenshotMode, !didConfigureScreenshotWindow, let window else { return }
didConfigureScreenshotWindow = true
DispatchQueue.main.async {
window.hasShadow = true
let baseSize = window.contentLayoutRect.size
let targetHeight = screenshotTargetHeight(baseHeight: baseSize.height, window: window)
let targetSize = NSSize(width: baseSize.width, height: targetHeight)
guard targetSize.width > 0, targetSize.height > 0 else { return }
window.contentMinSize = targetSize
window.contentMaxSize = targetSize
window.minSize = targetSize
window.maxSize = targetSize
window.setContentSize(targetSize)
if let pending = pendingScreenshotSelection, pending != viewModel.selection {
viewModel.selection = pending
pendingScreenshotSelection = nil
}
}
})
.onAppear {
viewModel.onAppear(environments: environments)
}
+6 -1
View File
@@ -6,10 +6,15 @@ import SwiftUI
@MainActor
public class MainViewModel: BaseViewModel {
@Published public var selection = NavigationPage.dashboard
@Published public var selection: NavigationPage
@Published public var importProfile: LibboxProfileContent?
@Published public var importRemoteProfile: LibboxImportRemoteProfile?
public init(selection: NavigationPage = .dashboard) {
self.selection = selection
super.init()
}
public func onAppear(environments: ExtensionEnvironments) {
environments.postReload()
#if !DEBUG
+2 -22
View File
@@ -9,7 +9,7 @@ private struct SidebarContentView: View {
var environments: ExtensionEnvironments
private var hasGroups: Bool {
environments.commandClient.groups?.isEmpty == false
Variant.screenshotMode || environments.commandClient.groups?.isEmpty == false
}
var body: some View {
@@ -77,9 +77,7 @@ public struct SidebarView: View {
}
public var body: some View {
if ApplicationLibrary.inPreview {
previewContent
} else if environments.extensionProfileLoading {
if environments.extensionProfileLoading {
ProgressView()
} else if let profile = environments.extensionProfile {
SidebarContentView(
@@ -93,24 +91,6 @@ public struct SidebarView: View {
}
}
@ViewBuilder
private var previewContent: some View {
List(selection: $localSelection) {
Section(NavigationPage.dashboard.title) {
Label("Overview", systemImage: "text.and.command.macwindow")
.tint(.textColor)
.tag(NavigationPage.dashboard)
NavigationPage.groups.label.tag(NavigationPage.groups)
NavigationPage.connections.label.tag(NavigationPage.connections)
}
ForEach(NavigationPage.macosDefaultPages, id: \.self) { it in
it.label
}
}
.listStyle(.sidebar)
.scrollDisabled(true)
}
@ViewBuilder
private var disconnectedContent: some View {
List(selection: $localSelection) {
+1 -1
View File
@@ -203,7 +203,7 @@ public class StatusBarController: NSObject, NSMenuDelegate {
guard let groups else { return }
let selectableGroups = groups.filter { $0.selectable }
let selectableGroups = groups.filter(\.selectable)
if selectableGroups.isEmpty {
groupsItem?.isHidden = true
return
+29
View File
@@ -0,0 +1,29 @@
import SwiftUI
struct WindowAccessor: NSViewRepresentable {
final class WindowReportingView: NSView {
var onWindowChange: ((NSWindow?) -> Void)?
override func viewDidMoveToWindow() {
super.viewDidMoveToWindow()
DispatchQueue.main.async { [weak self] in
self?.onWindowChange?(self?.window)
}
}
}
let callback: (NSWindow?) -> Void
func makeNSView(context _: Context) -> WindowReportingView {
let view = WindowReportingView()
view.onWindowChange = callback
return view
}
func updateNSView(_ nsView: WindowReportingView, context _: Context) {
nsView.onWindowChange = callback
DispatchQueue.main.async {
callback(nsView.window)
}
}
}