import SwiftUI import AppKit import OnyxDesign import OnyxWidgetKit /// Der Verlauf der Zwischenablage, zum Zurückholen. public struct ClipboardView: View { private let store: ClipboardStore private let compact: Bool @State private var hovered: UUID? @State private var justCopied: UUID? public init(store: ClipboardStore, compact: Bool = false) { self.store = store self.compact = compact } public var body: some View { VStack(alignment: .leading, spacing: Onyx.Space.sm) { if store.items.isEmpty { OnyxNotice(symbol: "doc.on.clipboard", text: store.isRecording ? String(localized: "clipboard.empty", bundle: .module) : String(localized: "clipboard.paused", bundle: .module)) } else { ScrollView { LazyVStack(spacing: 0) { ForEach(Array(store.items.prefix(compact ? 5 : 40).enumerated()), id: \.element.id) { index, item in if index > 0 { OnyxHairline() } row(item) } } } .scrollIndicators(.never) } } .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) } private func row(_ item: ClipboardItem) -> some View { // Die ganze Zeile ist der Knopf. Ein kleines Ziel neben dem Text wäre // genauer zu treffen zu verlangen, als die Aufgabe es hergibt. Button { copy(item) } label: { HStack(spacing: Onyx.Space.row) { Image(systemName: item.kind == .image ? "photo" : "text.alignleft") .font(.system(size: Onyx.Symbol.small)) .foregroundStyle(Onyx.Color.textTertiary) .frame(width: 16) Text(item.preview) .font(Onyx.Font.footnote) .foregroundStyle(Onyx.Color.textPrimary) .lineLimit(1) .truncationMode(.tail) Spacer(minLength: Onyx.Space.xs) if justCopied == item.id { // Eine Rückmeldung, die nichts verschiebt: ohne sie sieht // ein Klick, der die Ablage füllt, wie ein Klick aus, der // nichts getan hat. Image(systemName: "checkmark") .font(.system(size: Onyx.Symbol.caption)) .foregroundStyle(Onyx.Color.positive) } else if hovered == item.id { Button { store.remove(item) } label: { Image(systemName: "xmark.circle.fill") .font(.system(size: Onyx.Symbol.small)) .foregroundStyle(Onyx.Color.textTertiary) } .buttonStyle(.plain) } } .padding(.horizontal, Onyx.Space.row) .padding(.vertical, compact ? Onyx.Space.sm : Onyx.Space.md) .contentShape(.rect) } .buttonStyle(.plain) .background(hovered == item.id ? Onyx.Color.elevated : .clear) .onHover { hovered = $0 ? item.id : (hovered == item.id ? nil : hovered) } } private func copy(_ item: ClipboardItem) { store.copyBack(item) justCopied = item.id Task { try? await Task.sleep(for: .seconds(1.2)) if justCopied == item.id { justCopied = nil } } } } public struct ClipboardWidget: OnyxWidget { public let id = "clipboard" public var displayName: String { String(localized: "clipboard.name", bundle: .module) } public let symbolName = "doc.on.clipboard" private let store: ClipboardStore public init(store: ClipboardStore) { self.store = store } public func makeView() -> AnyView { AnyView(ClipboardView(store: store)) } }