@@ -131,6 +131,7 @@ export function History() {
131
131
const [ loading , setLoading ] = useState ( true ) ;
132
132
const [ expandedEntry , setExpandedEntry ] = useState < number | null > ( null ) ;
133
133
const [ diffData , setDiffData ] = useState < { [ key : number ] : TextDiff } > ( { } ) ;
134
+ const [ copiedStates , setCopiedStates ] = useState < { [ key : string ] : boolean } > ( { } ) ;
134
135
135
136
useEffect ( ( ) => {
136
137
loadHistoryData ( ) ;
@@ -167,6 +168,62 @@ export function History() {
167
168
}
168
169
} ;
169
170
171
+ const copyToClipboard = async ( text : string , type : 'original' | 'transformed' , entryIndex : number ) => {
172
+ try {
173
+ await navigator . clipboard . writeText ( text ) ;
174
+ const key = `${ entryIndex } -${ type } ` ;
175
+ setCopiedStates ( prev => ( { ...prev , [ key ] : true } ) ) ;
176
+
177
+ // Reset the copied state after 2 seconds
178
+ setTimeout ( ( ) => {
179
+ setCopiedStates ( prev => ( { ...prev , [ key ] : false } ) ) ;
180
+ } , 2000 ) ;
181
+ } catch ( error ) {
182
+ console . error ( 'Failed to copy text:' , error ) ;
183
+ await message ( 'Failed to copy text to clipboard' , { title : 'Error' , kind : 'error' } ) ;
184
+ }
185
+ } ;
186
+
187
+ const CopyButton = ( {
188
+ text,
189
+ type,
190
+ entryIndex
191
+ } : {
192
+ text : string ;
193
+ type : 'original' | 'transformed' ;
194
+ entryIndex : number ;
195
+ } ) => {
196
+ const key = `${ entryIndex } -${ type } ` ;
197
+ const isCopied = copiedStates [ key ] ;
198
+
199
+ return (
200
+ < button
201
+ onClick = { ( e ) => {
202
+ e . stopPropagation ( ) ;
203
+ copyToClipboard ( text , type , entryIndex ) ;
204
+ } }
205
+ className = "flex items-center gap-1 text-xs text-text-secondary hover:text-text-primary transition-colors px-2 py-1 rounded hover:bg-background-tertiary"
206
+ title = { `Copy ${ type } text` }
207
+ >
208
+ { isCopied ? (
209
+ < >
210
+ < svg className = "w-3 h-3 text-green-600" fill = "none" viewBox = "0 0 24 24" stroke = "currentColor" >
211
+ < path strokeLinecap = "round" strokeLinejoin = "round" strokeWidth = { 2 } d = "M5 13l4 4L19 7" />
212
+ </ svg >
213
+ < span className = "text-green-600" > Copied!</ span >
214
+ </ >
215
+ ) : (
216
+ < >
217
+ < svg className = "w-3 h-3" fill = "none" viewBox = "0 0 24 24" stroke = "currentColor" >
218
+ < path strokeLinecap = "round" strokeLinejoin = "round" strokeWidth = { 2 } d = "M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z" />
219
+ </ svg >
220
+ < span > Copy</ span >
221
+ </ >
222
+ ) }
223
+ </ button >
224
+ ) ;
225
+ } ;
226
+
170
227
const toggleDiffView = ( index : number ) => {
171
228
if ( expandedEntry === index ) {
172
229
setExpandedEntry ( null ) ;
@@ -300,23 +357,37 @@ export function History() {
300
357
{ expandedEntry === index && diffData [ index ] && (
301
358
< div className = "mt-4 space-y-4 border-t border-border-primary pt-4" >
302
359
< div >
303
- < div className = "flex items-center gap-2 mb-2" >
304
- < span className = "text-sm text-text-primary" > Original</ span >
305
- < span className = "text-xs text-red-600 bg-red-50 px-2 py-1 rounded" >
306
- -{ diffData [ index ] . removed_count } words
307
- </ span >
360
+ < div className = "flex items-center justify-between mb-2" >
361
+ < div className = "flex items-center gap-2" >
362
+ < span className = "text-sm text-text-primary" > Original</ span >
363
+ < span className = "text-xs text-red-600 bg-red-50 px-2 py-1 rounded" >
364
+ -{ diffData [ index ] . removed_count } words
365
+ </ span >
366
+ </ div >
367
+ < CopyButton
368
+ text = { entry . original_text }
369
+ type = "original"
370
+ entryIndex = { index }
371
+ />
308
372
</ div >
309
373
< div className = "border border-border-primary rounded-lg p-3 bg-background-tertiary" >
310
374
{ renderDiffText ( diffData [ index ] . original_diff , false ) }
311
375
</ div >
312
376
</ div >
313
377
314
378
< div >
315
- < div className = "flex items-center gap-2 mb-2" >
316
- < span className = "text-sm text-text-primary" > Transformed</ span >
317
- < span className = "text-xs text-green-600 bg-green-50 px-2 py-1 rounded" >
318
- +{ diffData [ index ] . added_count } words
319
- </ span >
379
+ < div className = "flex items-center justify-between mb-2" >
380
+ < div className = "flex items-center gap-2" >
381
+ < span className = "text-sm text-text-primary" > Transformed</ span >
382
+ < span className = "text-xs text-green-600 bg-green-50 px-2 py-1 rounded" >
383
+ +{ diffData [ index ] . added_count } words
384
+ </ span >
385
+ </ div >
386
+ < CopyButton
387
+ text = { entry . transformed_text }
388
+ type = "transformed"
389
+ entryIndex = { index }
390
+ />
320
391
</ div >
321
392
< div className = "border border-border-primary rounded-lg p-3 bg-background-tertiary" >
322
393
{ renderDiffText ( diffData [ index ] . transformed_diff , true ) }
0 commit comments