@@ -23,14 +23,17 @@ struct PromptToCodePanelView: View {
2323 TopBar ( store: store)
2424
2525 Content ( store: store)
26- . overlay ( alignment: . bottom) {
27- ActionBar ( store: store)
28- }
2926 . safeAreaInset ( edge: . bottom) {
30- if let inputField = customizedViews. contextInputField {
31- inputField
32- } else {
33- Toolbar ( store: store)
27+ VStack {
28+ StatusBar ( store: store)
29+
30+ ActionBar ( store: store)
31+
32+ if let inputField = customizedViews. contextInputField {
33+ inputField
34+ } else {
35+ Toolbar ( store: store)
36+ }
3437 }
3538 }
3639 }
@@ -143,6 +146,70 @@ extension PromptToCodePanelView {
143146 }
144147 }
145148
149+ struct StatusBar : View {
150+ let store : StoreOf < PromptToCodePanel >
151+ @State var isAllStatusesPresented = false
152+ var body : some View {
153+ WithPerceptionTracking {
154+ if store. promptToCodeState. isGenerating, !store. promptToCodeState. status. isEmpty {
155+ if let firstStatus = store. promptToCodeState. status. first {
156+ let count = store. promptToCodeState. status. count
157+ Button ( action: {
158+ isAllStatusesPresented = true
159+ } ) {
160+ HStack {
161+ Text ( firstStatus)
162+ . lineLimit ( 1 )
163+ . font ( . caption)
164+
165+ Text ( " \( count) " )
166+ . lineLimit ( 1 )
167+ . font ( . caption)
168+ . background (
169+ Circle ( )
170+ . fill ( Color . secondary. opacity ( 0.3 ) )
171+ . frame ( width: 12 , height: 12 )
172+ )
173+ }
174+ . padding ( 8 )
175+ . background (
176+ . regularMaterial,
177+ in: RoundedRectangle ( cornerRadius: 6 , style: . continuous)
178+ )
179+ . overlay {
180+ RoundedRectangle ( cornerRadius: 6 , style: . continuous)
181+ . stroke ( Color ( nsColor: . separatorColor) , lineWidth: 1 )
182+ }
183+ . contentShape ( Rectangle ( ) )
184+ }
185+ . buttonStyle ( . plain)
186+ . frame ( maxWidth: 400 )
187+ . popover ( isPresented: $isAllStatusesPresented, arrowEdge: . top) {
188+ WithPerceptionTracking {
189+ VStack ( alignment: . leading, spacing: 16 ) {
190+ ForEach ( store. promptToCodeState. status, id: \. self) { status in
191+ HStack {
192+ ProgressView ( )
193+ . progressViewStyle ( CircularProgressViewStyle ( ) )
194+ . controlSize ( . small)
195+ Text ( status)
196+ }
197+ }
198+ }
199+ . padding ( )
200+ }
201+ }
202+ . onChange ( of: store. promptToCodeState. isGenerating) { isGenerating in
203+ if !isGenerating {
204+ isAllStatusesPresented = false
205+ }
206+ }
207+ }
208+ }
209+ }
210+ }
211+ }
212+
146213 struct ActionBar : View {
147214 let store : StoreOf < PromptToCodePanel >
148215
@@ -433,7 +500,7 @@ extension PromptToCodePanelView {
433500 }
434501 }
435502 }
436-
503+
437504 Spacer ( minLength: 56 )
438505 }
439506 }
@@ -575,7 +642,7 @@ extension PromptToCodePanelView {
575642 presentAllContent: !isGenerating
576643 )
577644 } else {
578- ScrollView ( . horizontal ) {
645+ MinScrollView {
579646 CodeBlockInContent (
580647 store: store,
581648 language: language,
@@ -607,6 +674,37 @@ extension PromptToCodePanelView {
607674 }
608675 }
609676
677+ struct MinWidthPreferenceKey : PreferenceKey {
678+ static func reduce( value: inout CGFloat , nextValue: ( ) -> CGFloat ) {
679+ value = nextValue ( )
680+ }
681+
682+ static var defaultValue : CGFloat = 0
683+ }
684+
685+ struct MinScrollView < Content: View > : View {
686+ @ViewBuilder let content : Content
687+ @State var minWidth : CGFloat = 0
688+
689+ var body : some View {
690+ ScrollView ( . horizontal) {
691+ content
692+ . frame ( minWidth: minWidth)
693+ }
694+ . overlay {
695+ GeometryReader { proxy in
696+ Color . clear. preference (
697+ key: MinWidthPreferenceKey . self,
698+ value: proxy. size. width
699+ )
700+ }
701+ }
702+ . onPreferenceChange ( MinWidthPreferenceKey . self) {
703+ minWidth = $0
704+ }
705+ }
706+ }
707+
610708 struct CodeBlockInContent : View {
611709 let store : StoreOf < PromptToCodeSnippetPanel >
612710 let language : CodeLanguage
@@ -857,3 +955,59 @@ extension PromptToCodePanelView {
857955 . frame ( width: 500 , height: 500 , alignment: . center)
858956}
859957
958+ #Preview( " Generating " ) {
959+ PromptToCodePanelView ( store: . init( initialState: . init(
960+ promptToCodeState: Shared ( ModificationState (
961+ source: . init(
962+ language: CodeLanguage . builtIn ( . swift) ,
963+ documentURL: URL (
964+ fileURLWithPath: " path/to/file-name-is-super-long-what-should-we-do-with-it-hah.txt "
965+ ) ,
966+ projectRootURL: URL ( fileURLWithPath: " path/to/file.txt " ) ,
967+ content: " " ,
968+ lines: [ ]
969+ ) ,
970+ snippets: [
971+ . init(
972+ startLineIndex: 8 ,
973+ originalCode: " print(foo) " ,
974+ modifiedCode: " print(bar) " ,
975+ description: " " ,
976+ error: " Error " ,
977+ attachedRange: CursorRange (
978+ start: . init( line: 8 , character: 0 ) ,
979+ end: . init( line: 12 , character: 2 )
980+ )
981+ ) ,
982+ . init(
983+ startLineIndex: 13 ,
984+ originalCode: """
985+ struct Bar {
986+ var foo: Int
987+ }
988+ """ ,
989+ modifiedCode: """
990+ struct Bar {
991+ var foo: String
992+ }
993+ """ ,
994+ description: " Cool " ,
995+ error: nil ,
996+ attachedRange: CursorRange (
997+ start: . init( line: 13 , character: 0 ) ,
998+ end: . init( line: 12 , character: 2 )
999+ )
1000+ ) ,
1001+ ] ,
1002+ extraSystemPrompt: " " ,
1003+ isAttachedToTarget: true ,
1004+ isGenerating: true ,
1005+ status: [ " Status 1 " , " Status 2 " ]
1006+ ) ) ,
1007+ instruction: nil ,
1008+ commandName: " Generate Code "
1009+ ) , reducer: { PromptToCodePanel ( ) } ) )
1010+ . frame ( maxWidth: 450 , maxHeight: Style . panelHeight)
1011+ . fixedSize ( horizontal: false , vertical: true )
1012+ . frame ( width: 500 , height: 500 , alignment: . center)
1013+ }
0 commit comments