@@ -3,33 +3,54 @@ import ComposableArchitecture
33import Foundation
44import OpenAIService
55import Preferences
6+ import Terminal
67
7- public struct ChatMessage : Equatable {
8- public enum Role {
8+ public struct DisplayedChatMessage : Equatable {
9+ public enum Role : Equatable {
910 case user
1011 case assistant
1112 case function
1213 case ignored
1314 }
1415
16+ public struct Reference : Equatable {
17+ public var title : String
18+ public var subtitle : String
19+ public var uri : String
20+ public var startLine : Int ?
21+
22+ public init ( title: String , subtitle: String , uri: String , startLine: Int ? ) {
23+ self . title = title
24+ self . subtitle = subtitle
25+ self . uri = uri
26+ self . startLine = startLine
27+ }
28+ }
29+
1530 public var id : String
1631 public var role : Role
1732 public var text : String
33+ public var references : [ Reference ] = [ ]
1834
19- public init ( id: String , role: Role , text: String ) {
35+ public init ( id: String , role: Role , text: String , references : [ Reference ] ) {
2036 self . id = id
2137 self . role = role
2238 self . text = text
39+ self . references = references
2340 }
2441}
2542
43+ private var isPreview : Bool {
44+ ProcessInfo . processInfo. environment [ " XCODE_RUNNING_FOR_PREVIEWS " ] == " 1 "
45+ }
46+
2647struct Chat : ReducerProtocol {
2748 public typealias MessageID = String
2849
2950 struct State : Equatable {
3051 var title : String = " Chat "
3152 @BindingState var typedMessage = " "
32- var history : [ ChatMessage ] = [ ]
53+ var history : [ DisplayedChatMessage ] = [ ]
3354 @BindingState var isReceivingMessage = false
3455 var chatMenu = ChatMenu . State ( )
3556 @BindingState var focusedField : Field ?
@@ -51,6 +72,7 @@ struct Chat: ReducerProtocol {
5172 case resendMessageButtonTapped( MessageID )
5273 case setAsExtraPromptButtonTapped( MessageID )
5374 case focusOnTextField
75+ case referenceClicked( DisplayedChatMessage . Reference )
5476
5577 case observeChatService
5678 case observeHistoryChange
@@ -77,8 +99,11 @@ struct Chat: ReducerProtocol {
7799 case observeSystemPromptChange( UUID )
78100 case observeExtraSystemPromptChange( UUID )
79101 case observeDefaultScopesChange( UUID )
102+ case sendMessage( UUID )
80103 }
81104
105+ @Dependency ( \. openURL) var openURL
106+
82107 var body : some ReducerProtocol < State , Action > {
83108 BindingReducer ( )
84109
@@ -90,6 +115,7 @@ struct Chat: ReducerProtocol {
90115 switch action {
91116 case . appear:
92117 return . run { send in
118+ if isPreview { return }
93119 await send ( . observeChatService)
94120 await send ( . historyChanged)
95121 await send ( . isReceivingMessageChanged)
@@ -104,16 +130,19 @@ struct Chat: ReducerProtocol {
104130 state. typedMessage = " "
105131 return . run { _ in
106132 try await service. send ( content: message)
107- }
133+ } . cancellable ( id : CancelID . sendMessage ( id ) )
108134
109135 case . returnButtonTapped:
110136 state. typedMessage += " \n "
111137 return . none
112138
113139 case . stopRespondingButtonTapped:
114- return . run { _ in
115- await service. stopReceivingMessage ( )
116- }
140+ return . merge(
141+ . run { _ in
142+ await service. stopReceivingMessage ( )
143+ } ,
144+ . cancel( id: CancelID . sendMessage ( id) )
145+ )
117146
118147 case . clearButtonTap:
119148 return . run { _ in
@@ -134,7 +163,29 @@ struct Chat: ReducerProtocol {
134163 return . run { _ in
135164 await service. setMessageAsExtraPrompt ( id: id)
136165 }
137-
166+
167+ case let . referenceClicked( reference) :
168+ let fileURL = URL ( fileURLWithPath: reference. uri)
169+ return . run { _ in
170+ if FileManager . default. fileExists ( atPath: fileURL. path) {
171+ let terminal = Terminal ( )
172+ do {
173+ _ = try await terminal. runCommand (
174+ " /bin/bash " ,
175+ arguments: [
176+ " -c " ,
177+ " xed -l \( reference. startLine ?? 0 ) \" \( reference. uri) \" " ,
178+ ] ,
179+ environment: [ : ]
180+ )
181+ } catch {
182+ print ( error)
183+ }
184+ } else if let url = URL ( string: reference. uri) , url. scheme != nil {
185+ await openURL ( url)
186+ }
187+ }
188+
138189 case . focusOnTextField:
139190 state. focusedField = . textField
140191 return . none
@@ -247,7 +298,15 @@ struct Chat: ReducerProtocol {
247298 case . function: return . function
248299 }
249300 } ( ) ,
250- text: message. summary ?? message. content ?? " "
301+ text: message. summary ?? message. content ?? " " ,
302+ references: message. references. map {
303+ . init(
304+ title: $0. title,
305+ subtitle: $0. subTitle,
306+ uri: $0. uri,
307+ startLine: $0. startLine
308+ )
309+ }
251310 )
252311 }
253312
0 commit comments