cocottetech/@platform/codebase/@features/ai-copilot/cockpit-kit/Sources/CocotteCockpitKit/Components.swift
Natalie d114d9d375 feat(cockpit-kit): 📸 add bump screenshot overlay
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-10 05:00:56 -07:00

132 lines
4.4 KiB
Swift

import SwiftUI
#if canImport(UIKit)
import UIKit
#elseif canImport(AppKit)
import AppKit
#endif
// Shared cockpit components every color/space comes from @Environment(\.tokens).
/// Decode image bytes to a SwiftUI `Image` on whichever Apple UI framework is
/// present. NSImage/UIImage decode HEIC + PNG natively. Shared by the asset
/// library tiles and the bump-screenshot card.
func decodeImage(_ data: Data) -> Image? {
#if canImport(UIKit)
guard let ui = UIImage(data: data) else { return nil }
return Image(uiImage: ui)
#elseif canImport(AppKit)
guard let ns = NSImage(data: data) else { return nil }
return Image(nsImage: ns)
#else
return nil
#endif
}
struct SectionLabel: View {
@Environment(\.tokens) private var t
let text: String
var body: some View {
Text(text.uppercased())
.font(.system(size: 11, weight: .semibold))
.tracking(0.8)
.foregroundStyle(t.ink3)
}
}
struct SurfaceChip: View {
@Environment(\.tokens) private var t
let surface: Surface
var body: some View {
HStack(spacing: 4) {
Text(surface.glyph).font(.system(size: 10, weight: .bold))
Text(surface.label).font(.system(size: 11, weight: .medium))
}
.padding(.horizontal, 7).padding(.vertical, 3)
.foregroundStyle(surface.isNSFWAnchor ? t.anchorFg : t.ink2)
.background(surface.isNSFWAnchor ? t.anchorBg : t.bgElev)
.clipShape(RoundedRectangle(cornerRadius: 6))
}
}
struct TosBadge: View {
@Environment(\.tokens) private var t
let tos: TosStatus
var body: some View {
let label: String = { switch tos { case .ok: return "K3 ok"; case .flagged: return "flagged"; case .blocked: return "blocked" } }()
Text(label)
.font(.system(size: 10, weight: .semibold))
.foregroundStyle(t.tos(tos))
.padding(.horizontal, 6).padding(.vertical, 2)
.background(t.tos(tos).opacity(0.14))
.clipShape(Capsule())
}
}
struct ConfidenceBadge: View {
@Environment(\.tokens) private var t
let value: Double
var body: some View {
Text(String(format: "%.2f", value))
.font(.system(size: 11, weight: .semibold, design: .monospaced))
.foregroundStyle(value >= 0.85 ? t.ok : (value >= 0.7 ? t.warn : t.accent))
}
}
// ImageRenderer can't capture ScrollView content; this is a real ScrollView in
// the app but a plain stack under the render flag, so headless renders are full.
private struct RenderModeKey: EnvironmentKey { static let defaultValue = false }
extension EnvironmentValues {
public var renderMode: Bool {
get { self[RenderModeKey.self] }
set { self[RenderModeKey.self] = newValue }
}
}
struct Scroll<Content: View>: View {
@Environment(\.renderMode) private var renderMode
@ViewBuilder var content: () -> Content
var body: some View {
if renderMode {
content()
} else {
ScrollView { content() }
}
}
}
// Pure-SwiftUI themed segmented control (no AppKit Picker themable + renders
// in ImageRenderer).
struct Segmented<T: Hashable>: View {
@Environment(\.tokens) private var t
let options: [(String, T)]
@Binding var selection: T
var body: some View {
HStack(spacing: 2) {
ForEach(options, id: \.1) { label, value in
Text(label)
.font(.system(size: 11, weight: selection == value ? .semibold : .regular))
.foregroundStyle(selection == value ? t.accentFg : t.ink3)
.padding(.horizontal, 10).padding(.vertical, 4)
.background(selection == value ? t.accent : Color.clear)
.clipShape(Capsule())
.contentShape(Capsule())
.onTapGesture { selection = value }
}
}
.padding(3).background(t.bgElev).clipShape(Capsule())
}
}
struct Card<Content: View>: View {
@Environment(\.tokens) private var t
var elevated = false
@ViewBuilder var content: () -> Content
var body: some View {
content()
.padding(14)
.background(elevated ? t.bgElev : t.bgCard)
.clipShape(RoundedRectangle(cornerRadius: t.radius))
.overlay(RoundedRectangle(cornerRadius: t.radius).strokeBorder(t.line, lineWidth: 1))
}
}