继续多语言处理
This commit is contained in:
@@ -15,8 +15,16 @@ export const LiveMessageBlock = memo(function LiveMessageBlock({
|
||||
message,
|
||||
}: LiveMessageBlockProps) {
|
||||
const t = useTranslations("Folder.chat.liveMessageBlock")
|
||||
const sharedT = useTranslations("Folder.chat.shared")
|
||||
const hasContent = message.content.length > 0
|
||||
const adapted = useMemo(() => adaptLiveMessageFromAcp(message), [message])
|
||||
const adapted = useMemo(
|
||||
() =>
|
||||
adaptLiveMessageFromAcp(message, {
|
||||
toolCallFailedText: sharedT("toolCallFailed"),
|
||||
planUpdatedText: sharedT("planUpdated"),
|
||||
}),
|
||||
[message, sharedT]
|
||||
)
|
||||
|
||||
return (
|
||||
<Message from="assistant">
|
||||
|
||||
@@ -87,6 +87,8 @@ export function WelcomeInputPanel({
|
||||
isActive = true,
|
||||
}: WelcomeInputPanelProps) {
|
||||
const t = useTranslations("Folder.chat.welcomeInputPanel")
|
||||
const tabT = useTranslations("Folder.tabContext")
|
||||
const sharedT = useTranslations("Folder.chat.shared")
|
||||
const fallbackContextId = useMemo(() => crypto.randomUUID(), [])
|
||||
const contextKey = tabId ?? `new-${fallbackContextId}`
|
||||
|
||||
@@ -159,7 +161,10 @@ export function WelcomeInputPanel({
|
||||
const detail = await getFolderConversation(conversationId)
|
||||
if (refreshSeq !== statsRefreshSeqRef.current) return
|
||||
|
||||
const messages = adaptMessageTurns(detail.turns)
|
||||
const messages = adaptMessageTurns(detail.turns, {
|
||||
attachedResources: sharedT("attachedResources"),
|
||||
toolCallFailed: sharedT("toolCallFailed"),
|
||||
})
|
||||
const stats = detail.session_stats ?? null
|
||||
latestMessages = messages
|
||||
latestStats = stats
|
||||
@@ -196,7 +201,7 @@ export function WelcomeInputPanel({
|
||||
applySessionStats(latestStats)
|
||||
}
|
||||
},
|
||||
[applySessionStats, hasAssistantUsage, hasTokenStats]
|
||||
[applySessionStats, hasAssistantUsage, hasTokenStats, sharedT]
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
@@ -354,6 +359,8 @@ export function WelcomeInputPanel({
|
||||
if (conn.liveMessage && conn.liveMessage.content.length > 0) {
|
||||
const adapted = adaptLiveMessageFromAcp(conn.liveMessage, {
|
||||
isLiveStreaming: false,
|
||||
toolCallFailedText: sharedT("toolCallFailed"),
|
||||
planUpdatedText: sharedT("planUpdated"),
|
||||
})
|
||||
|
||||
setHistory((h) => [...h, adapted])
|
||||
@@ -376,7 +383,7 @@ export function WelcomeInputPanel({
|
||||
)
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps -- conn.liveMessage, lifecycleSend intentionally omitted: effect only fires on status transitions
|
||||
}, [connStatus, refreshConversations, refreshConversationFromDb])
|
||||
}, [connStatus, refreshConversations, refreshConversationFromDb, sharedT])
|
||||
|
||||
// When connection becomes "connected" and we have a pending prompt, send it
|
||||
useEffect(() => {
|
||||
@@ -394,7 +401,7 @@ export function WelcomeInputPanel({
|
||||
const tid = tabIdRef.current
|
||||
const convId = dbConvIdRef.current
|
||||
const agent = selectedAgentRef.current
|
||||
const title = convTitleRef.current || "Untitled"
|
||||
const title = convTitleRef.current || tabT("untitledConversation")
|
||||
const canonicalContextKey = `conv-${agent}-${convId}`
|
||||
|
||||
// Keep in-flight stream/state attached when this new-conversation view
|
||||
@@ -410,6 +417,7 @@ export function WelcomeInputPanel({
|
||||
refreshConversations,
|
||||
migrateContextKey,
|
||||
contextKey,
|
||||
tabT,
|
||||
])
|
||||
|
||||
// Update conversation status on disconnect/error + promote tab
|
||||
@@ -466,11 +474,17 @@ export function WelcomeInputPanel({
|
||||
// Welcome phase: submit first message.
|
||||
const handleWelcomeSend = useCallback(
|
||||
(draft: PromptDraft, selectedModeId?: string | null) => {
|
||||
const displayText = getPromptDraftDisplayText(draft)
|
||||
const displayText = getPromptDraftDisplayText(
|
||||
draft,
|
||||
sharedT("attachedResources")
|
||||
)
|
||||
const userMsg: AdaptedMessage = {
|
||||
id: crypto.randomUUID(),
|
||||
role: "user",
|
||||
content: buildUserMessageTextPartsFromDraft(draft),
|
||||
content: buildUserMessageTextPartsFromDraft(
|
||||
draft,
|
||||
sharedT("attachedResources")
|
||||
),
|
||||
userResources: extractUserResourcesFromDraft(draft),
|
||||
timestamp: new Date().toISOString(),
|
||||
}
|
||||
@@ -536,6 +550,7 @@ export function WelcomeInputPanel({
|
||||
trySaveExternalId,
|
||||
applySessionStats,
|
||||
newConversationDraftStorageKey,
|
||||
sharedT,
|
||||
]
|
||||
)
|
||||
|
||||
@@ -545,7 +560,10 @@ export function WelcomeInputPanel({
|
||||
const userMsg: AdaptedMessage = {
|
||||
id: crypto.randomUUID(),
|
||||
role: "user",
|
||||
content: buildUserMessageTextPartsFromDraft(draft),
|
||||
content: buildUserMessageTextPartsFromDraft(
|
||||
draft,
|
||||
sharedT("attachedResources")
|
||||
),
|
||||
userResources: extractUserResourcesFromDraft(draft),
|
||||
timestamp: new Date().toISOString(),
|
||||
}
|
||||
@@ -562,7 +580,7 @@ export function WelcomeInputPanel({
|
||||
statusUpdatedRef.current = false
|
||||
}
|
||||
},
|
||||
[lifecycleSend, refreshConversations]
|
||||
[lifecycleSend, refreshConversations, sharedT]
|
||||
)
|
||||
|
||||
const handleOpenAgentsSettings = useCallback(() => {
|
||||
|
||||
@@ -43,6 +43,7 @@ const ExistingConversationView = memo(function ExistingConversationView({
|
||||
reloadSignal,
|
||||
}: ExistingConversationViewProps) {
|
||||
const t = useTranslations("Folder.conversation")
|
||||
const sharedT = useTranslations("Folder.chat.shared")
|
||||
const { refreshConversations, folder } = useFolderContext()
|
||||
const contextKey = `conv-${agentType}-${conversationId}`
|
||||
|
||||
@@ -114,7 +115,10 @@ const ExistingConversationView = memo(function ExistingConversationView({
|
||||
{
|
||||
id: `pending-${Date.now()}`,
|
||||
role: "user",
|
||||
content: buildUserMessageTextPartsFromDraft(draft),
|
||||
content: buildUserMessageTextPartsFromDraft(
|
||||
draft,
|
||||
sharedT("attachedResources")
|
||||
),
|
||||
userResources: extractUserResourcesFromDraft(draft),
|
||||
timestamp: new Date().toISOString(),
|
||||
},
|
||||
@@ -125,7 +129,7 @@ const ExistingConversationView = memo(function ExistingConversationView({
|
||||
statusUpdatedRef.current = false
|
||||
handleSend(draft, selectedModeId)
|
||||
},
|
||||
[conversationId, handleSend, refreshConversations]
|
||||
[conversationId, handleSend, refreshConversations, sharedT]
|
||||
)
|
||||
|
||||
// Update status on turn complete
|
||||
|
||||
@@ -1660,6 +1660,7 @@ const CODE_FIELDS = new Set([
|
||||
const HIDDEN_FIELDS = new Set(["dangerouslyDisableSandbox"])
|
||||
|
||||
function GenericToolInput({ input }: { input: string }) {
|
||||
const t = useTranslations("Folder.chat.contentParts")
|
||||
const parsed = tryParseJson(input)
|
||||
|
||||
if (!parsed) {
|
||||
@@ -1677,6 +1678,9 @@ function GenericToolInput({ input }: { input: string }) {
|
||||
return (
|
||||
<div className="space-y-3">
|
||||
{entries.map(([key, value]) => {
|
||||
const labelKey = fieldLabelKey(key)
|
||||
const label = labelKey ? t(labelKey) : key
|
||||
|
||||
if (CODE_FIELDS.has(key) && typeof value === "string") {
|
||||
const lang =
|
||||
key === "command"
|
||||
@@ -1685,7 +1689,7 @@ function GenericToolInput({ input }: { input: string }) {
|
||||
? ("log" as const)
|
||||
: ("log" as const)
|
||||
return (
|
||||
<FieldBlock key={key} label={fieldLabel(key)}>
|
||||
<FieldBlock key={key} label={label}>
|
||||
<CodeBlock code={value} language={lang} />
|
||||
</FieldBlock>
|
||||
)
|
||||
@@ -1694,29 +1698,23 @@ function GenericToolInput({ input }: { input: string }) {
|
||||
if (typeof value === "string") {
|
||||
if (value.length > 200) {
|
||||
return (
|
||||
<FieldBlock key={key} label={fieldLabel(key)}>
|
||||
<FieldBlock key={key} label={label}>
|
||||
<pre className="whitespace-pre-wrap break-all rounded-md bg-muted/50 p-3 text-xs">
|
||||
{value}
|
||||
</pre>
|
||||
</FieldBlock>
|
||||
)
|
||||
}
|
||||
return <FieldInline key={key} label={fieldLabel(key)} value={value} />
|
||||
return <FieldInline key={key} label={label} value={value} />
|
||||
}
|
||||
|
||||
if (typeof value === "number" || typeof value === "boolean") {
|
||||
return (
|
||||
<FieldInline
|
||||
key={key}
|
||||
label={fieldLabel(key)}
|
||||
value={String(value)}
|
||||
/>
|
||||
)
|
||||
return <FieldInline key={key} label={label} value={String(value)} />
|
||||
}
|
||||
|
||||
if (value !== null && value !== undefined) {
|
||||
return (
|
||||
<FieldBlock key={key} label={fieldLabel(key)}>
|
||||
<FieldBlock key={key} label={label}>
|
||||
<CodeBlock
|
||||
code={JSON.stringify(value, null, 2)}
|
||||
language="json"
|
||||
@@ -1836,41 +1834,45 @@ function FieldBlock({
|
||||
)
|
||||
}
|
||||
|
||||
function fieldLabel(key: string): string {
|
||||
const map: Record<string, string> = {
|
||||
file_path: "File",
|
||||
notebook_path: "Notebook",
|
||||
command: "Command",
|
||||
cmd: "Command",
|
||||
old_string: "Old",
|
||||
new_string: "New",
|
||||
pattern: "Pattern",
|
||||
path: "Path",
|
||||
query: "Query",
|
||||
url: "URL",
|
||||
description: "Description",
|
||||
content: "Content",
|
||||
new_source: "Source",
|
||||
prompt: "Prompt",
|
||||
subject: "Subject",
|
||||
taskId: "Task ID",
|
||||
status: "Status",
|
||||
skill: "Skill",
|
||||
args: "Args",
|
||||
offset: "Offset",
|
||||
limit: "Limit",
|
||||
glob: "Glob",
|
||||
type: "Type",
|
||||
output_mode: "Output",
|
||||
replace_all: "Replace All",
|
||||
language: "Language",
|
||||
timeout: "Timeout",
|
||||
run_in_background: "Background",
|
||||
subagent_type: "Agent Type",
|
||||
libraryName: "Library",
|
||||
libraryId: "Library ID",
|
||||
}
|
||||
return map[key] ?? key
|
||||
const FIELD_LABEL_KEYS = {
|
||||
file_path: "field.file",
|
||||
notebook_path: "field.notebook",
|
||||
command: "field.command",
|
||||
cmd: "field.command",
|
||||
old_string: "field.old",
|
||||
new_string: "field.new",
|
||||
pattern: "field.pattern",
|
||||
path: "field.path",
|
||||
query: "field.query",
|
||||
url: "field.url",
|
||||
description: "field.description",
|
||||
content: "field.content",
|
||||
new_source: "field.source",
|
||||
prompt: "field.prompt",
|
||||
subject: "field.subject",
|
||||
taskId: "field.taskId",
|
||||
status: "field.status",
|
||||
skill: "field.skill",
|
||||
args: "field.args",
|
||||
offset: "field.offset",
|
||||
limit: "field.limit",
|
||||
glob: "field.glob",
|
||||
type: "field.type",
|
||||
output_mode: "field.output",
|
||||
replace_all: "field.replaceAll",
|
||||
language: "field.language",
|
||||
timeout: "field.timeout",
|
||||
run_in_background: "field.background",
|
||||
subagent_type: "field.agentType",
|
||||
libraryName: "field.library",
|
||||
libraryId: "field.libraryId",
|
||||
} as const
|
||||
|
||||
function fieldLabelKey(
|
||||
key: string
|
||||
): (typeof FIELD_LABEL_KEYS)[keyof typeof FIELD_LABEL_KEYS] | null {
|
||||
const translationKey = FIELD_LABEL_KEYS[key as keyof typeof FIELD_LABEL_KEYS]
|
||||
return translationKey ?? null
|
||||
}
|
||||
|
||||
function commandOutputFromJsonString(output: string): string | null {
|
||||
|
||||
@@ -169,6 +169,7 @@ export function MessageListView({
|
||||
isActive = true,
|
||||
}: MessageListViewProps) {
|
||||
const t = useTranslations("Folder.chat.messageList")
|
||||
const sharedT = useTranslations("Folder.chat.shared")
|
||||
const { detail, loading, error, refetch } = useDbMessageDetail(conversationId)
|
||||
const turnCount = detail?.turns.length ?? 0
|
||||
|
||||
@@ -213,8 +214,14 @@ export function MessageListView({
|
||||
const shouldUseSmoothResize = !(isActive && !loading && detail)
|
||||
|
||||
const messages = useMemo(
|
||||
() => (detail ? adaptMessageTurns(detail.turns) : []),
|
||||
[detail]
|
||||
() =>
|
||||
detail
|
||||
? adaptMessageTurns(detail.turns, {
|
||||
attachedResources: sharedT("attachedResources"),
|
||||
toolCallFailed: sharedT("toolCallFailed"),
|
||||
})
|
||||
: [],
|
||||
[detail, sharedT]
|
||||
)
|
||||
|
||||
const groups = useMemo(() => groupAdaptedMessages(messages), [messages])
|
||||
@@ -234,15 +241,17 @@ export function MessageListView({
|
||||
)
|
||||
const resolvedGroups = useMemo(
|
||||
() =>
|
||||
groups.map((group) => resolveMessageGroup(group, t("attachedResources"))),
|
||||
[groups, t]
|
||||
groups.map((group) =>
|
||||
resolveMessageGroup(group, sharedT("attachedResources"))
|
||||
),
|
||||
[groups, sharedT]
|
||||
)
|
||||
const resolvedPendingGroups = useMemo(
|
||||
() =>
|
||||
pendingGroups.map((group) =>
|
||||
resolveMessageGroup(group, t("attachedResources"))
|
||||
resolveMessageGroup(group, sharedT("attachedResources"))
|
||||
),
|
||||
[pendingGroups, t]
|
||||
[pendingGroups, sharedT]
|
||||
)
|
||||
|
||||
const showLiveMessage = Boolean(
|
||||
|
||||
@@ -33,6 +33,8 @@ function stripClonePrefix(message: string): string {
|
||||
function mapCommonCodeToKey(code: string): WelcomeErrorKey {
|
||||
switch (code) {
|
||||
case "invalid_input":
|
||||
case "configuration_missing":
|
||||
case "configuration_invalid":
|
||||
return "errors.invalidInput"
|
||||
case "not_found":
|
||||
return "errors.notFound"
|
||||
|
||||
Reference in New Issue
Block a user