22import { ThreadMessage } from '@janhq/core'
33import { RenderMarkdown } from './RenderMarkdown'
44import React , { Fragment , memo , useCallback , useMemo , useState } from 'react'
5- import {
6- IconCopy ,
7- IconCopyCheck ,
8- IconRefresh ,
9- IconTrash ,
10- IconPencil ,
11- IconInfoCircle ,
12- } from '@tabler/icons-react'
5+ import { IconCopy , IconCopyCheck , IconRefresh } from '@tabler/icons-react'
136import { useAppState } from '@/hooks/useAppState'
147import { cn } from '@/lib/utils'
158import { useMessages } from '@/hooks/useMessages'
169import ThinkingBlock from '@/containers/ThinkingBlock'
1710import ToolCallBlock from '@/containers/ToolCallBlock'
1811import { useChat } from '@/hooks/useChat'
1912import {
20- Dialog ,
21- DialogClose ,
22- DialogContent ,
23- DialogFooter ,
24- DialogHeader ,
25- DialogTitle ,
26- DialogTrigger ,
27- } from '@/components/ui/dialog'
28- import { Button } from '@/components/ui/button'
29- import { Textarea } from '@/components/ui/textarea'
13+ EditMessageDialog ,
14+ MessageMetadataDialog ,
15+ DeleteMessageDialog ,
16+ } from '@/containers/dialogs'
3017import {
3118 Tooltip ,
3219 TooltipContent ,
@@ -37,8 +24,6 @@ import { AvatarEmoji } from '@/containers/AvatarEmoji'
3724
3825import TokenSpeedIndicator from '@/containers/TokenSpeedIndicator'
3926
40- import CodeEditor from '@uiw/react-textarea-code-editor'
41- import '@uiw/react-textarea-code-editor/dist.css'
4227import { useTranslation } from '@/i18n/react-i18next-compat'
4328import { useModelProvider } from '@/hooks/useModelProvider'
4429
@@ -76,69 +61,6 @@ const CopyButton = ({ text }: { text: string }) => {
7661 )
7762}
7863
79- const EditDialog = ( {
80- message,
81- setMessage,
82- } : {
83- message : string
84- setMessage : ( message : string ) => void
85- } ) => {
86- const { t } = useTranslation ( )
87- const [ draft , setDraft ] = useState ( message )
88-
89- const handleSave = ( ) => {
90- if ( draft !== message ) {
91- setMessage ( draft )
92- }
93- }
94-
95- return (
96- < Dialog >
97- < DialogTrigger >
98- < Tooltip >
99- < TooltipTrigger asChild >
100- < div className = "flex outline-0 items-center gap-1 hover:text-accent transition-colors cursor-pointer group relative" >
101- < IconPencil size = { 16 } />
102- </ div >
103- </ TooltipTrigger >
104- < TooltipContent >
105- < p > { t ( 'edit' ) } </ p >
106- </ TooltipContent >
107- </ Tooltip >
108- </ DialogTrigger >
109- < DialogContent className = "w-3/4" >
110- < DialogHeader >
111- < DialogTitle > { t ( 'common:dialogs.editMessage.title' ) } </ DialogTitle >
112- < Textarea
113- value = { draft }
114- onChange = { ( e ) => setDraft ( e . target . value ) }
115- className = "mt-2 resize-none w-full"
116- onKeyDown = { ( e ) => {
117- // Prevent key from being captured by parent components
118- e . stopPropagation ( )
119- } }
120- />
121- < DialogFooter className = "mt-2 flex items-center" >
122- < DialogClose asChild >
123- < Button variant = "link" size = "sm" className = "hover:no-underline" >
124- Cancel
125- </ Button >
126- </ DialogClose >
127- < DialogClose asChild >
128- < Button
129- disabled = { draft === message || ! draft }
130- onClick = { handleSave }
131- >
132- Save
133- </ Button >
134- </ DialogClose >
135- </ DialogFooter >
136- </ DialogHeader >
137- </ DialogContent >
138- </ Dialog >
139- )
140- }
141-
14264// Use memo to prevent unnecessary re-renders, but allow re-renders when props change
14365export const ThreadContent = memo (
14466 (
@@ -349,32 +271,20 @@ export const ThreadContent = memo(
349271 ) }
350272
351273 < div className = "flex items-center justify-end gap-2 text-main-view-fg/60 text-xs mt-2" >
352- < EditDialog
274+ < EditMessageDialog
353275 message = {
354276 item . content ?. find ( ( c ) => c . type === 'text' ) ?. text ?. value ||
355277 ''
356278 }
357- setMessage = { ( message ) => {
279+ onSave = { ( message ) => {
358280 if ( item . updateMessage ) {
359281 item . updateMessage ( item , message )
360282 }
361283 } }
362284 />
363- < Tooltip >
364- < TooltipTrigger asChild >
365- < button
366- className = "flex items-center gap-1 hover:text-accent transition-colors cursor-pointer group relative"
367- onClick = { ( ) => {
368- deleteMessage ( item . thread_id , item . id )
369- } }
370- >
371- < IconTrash size = { 16 } />
372- </ button >
373- </ TooltipTrigger >
374- < TooltipContent >
375- < p > { t ( 'delete' ) } </ p >
376- </ TooltipContent >
377- </ Tooltip >
285+ < DeleteMessageDialog
286+ onDelete = { ( ) => deleteMessage ( item . thread_id , item . id ) }
287+ />
378288 </ div >
379289 </ div >
380290 ) }
@@ -456,68 +366,15 @@ export const ThreadContent = memo(
456366 'hidden'
457367 ) }
458368 >
459- < EditDialog
460- message = { item . content ?. [ 0 ] ?. text . value }
461- setMessage = { ( message ) =>
369+ < EditMessageDialog
370+ message = { item . content ?. [ 0 ] ?. text . value || '' }
371+ onSave = { ( message ) =>
462372 item . updateMessage && item . updateMessage ( item , message )
463373 }
464374 />
465375 < CopyButton text = { item . content ?. [ 0 ] ?. text . value || '' } />
466- < Tooltip >
467- < TooltipTrigger asChild >
468- < button
469- className = "flex items-center gap-1 hover:text-accent transition-colors cursor-pointer group relative"
470- onClick = { ( ) => {
471- removeMessage ( )
472- } }
473- >
474- < IconTrash size = { 16 } />
475- </ button >
476- </ TooltipTrigger >
477- < TooltipContent >
478- < p > { t ( 'delete' ) } </ p >
479- </ TooltipContent >
480- </ Tooltip >
481- < Dialog >
482- < DialogTrigger >
483- < Tooltip >
484- < TooltipTrigger asChild >
485- < div className = "outline-0 focus:outline-0 flex items-center gap-1 hover:text-accent transition-colors cursor-pointer group relative" >
486- < IconInfoCircle size = { 16 } />
487- </ div >
488- </ TooltipTrigger >
489- < TooltipContent >
490- < p > { t ( 'metadata' ) } </ p >
491- </ TooltipContent >
492- </ Tooltip >
493- </ DialogTrigger >
494- < DialogContent >
495- < DialogHeader >
496- < DialogTitle >
497- { t ( 'common:dialogs.messageMetadata.title' ) }
498- </ DialogTitle >
499- < div className = "space-y-2" >
500- < div className = "border border-main-view-fg/10 rounded-md overflow-hidden" >
501- < CodeEditor
502- value = { JSON . stringify (
503- item . metadata || { } ,
504- null ,
505- 2
506- ) }
507- language = "json"
508- readOnly
509- style = { {
510- fontFamily : 'ui-monospace' ,
511- backgroundColor : 'transparent' ,
512- height : '100%' ,
513- } }
514- className = "w-full h-full !text-sm"
515- />
516- </ div >
517- </ div >
518- </ DialogHeader >
519- </ DialogContent >
520- </ Dialog >
376+ < DeleteMessageDialog onDelete = { removeMessage } />
377+ < MessageMetadataDialog metadata = { item . metadata } />
521378
522379 { item . isLastMessage && selectedModel && (
523380 < Tooltip >
0 commit comments