1
+ import React , { useState , useCallback } from "react" ;
2
+ import { type JSONContentZod } from "tentix-server/types" ;
3
+ import { useTextOptimizer } from "../../hooks/use-text-optimizer" ;
4
+ import { OptimizeButton , OptimizeStatus } from "./optimize-button" ;
5
+ import { useToast } from "tentix-ui" ;
6
+
7
+ interface EnhancedMessageInputProps {
8
+ ticketId : string ;
9
+ messageType ?: "public" | "internal" ;
10
+ priority ?: string ;
11
+ onSendMessage : ( content : JSONContentZod ) => Promise < void > ;
12
+ onTyping ?: ( ) => void ;
13
+ isLoading ?: boolean ;
14
+ children : React . ReactElement ;
15
+ }
16
+
17
+ export function EnhancedMessageInput ( {
18
+ ticketId,
19
+ messageType = "public" ,
20
+ priority,
21
+ onTyping,
22
+ isLoading,
23
+ children,
24
+ } : EnhancedMessageInputProps ) {
25
+ const { toast } = useToast ( ) ;
26
+ const [ currentText , setCurrentText ] = useState ( "" ) ;
27
+ const [ isEnabled , setIsEnabled ] = useState ( true ) ;
28
+
29
+ const {
30
+ isOptimizing,
31
+ lastOptimization,
32
+ hasOptimization,
33
+ optimizeText,
34
+ undoOptimization,
35
+ cancelOptimization,
36
+ } = useTextOptimizer ( { ticketId, messageType, priority } ) ;
37
+
38
+
39
+ const extractTextFromContent = useCallback ( ( content : JSONContentZod ) : string => {
40
+ if ( ! content || ! content . content ) return "" ;
41
+
42
+ const extractFromNode = ( node : any ) : string => {
43
+ if ( typeof node === "string" ) return node ;
44
+ if ( node . type === "text" ) return node . text || "" ;
45
+ if ( node . content && Array . isArray ( node . content ) ) {
46
+ return node . content . map ( extractFromNode ) . join ( "" ) ;
47
+ }
48
+ return "" ;
49
+ } ;
50
+
51
+ return content . content . map ( extractFromNode ) . join ( " " ) . trim ( ) ;
52
+ } , [ ] ) ;
53
+
54
+
55
+ const createOptimizedContent = useCallback ( ( optimizedText : string ) : JSONContentZod => {
56
+ return {
57
+ type : "doc" ,
58
+ content : [
59
+ {
60
+ type : "paragraph" ,
61
+ content : [ { type : "text" , text : optimizedText } ] ,
62
+ } ,
63
+ ] ,
64
+ } ;
65
+ } , [ ] ) ;
66
+
67
+
68
+ const handleOptimize = useCallback ( async ( text ?: string ) => {
69
+ const textToOptimize = text || currentText ;
70
+ if ( ! textToOptimize . trim ( ) ) {
71
+ toast ( {
72
+ title : "无法优化" ,
73
+ description : "请先输入一些文本" ,
74
+ variant : "destructive" ,
75
+ } ) ;
76
+ return ;
77
+ }
78
+
79
+ const result = await optimizeText ( textToOptimize ) ;
80
+ if ( result ) {
81
+
82
+ const childRef = ( children as any ) ?. ref ;
83
+ if ( childRef ?. current ) {
84
+ const optimizedContent = createOptimizedContent ( result . optimizedText ) ;
85
+ childRef . current . setContent ?.( optimizedContent ) ;
86
+ setCurrentText ( result . optimizedText ) ;
87
+ }
88
+ }
89
+ } , [ currentText , optimizeText , createOptimizedContent , toast , children ] ) ;
90
+
91
+
92
+ const handleUndo = useCallback ( ( ) => {
93
+ const originalText = undoOptimization ( ) ;
94
+ if ( originalText ) {
95
+ const childRef = ( children as any ) ?. ref ;
96
+ if ( childRef ?. current ) {
97
+ const originalContent = createOptimizedContent ( originalText ) ;
98
+ childRef . current . setContent ?.( originalContent ) ;
99
+ setCurrentText ( originalText ) ;
100
+ }
101
+ }
102
+ } , [ undoOptimization , createOptimizedContent , children ] ) ;
103
+
104
+
105
+ const childProps = children . props as any ;
106
+ const enhancedChildren = React . cloneElement ( children , {
107
+ ...childProps ,
108
+ onChange : ( content : JSONContentZod ) => {
109
+
110
+ if ( childProps . onChange ) {
111
+ childProps . onChange ( content ) ;
112
+ }
113
+
114
+
115
+ const text = extractTextFromContent ( content ) ;
116
+ setCurrentText ( text ) ;
117
+
118
+
119
+ if ( onTyping ) {
120
+ onTyping ( ) ;
121
+ }
122
+ } ,
123
+
124
+
125
+ editorProps : {
126
+ ...childProps . editorProps ,
127
+ handleKeyDown : ( view : any , event : KeyboardEvent ) => {
128
+
129
+ if ( event . key === 'Tab' && ! event . shiftKey && ! event . ctrlKey && ! event . metaKey ) {
130
+ event . preventDefault ( ) ;
131
+ if ( isEnabled && ! isOptimizing && currentText . trim ( ) ) {
132
+ handleOptimize ( ) ;
133
+ return true ;
134
+ }
135
+ }
136
+
137
+
138
+ if ( childProps . editorProps ?. handleKeyDown ) {
139
+ return childProps . editorProps . handleKeyDown ( view , event ) ;
140
+ }
141
+
142
+ return false ;
143
+ } ,
144
+ } ,
145
+ } ) ;
146
+
147
+ return (
148
+ < div className = "border-t relative" >
149
+
150
+ < div className = "flex items-center justify-between p-2 border-b bg-gray-50/50" >
151
+ < div className = "flex items-center gap-3" >
152
+ < OptimizeButton
153
+ isOptimizing = { isOptimizing }
154
+ hasOptimization = { hasOptimization }
155
+ confidence = { lastOptimization ?. confidence }
156
+ onOptimize = { ( ) => handleOptimize ( ) }
157
+ onUndo = { handleUndo }
158
+ onCancel = { cancelOptimization }
159
+ disabled = { isLoading || ! isEnabled }
160
+ />
161
+
162
+ < label className = "flex items-center gap-2 text-xs" >
163
+ < input
164
+ type = "checkbox"
165
+ checked = { isEnabled }
166
+ onChange = { ( e ) => setIsEnabled ( e . target . checked ) }
167
+ className = "w-3 h-3"
168
+ />
169
+ < span className = "text-gray-600" > 启用Tab键优化</ span >
170
+ </ label >
171
+ </ div >
172
+
173
+ < OptimizeStatus
174
+ isOptimizing = { isOptimizing }
175
+ hasOptimization = { hasOptimization }
176
+ confidence = { lastOptimization ?. confidence }
177
+ suggestions = { lastOptimization ?. suggestions }
178
+ />
179
+ </ div >
180
+
181
+
182
+ { enhancedChildren }
183
+ </ div >
184
+ ) ;
185
+ }
0 commit comments