import SstData import SstUI import SwiftUI /// The transport deck: VU meter, counter, LED bar and the REC/STOP keys. Always visible at the top. struct DeckView: View { @EnvironmentObject private var model: AppModel var body: some View { HStack(alignment: .top, spacing: Layout.panelGap) { instruments Spacer(minLength: 0) controls } .padding(Layout.panelPadding) .background(PanelBackground()) } private var instruments: some View { TimelineView(.animation(minimumInterval: 1 / Motion.needleUpdateHz, paused: !model.isRecording)) { context in let dbfs = Double(model.levelSource.dbfs) HStack(alignment: .top, spacing: Layout.panelGap) { VUMeterView(levelDbfs: dbfs, isActive: model.isRecording, brand: AppIdentity.brandCaption) .frame(width: MeterTokens.vuWidth) VStack(alignment: .leading, spacing: Layout.panelGap) { SilkLabel(Strings.time, style: Typography.labelSmall) SegmentCounterView(StopwatchFormat.string(elapsed(at: context.date))) SilkLabel(Strings.level, style: Typography.labelSmall) LEDBarView(fraction: model.isRecording ? VUScale.fraction(forDbfs: dbfs) : 0) } } } } private func elapsed(at date: Date) -> TimeInterval { if let started = model.recordingStartedAt { return date.timeIntervalSince(started) } return model.lastDuration ?? 0 } private var controls: some View { VStack(alignment: .trailing, spacing: Layout.panelGap) { HStack(spacing: Layout.panelGap) { Button { model.toggleWindowRecording() } label: { HStack(spacing: Space.s3) { LampView(isOn: model.isRecording) Text(Strings.record) } } .buttonStyle(KeyButtonStyle(kind: .record, latched: model.isRecording)) .disabled(!model.isRecording && model.windowRecordingBlocker != nil) .help(model.windowRecordingBlocker ?? "") Button(Strings.stop) { model.stopWindowRecording() } .buttonStyle(KeyButtonStyle()) .disabled(!model.isRecording) } if let error = model.lastError { HStack(spacing: Space.s3) { LampView(isOn: true, tint: .amber) Text(error) .textStyle(Typography.meta) .foregroundStyle(Palette.inkSecondary) .multilineTextAlignment(.trailing) } } else { Text(model.statusLine) .textStyle(Typography.meta) .foregroundStyle(Palette.inkSecondary) .multilineTextAlignment(.trailing) } } .frame(maxWidth: Layout.sidebarWidth, alignment: .trailing) } }