mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-09-07 08:26:51 +00:00
Make the prompts editable now (#5359)
This commit is contained in:
parent
edce187c03
commit
a9a87c2e0f
22 changed files with 1464 additions and 153 deletions
File diff suppressed because it is too large
Load diff
|
|
@ -6,9 +6,16 @@ import pWaitFor from "p-wait-for"
|
|||
import * as vscode from "vscode"
|
||||
import * as yaml from "yaml"
|
||||
|
||||
import { type Language, type ProviderSettings, type GlobalState, TelemetryEventName } from "@roo-code/types"
|
||||
import {
|
||||
type Language,
|
||||
type ProviderSettings,
|
||||
type GlobalState,
|
||||
type ClineMessage,
|
||||
TelemetryEventName,
|
||||
} from "@roo-code/types"
|
||||
import { CloudService } from "@roo-code/cloud"
|
||||
import { TelemetryService } from "@roo-code/telemetry"
|
||||
import { type ApiMessage } from "../task-persistence/apiMessages"
|
||||
|
||||
import { ClineProvider } from "./ClineProvider"
|
||||
import { changeLanguage, t } from "../../i18n"
|
||||
|
|
@ -58,6 +65,200 @@ export const webviewMessageHandler = async (
|
|||
const updateGlobalState = async <K extends keyof GlobalState>(key: K, value: GlobalState[K]) =>
|
||||
await provider.contextProxy.setValue(key, value)
|
||||
|
||||
/**
|
||||
* Shared utility to find message indices based on timestamp
|
||||
*/
|
||||
const findMessageIndices = (messageTs: number, currentCline: any) => {
|
||||
const timeCutoff = messageTs - 1000 // 1 second buffer before the message
|
||||
const messageIndex = currentCline.clineMessages.findIndex((msg: ClineMessage) => msg.ts && msg.ts >= timeCutoff)
|
||||
const apiConversationHistoryIndex = currentCline.apiConversationHistory.findIndex(
|
||||
(msg: ApiMessage) => msg.ts && msg.ts >= timeCutoff,
|
||||
)
|
||||
return { messageIndex, apiConversationHistoryIndex }
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes just the target message, preserving messages after the next user message
|
||||
*/
|
||||
const removeMessagesJustThis = async (
|
||||
currentCline: any,
|
||||
messageIndex: number,
|
||||
apiConversationHistoryIndex: number,
|
||||
) => {
|
||||
// Find the next user message first
|
||||
const nextUserMessage = currentCline.clineMessages
|
||||
.slice(messageIndex + 1)
|
||||
.find((msg: ClineMessage) => msg.type === "say" && msg.say === "user_feedback")
|
||||
|
||||
// Handle UI messages
|
||||
if (nextUserMessage) {
|
||||
// Find absolute index of next user message
|
||||
const nextUserMessageIndex = currentCline.clineMessages.findIndex(
|
||||
(msg: ClineMessage) => msg === nextUserMessage,
|
||||
)
|
||||
|
||||
// Keep messages before current message and after next user message
|
||||
await currentCline.overwriteClineMessages([
|
||||
...currentCline.clineMessages.slice(0, messageIndex),
|
||||
...currentCline.clineMessages.slice(nextUserMessageIndex),
|
||||
])
|
||||
} else {
|
||||
// If no next user message, keep only messages before current message
|
||||
await currentCline.overwriteClineMessages(currentCline.clineMessages.slice(0, messageIndex))
|
||||
}
|
||||
|
||||
// Handle API messages
|
||||
if (apiConversationHistoryIndex !== -1) {
|
||||
if (nextUserMessage && nextUserMessage.ts) {
|
||||
// Keep messages before current API message and after next user message
|
||||
await currentCline.overwriteApiConversationHistory([
|
||||
...currentCline.apiConversationHistory.slice(0, apiConversationHistoryIndex),
|
||||
...currentCline.apiConversationHistory.filter(
|
||||
(msg: ApiMessage) => msg.ts && msg.ts >= nextUserMessage.ts,
|
||||
),
|
||||
])
|
||||
} else {
|
||||
// If no next user message, keep only messages before current API message
|
||||
await currentCline.overwriteApiConversationHistory(
|
||||
currentCline.apiConversationHistory.slice(0, apiConversationHistoryIndex),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the target message and all subsequent messages
|
||||
*/
|
||||
const removeMessagesThisAndSubsequent = async (
|
||||
currentCline: any,
|
||||
messageIndex: number,
|
||||
apiConversationHistoryIndex: number,
|
||||
) => {
|
||||
// Delete this message and all that follow
|
||||
await currentCline.overwriteClineMessages(currentCline.clineMessages.slice(0, messageIndex))
|
||||
|
||||
if (apiConversationHistoryIndex !== -1) {
|
||||
await currentCline.overwriteApiConversationHistory(
|
||||
currentCline.apiConversationHistory.slice(0, apiConversationHistoryIndex),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles message deletion operations with user confirmation
|
||||
*/
|
||||
const handleDeleteOperation = async (messageTs: number): Promise<void> => {
|
||||
const options = [
|
||||
t("common:confirmation.delete_just_this_message"),
|
||||
t("common:confirmation.delete_this_and_subsequent"),
|
||||
]
|
||||
|
||||
const answer = await vscode.window.showInformationMessage(
|
||||
t("common:confirmation.delete_message"),
|
||||
{ modal: true },
|
||||
...options,
|
||||
)
|
||||
|
||||
// Only proceed if user selected one of the options and we have a current cline
|
||||
if (answer && options.includes(answer) && provider.getCurrentCline()) {
|
||||
const currentCline = provider.getCurrentCline()!
|
||||
const { messageIndex, apiConversationHistoryIndex } = findMessageIndices(messageTs, currentCline)
|
||||
|
||||
if (messageIndex !== -1) {
|
||||
try {
|
||||
const { historyItem } = await provider.getTaskWithId(currentCline.taskId)
|
||||
|
||||
// Check which option the user selected
|
||||
if (answer === options[0]) {
|
||||
// Delete just this message
|
||||
await removeMessagesJustThis(currentCline, messageIndex, apiConversationHistoryIndex)
|
||||
} else if (answer === options[1]) {
|
||||
// Delete this message and all subsequent
|
||||
await removeMessagesThisAndSubsequent(currentCline, messageIndex, apiConversationHistoryIndex)
|
||||
}
|
||||
|
||||
// Initialize with history item after deletion
|
||||
await provider.initClineWithHistoryItem(historyItem)
|
||||
} catch (error) {
|
||||
console.error("Error in delete message:", error)
|
||||
vscode.window.showErrorMessage(
|
||||
`Error deleting message: ${error instanceof Error ? error.message : String(error)}`,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles message editing operations with user confirmation
|
||||
*/
|
||||
const handleEditOperation = async (messageTs: number, editedContent: string): Promise<void> => {
|
||||
const options = [
|
||||
t("common:confirmation.edit_this_and_delete_subsequent"),
|
||||
t("common:confirmation.edit_just_this_message"),
|
||||
]
|
||||
|
||||
const answer = await vscode.window.showInformationMessage(
|
||||
t("common:confirmation.edit_message"),
|
||||
{ modal: true },
|
||||
...options,
|
||||
)
|
||||
|
||||
// Only proceed if user selected one of the options and we have a current cline
|
||||
if (answer && options.includes(answer) && provider.getCurrentCline()) {
|
||||
const currentCline = provider.getCurrentCline()!
|
||||
const { messageIndex, apiConversationHistoryIndex } = findMessageIndices(messageTs, currentCline)
|
||||
|
||||
if (messageIndex !== -1) {
|
||||
try {
|
||||
// Check which option the user selected
|
||||
if (answer === options[0]) {
|
||||
// Edit this message and delete subsequent
|
||||
await removeMessagesThisAndSubsequent(currentCline, messageIndex, apiConversationHistoryIndex)
|
||||
} else if (answer === options[1]) {
|
||||
// Edit just this message
|
||||
await removeMessagesJustThis(currentCline, messageIndex, apiConversationHistoryIndex)
|
||||
}
|
||||
|
||||
// Process the edited message as a regular user message
|
||||
// This will add it to the conversation and trigger an AI response
|
||||
webviewMessageHandler(provider, {
|
||||
type: "askResponse",
|
||||
askResponse: "messageResponse",
|
||||
text: editedContent,
|
||||
})
|
||||
|
||||
// Don't initialize with history item for edit operations
|
||||
// The webviewMessageHandler will handle the conversation state
|
||||
} catch (error) {
|
||||
console.error("Error in edit message:", error)
|
||||
vscode.window.showErrorMessage(
|
||||
`Error editing message: ${error instanceof Error ? error.message : String(error)}`,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles message modification operations (delete or edit) with confirmation dialog
|
||||
* @param messageTs Timestamp of the message to operate on
|
||||
* @param operation Type of operation ('delete' or 'edit')
|
||||
* @param editedContent New content for edit operations
|
||||
* @returns Promise<void>
|
||||
*/
|
||||
const handleMessageModificationsOperation = async (
|
||||
messageTs: number,
|
||||
operation: "delete" | "edit",
|
||||
editedContent?: string,
|
||||
): Promise<void> => {
|
||||
if (operation === "delete") {
|
||||
await handleDeleteOperation(messageTs)
|
||||
} else if (operation === "edit" && editedContent) {
|
||||
await handleEditOperation(messageTs, editedContent)
|
||||
}
|
||||
}
|
||||
|
||||
switch (message.type) {
|
||||
case "webviewDidLaunch":
|
||||
// Load custom modes first
|
||||
|
|
@ -989,108 +1190,19 @@ export const webviewMessageHandler = async (
|
|||
}
|
||||
break
|
||||
case "deleteMessage": {
|
||||
const answer = await vscode.window.showInformationMessage(
|
||||
t("common:confirmation.delete_message"),
|
||||
{ modal: true },
|
||||
t("common:confirmation.just_this_message"),
|
||||
t("common:confirmation.this_and_subsequent"),
|
||||
)
|
||||
|
||||
if (provider.getCurrentCline() && typeof message.value === "number" && message.value) {
|
||||
await handleMessageModificationsOperation(message.value, "delete")
|
||||
}
|
||||
break
|
||||
}
|
||||
case "submitEditedMessage": {
|
||||
if (
|
||||
(answer === t("common:confirmation.just_this_message") ||
|
||||
answer === t("common:confirmation.this_and_subsequent")) &&
|
||||
provider.getCurrentCline() &&
|
||||
typeof message.value === "number" &&
|
||||
message.value
|
||||
message.value &&
|
||||
message.editedMessageContent
|
||||
) {
|
||||
const timeCutoff = message.value - 1000 // 1 second buffer before the message to delete
|
||||
|
||||
const messageIndex = provider
|
||||
.getCurrentCline()!
|
||||
.clineMessages.findIndex((msg) => msg.ts && msg.ts >= timeCutoff)
|
||||
|
||||
const apiConversationHistoryIndex = provider
|
||||
.getCurrentCline()
|
||||
?.apiConversationHistory.findIndex((msg) => msg.ts && msg.ts >= timeCutoff)
|
||||
|
||||
if (messageIndex !== -1) {
|
||||
const { historyItem } = await provider.getTaskWithId(provider.getCurrentCline()!.taskId)
|
||||
|
||||
if (answer === t("common:confirmation.just_this_message")) {
|
||||
// Find the next user message first
|
||||
const nextUserMessage = provider
|
||||
.getCurrentCline()!
|
||||
.clineMessages.slice(messageIndex + 1)
|
||||
.find((msg) => msg.type === "say" && msg.say === "user_feedback")
|
||||
|
||||
// Handle UI messages
|
||||
if (nextUserMessage) {
|
||||
// Find absolute index of next user message
|
||||
const nextUserMessageIndex = provider
|
||||
.getCurrentCline()!
|
||||
.clineMessages.findIndex((msg) => msg === nextUserMessage)
|
||||
|
||||
// Keep messages before current message and after next user message
|
||||
await provider
|
||||
.getCurrentCline()!
|
||||
.overwriteClineMessages([
|
||||
...provider.getCurrentCline()!.clineMessages.slice(0, messageIndex),
|
||||
...provider.getCurrentCline()!.clineMessages.slice(nextUserMessageIndex),
|
||||
])
|
||||
} else {
|
||||
// If no next user message, keep only messages before current message
|
||||
await provider
|
||||
.getCurrentCline()!
|
||||
.overwriteClineMessages(
|
||||
provider.getCurrentCline()!.clineMessages.slice(0, messageIndex),
|
||||
)
|
||||
}
|
||||
|
||||
// Handle API messages
|
||||
if (apiConversationHistoryIndex !== -1) {
|
||||
if (nextUserMessage && nextUserMessage.ts) {
|
||||
// Keep messages before current API message and after next user message
|
||||
await provider
|
||||
.getCurrentCline()!
|
||||
.overwriteApiConversationHistory([
|
||||
...provider
|
||||
.getCurrentCline()!
|
||||
.apiConversationHistory.slice(0, apiConversationHistoryIndex),
|
||||
...provider
|
||||
.getCurrentCline()!
|
||||
.apiConversationHistory.filter(
|
||||
(msg) => msg.ts && msg.ts >= nextUserMessage.ts,
|
||||
),
|
||||
])
|
||||
} else {
|
||||
// If no next user message, keep only messages before current API message
|
||||
await provider
|
||||
.getCurrentCline()!
|
||||
.overwriteApiConversationHistory(
|
||||
provider
|
||||
.getCurrentCline()!
|
||||
.apiConversationHistory.slice(0, apiConversationHistoryIndex),
|
||||
)
|
||||
}
|
||||
}
|
||||
} else if (answer === t("common:confirmation.this_and_subsequent")) {
|
||||
// Delete this message and all that follow
|
||||
await provider
|
||||
.getCurrentCline()!
|
||||
.overwriteClineMessages(provider.getCurrentCline()!.clineMessages.slice(0, messageIndex))
|
||||
if (apiConversationHistoryIndex !== -1) {
|
||||
await provider
|
||||
.getCurrentCline()!
|
||||
.overwriteApiConversationHistory(
|
||||
provider
|
||||
.getCurrentCline()!
|
||||
.apiConversationHistory.slice(0, apiConversationHistoryIndex),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
await provider.initClineWithHistoryItem(historyItem)
|
||||
}
|
||||
await handleMessageModificationsOperation(message.value, "edit", message.editedMessageContent)
|
||||
}
|
||||
break
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,8 +23,11 @@
|
|||
"delete_config_profile": "Estàs segur que vols eliminar aquest perfil de configuració?",
|
||||
"delete_custom_mode_with_rules": "Esteu segur que voleu suprimir aquest mode {scope}?\n\nAixò també suprimirà la carpeta de regles associada a:\n{rulesFolderPath}",
|
||||
"delete_message": "Què vols eliminar?",
|
||||
"just_this_message": "Només aquest missatge",
|
||||
"this_and_subsequent": "Aquest i tots els missatges posteriors"
|
||||
"edit_message": "Eliminar tots els missatges després d'aquest?",
|
||||
"delete_just_this_message": "Només aquest missatge",
|
||||
"edit_just_this_message": "No, només editar aquest",
|
||||
"delete_this_and_subsequent": "Aquest i tots els missatges posteriors",
|
||||
"edit_this_and_delete_subsequent": "Sí"
|
||||
},
|
||||
"errors": {
|
||||
"invalid_data_uri": "Format d'URI de dades no vàlid",
|
||||
|
|
@ -112,6 +115,11 @@
|
|||
"remove": "Eliminar",
|
||||
"keep": "Mantenir"
|
||||
},
|
||||
"buttons": {
|
||||
"save": "Desar",
|
||||
"cancel": "Cancel·lar",
|
||||
"edit": "Editar"
|
||||
},
|
||||
"tasks": {
|
||||
"canceled": "Error de tasca: Ha estat aturada i cancel·lada per l'usuari.",
|
||||
"deleted": "Fallada de tasca: Ha estat aturada i eliminada per l'usuari.",
|
||||
|
|
|
|||
|
|
@ -19,8 +19,11 @@
|
|||
"delete_config_profile": "Möchtest du dieses Konfigurationsprofil wirklich löschen?",
|
||||
"delete_custom_mode_with_rules": "Bist du sicher, dass du diesen {scope}-Modus löschen möchtest?\n\nDadurch wird auch der zugehörige Regelordner unter folgender Adresse gelöscht:\n{rulesFolderPath}",
|
||||
"delete_message": "Was möchtest du löschen?",
|
||||
"just_this_message": "Nur diese Nachricht",
|
||||
"this_and_subsequent": "Diese und alle nachfolgenden Nachrichten"
|
||||
"edit_message": "Alle Nachrichten nach dieser löschen?",
|
||||
"delete_just_this_message": "Nur diese Nachricht",
|
||||
"edit_just_this_message": "Nein, nur diese bearbeiten",
|
||||
"delete_this_and_subsequent": "Diese und alle nachfolgenden Nachrichten",
|
||||
"edit_this_and_delete_subsequent": "Ja"
|
||||
},
|
||||
"errors": {
|
||||
"invalid_data_uri": "Ungültiges Daten-URI-Format",
|
||||
|
|
@ -108,6 +111,11 @@
|
|||
"remove": "Entfernen",
|
||||
"keep": "Behalten"
|
||||
},
|
||||
"buttons": {
|
||||
"save": "Speichern",
|
||||
"cancel": "Abbrechen",
|
||||
"edit": "Bearbeiten"
|
||||
},
|
||||
"tasks": {
|
||||
"canceled": "Aufgabenfehler: Die Aufgabe wurde vom Benutzer gestoppt und abgebrochen.",
|
||||
"deleted": "Aufgabenfehler: Die Aufgabe wurde vom Benutzer gestoppt und gelöscht.",
|
||||
|
|
|
|||
|
|
@ -19,8 +19,11 @@
|
|||
"delete_config_profile": "Are you sure you want to delete this configuration profile?",
|
||||
"delete_custom_mode_with_rules": "Are you sure you want to delete this {scope} mode?\n\nThis will also delete the associated rules folder at:\n{rulesFolderPath}",
|
||||
"delete_message": "What would you like to delete?",
|
||||
"just_this_message": "Just this message",
|
||||
"this_and_subsequent": "This and all subsequent messages"
|
||||
"edit_message": "Delete all messages after this one?",
|
||||
"delete_just_this_message": "Just this message",
|
||||
"edit_just_this_message": "No, just edit this one",
|
||||
"delete_this_and_subsequent": "This and all subsequent messages",
|
||||
"edit_this_and_delete_subsequent": "Yes"
|
||||
},
|
||||
"errors": {
|
||||
"invalid_data_uri": "Invalid data URI format",
|
||||
|
|
@ -108,6 +111,11 @@
|
|||
"remove": "Remove",
|
||||
"keep": "Keep"
|
||||
},
|
||||
"buttons": {
|
||||
"save": "Save",
|
||||
"cancel": "Cancel",
|
||||
"edit": "Edit"
|
||||
},
|
||||
"tasks": {
|
||||
"canceled": "Task error: It was stopped and canceled by the user.",
|
||||
"deleted": "Task failure: It was stopped and deleted by the user.",
|
||||
|
|
|
|||
|
|
@ -19,8 +19,11 @@
|
|||
"delete_config_profile": "¿Estás seguro de que deseas eliminar este perfil de configuración?",
|
||||
"delete_custom_mode_with_rules": "¿Estás seguro de que quieres eliminar este modo {scope}?\n\nEsto también eliminará la carpeta de reglas asociada en:\n{rulesFolderPath}",
|
||||
"delete_message": "¿Qué deseas eliminar?",
|
||||
"just_this_message": "Solo este mensaje",
|
||||
"this_and_subsequent": "Este y todos los mensajes posteriores"
|
||||
"edit_message": "¿Eliminar todos los mensajes posteriores a este?",
|
||||
"delete_just_this_message": "Solo este mensaje",
|
||||
"edit_just_this_message": "No, solo editar este",
|
||||
"delete_this_and_subsequent": "Este y todos los mensajes posteriores",
|
||||
"edit_this_and_delete_subsequent": "Sí"
|
||||
},
|
||||
"errors": {
|
||||
"invalid_data_uri": "Formato de URI de datos no válido",
|
||||
|
|
@ -108,6 +111,11 @@
|
|||
"remove": "Eliminar",
|
||||
"keep": "Mantener"
|
||||
},
|
||||
"buttons": {
|
||||
"save": "Guardar",
|
||||
"cancel": "Cancelar",
|
||||
"edit": "Editar"
|
||||
},
|
||||
"tasks": {
|
||||
"canceled": "Error de tarea: Fue detenida y cancelada por el usuario.",
|
||||
"deleted": "Fallo de tarea: Fue detenida y eliminada por el usuario.",
|
||||
|
|
|
|||
|
|
@ -19,8 +19,11 @@
|
|||
"delete_config_profile": "Êtes-vous sûr de vouloir supprimer ce profil de configuration ?",
|
||||
"delete_custom_mode_with_rules": "Êtes-vous sûr de vouloir supprimer ce mode {scope} ?\n\nCela supprimera également le dossier de règles associé à l'adresse :\n{rulesFolderPath}",
|
||||
"delete_message": "Que souhaitez-vous supprimer ?",
|
||||
"just_this_message": "Uniquement ce message",
|
||||
"this_and_subsequent": "Ce message et tous les messages suivants"
|
||||
"edit_message": "Supprimer tous les messages après celui-ci ?",
|
||||
"delete_just_this_message": "Uniquement ce message",
|
||||
"edit_just_this_message": "Non, modifier uniquement celui-ci",
|
||||
"delete_this_and_subsequent": "Ce message et tous les messages suivants",
|
||||
"edit_this_and_delete_subsequent": "Oui"
|
||||
},
|
||||
"errors": {
|
||||
"invalid_data_uri": "Format d'URI de données invalide",
|
||||
|
|
@ -108,6 +111,11 @@
|
|||
"remove": "Supprimer",
|
||||
"keep": "Conserver"
|
||||
},
|
||||
"buttons": {
|
||||
"save": "Enregistrer",
|
||||
"cancel": "Annuler",
|
||||
"edit": "Modifier"
|
||||
},
|
||||
"tasks": {
|
||||
"canceled": "Erreur de tâche : Elle a été arrêtée et annulée par l'utilisateur.",
|
||||
"deleted": "Échec de la tâche : Elle a été arrêtée et supprimée par l'utilisateur.",
|
||||
|
|
|
|||
|
|
@ -19,8 +19,11 @@
|
|||
"delete_config_profile": "क्या आप वाकई इस कॉन्फ़िगरेशन प्रोफ़ाइल को हटाना चाहते हैं?",
|
||||
"delete_custom_mode_with_rules": "क्या आप वाकई इस {scope} मोड को हटाना चाहते हैं?\n\nयह संबंधित नियम फ़ोल्डर को भी यहाँ हटा देगा:\n{rulesFolderPath}",
|
||||
"delete_message": "आप क्या हटाना चाहते हैं?",
|
||||
"just_this_message": "सिर्फ यह संदेश",
|
||||
"this_and_subsequent": "यह और सभी बाद के संदेश"
|
||||
"edit_message": "इसके बाद के सभी संदेशों को हटाएं?",
|
||||
"delete_just_this_message": "सिर्फ यह संदेश",
|
||||
"edit_just_this_message": "नहीं, केवल इसे संपादित करें",
|
||||
"delete_this_and_subsequent": "यह और सभी बाद के संदेश",
|
||||
"edit_this_and_delete_subsequent": "हां"
|
||||
},
|
||||
"errors": {
|
||||
"invalid_data_uri": "अमान्य डेटा URI फॉर्मेट",
|
||||
|
|
@ -108,6 +111,11 @@
|
|||
"remove": "हटाएं",
|
||||
"keep": "रखें"
|
||||
},
|
||||
"buttons": {
|
||||
"save": "सहेजें",
|
||||
"cancel": "रद्द करें",
|
||||
"edit": "संपादित करें"
|
||||
},
|
||||
"tasks": {
|
||||
"canceled": "टास्क त्रुटि: इसे उपयोगकर्ता द्वारा रोका और रद्द किया गया था।",
|
||||
"deleted": "टास्क विफलता: इसे उपयोगकर्ता द्वारा रोका और हटाया गया था।",
|
||||
|
|
|
|||
|
|
@ -19,8 +19,11 @@
|
|||
"delete_config_profile": "Apakah kamu yakin ingin menghapus profil konfigurasi ini?",
|
||||
"delete_custom_mode_with_rules": "Anda yakin ingin menghapus mode {scope} ini?\n\nIni juga akan menghapus folder aturan terkait di:\n{rulesFolderPath}",
|
||||
"delete_message": "Apa yang ingin kamu hapus?",
|
||||
"just_this_message": "Hanya pesan ini",
|
||||
"this_and_subsequent": "Ini dan semua pesan selanjutnya"
|
||||
"edit_message": "Hapus semua pesan setelah ini?",
|
||||
"delete_just_this_message": "Hanya pesan ini",
|
||||
"edit_just_this_message": "Tidak, hanya edit yang ini",
|
||||
"delete_this_and_subsequent": "Ini dan semua pesan selanjutnya",
|
||||
"edit_this_and_delete_subsequent": "Ya"
|
||||
},
|
||||
"errors": {
|
||||
"invalid_data_uri": "Format data URI tidak valid",
|
||||
|
|
@ -108,6 +111,11 @@
|
|||
"remove": "Hapus",
|
||||
"keep": "Simpan"
|
||||
},
|
||||
"buttons": {
|
||||
"save": "Simpan",
|
||||
"cancel": "Batal",
|
||||
"edit": "Edit"
|
||||
},
|
||||
"tasks": {
|
||||
"canceled": "Error tugas: Dihentikan dan dibatalkan oleh pengguna.",
|
||||
"deleted": "Kegagalan tugas: Dihentikan dan dihapus oleh pengguna.",
|
||||
|
|
|
|||
|
|
@ -19,8 +19,11 @@
|
|||
"delete_config_profile": "Sei sicuro di voler eliminare questo profilo di configurazione?",
|
||||
"delete_custom_mode_with_rules": "Sei sicuro di voler eliminare questa modalità {scope}?\n\nQuesto eliminerà anche la cartella delle regole associata in:\n{rulesFolderPath}",
|
||||
"delete_message": "Cosa desideri eliminare?",
|
||||
"just_this_message": "Solo questo messaggio",
|
||||
"this_and_subsequent": "Questo e tutti i messaggi successivi"
|
||||
"edit_message": "Eliminare tutti i messaggi dopo questo?",
|
||||
"delete_just_this_message": "Solo questo messaggio",
|
||||
"edit_just_this_message": "No, modifica solo questo",
|
||||
"delete_this_and_subsequent": "Questo e tutti i messaggi successivi",
|
||||
"edit_this_and_delete_subsequent": "Sì"
|
||||
},
|
||||
"errors": {
|
||||
"invalid_data_uri": "Formato URI dati non valido",
|
||||
|
|
@ -108,6 +111,11 @@
|
|||
"remove": "Rimuovi",
|
||||
"keep": "Mantieni"
|
||||
},
|
||||
"buttons": {
|
||||
"save": "Salva",
|
||||
"cancel": "Annulla",
|
||||
"edit": "Modifica"
|
||||
},
|
||||
"tasks": {
|
||||
"canceled": "Errore attività: È stata interrotta e annullata dall'utente.",
|
||||
"deleted": "Fallimento attività: È stata interrotta ed eliminata dall'utente.",
|
||||
|
|
|
|||
|
|
@ -19,8 +19,11 @@
|
|||
"delete_config_profile": "この設定プロファイルを削除してもよろしいですか?",
|
||||
"delete_custom_mode_with_rules": "この{scope}モードを削除してもよろしいですか?\n\nこれにより、関連するルールフォルダも次の場所で削除されます:\n{rulesFolderPath}",
|
||||
"delete_message": "何を削除しますか?",
|
||||
"just_this_message": "このメッセージのみ",
|
||||
"this_and_subsequent": "これ以降のすべてのメッセージ"
|
||||
"edit_message": "これ以降のメッセージをすべて削除しますか?",
|
||||
"delete_just_this_message": "このメッセージのみ",
|
||||
"edit_just_this_message": "いいえ、これだけを編集",
|
||||
"delete_this_and_subsequent": "これ以降のすべてのメッセージ",
|
||||
"edit_this_and_delete_subsequent": "はい"
|
||||
},
|
||||
"errors": {
|
||||
"invalid_data_uri": "データURIフォーマットが無効です",
|
||||
|
|
@ -108,6 +111,11 @@
|
|||
"remove": "削除",
|
||||
"keep": "保持"
|
||||
},
|
||||
"buttons": {
|
||||
"save": "保存",
|
||||
"cancel": "キャンセル",
|
||||
"edit": "編集"
|
||||
},
|
||||
"tasks": {
|
||||
"canceled": "タスクエラー:ユーザーによって停止およびキャンセルされました。",
|
||||
"deleted": "タスク失敗:ユーザーによって停止および削除されました。",
|
||||
|
|
|
|||
|
|
@ -19,8 +19,11 @@
|
|||
"delete_config_profile": "이 구성 프로필을 삭제하시겠습니까?",
|
||||
"delete_custom_mode_with_rules": "이 {scope} 모드를 삭제하시겠습니까?\n\n이렇게 하면 연결된 규칙 폴더도 다음 위치에서 삭제됩니다:\n{rulesFolderPath}",
|
||||
"delete_message": "무엇을 삭제하시겠습니까?",
|
||||
"just_this_message": "이 메시지만",
|
||||
"this_and_subsequent": "이 메시지와 모든 후속 메시지"
|
||||
"edit_message": "이 메시지 이후의 모든 메시지를 삭제하시겠습니까?",
|
||||
"delete_just_this_message": "이 메시지만",
|
||||
"edit_just_this_message": "아니요, 이것만 편집",
|
||||
"delete_this_and_subsequent": "이 메시지와 모든 후속 메시지",
|
||||
"edit_this_and_delete_subsequent": "예"
|
||||
},
|
||||
"errors": {
|
||||
"invalid_data_uri": "잘못된 데이터 URI 형식",
|
||||
|
|
@ -108,6 +111,11 @@
|
|||
"remove": "제거",
|
||||
"keep": "유지"
|
||||
},
|
||||
"buttons": {
|
||||
"save": "저장",
|
||||
"cancel": "취소",
|
||||
"edit": "편집"
|
||||
},
|
||||
"tasks": {
|
||||
"canceled": "작업 오류: 사용자에 의해 중지 및 취소되었습니다.",
|
||||
"deleted": "작업 실패: 사용자에 의해 중지 및 삭제되었습니다.",
|
||||
|
|
|
|||
|
|
@ -19,8 +19,11 @@
|
|||
"delete_config_profile": "Weet je zeker dat je dit configuratieprofiel wilt verwijderen?",
|
||||
"delete_custom_mode_with_rules": "Weet je zeker dat je deze {scope}-modus wilt verwijderen?\n\nDit verwijdert ook de bijbehorende regelsmap op:\n{rulesFolderPath}",
|
||||
"delete_message": "Wat wil je verwijderen?",
|
||||
"just_this_message": "Alleen dit bericht",
|
||||
"this_and_subsequent": "Dit en alle volgende berichten"
|
||||
"delete_just_this_message": "Alleen dit bericht",
|
||||
"delete_this_and_subsequent": "Dit en alle volgende berichten",
|
||||
"edit_message": "Alle berichten na dit bericht verwijderen?",
|
||||
"edit_just_this_message": "Nee, alleen dit bericht bewerken",
|
||||
"edit_this_and_delete_subsequent": "Ja"
|
||||
},
|
||||
"errors": {
|
||||
"invalid_data_uri": "Ongeldig data-URI-formaat",
|
||||
|
|
@ -108,6 +111,11 @@
|
|||
"remove": "Verwijderen",
|
||||
"keep": "Behouden"
|
||||
},
|
||||
"buttons": {
|
||||
"save": "Opslaan",
|
||||
"cancel": "Annuleren",
|
||||
"edit": "Bewerken"
|
||||
},
|
||||
"tasks": {
|
||||
"canceled": "Taakfout: gestopt en geannuleerd door gebruiker.",
|
||||
"deleted": "Taakfout: gestopt en verwijderd door gebruiker.",
|
||||
|
|
|
|||
|
|
@ -19,8 +19,11 @@
|
|||
"delete_config_profile": "Czy na pewno chcesz usunąć ten profil konfiguracyjny?",
|
||||
"delete_custom_mode_with_rules": "Czy na pewno chcesz usunąć ten tryb {scope}?\n\nSpowoduje to również usunięcie powiązanego folderu reguł pod adresem:\n{rulesFolderPath}",
|
||||
"delete_message": "Co chcesz usunąć?",
|
||||
"just_this_message": "Tylko tę wiadomość",
|
||||
"this_and_subsequent": "Tę i wszystkie kolejne wiadomości"
|
||||
"delete_just_this_message": "Tylko tę wiadomość",
|
||||
"delete_this_and_subsequent": "Tę i wszystkie kolejne wiadomości",
|
||||
"edit_message": "Usunąć wszystkie wiadomości po tej?",
|
||||
"edit_just_this_message": "Nie, tylko edytuj tę wiadomość",
|
||||
"edit_this_and_delete_subsequent": "Tak"
|
||||
},
|
||||
"errors": {
|
||||
"invalid_data_uri": "Nieprawidłowy format URI danych",
|
||||
|
|
@ -108,6 +111,11 @@
|
|||
"remove": "Usuń",
|
||||
"keep": "Zachowaj"
|
||||
},
|
||||
"buttons": {
|
||||
"save": "Zapisz",
|
||||
"cancel": "Anuluj",
|
||||
"edit": "Edytuj"
|
||||
},
|
||||
"tasks": {
|
||||
"canceled": "Błąd zadania: Zostało zatrzymane i anulowane przez użytkownika.",
|
||||
"deleted": "Niepowodzenie zadania: Zostało zatrzymane i usunięte przez użytkownika.",
|
||||
|
|
|
|||
|
|
@ -23,8 +23,11 @@
|
|||
"delete_config_profile": "Tem certeza de que deseja excluir este perfil de configuração?",
|
||||
"delete_custom_mode_with_rules": "Tem certeza de que deseja excluir este modo {scope}?\n\nIsso também excluirá a pasta de regras associada em:\n{rulesFolderPath}",
|
||||
"delete_message": "O que você gostaria de excluir?",
|
||||
"just_this_message": "Apenas esta mensagem",
|
||||
"this_and_subsequent": "Esta e todas as mensagens subsequentes"
|
||||
"delete_just_this_message": "Apenas esta mensagem",
|
||||
"delete_this_and_subsequent": "Esta e todas as mensagens subsequentes",
|
||||
"edit_message": "Excluir todas as mensagens após esta?",
|
||||
"edit_just_this_message": "Não, apenas editar esta",
|
||||
"edit_this_and_delete_subsequent": "Sim"
|
||||
},
|
||||
"errors": {
|
||||
"invalid_data_uri": "Formato de URI de dados inválido",
|
||||
|
|
@ -112,6 +115,11 @@
|
|||
"remove": "Remover",
|
||||
"keep": "Manter"
|
||||
},
|
||||
"buttons": {
|
||||
"save": "Salvar",
|
||||
"cancel": "Cancelar",
|
||||
"edit": "Editar"
|
||||
},
|
||||
"tasks": {
|
||||
"canceled": "Erro na tarefa: Foi interrompida e cancelada pelo usuário.",
|
||||
"deleted": "Falha na tarefa: Foi interrompida e excluída pelo usuário.",
|
||||
|
|
|
|||
|
|
@ -19,8 +19,11 @@
|
|||
"delete_config_profile": "Вы уверены, что хотите удалить этот профиль конфигурации?",
|
||||
"delete_custom_mode_with_rules": "Вы уверены, что хотите удалить этот режим {scope}?\n\nЭто также приведет к удалению соответствующей папки правил по адресу:\n{rulesFolderPath}",
|
||||
"delete_message": "Что вы хотите удалить?",
|
||||
"just_this_message": "Только это сообщение",
|
||||
"this_and_subsequent": "Это и все последующие сообщения"
|
||||
"delete_just_this_message": "Только это сообщение",
|
||||
"delete_this_and_subsequent": "Это и все последующие сообщения",
|
||||
"edit_message": "Удалить все сообщения после этого?",
|
||||
"edit_just_this_message": "Нет, только редактировать это",
|
||||
"edit_this_and_delete_subsequent": "Да"
|
||||
},
|
||||
"errors": {
|
||||
"invalid_data_uri": "Неверный формат URI данных",
|
||||
|
|
@ -108,6 +111,11 @@
|
|||
"remove": "Удалить",
|
||||
"keep": "Оставить"
|
||||
},
|
||||
"buttons": {
|
||||
"save": "Сохранить",
|
||||
"cancel": "Отмена",
|
||||
"edit": "Редактировать"
|
||||
},
|
||||
"tasks": {
|
||||
"canceled": "Ошибка задачи: Она была остановлена и отменена пользователем.",
|
||||
"deleted": "Сбой задачи: Она была остановлена и удалена пользователем.",
|
||||
|
|
|
|||
|
|
@ -19,8 +19,11 @@
|
|||
"delete_config_profile": "Bu yapılandırma profilini silmek istediğinizden emin misiniz?",
|
||||
"delete_custom_mode_with_rules": "Bu {scope} modunu silmek istediğinizden emin misiniz?\n\nBu işlem, ilişkili kurallar klasörünü de şu konumdan silecektir:\n{rulesFolderPath}",
|
||||
"delete_message": "Neyi silmek istersiniz?",
|
||||
"just_this_message": "Sadece bu mesajı",
|
||||
"this_and_subsequent": "Bu ve sonraki tüm mesajları"
|
||||
"delete_just_this_message": "Sadece bu mesajı",
|
||||
"delete_this_and_subsequent": "Bu ve sonraki tüm mesajları",
|
||||
"edit_message": "Bu mesajdan sonraki tüm mesajlar silinsin mi?",
|
||||
"edit_just_this_message": "Hayır, sadece bunu düzenle",
|
||||
"edit_this_and_delete_subsequent": "Evet"
|
||||
},
|
||||
"errors": {
|
||||
"invalid_data_uri": "Geçersiz veri URI formatı",
|
||||
|
|
@ -108,6 +111,11 @@
|
|||
"remove": "Kaldır",
|
||||
"keep": "Koru"
|
||||
},
|
||||
"buttons": {
|
||||
"save": "Kaydet",
|
||||
"cancel": "İptal",
|
||||
"edit": "Düzenle"
|
||||
},
|
||||
"tasks": {
|
||||
"canceled": "Görev hatası: Kullanıcı tarafından durduruldu ve iptal edildi.",
|
||||
"deleted": "Görev başarısız: Kullanıcı tarafından durduruldu ve silindi.",
|
||||
|
|
|
|||
|
|
@ -19,8 +19,11 @@
|
|||
"delete_config_profile": "Bạn có chắc chắn muốn xóa hồ sơ cấu hình này không?",
|
||||
"delete_custom_mode_with_rules": "Bạn có chắc chắn muốn xóa chế độ {scope} này không?\n\nThao tác này cũng sẽ xóa thư mục quy tắc liên quan tại:\n{rulesFolderPath}",
|
||||
"delete_message": "Bạn muốn xóa gì?",
|
||||
"just_this_message": "Chỉ tin nhắn này",
|
||||
"this_and_subsequent": "Tin nhắn này và tất cả tin nhắn tiếp theo"
|
||||
"delete_just_this_message": "Chỉ tin nhắn này",
|
||||
"delete_this_and_subsequent": "Tin nhắn này và tất cả tin nhắn tiếp theo",
|
||||
"edit_message": "Xóa tất cả tin nhắn sau tin nhắn này?",
|
||||
"edit_just_this_message": "Không, chỉ chỉnh sửa tin nhắn này",
|
||||
"edit_this_and_delete_subsequent": "Có"
|
||||
},
|
||||
"errors": {
|
||||
"invalid_data_uri": "Định dạng URI dữ liệu không hợp lệ",
|
||||
|
|
@ -108,6 +111,11 @@
|
|||
"remove": "Xóa",
|
||||
"keep": "Giữ"
|
||||
},
|
||||
"buttons": {
|
||||
"save": "Lưu",
|
||||
"cancel": "Hủy",
|
||||
"edit": "Chỉnh sửa"
|
||||
},
|
||||
"tasks": {
|
||||
"canceled": "Lỗi nhiệm vụ: Nó đã bị dừng và hủy bởi người dùng.",
|
||||
"deleted": "Nhiệm vụ thất bại: Nó đã bị dừng và xóa bởi người dùng.",
|
||||
|
|
|
|||
|
|
@ -19,8 +19,11 @@
|
|||
"delete_config_profile": "您确定要删除此配置文件吗?",
|
||||
"delete_custom_mode_with_rules": "您确定要删除此 {scope} 模式吗?\n\n这也将删除位于以下位置的关联规则文件夹:\n{rulesFolderPath}",
|
||||
"delete_message": "您想删除什么?",
|
||||
"just_this_message": "仅此消息",
|
||||
"this_and_subsequent": "此消息及所有后续消息"
|
||||
"edit_message": "删除此消息后的所有消息?",
|
||||
"delete_just_this_message": "仅此消息",
|
||||
"edit_just_this_message": "不,仅编辑此消息",
|
||||
"delete_this_and_subsequent": "此消息及所有后续消息",
|
||||
"edit_this_and_delete_subsequent": "是"
|
||||
},
|
||||
"errors": {
|
||||
"invalid_mcp_config": "项目MCP配置格式无效",
|
||||
|
|
@ -113,6 +116,11 @@
|
|||
"remove": "删除",
|
||||
"keep": "保留"
|
||||
},
|
||||
"buttons": {
|
||||
"save": "保存",
|
||||
"cancel": "取消",
|
||||
"edit": "编辑"
|
||||
},
|
||||
"tasks": {
|
||||
"canceled": "任务错误:它已被用户停止并取消。",
|
||||
"deleted": "任务失败:它已被用户停止并删除。",
|
||||
|
|
|
|||
|
|
@ -19,8 +19,11 @@
|
|||
"delete_config_profile": "您確定要刪除此設定檔案嗎?",
|
||||
"delete_custom_mode_with_rules": "您確定要刪除此 {scope} 模式嗎?\n\n這也將刪除位於以下位置的關聯規則資料夾:\n{rulesFolderPath}",
|
||||
"delete_message": "您想刪除哪些內容?",
|
||||
"just_this_message": "僅這則訊息",
|
||||
"this_and_subsequent": "這則訊息及所有後續訊息"
|
||||
"edit_message": "刪除此訊息後的所有訊息?",
|
||||
"delete_just_this_message": "僅這則訊息",
|
||||
"edit_just_this_message": "否,僅編輯此訊息",
|
||||
"delete_this_and_subsequent": "這則訊息及所有後續訊息",
|
||||
"edit_this_and_delete_subsequent": "是"
|
||||
},
|
||||
"errors": {
|
||||
"invalid_data_uri": "資料 URI 格式無效",
|
||||
|
|
@ -108,6 +111,11 @@
|
|||
"remove": "刪除",
|
||||
"keep": "保留"
|
||||
},
|
||||
"buttons": {
|
||||
"save": "儲存",
|
||||
"cancel": "取消",
|
||||
"edit": "編輯"
|
||||
},
|
||||
"tasks": {
|
||||
"canceled": "工作錯誤:它已被使用者停止並取消。",
|
||||
"deleted": "工作失敗:它已被使用者停止並刪除。",
|
||||
|
|
|
|||
|
|
@ -110,6 +110,7 @@ export interface WebviewMessage {
|
|||
| "enhancedPrompt"
|
||||
| "draggedImages"
|
||||
| "deleteMessage"
|
||||
| "submitEditedMessage"
|
||||
| "terminalOutputLineLimit"
|
||||
| "terminalShellIntegrationTimeout"
|
||||
| "terminalShellIntegrationDisabled"
|
||||
|
|
@ -193,6 +194,7 @@ export interface WebviewMessage {
|
|||
| "saveCodeIndexSettingsAtomic"
|
||||
| "requestCodeIndexSecretStatus"
|
||||
text?: string
|
||||
editedMessageContent?: string
|
||||
tab?: "settings" | "history" | "mcp" | "modes" | "chat" | "marketplace" | "account"
|
||||
disabled?: boolean
|
||||
dataUri?: string
|
||||
|
|
|
|||
|
|
@ -111,6 +111,8 @@ export const ChatRowContent = ({
|
|||
const [reasoningCollapsed, setReasoningCollapsed] = useState(true)
|
||||
const [isDiffErrorExpanded, setIsDiffErrorExpanded] = useState(false)
|
||||
const [showCopySuccess, setShowCopySuccess] = useState(false)
|
||||
const [isEditing, setIsEditing] = useState(false)
|
||||
const [editedContent, setEditedContent] = useState("")
|
||||
const { copyWithFeedback } = useCopyToClipboard()
|
||||
|
||||
// Memoized callback to prevent re-renders caused by inline arrow functions
|
||||
|
|
@ -118,6 +120,31 @@ export const ChatRowContent = ({
|
|||
onToggleExpand(message.ts)
|
||||
}, [onToggleExpand, message.ts])
|
||||
|
||||
// Handle edit button click
|
||||
const handleEditClick = useCallback(() => {
|
||||
setIsEditing(true)
|
||||
setEditedContent(message.text || "")
|
||||
// Edit mode is now handled entirely in the frontend
|
||||
// No need to notify the backend
|
||||
}, [message.text])
|
||||
|
||||
// Handle cancel edit
|
||||
const handleCancelEdit = useCallback(() => {
|
||||
setIsEditing(false)
|
||||
setEditedContent(message.text || "")
|
||||
}, [message.text])
|
||||
|
||||
// Handle save edit
|
||||
const handleSaveEdit = useCallback(() => {
|
||||
setIsEditing(false)
|
||||
// Send edited message to backend
|
||||
vscode.postMessage({
|
||||
type: "submitEditedMessage",
|
||||
value: message.ts,
|
||||
editedMessageContent: editedContent,
|
||||
})
|
||||
}, [message.ts, editedContent])
|
||||
|
||||
const [cost, apiReqCancelReason, apiReqStreamingFailedMessage] = useMemo(() => {
|
||||
if (message.text !== null && message.text !== undefined && message.say === "api_req_started") {
|
||||
const info = safeJsonParse<ClineApiReqInfo>(message.text)
|
||||
|
|
@ -1001,23 +1028,56 @@ export const ChatRowContent = ({
|
|||
case "user_feedback":
|
||||
return (
|
||||
<div className="bg-vscode-editor-background border rounded-xs p-1 overflow-hidden whitespace-pre-wrap">
|
||||
<div className="flex justify-between">
|
||||
<div className="flex-grow px-2 py-1 wrap-anywhere">
|
||||
<Mention text={message.text} withShadow />
|
||||
{isEditing ? (
|
||||
<div className="flex flex-col gap-2 p-2">
|
||||
<textarea
|
||||
className="w-full p-2 bg-vscode-input-background text-vscode-input-foreground border border-vscode-input-border rounded-xs"
|
||||
value={editedContent}
|
||||
onChange={(e) => setEditedContent(e.target.value)}
|
||||
rows={5}
|
||||
autoFocus
|
||||
/>
|
||||
<div className="flex justify-end gap-2">
|
||||
<Button variant="secondary" size="sm" onClick={handleCancelEdit}>
|
||||
{t("chat:cancel.title")}
|
||||
</Button>
|
||||
<Button variant="default" size="sm" onClick={handleSaveEdit}>
|
||||
{t("chat:save.title")}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="shrink-0"
|
||||
disabled={isStreaming}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
vscode.postMessage({ type: "deleteMessage", value: message.ts })
|
||||
}}>
|
||||
<span className="codicon codicon-trash" />
|
||||
</Button>
|
||||
</div>
|
||||
{message.images && message.images.length > 0 && (
|
||||
) : (
|
||||
<div className="flex justify-between">
|
||||
<div className="flex-grow px-2 py-1 wrap-anywhere">
|
||||
<Mention text={message.text} withShadow />
|
||||
</div>
|
||||
<div className="flex">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="shrink-0"
|
||||
disabled={isStreaming}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
handleEditClick()
|
||||
}}>
|
||||
<span className="codicon codicon-edit" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="shrink-0"
|
||||
disabled={isStreaming}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
vscode.postMessage({ type: "deleteMessage", value: message.ts })
|
||||
}}>
|
||||
<span className="codicon codicon-trash" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{!isEditing && message.images && message.images.length > 0 && (
|
||||
<Thumbnails images={message.images} style={{ marginTop: "8px" }} />
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue