mirror of
https://github.com/langgenius/dify.git
synced 2026-04-15 18:06:36 +08:00
feat: mouse right click can add new comment
This commit is contained in:
parent
59e752dcd3
commit
205d771bfa
@ -7,14 +7,25 @@ const CommentManager = () => {
|
||||
const { handleCreateComment, handleCommentCancel } = useWorkflowComment()
|
||||
|
||||
useEventListener('click', (e) => {
|
||||
const { controlMode, mousePosition, pendingComment } = workflowStore.getState()
|
||||
const { controlMode, mousePosition, pendingComment, isCommentPlacing } = workflowStore.getState()
|
||||
const target = e.target as HTMLElement
|
||||
const isInDropdown = target.closest('[data-mention-dropdown]')
|
||||
const isInCommentInput = target.closest('[data-comment-input]')
|
||||
const isOnCanvasPane = target.closest('.react-flow__pane')
|
||||
|
||||
if (isCommentPlacing) {
|
||||
if (!isInDropdown && !isInCommentInput && isOnCanvasPane) {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
workflowStore.setState({
|
||||
pendingComment: mousePosition,
|
||||
isCommentPlacing: false,
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if (controlMode === 'comment') {
|
||||
const target = e.target as HTMLElement
|
||||
const isInDropdown = target.closest('[data-mention-dropdown]')
|
||||
const isInCommentInput = target.closest('[data-comment-input]')
|
||||
const isOnCanvasPane = target.closest('.react-flow__pane')
|
||||
|
||||
// Only when clicking on the React Flow canvas pane (background),
|
||||
// and not inside comment input or its dropdown
|
||||
if (!isInDropdown && !isInCommentInput && isOnCanvasPane) {
|
||||
@ -28,6 +39,16 @@ const CommentManager = () => {
|
||||
}
|
||||
})
|
||||
|
||||
useEventListener('contextmenu', () => {
|
||||
const { isCommentPlacing } = workflowStore.getState()
|
||||
if (!isCommentPlacing)
|
||||
return
|
||||
workflowStore.setState({
|
||||
isCommentPlacing: false,
|
||||
isCommentQuickAdd: false,
|
||||
})
|
||||
})
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
@ -8,6 +8,7 @@ type MentionInputProps = {
|
||||
onChange: (value: string) => void
|
||||
onSubmit: (content: string, mentionedUserIds: string[]) => void
|
||||
placeholder?: string
|
||||
disabled?: boolean
|
||||
autoFocus?: boolean
|
||||
className?: string
|
||||
}
|
||||
@ -70,6 +71,7 @@ describe('CommentInput', () => {
|
||||
|
||||
expect(mentionInputProps?.placeholder).toBe('workflow.comments.placeholder.add')
|
||||
expect(mentionInputProps?.autoFocus).toBe(true)
|
||||
expect(mentionInputProps?.disabled).toBe(false)
|
||||
})
|
||||
|
||||
it('calls onCancel when Escape is pressed', () => {
|
||||
|
||||
@ -10,6 +10,8 @@ type CommentInputProps = {
|
||||
position: { x: number, y: number }
|
||||
onSubmit: (content: string, mentionedUserIds: string[]) => void
|
||||
onCancel: () => void
|
||||
autoFocus?: boolean
|
||||
disabled?: boolean
|
||||
onPositionChange?: (position: {
|
||||
pageX: number
|
||||
pageY: number
|
||||
@ -18,7 +20,14 @@ type CommentInputProps = {
|
||||
}) => void
|
||||
}
|
||||
|
||||
export const CommentInput: FC<CommentInputProps> = memo(({ position, onSubmit, onCancel, onPositionChange }) => {
|
||||
export const CommentInput: FC<CommentInputProps> = memo(({
|
||||
position,
|
||||
onSubmit,
|
||||
onCancel,
|
||||
autoFocus = true,
|
||||
disabled = false,
|
||||
onPositionChange,
|
||||
}) => {
|
||||
const [content, setContent] = useState('')
|
||||
const { t } = useTranslation()
|
||||
const { userProfile } = useAppContext()
|
||||
@ -124,7 +133,10 @@ export const CommentInput: FC<CommentInputProps> = memo(({ position, onSubmit, o
|
||||
|
||||
return (
|
||||
<div
|
||||
className="absolute z-[60] w-96"
|
||||
className={cn(
|
||||
'absolute z-[60] w-96',
|
||||
disabled && 'pointer-events-none opacity-80',
|
||||
)}
|
||||
style={{
|
||||
left: position.x,
|
||||
top: position.y,
|
||||
@ -162,7 +174,8 @@ export const CommentInput: FC<CommentInputProps> = memo(({ position, onSubmit, o
|
||||
onChange={setContent}
|
||||
onSubmit={handleMentionSubmit}
|
||||
placeholder={t('comments.placeholder.add', { ns: 'workflow' })}
|
||||
autoFocus
|
||||
autoFocus={autoFocus}
|
||||
disabled={disabled}
|
||||
className="relative"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -5,6 +5,7 @@ import { CommentCursor } from './cursor'
|
||||
|
||||
const mockState = {
|
||||
controlMode: ControlMode.Pointer,
|
||||
isCommentPlacing: false,
|
||||
mousePosition: {
|
||||
elementX: 10,
|
||||
elementY: 20,
|
||||
@ -34,6 +35,7 @@ describe('CommentCursor', () => {
|
||||
|
||||
it('renders at current mouse position when in comment mode', () => {
|
||||
mockState.controlMode = ControlMode.Comment
|
||||
mockState.isCommentPlacing = false
|
||||
|
||||
render(<CommentCursor />)
|
||||
|
||||
@ -42,4 +44,13 @@ describe('CommentCursor', () => {
|
||||
|
||||
expect(container).toHaveStyle({ left: '10px', top: '20px' })
|
||||
})
|
||||
|
||||
it('renders nothing when comment is in placing mode', () => {
|
||||
mockState.controlMode = ControlMode.Comment
|
||||
mockState.isCommentPlacing = true
|
||||
|
||||
render(<CommentCursor />)
|
||||
|
||||
expect(screen.queryByTestId('comment-icon')).not.toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
@ -7,8 +7,9 @@ import { ControlMode } from '../types'
|
||||
export const CommentCursor: FC = memo(() => {
|
||||
const controlMode = useStore(s => s.controlMode)
|
||||
const mousePosition = useStore(s => s.mousePosition)
|
||||
const isCommentPlacing = useStore(s => s.isCommentPlacing)
|
||||
|
||||
if (controlMode !== ControlMode.Comment)
|
||||
if (controlMode !== ControlMode.Comment || isCommentPlacing)
|
||||
return null
|
||||
|
||||
return (
|
||||
|
||||
@ -25,6 +25,9 @@ export const useWorkflowComment = () => {
|
||||
const controlMode = useStore(s => s.controlMode)
|
||||
const pendingComment = useStore(s => s.pendingComment)
|
||||
const setPendingComment = useStore(s => s.setPendingComment)
|
||||
const isCommentQuickAdd = useStore(s => s.isCommentQuickAdd)
|
||||
const setCommentQuickAdd = useStore(s => s.setCommentQuickAdd)
|
||||
const isCommentPlacing = useStore(s => s.isCommentPlacing)
|
||||
const setActiveCommentId = useStore(s => s.setActiveCommentId)
|
||||
const activeCommentId = useStore(s => s.activeCommentId)
|
||||
const comments = useStore(s => s.comments)
|
||||
@ -204,21 +207,29 @@ export const useWorkflowComment = () => {
|
||||
collaborationManager.emitCommentsUpdate(appId)
|
||||
|
||||
setPendingComment(null)
|
||||
setCommentQuickAdd(false)
|
||||
}
|
||||
catch (error) {
|
||||
console.error('Failed to create comment:', error)
|
||||
setPendingComment(null)
|
||||
setCommentQuickAdd(false)
|
||||
}
|
||||
}, [appId, pendingComment, setPendingComment, reactflow, comments, setComments, userProfile, setCommentDetailCache, mentionableUsers])
|
||||
}, [appId, pendingComment, setPendingComment, setCommentQuickAdd, reactflow, comments, setComments, userProfile, setCommentDetailCache, mentionableUsers])
|
||||
|
||||
const handleCommentCancel = useCallback(() => {
|
||||
setPendingComment(null)
|
||||
}, [setPendingComment])
|
||||
setCommentQuickAdd(false)
|
||||
}, [setPendingComment, setCommentQuickAdd])
|
||||
|
||||
useEffect(() => {
|
||||
if (controlMode !== ControlMode.Comment)
|
||||
if (controlMode !== ControlMode.Comment && !isCommentQuickAdd)
|
||||
setPendingComment(null)
|
||||
}, [controlMode, setPendingComment])
|
||||
}, [controlMode, isCommentQuickAdd, setPendingComment])
|
||||
|
||||
useEffect(() => {
|
||||
if (!pendingComment && !isCommentPlacing && isCommentQuickAdd)
|
||||
setCommentQuickAdd(false)
|
||||
}, [isCommentPlacing, isCommentQuickAdd, pendingComment, setCommentQuickAdd])
|
||||
|
||||
const handleCommentIconClick = useCallback(async (comment: WorkflowCommentList) => {
|
||||
setPendingComment(null)
|
||||
|
||||
@ -154,6 +154,37 @@ export type WorkflowProps = {
|
||||
myUserId?: string | null
|
||||
onlineUsers?: OnlineUser[]
|
||||
}
|
||||
|
||||
const CommentPlacementPreview = memo(({
|
||||
onSubmit,
|
||||
onCancel,
|
||||
}: {
|
||||
onSubmit: (content: string, mentionedUserIds: string[]) => void
|
||||
onCancel: () => void
|
||||
}) => {
|
||||
const isCommentPlacing = useStore(s => s.isCommentPlacing)
|
||||
const pendingComment = useStore(s => s.pendingComment)
|
||||
const mousePosition = useStore(s => s.mousePosition)
|
||||
|
||||
if (!isCommentPlacing || pendingComment)
|
||||
return null
|
||||
|
||||
return (
|
||||
<CommentInput
|
||||
position={{
|
||||
x: mousePosition.elementX,
|
||||
y: mousePosition.elementY,
|
||||
}}
|
||||
onSubmit={onSubmit}
|
||||
onCancel={onCancel}
|
||||
autoFocus={false}
|
||||
disabled
|
||||
/>
|
||||
)
|
||||
})
|
||||
|
||||
CommentPlacementPreview.displayName = 'CommentPlacementPreview'
|
||||
|
||||
export const Workflow: FC<WorkflowProps> = memo(({
|
||||
nodes: originalNodes,
|
||||
edges: originalEdges,
|
||||
@ -264,8 +295,11 @@ export const Workflow: FC<WorkflowProps> = memo(({
|
||||
const showUserCursors = useStore(s => s.showUserCursors)
|
||||
const showResolvedComments = useStore(s => s.showResolvedComments)
|
||||
const isCommentPreviewHovering = useStore(s => s.isCommentPreviewHovering)
|
||||
const isCommentPlacing = useStore(s => s.isCommentPlacing)
|
||||
const setCommentPlacing = useStore(s => s.setCommentPlacing)
|
||||
const setCommentQuickAdd = useStore(s => s.setCommentQuickAdd)
|
||||
const setPendingCommentState = useStore(s => s.setPendingComment)
|
||||
const isCommentInputActive = Boolean(pendingComment)
|
||||
const isCommentInputActive = Boolean(pendingComment) || isCommentPlacing
|
||||
const { t } = useTranslation()
|
||||
const visibleComments = useMemo(() => {
|
||||
if (showResolvedComments)
|
||||
@ -320,6 +354,12 @@ export const Workflow: FC<WorkflowProps> = memo(({
|
||||
setPendingCommentState(position)
|
||||
}, [setPendingCommentState])
|
||||
|
||||
const handleCommentPlacementCancel = useCallback(() => {
|
||||
setPendingCommentState(null)
|
||||
setCommentPlacing(false)
|
||||
setCommentQuickAdd(false)
|
||||
}, [setCommentPlacing, setCommentQuickAdd, setPendingCommentState])
|
||||
|
||||
const { handleRefreshWorkflowDraft } = useWorkflowRefreshDraft()
|
||||
const handleSyncWorkflowDraftWhenPageClose = useCallback(() => {
|
||||
if (document.visibilityState === 'hidden') {
|
||||
@ -571,6 +611,10 @@ export const Workflow: FC<WorkflowProps> = memo(({
|
||||
{controlMode === ControlMode.Comment && isMouseOverCanvas && (
|
||||
<CommentCursor />
|
||||
)}
|
||||
<CommentPlacementPreview
|
||||
onSubmit={handleCommentSubmit}
|
||||
onCancel={handleCommentPlacementCancel}
|
||||
/>
|
||||
{pendingComment && (
|
||||
<CommentInput
|
||||
position={{
|
||||
|
||||
@ -10,6 +10,7 @@ import {
|
||||
useDSL,
|
||||
useNodesInteractions,
|
||||
usePanelInteractions,
|
||||
useWorkflowMoveMode,
|
||||
useWorkflowStartRun,
|
||||
} from './hooks'
|
||||
import AddBlock from './operator/add-block'
|
||||
@ -23,10 +24,14 @@ const PanelContextmenu = () => {
|
||||
const panelMenu = useStore(s => s.panelMenu)
|
||||
const clipboardElements = useStore(s => s.clipboardElements)
|
||||
const setShowImportDSLModal = useStore(s => s.setShowImportDSLModal)
|
||||
const pendingComment = useStore(s => s.pendingComment)
|
||||
const setCommentPlacing = useStore(s => s.setCommentPlacing)
|
||||
const setCommentQuickAdd = useStore(s => s.setCommentQuickAdd)
|
||||
const { handleNodesPaste } = useNodesInteractions()
|
||||
const { handlePaneContextmenuCancel } = usePanelInteractions()
|
||||
const { handleStartWorkflowRun } = useWorkflowStartRun()
|
||||
const { handleAddNote } = useOperator()
|
||||
const { isCommentModeAvailable } = useWorkflowMoveMode()
|
||||
const { exportCheck } = useDSL()
|
||||
|
||||
useClickAway(() => {
|
||||
@ -73,6 +78,24 @@ const PanelContextmenu = () => {
|
||||
>
|
||||
{t('nodes.note.addNote', { ns: 'workflow' })}
|
||||
</div>
|
||||
{isCommentModeAvailable && (
|
||||
<div
|
||||
className={cn(
|
||||
'flex h-8 items-center justify-between rounded-lg px-3 text-sm text-text-secondary',
|
||||
pendingComment ? 'cursor-not-allowed opacity-50' : 'cursor-pointer hover:bg-state-base-hover',
|
||||
)}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
if (pendingComment)
|
||||
return
|
||||
setCommentQuickAdd(true)
|
||||
setCommentPlacing(true)
|
||||
handlePaneContextmenuCancel()
|
||||
}}
|
||||
>
|
||||
{t('comments.actions.addComment', { ns: 'workflow' })}
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
className="flex h-8 cursor-pointer items-center justify-between rounded-lg px-3 text-sm text-text-secondary hover:bg-state-base-hover"
|
||||
onClick={() => {
|
||||
|
||||
@ -43,6 +43,10 @@ export type WorkflowSliceShape = {
|
||||
setControlMode: (controlMode: WorkflowSliceShape['controlMode']) => void
|
||||
pendingComment: MousePosition | null
|
||||
setPendingComment: (pendingComment: WorkflowSliceShape['pendingComment']) => void
|
||||
isCommentPlacing: boolean
|
||||
setCommentPlacing: (isCommentPlacing: boolean) => void
|
||||
isCommentQuickAdd: boolean
|
||||
setCommentQuickAdd: (isCommentQuickAdd: boolean) => void
|
||||
isCommentPreviewHovering: boolean
|
||||
setCommentPreviewHovering: (hovering: boolean) => void
|
||||
mousePosition: { pageX: number, pageY: number, elementX: number, elementY: number }
|
||||
@ -89,6 +93,10 @@ export const createWorkflowSlice: StateCreator<WorkflowSliceShape> = set => ({
|
||||
},
|
||||
pendingComment: null,
|
||||
setPendingComment: pendingComment => set(() => ({ pendingComment })),
|
||||
isCommentPlacing: false,
|
||||
setCommentPlacing: isCommentPlacing => set(() => ({ isCommentPlacing })),
|
||||
isCommentQuickAdd: false,
|
||||
setCommentQuickAdd: isCommentQuickAdd => set(() => ({ isCommentQuickAdd })),
|
||||
mousePosition: { pageX: 0, pageY: 0, elementX: 0, elementY: 0 },
|
||||
setMousePosition: mousePosition => set(() => ({ mousePosition })),
|
||||
isCommentPreviewHovering: false,
|
||||
|
||||
@ -111,6 +111,7 @@
|
||||
"chatVariable.updatedAt": "تم التحديث في ",
|
||||
"comments.actions.deleteReply": "حذف الرد",
|
||||
"comments.actions.editReply": "تعديل الرد",
|
||||
"comments.actions.addComment": "Add Comment",
|
||||
"comments.aria.closeComment": "إغلاق التعليق",
|
||||
"comments.aria.deleteComment": "حذف المناقشة",
|
||||
"comments.aria.nextComment": "التعليق التالي",
|
||||
|
||||
@ -111,6 +111,7 @@
|
||||
"chatVariable.updatedAt": "Aktualisiert am ",
|
||||
"comments.actions.deleteReply": "Antwort löschen",
|
||||
"comments.actions.editReply": "Antwort bearbeiten",
|
||||
"comments.actions.addComment": "Add Comment",
|
||||
"comments.aria.closeComment": "Kommentar schließen",
|
||||
"comments.aria.deleteComment": "Kommentar löschen",
|
||||
"comments.aria.nextComment": "Nächster Kommentar",
|
||||
|
||||
@ -111,6 +111,7 @@
|
||||
"chatVariable.updatedAt": "Updated at ",
|
||||
"comments.actions.deleteReply": "Delete reply",
|
||||
"comments.actions.editReply": "Edit reply",
|
||||
"comments.actions.addComment": "Add Comment",
|
||||
"comments.aria.closeComment": "Close comment",
|
||||
"comments.aria.deleteComment": "Delete thread",
|
||||
"comments.aria.nextComment": "Next comment",
|
||||
|
||||
@ -111,6 +111,7 @@
|
||||
"chatVariable.updatedAt": "Actualizado el ",
|
||||
"comments.actions.deleteReply": "Eliminar respuesta",
|
||||
"comments.actions.editReply": "Editar respuesta",
|
||||
"comments.actions.addComment": "Add Comment",
|
||||
"comments.aria.closeComment": "Cerrar comentario",
|
||||
"comments.aria.deleteComment": "Eliminar comentario",
|
||||
"comments.aria.nextComment": "Comentario siguiente",
|
||||
|
||||
@ -111,6 +111,7 @@
|
||||
"chatVariable.updatedAt": "بهروزرسانی شده در ",
|
||||
"comments.actions.deleteReply": "حذف پاسخ",
|
||||
"comments.actions.editReply": "ویرایش پاسخ",
|
||||
"comments.actions.addComment": "Add Comment",
|
||||
"comments.aria.closeComment": "بستن دیدگاه",
|
||||
"comments.aria.deleteComment": "حذف دیدگاه",
|
||||
"comments.aria.nextComment": "دیدگاه بعدی",
|
||||
|
||||
@ -111,6 +111,7 @@
|
||||
"chatVariable.updatedAt": "Mis à jour le ",
|
||||
"comments.actions.deleteReply": "Supprimer la réponse",
|
||||
"comments.actions.editReply": "Modifier la réponse",
|
||||
"comments.actions.addComment": "Add Comment",
|
||||
"comments.aria.closeComment": "Fermer le commentaire",
|
||||
"comments.aria.deleteComment": "Supprimer le commentaire",
|
||||
"comments.aria.nextComment": "Commentaire suivant",
|
||||
|
||||
@ -111,6 +111,7 @@
|
||||
"chatVariable.updatedAt": "अपडेट किया गया ",
|
||||
"comments.actions.deleteReply": "जवाब हटाएं",
|
||||
"comments.actions.editReply": "जवाब संपादित करें",
|
||||
"comments.actions.addComment": "Add Comment",
|
||||
"comments.aria.closeComment": "टिप्पणी बंद करें",
|
||||
"comments.aria.deleteComment": "टिप्पणी हटाएं",
|
||||
"comments.aria.nextComment": "अगली टिप्पणी",
|
||||
|
||||
@ -111,6 +111,7 @@
|
||||
"chatVariable.updatedAt": "Diperbarui pada",
|
||||
"comments.actions.deleteReply": "Hapus balasan",
|
||||
"comments.actions.editReply": "Edit balasan",
|
||||
"comments.actions.addComment": "Add Comment",
|
||||
"comments.aria.closeComment": "Tutup komentar",
|
||||
"comments.aria.deleteComment": "Hapus komentar",
|
||||
"comments.aria.nextComment": "Komentar berikutnya",
|
||||
|
||||
@ -111,6 +111,7 @@
|
||||
"chatVariable.updatedAt": "Aggiornato il ",
|
||||
"comments.actions.deleteReply": "Elimina risposta",
|
||||
"comments.actions.editReply": "Modifica risposta",
|
||||
"comments.actions.addComment": "Add Comment",
|
||||
"comments.aria.closeComment": "Chiudi commento",
|
||||
"comments.aria.deleteComment": "Elimina commento",
|
||||
"comments.aria.nextComment": "Commento successivo",
|
||||
|
||||
@ -111,6 +111,7 @@
|
||||
"chatVariable.updatedAt": "最終更新:",
|
||||
"comments.actions.deleteReply": "返信を削除",
|
||||
"comments.actions.editReply": "返信を編集",
|
||||
"comments.actions.addComment": "Add Comment",
|
||||
"comments.aria.closeComment": "コメントを閉じる",
|
||||
"comments.aria.deleteComment": "スレッドを削除",
|
||||
"comments.aria.nextComment": "次のコメント",
|
||||
|
||||
@ -111,6 +111,7 @@
|
||||
"chatVariable.updatedAt": "업데이트 시간: ",
|
||||
"comments.actions.deleteReply": "답글 삭제",
|
||||
"comments.actions.editReply": "답글 편집",
|
||||
"comments.actions.addComment": "Add Comment",
|
||||
"comments.aria.closeComment": "댓글 닫기",
|
||||
"comments.aria.deleteComment": "댓글 삭제",
|
||||
"comments.aria.nextComment": "다음 댓글",
|
||||
|
||||
@ -109,6 +109,7 @@
|
||||
"chatVariable.panelTitle": "Conversation Variables",
|
||||
"chatVariable.storedContent": "Stored content",
|
||||
"chatVariable.updatedAt": "Updated at ",
|
||||
"comments.actions.addComment": "Add Comment",
|
||||
"common.ImageUploadLegacyTip": "You can now create file type variables in the start form. We will no longer support the image upload feature in the future. ",
|
||||
"common.accessAPIReference": "Access API Reference",
|
||||
"common.addBlock": "Add Node",
|
||||
|
||||
@ -111,6 +111,7 @@
|
||||
"chatVariable.updatedAt": "Zaktualizowano ",
|
||||
"comments.actions.deleteReply": "Delete reply",
|
||||
"comments.actions.editReply": "Edit reply",
|
||||
"comments.actions.addComment": "Add Comment",
|
||||
"comments.aria.closeComment": "Close comment",
|
||||
"comments.aria.deleteComment": "Delete thread",
|
||||
"comments.aria.nextComment": "Next comment",
|
||||
|
||||
@ -111,6 +111,7 @@
|
||||
"chatVariable.updatedAt": "Atualizado em ",
|
||||
"comments.actions.deleteReply": "Delete reply",
|
||||
"comments.actions.editReply": "Edit reply",
|
||||
"comments.actions.addComment": "Add Comment",
|
||||
"comments.aria.closeComment": "Close comment",
|
||||
"comments.aria.deleteComment": "Delete thread",
|
||||
"comments.aria.nextComment": "Next comment",
|
||||
|
||||
@ -111,6 +111,7 @@
|
||||
"chatVariable.updatedAt": "Actualizat la ",
|
||||
"comments.actions.deleteReply": "Delete reply",
|
||||
"comments.actions.editReply": "Edit reply",
|
||||
"comments.actions.addComment": "Add Comment",
|
||||
"comments.aria.closeComment": "Close comment",
|
||||
"comments.aria.deleteComment": "Delete thread",
|
||||
"comments.aria.nextComment": "Next comment",
|
||||
|
||||
@ -111,6 +111,7 @@
|
||||
"chatVariable.updatedAt": "Обновлено в ",
|
||||
"comments.actions.deleteReply": "Delete reply",
|
||||
"comments.actions.editReply": "Edit reply",
|
||||
"comments.actions.addComment": "Add Comment",
|
||||
"comments.aria.closeComment": "Close comment",
|
||||
"comments.aria.deleteComment": "Delete thread",
|
||||
"comments.aria.nextComment": "Next comment",
|
||||
|
||||
@ -111,6 +111,7 @@
|
||||
"chatVariable.updatedAt": "Posodobljeno ob",
|
||||
"comments.actions.deleteReply": "Delete reply",
|
||||
"comments.actions.editReply": "Edit reply",
|
||||
"comments.actions.addComment": "Add Comment",
|
||||
"comments.aria.closeComment": "Close comment",
|
||||
"comments.aria.deleteComment": "Delete thread",
|
||||
"comments.aria.nextComment": "Next comment",
|
||||
|
||||
@ -111,6 +111,7 @@
|
||||
"chatVariable.updatedAt": "อัพเดทเมื่อ",
|
||||
"comments.actions.deleteReply": "Delete reply",
|
||||
"comments.actions.editReply": "Edit reply",
|
||||
"comments.actions.addComment": "Add Comment",
|
||||
"comments.aria.closeComment": "Close comment",
|
||||
"comments.aria.deleteComment": "Delete thread",
|
||||
"comments.aria.nextComment": "Next comment",
|
||||
|
||||
@ -111,6 +111,7 @@
|
||||
"chatVariable.updatedAt": "Güncellenme zamanı: ",
|
||||
"comments.actions.deleteReply": "Delete reply",
|
||||
"comments.actions.editReply": "Edit reply",
|
||||
"comments.actions.addComment": "Add Comment",
|
||||
"comments.aria.closeComment": "Close comment",
|
||||
"comments.aria.deleteComment": "Delete thread",
|
||||
"comments.aria.nextComment": "Next comment",
|
||||
|
||||
@ -111,6 +111,7 @@
|
||||
"chatVariable.updatedAt": "Оновлено ",
|
||||
"comments.actions.deleteReply": "Delete reply",
|
||||
"comments.actions.editReply": "Edit reply",
|
||||
"comments.actions.addComment": "Add Comment",
|
||||
"comments.aria.closeComment": "Close comment",
|
||||
"comments.aria.deleteComment": "Delete thread",
|
||||
"comments.aria.nextComment": "Next comment",
|
||||
|
||||
@ -111,6 +111,7 @@
|
||||
"chatVariable.updatedAt": "Cập nhật lúc ",
|
||||
"comments.actions.deleteReply": "Delete reply",
|
||||
"comments.actions.editReply": "Edit reply",
|
||||
"comments.actions.addComment": "Add Comment",
|
||||
"comments.aria.closeComment": "Close comment",
|
||||
"comments.aria.deleteComment": "Delete thread",
|
||||
"comments.aria.nextComment": "Next comment",
|
||||
|
||||
@ -111,6 +111,7 @@
|
||||
"chatVariable.updatedAt": "更新时间 ",
|
||||
"comments.actions.deleteReply": "删除回复",
|
||||
"comments.actions.editReply": "编辑回复",
|
||||
"comments.actions.addComment": "Add Comment",
|
||||
"comments.aria.closeComment": "关闭评论",
|
||||
"comments.aria.deleteComment": "删除讨论",
|
||||
"comments.aria.nextComment": "下一条评论",
|
||||
|
||||
@ -111,6 +111,7 @@
|
||||
"chatVariable.updatedAt": "更新於 ",
|
||||
"comments.actions.deleteReply": "刪除回覆",
|
||||
"comments.actions.editReply": "編輯回覆",
|
||||
"comments.actions.addComment": "Add Comment",
|
||||
"comments.aria.closeComment": "關閉評論",
|
||||
"comments.aria.deleteComment": "刪除評論",
|
||||
"comments.aria.nextComment": "下一則評論",
|
||||
|
||||
Loading…
Reference in New Issue
Block a user