@@ -185,6 +185,7 @@ final class PetCoordinator: NSObject {
185185 private let placementStore = PetPlacementStore . shared
186186 private let characterLibrary = PetCharacterLibrary . shared
187187 private lazy var ipcClient = PetIPCClient ( configuration: configuration, delegate: self )
188+ private let speechCoordinator = PetSpeechCoordinator ( )
188189
189190 private var window : NSWindow ?
190191 private var movementTimer : Timer ?
@@ -296,6 +297,10 @@ final class PetCoordinator: NSObject {
296297 selectCharacter ( id: characterId, announce: true )
297298 }
298299
300+ @objc private func promptConversationMenuAction( ) {
301+ promptConversation ( source: " menu " )
302+ }
303+
299304 @objc private func quit( ) {
300305 NSApp . terminate ( nil )
301306 }
@@ -376,6 +381,9 @@ final class PetCoordinator: NSObject {
376381 onCharacterSelected: { [ weak self] characterID in
377382 self ? . selectCharacter ( id: characterID, announce: true )
378383 } ,
384+ onChatRequested: { [ weak self] in
385+ self ? . promptConversation ( source: " context_menu " )
386+ } ,
379387 onOpenProviderSettingsRequested: { [ weak self] in
380388 self ? . requestOpenProviderSettings ( source: " context_menu " )
381389 } ,
@@ -420,6 +428,12 @@ final class PetCoordinator: NSObject {
420428
421429 let menu = NSMenu ( )
422430
431+ let chatItem = NSMenuItem ( title: " 和我说话… " , action: #selector( promptConversationMenuAction) , keyEquivalent: " t " )
432+ chatItem. target = self
433+ menu. addItem ( chatItem)
434+
435+ menu. addItem ( NSMenuItem . separator ( ) )
436+
423437 let reconnectItem = NSMenuItem ( title: " 重连 Lime " , action: #selector( reconnectIPC) , keyEquivalent: " r " )
424438 reconnectItem. target = self
425439 menu. addItem ( reconnectItem)
@@ -990,6 +1004,35 @@ final class PetCoordinator: NSObject {
9901004 ipcClient. requestPetNextStep ( source: " triple_tap " )
9911005 }
9921006
1007+ private func promptConversation( source: String ) {
1008+ sceneModel. setDragging ( false )
1009+ sceneModel. markInteraction ( )
1010+
1011+ guard let text = PetConversationPrompt . present ( characterDisplayName: currentCharacter. displayName) else {
1012+ return
1013+ }
1014+
1015+ requestChatReply ( text: text, source: source)
1016+ }
1017+
1018+ private func requestChatReply( text: String , source: String ) {
1019+ let normalizedText = text. trimmingCharacters ( in: . whitespacesAndNewlines)
1020+ guard !normalizedText. isEmpty else {
1021+ sceneModel. showBubble ( " 你先跟我说一句话吧 " , autoHideMs: 1400 )
1022+ return
1023+ }
1024+
1025+ guard sceneModel. isConnected else {
1026+ sceneModel. showBubble ( " Lime 还没连上,我先等它 " , autoHideMs: 1400 )
1027+ ipcClient. reconnect ( )
1028+ return
1029+ }
1030+
1031+ sceneModel. showBubble ( " 我来想想怎么回答你… " , autoHideMs: 1500 )
1032+ resetPatrolCycle ( startPaused: true )
1033+ ipcClient. requestChatReply ( text: normalizedText, source: source)
1034+ }
1035+
9931036 private func requestOpenProviderSettings( source: String ) {
9941037 sceneModel. setDragging ( false )
9951038 sceneModel. markInteraction ( )
@@ -1144,6 +1187,9 @@ extension PetCoordinator: PetIPCClientDelegate {
11441187 updateWindowVisibility ( )
11451188 case . showBubble( let text, let autoHideMs) :
11461189 sceneModel. showBubble ( text, autoHideMs: autoHideMs)
1190+ if sceneModel. state != . hidden, ( autoHideMs ?? 0 ) >= 2200 {
1191+ speechCoordinator. speak ( text)
1192+ }
11471193 case . openChatAnchor:
11481194 sceneModel. showBubble ( " 点我打开 Lime 对话 " , autoHideMs: 1600 )
11491195 case . providerOverview( let overview) :
0 commit comments