@@ -62,6 +62,8 @@ struct ContentView: View {
6262 @State private var isGenerating = false
6363 @State private var shouldStopGenerating = false
6464 @State private var shouldStopShowingToken = false
65+ @State private var thinkingMode = false
66+ @State private var showThinkingModeNotification = false
6567 private let runnerQueue = DispatchQueue ( label: " org.pytorch.executorch.llama " )
6668 @StateObject private var runnerHolder = RunnerHolder ( )
6769 @StateObject private var resourceManager = ResourceManager ( )
@@ -100,107 +102,134 @@ struct ContentView: View {
100102
101103 var body : some View {
102104 NavigationView {
103- VStack {
104- if showingSettings {
105- VStack ( spacing: 20 ) {
106- HStack {
107- VStack ( spacing: 10 ) {
108- Button ( action: { pickerType = . model } ) {
109- Label ( modelTitle, systemImage: " doc " )
110- . lineLimit ( 1 )
111- . truncationMode ( . middle)
112- . frame ( maxWidth: 300 , alignment: . leading)
113- }
114- Button ( action: { pickerType = . tokenizer } ) {
115- Label ( tokenizerTitle, systemImage: " doc " )
116- . lineLimit ( 1 )
117- . truncationMode ( . middle)
118- . frame ( maxWidth: 300 , alignment: . leading)
105+ ZStack {
106+ VStack {
107+ if showingSettings {
108+ VStack ( spacing: 20 ) {
109+ HStack {
110+ VStack ( spacing: 10 ) {
111+ Button ( action: { pickerType = . model } ) {
112+ Label ( modelTitle, systemImage: " doc " )
113+ . lineLimit ( 1 )
114+ . truncationMode ( . middle)
115+ . frame ( maxWidth: 300 , alignment: . leading)
116+ }
117+ Button ( action: { pickerType = . tokenizer } ) {
118+ Label ( tokenizerTitle, systemImage: " doc " )
119+ . lineLimit ( 1 )
120+ . truncationMode ( . middle)
121+ . frame ( maxWidth: 300 , alignment: . leading)
122+ }
119123 }
124+ . padding ( )
125+ . background ( Color . gray. opacity ( 0.1 ) )
126+ . cornerRadius ( 10 )
127+ . fixedSize ( horizontal: true , vertical: false )
128+ Spacer ( )
120129 }
121130 . padding ( )
122- . background ( Color . gray. opacity ( 0.1 ) )
123- . cornerRadius ( 10 )
124- . fixedSize ( horizontal: true , vertical: false )
125- Spacer ( )
126131 }
127- . padding ( )
128132 }
129- }
130133
131- MessageListView ( messages: $messages)
132- . simultaneousGesture (
133- DragGesture ( ) . onChanged { value in
134- if value. translation. height > 10 {
135- hideKeyboard ( )
134+ MessageListView ( messages: $messages)
135+ . simultaneousGesture (
136+ DragGesture ( ) . onChanged { value in
137+ if value. translation. height > 10 {
138+ hideKeyboard ( )
139+ }
140+ showingSettings = false
141+ textFieldFocused = false
136142 }
143+ )
144+ . onTapGesture {
137145 showingSettings = false
138146 textFieldFocused = false
139147 }
140- )
141- . onTapGesture {
142- showingSettings = false
143- textFieldFocused = false
144- }
145-
146- HStack {
147- Button ( action: {
148- imagePickerSourceType = . photoLibrary
149- isImagePickerPresented = true
150- } ) {
151- Image ( systemName: " photo.on.rectangle " )
152- . resizable ( )
153- . scaledToFit ( )
154- . frame ( width: 24 , height: 24 )
155- }
156- . background ( Color . clear)
157- . cornerRadius ( 8 )
158148
159- Button ( action : {
160- if UIImagePickerController . isSourceTypeAvailable ( . camera ) {
161- imagePickerSourceType = . camera
149+ HStack {
150+ Button ( action : {
151+ imagePickerSourceType = . photoLibrary
162152 isImagePickerPresented = true
163- } else {
164- print ( " Camera not available " )
153+ } ) {
154+ Image ( systemName: " photo.on.rectangle " )
155+ . resizable ( )
156+ . scaledToFit ( )
157+ . frame ( width: 24 , height: 24 )
165158 }
166- } ) {
167- Image ( systemName: " camera " )
168- . resizable ( )
169- . scaledToFit ( )
170- . frame ( width: 24 , height: 24 )
171- }
172- . background ( Color . clear)
173- . cornerRadius ( 8 )
159+ . background ( Color . clear)
160+ . cornerRadius ( 8 )
174161
175- TextField ( placeholder, text: $prompt, axis: . vertical)
176- . padding ( 8 )
177- . background ( Color . gray. opacity ( 0.1 ) )
178- . cornerRadius ( 20 )
179- . lineLimit ( 1 ... 10 )
180- . overlay (
181- RoundedRectangle ( cornerRadius: 20 )
182- . stroke ( isInputEnabled ? Color . blue : Color . gray, lineWidth: 1 )
183- )
184- . disabled ( !isInputEnabled)
185- . focused ( $textFieldFocused)
186- . onAppear { textFieldFocused = false }
187- . onTapGesture {
188- showingSettings = false
162+ Button ( action: {
163+ if UIImagePickerController . isSourceTypeAvailable ( . camera) {
164+ imagePickerSourceType = . camera
165+ isImagePickerPresented = true
166+ } else {
167+ print ( " Camera not available " )
168+ }
169+ } ) {
170+ Image ( systemName: " camera " )
171+ . resizable ( )
172+ . scaledToFit ( )
173+ . frame ( width: 24 , height: 24 )
174+ }
175+ . background ( Color . clear)
176+ . cornerRadius ( 8 )
177+
178+ Button ( action: {
179+ thinkingMode. toggle ( )
180+ showThinkingModeNotification = true
181+ DispatchQueue . main. asyncAfter ( deadline: . now( ) + 3 ) {
182+ showThinkingModeNotification = false
183+ }
184+ } ) {
185+ Image ( systemName: " brain " )
186+ . resizable ( )
187+ . scaledToFit ( )
188+ . frame ( width: 24 , height: 24 )
189+ . foregroundColor ( thinkingMode ? . blue : . gray)
189190 }
191+ . background ( Color . clear)
192+ . cornerRadius ( 8 )
193+
194+ TextField ( placeholder, text: $prompt, axis: . vertical)
195+ . padding ( 8 )
196+ . background ( Color . gray. opacity ( 0.1 ) )
197+ . cornerRadius ( 20 )
198+ . lineLimit ( 1 ... 10 )
199+ . overlay (
200+ RoundedRectangle ( cornerRadius: 20 )
201+ . stroke ( isInputEnabled ? Color . blue : Color . gray, lineWidth: 1 )
202+ )
203+ . disabled ( !isInputEnabled)
204+ . focused ( $textFieldFocused)
205+ . onAppear { textFieldFocused = false }
206+ . onTapGesture {
207+ showingSettings = false
208+ }
190209
191- Button ( action: isGenerating ? stop : generate) {
192- Image ( systemName: isGenerating ? " stop.circle " : " arrowshape.up.circle.fill " )
193- . resizable ( )
194- . aspectRatio ( contentMode: . fit)
195- . frame ( height: 28 )
210+ Button ( action: isGenerating ? stop : generate) {
211+ Image ( systemName: isGenerating ? " stop.circle " : " arrowshape.up.circle.fill " )
212+ . resizable ( )
213+ . aspectRatio ( contentMode: . fit)
214+ . frame ( height: 28 )
215+ }
216+ . disabled ( isGenerating ? shouldStopGenerating : ( !isInputEnabled || prompt. isEmpty) )
196217 }
197- . disabled ( isGenerating ? shouldStopGenerating : ( !isInputEnabled || prompt . isEmpty ) )
218+ . padding ( [ . leading , . trailing , . bottom ] , 10 )
198219 }
199- . padding ( [ . leading, . trailing, . bottom] , 10 )
200220 . sheet ( isPresented: $isImagePickerPresented, onDismiss: addSelectedImageMessage) {
201221 ImagePicker ( selectedImage: $selectedImage, sourceType: imagePickerSourceType)
202222 . id ( imagePickerSourceType. rawValue)
203223 }
224+
225+ if showThinkingModeNotification {
226+ Text ( thinkingMode ? " Thinking mode enabled " : " Thinking mode disabled " )
227+ . padding ( 8 )
228+ . background ( Color ( UIColor . secondarySystemBackground) )
229+ . cornerRadius ( 8 )
230+ . transition ( . opacity)
231+ . animation ( . easeInOut( duration: 0.2 ) , value: showThinkingModeNotification)
232+ }
204233 }
205234 . navigationBarTitle ( title, displayMode: . inline)
206235 . navigationBarItems (
0 commit comments