import SwiftUI import SstUI @main struct GalleryApp: App { init() { // `SstGallery --snapshot out.png` renders both finishes to a PNG and exits (for checking without a screen grab). let args = CommandLine.arguments guard let flag = args.firstIndex(of: "--snapshot"), args.count > flag + 1 else { return } let path = args[flag + 1] Task { @MainActor in let view = HStack(spacing: 0) { Finish(scheme: .light, title: "Aluminium", frozen: true) Finish(scheme: .dark, title: "Graphite", frozen: true) } .frame(width: Layout.windowDefaultWidth, height: Layout.windowDefaultHeight) let renderer = ImageRenderer(content: view) renderer.scale = 2 if let tiff = renderer.nsImage?.tiffRepresentation, let png = NSBitmapImageRep(data: tiff)?.representation(using: .png, properties: [:]) { try? png.write(to: URL(fileURLWithPath: path)) } exit(0) } } var body: some Scene { WindowGroup("Sst componenten") { HStack(spacing: 0) { Finish(scheme: .light, title: "Aluminium") Finish(scheme: .dark, title: "Graphite") } .frame(minWidth: Layout.windowMinWidth, minHeight: Layout.windowMinHeight) } .defaultSize(width: Layout.windowDefaultWidth, height: Layout.windowDefaultHeight) } } /// Fake speech level so the meter, LEDs and counter can be judged in motion. private func simulatedDbfs(at t: Double) -> Double { if t.truncatingRemainder(dividingBy: 9) > 6.2 { return -90 } let syllable = floor(t * 4.2) let r = abs(sin(syllable * 12.9898) * 43758.5453).truncatingRemainder(dividingBy: 1) return r > 0.12 ? -38 + r * 22 : -90 } struct Finish: View { let scheme: ColorScheme let title: String var frozen = false @State private var recording = true @State private var started = Date() @State private var query = "" var body: some View { Group { if frozen { content(elapsed: 12.3, dbfs: -26) } else { TimelineView(.animation(minimumInterval: 1.0 / 30.0)) { timeline in let elapsed = timeline.date.timeIntervalSince(started) let dbfs = recording ? simulatedDbfs(at: elapsed) : -120 content(elapsed: recording ? elapsed : 0, dbfs: dbfs) } } } .environment(\.colorScheme, scheme) } private func content(elapsed: Double, dbfs: Double) -> some View { ZStack { BrushedBackground() VStack(alignment: .leading, spacing: Layout.panelGap) { SilkLabel("Afwerking ยท \(title)") VStack(alignment: .leading, spacing: Layout.panelGap) { HStack(alignment: .top, spacing: Layout.panelGap) { VUMeterView(levelDbfs: dbfs, isActive: recording, initialFraction: frozen ? VUScale.fraction(forDbfs: dbfs) : 0) VStack(alignment: .leading, spacing: Layout.panelGap) { SilkLabel("Tijd", style: Typography.labelSmall) SegmentCounterView(Self.clock(elapsed)) SilkLabel("Niveau", style: Typography.labelSmall) LEDBarView(fraction: VUScale.fraction(forDbfs: dbfs)) } } SeamDivider() HStack(spacing: Layout.panelGap) { Button { recording.toggle(); started = Date() } label: { HStack { LampView(isOn: recording); Text("Rec") } } .buttonStyle(KeyButtonStyle(kind: .record, latched: recording)) Button("Stop") { recording = false }.buttonStyle(KeyButtonStyle()) Button("Kopieer") {}.buttonStyle(KeyButtonStyle()) Button("Uit") {}.buttonStyle(KeyButtonStyle()).disabled(true) Spacer() LampView(isOn: recording, large: true) LampView(isOn: true, tint: .amber) } } .padding(Layout.panelPadding) .background(PanelBackground()) VStack(alignment: .leading, spacing: 0) { row("14:02:11", "Ik wil de meeting verplaatsen naar tomorrow morning.", selected: false) row("14:03:40", "Hij stuurt het door naar Claude Code vandaag.", selected: true) } .background(PaperBackground()) HStack { SilkLabel("Zoek") Group { if frozen { Text("claude").textStyle(Typography.body).frame(maxWidth: .infinity, alignment: .leading) } else { TextField("", text: $query).textFieldStyle(.plain).textStyle(Typography.body) } } .padding(Space.s3).background(WellBackground()) } } .padding(Layout.panelPadding) } } private func row(_ time: String, _ text: String, selected: Bool) -> some View { HStack(alignment: .firstTextBaseline, spacing: Space.s4) { Text(time).textStyle(Typography.counterSmall).foregroundStyle(Palette.inkSecondary) Text(text).textStyle(Typography.reading).foregroundStyle(Palette.inkPrimary) Spacer() } .padding(.horizontal, Space.s4).padding(.vertical, Space.s3) .frame(minHeight: Size.rowHeight) .background(selected ? Palette.surfaceSelection : Color.clear) } private static func clock(_ seconds: Double) -> String { let total = Int(seconds * 10) return String(format: "%02d:%02d.%d", (total / 600) % 100, (total / 10) % 60, total % 10) } }