import Environment import SwiftUI @MainActor final class SuggestionPanelViewModel: ObservableObject { @Published var startLineIndex: Int @Published var suggestion: [String] @Published var isPanelDisplayed: Bool @Published var suggestionCount: Int @Published var currentSuggestionIndex: Int var onAcceptButtonTapped: (() -> Void)? var onRejectButtonTapped: (() -> Void)? var onPreviousButtonTapped: (() -> Void)? var onNextButtonTapped: (() -> Void)? public init( startLineIndex: Int = 0, suggestion: [String] = [], isPanelDisplayed: Bool = false, suggestionCount: Int = 0, currentSuggestionIndex: Int = 0, onAcceptButtonTapped: (() -> Void)? = nil, onRejectButtonTapped: (() -> Void)? = nil, onPreviousButtonTapped: (() -> Void)? = nil, onNextButtonTapped: (() -> Void)? = nil ) { self.startLineIndex = startLineIndex self.suggestion = suggestion self.isPanelDisplayed = isPanelDisplayed self.suggestionCount = suggestionCount self.currentSuggestionIndex = currentSuggestionIndex self.onAcceptButtonTapped = onAcceptButtonTapped self.onRejectButtonTapped = onRejectButtonTapped self.onPreviousButtonTapped = onPreviousButtonTapped self.onNextButtonTapped = onNextButtonTapped } } struct SuggestionPanelView: View { @ObservedObject var viewModel: SuggestionPanelViewModel @State var isHovering: Bool = false @State var codeHeight: Double = 0 var body: some View { VStack { Spacer() .frame(minHeight: 0, maxHeight: .infinity) .allowsHitTesting(false) ZStack(alignment: .topLeading) { VStack(spacing: 0) { ScrollView { CodeBlock(viewModel: viewModel) } .background(Color(red: 31 / 255, green: 31 / 255, blue: 36 / 255)) ToolBar(viewModel: viewModel) } } .frame(maxWidth: .infinity, maxHeight: Style.panelHeight) .fixedSize(horizontal: false, vertical: true) .clipShape(RoundedRectangle(cornerRadius: 8, style: .continuous)) .overlay( RoundedRectangle(cornerRadius: 8, style: .continuous) .stroke(Color.black.opacity(0.3), style: .init(lineWidth: 1)) ) .overlay( RoundedRectangle(cornerRadius: 7, style: .continuous) .stroke(Color.white.opacity(0.2), style: .init(lineWidth: 1)) .padding(2) ) .onHover { yes in withAnimation(.easeInOut(duration: 0.2)) { isHovering = yes } } .allowsHitTesting(viewModel.isPanelDisplayed && !viewModel.suggestion.isEmpty) .preferredColorScheme(.dark) } .opacity({ guard viewModel.isPanelDisplayed else { return 0 } guard !viewModel.suggestion.isEmpty else { return 0 } return 1 }()) } } struct CodeBlock: View { struct SizePreferenceKey: PreferenceKey { public static var defaultValue: CGSize = .zero public static func reduce(value: inout CGSize, nextValue: () -> CGSize) { value = value.width + value.height > nextValue().width + nextValue() .height ? value : nextValue() } } @ObservedObject var viewModel: SuggestionPanelViewModel var body: some View { LazyVGrid(columns: [ GridItem(.fixed(30), alignment: .top), GridItem(.flexible()), ], spacing: 4) { ForEach(0.. some View { configuration.label .padding(.vertical, 4) .padding(.horizontal, 8) .foregroundColor(.white) .background( RoundedRectangle(cornerRadius: 4, style: .continuous) .fill(color.opacity(configuration.isPressed ? 0.8 : 1)) .animation(.easeOut(duration: 0.1), value: configuration.isPressed) ) .overlay { RoundedRectangle(cornerRadius: 4, style: .continuous) .stroke(Color.white.opacity(0.2), style: .init(lineWidth: 1)) } } } struct SuggestionPanelView_Preview: PreviewProvider { static var previews: some View { SuggestionPanelView(viewModel: .init( startLineIndex: 8, suggestion: """ LazyVGrid(columns: [GridItem(.fixed(30)), GridItem(.flexible())]) { ForEach(0..