mirror of
https://github.com/DayuanJiang/next-ai-draw-io.git
synced 2026-01-02 14:22:28 +08:00
i18n: add missing translations for chat UI components (#457)
* i18n: add missing translations for chat UI components * i18n: add missing translations for chat components and toast messages
This commit is contained in:
@@ -27,6 +27,7 @@ import {
|
|||||||
ReasoningTrigger,
|
ReasoningTrigger,
|
||||||
} from "@/components/ai-elements/reasoning"
|
} from "@/components/ai-elements/reasoning"
|
||||||
import { ScrollArea } from "@/components/ui/scroll-area"
|
import { ScrollArea } from "@/components/ui/scroll-area"
|
||||||
|
import { useDictionary } from "@/hooks/use-dictionary"
|
||||||
import { getApiEndpoint } from "@/lib/base-path"
|
import { getApiEndpoint } from "@/lib/base-path"
|
||||||
import {
|
import {
|
||||||
applyDiagramOperations,
|
applyDiagramOperations,
|
||||||
@@ -205,6 +206,7 @@ export function ChatMessageDisplay({
|
|||||||
onEditMessage,
|
onEditMessage,
|
||||||
status = "idle",
|
status = "idle",
|
||||||
}: ChatMessageDisplayProps) {
|
}: ChatMessageDisplayProps) {
|
||||||
|
const dict = useDictionary()
|
||||||
const { chartXML, loadDiagram: onDisplayChart } = useDiagram()
|
const { chartXML, loadDiagram: onDisplayChart } = useDiagram()
|
||||||
const messagesEndRef = useRef<HTMLDivElement>(null)
|
const messagesEndRef = useRef<HTMLDivElement>(null)
|
||||||
const previousXML = useRef<string>("")
|
const previousXML = useRef<string>("")
|
||||||
@@ -268,9 +270,7 @@ export function ChatMessageDisplay({
|
|||||||
setTimeout(() => setCopiedMessageId(null), 2000)
|
setTimeout(() => setCopiedMessageId(null), 2000)
|
||||||
} catch (fallbackErr) {
|
} catch (fallbackErr) {
|
||||||
console.error("Failed to copy message:", fallbackErr)
|
console.error("Failed to copy message:", fallbackErr)
|
||||||
toast.error(
|
toast.error(dict.chat.failedToCopyDetail)
|
||||||
"Failed to copy message. Please copy manually or check clipboard permissions.",
|
|
||||||
)
|
|
||||||
setCopyFailedMessageId(messageId)
|
setCopyFailedMessageId(messageId)
|
||||||
setTimeout(() => setCopyFailedMessageId(null), 2000)
|
setTimeout(() => setCopyFailedMessageId(null), 2000)
|
||||||
} finally {
|
} finally {
|
||||||
@@ -304,7 +304,7 @@ export function ChatMessageDisplay({
|
|||||||
})
|
})
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to log feedback:", error)
|
console.error("Failed to log feedback:", error)
|
||||||
toast.error("Failed to record your feedback. Please try again.")
|
toast.error(dict.errors.failedToRecordFeedback)
|
||||||
// Revert optimistic UI update
|
// Revert optimistic UI update
|
||||||
setFeedback((prev) => {
|
setFeedback((prev) => {
|
||||||
const next = { ...prev }
|
const next = { ...prev }
|
||||||
@@ -349,9 +349,7 @@ export function ChatMessageDisplay({
|
|||||||
console.error(
|
console.error(
|
||||||
"[ChatMessageDisplay] Malformed XML detected in final output",
|
"[ChatMessageDisplay] Malformed XML detected in final output",
|
||||||
)
|
)
|
||||||
toast.error(
|
toast.error(dict.errors.malformedXml)
|
||||||
"AI generated invalid diagram XML. Please try regenerating.",
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
return // Skip this update
|
return // Skip this update
|
||||||
}
|
}
|
||||||
@@ -402,9 +400,7 @@ export function ChatMessageDisplay({
|
|||||||
"[ChatMessageDisplay] XML validation failed:",
|
"[ChatMessageDisplay] XML validation failed:",
|
||||||
validation.error,
|
validation.error,
|
||||||
)
|
)
|
||||||
toast.error(
|
toast.error(dict.errors.validationFailed)
|
||||||
"Diagram validation failed. Please try regenerating.",
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(
|
console.error(
|
||||||
@@ -413,9 +409,7 @@ export function ChatMessageDisplay({
|
|||||||
)
|
)
|
||||||
// Only show toast if this is the final XML (not during streaming)
|
// Only show toast if this is the final XML (not during streaming)
|
||||||
if (showToast) {
|
if (showToast) {
|
||||||
toast.error(
|
toast.error(dict.errors.failedToProcess)
|
||||||
"Failed to process diagram. Please try regenerating.",
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -832,7 +826,10 @@ export function ChatMessageDisplay({
|
|||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
className="p-1.5 rounded-lg text-muted-foreground/60 hover:text-muted-foreground hover:bg-muted transition-colors"
|
className="p-1.5 rounded-lg text-muted-foreground/60 hover:text-muted-foreground hover:bg-muted transition-colors"
|
||||||
title="Edit message"
|
title={
|
||||||
|
dict.chat
|
||||||
|
.editMessage
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<Pencil className="h-3.5 w-3.5" />
|
<Pencil className="h-3.5 w-3.5" />
|
||||||
</button>
|
</button>
|
||||||
@@ -849,11 +846,13 @@ export function ChatMessageDisplay({
|
|||||||
title={
|
title={
|
||||||
copiedMessageId ===
|
copiedMessageId ===
|
||||||
message.id
|
message.id
|
||||||
? "Copied!"
|
? dict.chat.copied
|
||||||
: copyFailedMessageId ===
|
: copyFailedMessageId ===
|
||||||
message.id
|
message.id
|
||||||
? "Failed to copy"
|
? dict.chat
|
||||||
: "Copy message"
|
.failedToCopy
|
||||||
|
: dict.chat
|
||||||
|
.copyResponse
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{copiedMessageId ===
|
{copiedMessageId ===
|
||||||
@@ -968,7 +967,7 @@ export function ChatMessageDisplay({
|
|||||||
}}
|
}}
|
||||||
className="px-3 py-1.5 text-xs rounded-lg bg-muted hover:bg-muted/80 transition-colors"
|
className="px-3 py-1.5 text-xs rounded-lg bg-muted hover:bg-muted/80 transition-colors"
|
||||||
>
|
>
|
||||||
Cancel
|
{dict.common.cancel}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@@ -990,7 +989,7 @@ export function ChatMessageDisplay({
|
|||||||
disabled={!editText.trim()}
|
disabled={!editText.trim()}
|
||||||
className="px-3 py-1.5 text-xs rounded-lg bg-primary text-primary-foreground hover:bg-primary/90 disabled:opacity-50 transition-colors"
|
className="px-3 py-1.5 text-xs rounded-lg bg-primary text-primary-foreground hover:bg-primary/90 disabled:opacity-50 transition-colors"
|
||||||
>
|
>
|
||||||
Save & Submit
|
{dict.chat.saveAndSubmit}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1123,7 +1122,8 @@ export function ChatMessageDisplay({
|
|||||||
"user" &&
|
"user" &&
|
||||||
isLastUserMessage &&
|
isLastUserMessage &&
|
||||||
onEditMessage
|
onEditMessage
|
||||||
? "Click to edit"
|
? dict.chat
|
||||||
|
.clickToEdit
|
||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
@@ -1325,8 +1325,8 @@ export function ChatMessageDisplay({
|
|||||||
title={
|
title={
|
||||||
copiedMessageId ===
|
copiedMessageId ===
|
||||||
message.id
|
message.id
|
||||||
? "Copied!"
|
? dict.chat.copied
|
||||||
: "Copy response"
|
: dict.chat.copyResponse
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{copiedMessageId ===
|
{copiedMessageId ===
|
||||||
@@ -1352,7 +1352,9 @@ export function ChatMessageDisplay({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
className="p-1.5 rounded-lg text-muted-foreground/60 hover:text-foreground hover:bg-muted transition-colors"
|
className="p-1.5 rounded-lg text-muted-foreground/60 hover:text-foreground hover:bg-muted transition-colors"
|
||||||
title="Regenerate response"
|
title={
|
||||||
|
dict.chat.regenerate
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<RotateCcw className="h-3.5 w-3.5" />
|
<RotateCcw className="h-3.5 w-3.5" />
|
||||||
</button>
|
</button>
|
||||||
@@ -1374,7 +1376,7 @@ export function ChatMessageDisplay({
|
|||||||
? "text-green-600 bg-green-100"
|
? "text-green-600 bg-green-100"
|
||||||
: "text-muted-foreground/60 hover:text-green-600 hover:bg-green-50"
|
: "text-muted-foreground/60 hover:text-green-600 hover:bg-green-50"
|
||||||
}`}
|
}`}
|
||||||
title="Good response"
|
title={dict.chat.goodResponse}
|
||||||
>
|
>
|
||||||
<ThumbsUp className="h-3.5 w-3.5" />
|
<ThumbsUp className="h-3.5 w-3.5" />
|
||||||
</button>
|
</button>
|
||||||
@@ -1393,7 +1395,7 @@ export function ChatMessageDisplay({
|
|||||||
? "text-red-600 bg-red-100"
|
? "text-red-600 bg-red-100"
|
||||||
: "text-muted-foreground/60 hover:text-red-600 hover:bg-red-50"
|
: "text-muted-foreground/60 hover:text-red-600 hover:bg-red-50"
|
||||||
}`}
|
}`}
|
||||||
title="Bad response"
|
title={dict.chat.badResponse}
|
||||||
>
|
>
|
||||||
<ThumbsDown className="h-3.5 w-3.5" />
|
<ThumbsDown className="h-3.5 w-3.5" />
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import { useDictionary } from "@/hooks/use-dictionary"
|
|||||||
import { getSelectedAIConfig, useModelConfig } from "@/hooks/use-model-config"
|
import { getSelectedAIConfig, useModelConfig } from "@/hooks/use-model-config"
|
||||||
import { getApiEndpoint } from "@/lib/base-path"
|
import { getApiEndpoint } from "@/lib/base-path"
|
||||||
import { findCachedResponse } from "@/lib/cached-responses"
|
import { findCachedResponse } from "@/lib/cached-responses"
|
||||||
|
import { formatMessage } from "@/lib/i18n/utils"
|
||||||
import { isPdfFile, isTextFile } from "@/lib/pdf-utils"
|
import { isPdfFile, isTextFile } from "@/lib/pdf-utils"
|
||||||
import { type FileData, useFileProcessor } from "@/lib/use-file-processor"
|
import { type FileData, useFileProcessor } from "@/lib/use-file-processor"
|
||||||
import { useQuotaManager } from "@/lib/use-quota-manager"
|
import { useQuotaManager } from "@/lib/use-quota-manager"
|
||||||
@@ -389,7 +390,9 @@ export default function ChatPanel({
|
|||||||
MAX_CONTINUATION_RETRY_COUNT
|
MAX_CONTINUATION_RETRY_COUNT
|
||||||
) {
|
) {
|
||||||
toast.error(
|
toast.error(
|
||||||
`Continuation retry limit reached (${MAX_CONTINUATION_RETRY_COUNT}). The diagram may be too complex.`,
|
formatMessage(dict.errors.continuationRetryLimit, {
|
||||||
|
max: MAX_CONTINUATION_RETRY_COUNT,
|
||||||
|
}),
|
||||||
)
|
)
|
||||||
continuationRetryCountRef.current = 0
|
continuationRetryCountRef.current = 0
|
||||||
partialXmlRef.current = ""
|
partialXmlRef.current = ""
|
||||||
@@ -400,7 +403,9 @@ export default function ChatPanel({
|
|||||||
// Regular error: check retry count limit
|
// Regular error: check retry count limit
|
||||||
if (autoRetryCountRef.current >= MAX_AUTO_RETRY_COUNT) {
|
if (autoRetryCountRef.current >= MAX_AUTO_RETRY_COUNT) {
|
||||||
toast.error(
|
toast.error(
|
||||||
`Auto-retry limit reached (${MAX_AUTO_RETRY_COUNT}). Please try again manually.`,
|
formatMessage(dict.errors.retryLimit, {
|
||||||
|
max: MAX_AUTO_RETRY_COUNT,
|
||||||
|
}),
|
||||||
)
|
)
|
||||||
autoRetryCountRef.current = 0
|
autoRetryCountRef.current = 0
|
||||||
partialXmlRef.current = ""
|
partialXmlRef.current = ""
|
||||||
@@ -450,7 +455,7 @@ export default function ChatPanel({
|
|||||||
// On complete failure, clear storage to allow recovery
|
// On complete failure, clear storage to allow recovery
|
||||||
localStorage.removeItem(STORAGE_MESSAGES_KEY)
|
localStorage.removeItem(STORAGE_MESSAGES_KEY)
|
||||||
localStorage.removeItem(STORAGE_XML_SNAPSHOTS_KEY)
|
localStorage.removeItem(STORAGE_XML_SNAPSHOTS_KEY)
|
||||||
toast.error("Session data was corrupted. Starting fresh.")
|
toast.error(dict.errors.sessionCorrupted)
|
||||||
}
|
}
|
||||||
}, [setMessages])
|
}, [setMessages])
|
||||||
|
|
||||||
@@ -651,12 +656,10 @@ export default function ChatPanel({
|
|||||||
localStorage.removeItem(STORAGE_DIAGRAM_XML_KEY)
|
localStorage.removeItem(STORAGE_DIAGRAM_XML_KEY)
|
||||||
localStorage.setItem(STORAGE_SESSION_ID_KEY, newSessionId)
|
localStorage.setItem(STORAGE_SESSION_ID_KEY, newSessionId)
|
||||||
sessionStorage.removeItem(SESSION_STORAGE_INPUT_KEY)
|
sessionStorage.removeItem(SESSION_STORAGE_INPUT_KEY)
|
||||||
toast.success("Started a fresh chat")
|
toast.success(dict.dialogs.clearSuccess)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to clear localStorage:", error)
|
console.error("Failed to clear localStorage:", error)
|
||||||
toast.warning(
|
toast.warning(dict.errors.storageUpdateFailed)
|
||||||
"Chat cleared but browser storage could not be updated",
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setShowNewChatDialog(false)
|
setShowNewChatDialog(false)
|
||||||
@@ -889,7 +892,7 @@ export default function ChatPanel({
|
|||||||
return (
|
return (
|
||||||
<div className="h-full flex flex-col items-center pt-4 bg-card border border-border/30 rounded-xl">
|
<div className="h-full flex flex-col items-center pt-4 bg-card border border-border/30 rounded-xl">
|
||||||
<ButtonWithTooltip
|
<ButtonWithTooltip
|
||||||
tooltipContent="Show chat panel (Ctrl+B)"
|
tooltipContent={dict.nav.showPanel}
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="icon"
|
size="icon"
|
||||||
onClick={onToggleVisibility}
|
onClick={onToggleVisibility}
|
||||||
@@ -904,7 +907,7 @@ export default function ChatPanel({
|
|||||||
transform: "rotate(180deg)",
|
transform: "rotate(180deg)",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
AI Chat
|
{dict.nav.aiChat}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
@@ -956,7 +959,7 @@ export default function ChatPanel({
|
|||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className="text-sm text-muted-foreground hover:text-foreground transition-colors ml-2"
|
className="text-sm text-muted-foreground hover:text-foreground transition-colors ml-2"
|
||||||
>
|
>
|
||||||
About
|
{dict.nav.about}
|
||||||
</Link>
|
</Link>
|
||||||
)}
|
)}
|
||||||
{!isMobile && (
|
{!isMobile && (
|
||||||
@@ -966,7 +969,7 @@ export default function ChatPanel({
|
|||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
>
|
>
|
||||||
<ButtonWithTooltip
|
<ButtonWithTooltip
|
||||||
tooltipContent="Sponsored by ByteDance Doubao K2-thinking. See About page for details."
|
tooltipContent={dict.nav.sponsorTooltip}
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="icon"
|
size="icon"
|
||||||
className="h-6 w-6 text-amber-500 hover:text-amber-600"
|
className="h-6 w-6 text-amber-500 hover:text-amber-600"
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import { useEffect, useRef, useState } from "react"
|
import { useEffect, useRef, useState } from "react"
|
||||||
|
import { useDictionary } from "@/hooks/use-dictionary"
|
||||||
import { wrapWithMxFile } from "@/lib/utils"
|
import { wrapWithMxFile } from "@/lib/utils"
|
||||||
|
|
||||||
// Dev XML presets for streaming simulator
|
// Dev XML presets for streaming simulator
|
||||||
@@ -142,6 +143,7 @@ export function DevXmlSimulator({
|
|||||||
onDisplayChart,
|
onDisplayChart,
|
||||||
onShowQuotaToast,
|
onShowQuotaToast,
|
||||||
}: DevXmlSimulatorProps) {
|
}: DevXmlSimulatorProps) {
|
||||||
|
const dict = useDictionary()
|
||||||
const [devXml, setDevXml] = useState("")
|
const [devXml, setDevXml] = useState("")
|
||||||
const [isSimulating, setIsSimulating] = useState(false)
|
const [isSimulating, setIsSimulating] = useState(false)
|
||||||
const [devIntervalMs, setDevIntervalMs] = useState(1)
|
const [devIntervalMs, setDevIntervalMs] = useState(1)
|
||||||
@@ -178,7 +180,7 @@ export function DevXmlSimulator({
|
|||||||
parts: [
|
parts: [
|
||||||
{
|
{
|
||||||
type: "text" as const,
|
type: "text" as const,
|
||||||
text: "[Dev] Simulating XML streaming",
|
text: dict.dev.simulatingMessage,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
@@ -228,7 +230,7 @@ export function DevXmlSimulator({
|
|||||||
const lastMsg = updated[updated.length - 1] as any
|
const lastMsg = updated[updated.length - 1] as any
|
||||||
if (lastMsg?.role === "assistant" && lastMsg.parts?.[0]) {
|
if (lastMsg?.role === "assistant" && lastMsg.parts?.[0]) {
|
||||||
lastMsg.parts[0].state = "output-available"
|
lastMsg.parts[0].state = "output-available"
|
||||||
lastMsg.parts[0].output = "Successfully displayed the diagram."
|
lastMsg.parts[0].output = dict.dev.successMessage
|
||||||
lastMsg.parts[0].input = { xml }
|
lastMsg.parts[0].input = { xml }
|
||||||
}
|
}
|
||||||
return updated
|
return updated
|
||||||
@@ -245,12 +247,12 @@ export function DevXmlSimulator({
|
|||||||
<div className="border-t border-dashed border-orange-500/50 px-4 py-2 bg-orange-50/50 dark:bg-orange-950/30">
|
<div className="border-t border-dashed border-orange-500/50 px-4 py-2 bg-orange-50/50 dark:bg-orange-950/30">
|
||||||
<details>
|
<details>
|
||||||
<summary className="text-xs text-orange-600 dark:text-orange-400 cursor-pointer font-medium">
|
<summary className="text-xs text-orange-600 dark:text-orange-400 cursor-pointer font-medium">
|
||||||
Dev: XML Streaming Simulator
|
{dict.dev.title}
|
||||||
</summary>
|
</summary>
|
||||||
<div className="mt-2 space-y-2">
|
<div className="mt-2 space-y-2">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<label className="text-xs text-muted-foreground whitespace-nowrap">
|
<label className="text-xs text-muted-foreground whitespace-nowrap">
|
||||||
Preset:
|
{dict.dev.preset}
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
@@ -262,7 +264,7 @@ export function DevXmlSimulator({
|
|||||||
defaultValue=""
|
defaultValue=""
|
||||||
>
|
>
|
||||||
<option value="" disabled>
|
<option value="" disabled>
|
||||||
Select a preset...
|
{dict.dev.selectPreset}
|
||||||
</option>
|
</option>
|
||||||
{Object.keys(DEV_XML_PRESETS).map((name) => (
|
{Object.keys(DEV_XML_PRESETS).map((name) => (
|
||||||
<option key={name} value={name}>
|
<option key={name} value={name}>
|
||||||
@@ -275,19 +277,19 @@ export function DevXmlSimulator({
|
|||||||
onClick={() => setDevXml("")}
|
onClick={() => setDevXml("")}
|
||||||
className="px-2 py-1 text-xs text-muted-foreground hover:text-foreground border rounded"
|
className="px-2 py-1 text-xs text-muted-foreground hover:text-foreground border rounded"
|
||||||
>
|
>
|
||||||
Clear
|
{dict.dev.clear}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<textarea
|
<textarea
|
||||||
value={devXml}
|
value={devXml}
|
||||||
onChange={(e) => setDevXml(e.target.value)}
|
onChange={(e) => setDevXml(e.target.value)}
|
||||||
placeholder="Paste mxCell XML here or select a preset..."
|
placeholder={dict.dev.placeholder}
|
||||||
className="w-full h-24 text-xs font-mono p-2 border rounded bg-background"
|
className="w-full h-24 text-xs font-mono p-2 border rounded bg-background"
|
||||||
/>
|
/>
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-4">
|
||||||
<div className="flex items-center gap-2 flex-1">
|
<div className="flex items-center gap-2 flex-1">
|
||||||
<label className="text-xs text-muted-foreground whitespace-nowrap">
|
<label className="text-xs text-muted-foreground whitespace-nowrap">
|
||||||
Interval:
|
{dict.dev.interval}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="range"
|
type="range"
|
||||||
@@ -306,7 +308,7 @@ export function DevXmlSimulator({
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<label className="text-xs text-muted-foreground whitespace-nowrap">
|
<label className="text-xs text-muted-foreground whitespace-nowrap">
|
||||||
Chars:
|
{dict.dev.chars}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
@@ -330,8 +332,8 @@ export function DevXmlSimulator({
|
|||||||
className="px-3 py-1 text-xs bg-orange-500 text-white rounded hover:bg-orange-600 disabled:opacity-50 disabled:cursor-not-allowed"
|
className="px-3 py-1 text-xs bg-orange-500 text-white rounded hover:bg-orange-600 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||||
>
|
>
|
||||||
{isSimulating
|
{isSimulating
|
||||||
? "Streaming..."
|
? dict.dev.streaming
|
||||||
: `Simulate (${devChunkSize} chars/${devIntervalMs}ms)`}
|
: `${dict.dev.simulate} (${devChunkSize} chars/${devIntervalMs}ms)`}
|
||||||
</button>
|
</button>
|
||||||
{isSimulating && (
|
{isSimulating && (
|
||||||
<button
|
<button
|
||||||
@@ -341,7 +343,7 @@ export function DevXmlSimulator({
|
|||||||
}}
|
}}
|
||||||
className="px-3 py-1 text-xs bg-red-500 text-white rounded hover:bg-red-600"
|
className="px-3 py-1 text-xs bg-red-500 text-white rounded hover:bg-red-600"
|
||||||
>
|
>
|
||||||
Stop
|
{dict.dev.stop}
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
{onShowQuotaToast && (
|
{onShowQuotaToast && (
|
||||||
@@ -350,7 +352,7 @@ export function DevXmlSimulator({
|
|||||||
onClick={onShowQuotaToast}
|
onClick={onShowQuotaToast}
|
||||||
className="px-3 py-1 text-xs bg-purple-500 text-white rounded hover:bg-purple-600"
|
className="px-3 py-1 text-xs bg-purple-500 text-white rounded hover:bg-purple-600"
|
||||||
>
|
>
|
||||||
Test Quota Toast
|
{dict.dev.testQuotaToast}
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -18,7 +18,8 @@
|
|||||||
"settings": "Settings",
|
"settings": "Settings",
|
||||||
"hidePanel": "Hide chat panel (Ctrl+B)",
|
"hidePanel": "Hide chat panel (Ctrl+B)",
|
||||||
"showPanel": "Show chat panel (Ctrl+B)",
|
"showPanel": "Show chat panel (Ctrl+B)",
|
||||||
"aiChat": "AI Chat"
|
"aiChat": "AI Chat",
|
||||||
|
"sponsorTooltip": "Sponsored by ByteDance Doubao K2-thinking. See About page for details."
|
||||||
},
|
},
|
||||||
"providers": {
|
"providers": {
|
||||||
"useServerDefault": "Use Server Default",
|
"useServerDefault": "Use Server Default",
|
||||||
@@ -46,6 +47,7 @@
|
|||||||
"copyResponse": "Copy response",
|
"copyResponse": "Copy response",
|
||||||
"copied": "Copied!",
|
"copied": "Copied!",
|
||||||
"failedToCopy": "Failed to copy",
|
"failedToCopy": "Failed to copy",
|
||||||
|
"failedToCopyDetail": "Failed to copy message. Please copy manually or check clipboard permissions.",
|
||||||
"goodResponse": "Good response",
|
"goodResponse": "Good response",
|
||||||
"badResponse": "Bad response",
|
"badResponse": "Bad response",
|
||||||
"clickToEdit": "Click to edit",
|
"clickToEdit": "Click to edit",
|
||||||
@@ -141,6 +143,7 @@
|
|||||||
"invalidAccessCode": "Invalid or missing access code. Please configure it in Settings.",
|
"invalidAccessCode": "Invalid or missing access code. Please configure it in Settings.",
|
||||||
"networkError": "Network error. Please check your connection.",
|
"networkError": "Network error. Please check your connection.",
|
||||||
"retryLimit": "Auto-retry limit reached ({max}). Please try again manually.",
|
"retryLimit": "Auto-retry limit reached ({max}). Please try again manually.",
|
||||||
|
"continuationRetryLimit": "Continuation retry limit reached ({max}). The diagram may be too complex.",
|
||||||
"validationFailed": "Diagram validation failed. Please try regenerating.",
|
"validationFailed": "Diagram validation failed. Please try regenerating.",
|
||||||
"malformedXml": "AI generated invalid diagram XML. Please try regenerating.",
|
"malformedXml": "AI generated invalid diagram XML. Please try regenerating.",
|
||||||
"failedToProcess": "Failed to process diagram. Please try regenerating.",
|
"failedToProcess": "Failed to process diagram. Please try regenerating.",
|
||||||
@@ -149,7 +152,9 @@
|
|||||||
"failedToRestore": "Failed to restore from localStorage",
|
"failedToRestore": "Failed to restore from localStorage",
|
||||||
"failedToPersist": "Failed to persist state before unload",
|
"failedToPersist": "Failed to persist state before unload",
|
||||||
"failedToExport": "Error fetching chart data",
|
"failedToExport": "Error fetching chart data",
|
||||||
"failedToLoadExample": "Error loading example image"
|
"failedToLoadExample": "Error loading example image",
|
||||||
|
"failedToRecordFeedback": "Failed to record your feedback. Please try again.",
|
||||||
|
"storageUpdateFailed": "Chat cleared but browser storage could not be updated"
|
||||||
},
|
},
|
||||||
"quota": {
|
"quota": {
|
||||||
"dailyLimit": "Daily Quota Reached",
|
"dailyLimit": "Daily Quota Reached",
|
||||||
@@ -186,6 +191,21 @@
|
|||||||
"thoughtFor": "Thought for {duration} seconds",
|
"thoughtFor": "Thought for {duration} seconds",
|
||||||
"thoughtBrief": "Thought for a few seconds"
|
"thoughtBrief": "Thought for a few seconds"
|
||||||
},
|
},
|
||||||
|
"dev": {
|
||||||
|
"title": "Dev: XML Streaming Simulator",
|
||||||
|
"preset": "Preset:",
|
||||||
|
"selectPreset": "Select a preset...",
|
||||||
|
"clear": "Clear",
|
||||||
|
"placeholder": "Paste mxCell XML here or select a preset...",
|
||||||
|
"interval": "Interval:",
|
||||||
|
"chars": "Chars:",
|
||||||
|
"streaming": "Streaming...",
|
||||||
|
"simulate": "Simulate",
|
||||||
|
"stop": "Stop",
|
||||||
|
"testQuotaToast": "Test Quota Toast",
|
||||||
|
"simulatingMessage": "[Dev] Simulating XML streaming",
|
||||||
|
"successMessage": "Successfully displayed the diagram."
|
||||||
|
},
|
||||||
"about": {
|
"about": {
|
||||||
"modelChange": "Model Change & Usage Limits",
|
"modelChange": "Model Change & Usage Limits",
|
||||||
"walletCrying": "(Or: Why My Wallet is Crying)",
|
"walletCrying": "(Or: Why My Wallet is Crying)",
|
||||||
|
|||||||
@@ -18,7 +18,8 @@
|
|||||||
"settings": "設定",
|
"settings": "設定",
|
||||||
"hidePanel": "チャットパネルを非表示 (Ctrl+B)",
|
"hidePanel": "チャットパネルを非表示 (Ctrl+B)",
|
||||||
"showPanel": "チャットパネルを表示 (Ctrl+B)",
|
"showPanel": "チャットパネルを表示 (Ctrl+B)",
|
||||||
"aiChat": "AI チャット"
|
"aiChat": "AI チャット",
|
||||||
|
"sponsorTooltip": "ByteDance Doubao K2-thinking によるスポンサー。詳細は概要ページをご覧ください。"
|
||||||
},
|
},
|
||||||
"providers": {
|
"providers": {
|
||||||
"useServerDefault": "サーバーデフォルトを使用",
|
"useServerDefault": "サーバーデフォルトを使用",
|
||||||
@@ -46,6 +47,7 @@
|
|||||||
"copyResponse": "応答をコピー",
|
"copyResponse": "応答をコピー",
|
||||||
"copied": "コピーしました!",
|
"copied": "コピーしました!",
|
||||||
"failedToCopy": "コピーに失敗しました",
|
"failedToCopy": "コピーに失敗しました",
|
||||||
|
"failedToCopyDetail": "メッセージのコピーに失敗しました。手動でコピーするか、クリップボードの権限を確認してください。",
|
||||||
"goodResponse": "良い応答",
|
"goodResponse": "良い応答",
|
||||||
"badResponse": "悪い応答",
|
"badResponse": "悪い応答",
|
||||||
"clickToEdit": "クリックして編集",
|
"clickToEdit": "クリックして編集",
|
||||||
@@ -141,6 +143,7 @@
|
|||||||
"invalidAccessCode": "無効または欠落したアクセスコード。設定で入力してください。",
|
"invalidAccessCode": "無効または欠落したアクセスコード。設定で入力してください。",
|
||||||
"networkError": "ネットワークエラー。接続を確認してください。",
|
"networkError": "ネットワークエラー。接続を確認してください。",
|
||||||
"retryLimit": "自動再試行制限に達しました({max})。手動で再試行してください。",
|
"retryLimit": "自動再試行制限に達しました({max})。手動で再試行してください。",
|
||||||
|
"continuationRetryLimit": "継続再試行制限に達しました({max})。ダイアグラムが複雑すぎる可能性があります。",
|
||||||
"validationFailed": "ダイアグラムの検証に失敗しました。再生成してみてください。",
|
"validationFailed": "ダイアグラムの検証に失敗しました。再生成してみてください。",
|
||||||
"malformedXml": "AI が無効なダイアグラム XML を生成しました。再生成してみてください。",
|
"malformedXml": "AI が無効なダイアグラム XML を生成しました。再生成してみてください。",
|
||||||
"failedToProcess": "ダイアグラムの処理に失敗しました。再生成してみてください。",
|
"failedToProcess": "ダイアグラムの処理に失敗しました。再生成してみてください。",
|
||||||
@@ -149,7 +152,9 @@
|
|||||||
"failedToRestore": "localStorage からの復元に失敗しました",
|
"failedToRestore": "localStorage からの復元に失敗しました",
|
||||||
"failedToPersist": "アンロード前の状態の永続化に失敗しました",
|
"failedToPersist": "アンロード前の状態の永続化に失敗しました",
|
||||||
"failedToExport": "チャートデータの取得エラー",
|
"failedToExport": "チャートデータの取得エラー",
|
||||||
"failedToLoadExample": "例の画像の読み込みエラー"
|
"failedToLoadExample": "例の画像の読み込みエラー",
|
||||||
|
"failedToRecordFeedback": "フィードバックの記録に失敗しました。もう一度お試しください。",
|
||||||
|
"storageUpdateFailed": "チャットはクリアされましたが、ブラウザストレージを更新できませんでした"
|
||||||
},
|
},
|
||||||
"quota": {
|
"quota": {
|
||||||
"dailyLimit": "1日の割当量に達しました",
|
"dailyLimit": "1日の割当量に達しました",
|
||||||
@@ -186,6 +191,21 @@
|
|||||||
"thoughtFor": "{duration} 秒考えました",
|
"thoughtFor": "{duration} 秒考えました",
|
||||||
"thoughtBrief": "数秒考えました"
|
"thoughtBrief": "数秒考えました"
|
||||||
},
|
},
|
||||||
|
"dev": {
|
||||||
|
"title": "開発:XMLストリーミングシミュレーター",
|
||||||
|
"preset": "プリセット:",
|
||||||
|
"selectPreset": "プリセットを選択...",
|
||||||
|
"clear": "クリア",
|
||||||
|
"placeholder": "ここに mxCell XML を貼り付けるか、プリセットを選択...",
|
||||||
|
"interval": "間隔:",
|
||||||
|
"chars": "文字:",
|
||||||
|
"streaming": "ストリーミング中...",
|
||||||
|
"simulate": "シミュレート",
|
||||||
|
"stop": "停止",
|
||||||
|
"testQuotaToast": "クォータトーストをテスト",
|
||||||
|
"simulatingMessage": "[開発] XMLストリーミングをシミュレート中",
|
||||||
|
"successMessage": "ダイアグラムの表示に成功しました。"
|
||||||
|
},
|
||||||
"about": {
|
"about": {
|
||||||
"modelChange": "モデル変更と利用制限について",
|
"modelChange": "モデル変更と利用制限について",
|
||||||
"walletCrying": "(別名:お財布が悲鳴を上げています)",
|
"walletCrying": "(別名:お財布が悲鳴を上げています)",
|
||||||
|
|||||||
@@ -18,7 +18,8 @@
|
|||||||
"settings": "设置",
|
"settings": "设置",
|
||||||
"hidePanel": "隐藏聊天面板 (Ctrl+B)",
|
"hidePanel": "隐藏聊天面板 (Ctrl+B)",
|
||||||
"showPanel": "显示聊天面板 (Ctrl+B)",
|
"showPanel": "显示聊天面板 (Ctrl+B)",
|
||||||
"aiChat": "AI 聊天"
|
"aiChat": "AI 聊天",
|
||||||
|
"sponsorTooltip": "由字节跳动豆包 K2-thinking 赞助。详情请参阅关于页面。"
|
||||||
},
|
},
|
||||||
"providers": {
|
"providers": {
|
||||||
"useServerDefault": "使用服务器默认值",
|
"useServerDefault": "使用服务器默认值",
|
||||||
@@ -46,6 +47,7 @@
|
|||||||
"copyResponse": "复制响应",
|
"copyResponse": "复制响应",
|
||||||
"copied": "已复制!",
|
"copied": "已复制!",
|
||||||
"failedToCopy": "复制失败",
|
"failedToCopy": "复制失败",
|
||||||
|
"failedToCopyDetail": "复制消息失败。请手动复制或检查剪贴板权限。",
|
||||||
"goodResponse": "有帮助",
|
"goodResponse": "有帮助",
|
||||||
"badResponse": "无帮助",
|
"badResponse": "无帮助",
|
||||||
"clickToEdit": "点击编辑",
|
"clickToEdit": "点击编辑",
|
||||||
@@ -141,6 +143,7 @@
|
|||||||
"invalidAccessCode": "无效或缺少访问码。请在设置中配置。",
|
"invalidAccessCode": "无效或缺少访问码。请在设置中配置。",
|
||||||
"networkError": "网络错误。请检查您的连接。",
|
"networkError": "网络错误。请检查您的连接。",
|
||||||
"retryLimit": "已达到自动重试限制({max})。请手动重试。",
|
"retryLimit": "已达到自动重试限制({max})。请手动重试。",
|
||||||
|
"continuationRetryLimit": "已达到继续重试限制({max})。图表可能过于复杂。",
|
||||||
"validationFailed": "图表验证失败。请尝试重新生成。",
|
"validationFailed": "图表验证失败。请尝试重新生成。",
|
||||||
"malformedXml": "AI 生成的图表 XML 无效。请尝试重新生成。",
|
"malformedXml": "AI 生成的图表 XML 无效。请尝试重新生成。",
|
||||||
"failedToProcess": "无法处理图表。请尝试重新生成。",
|
"failedToProcess": "无法处理图表。请尝试重新生成。",
|
||||||
@@ -149,7 +152,9 @@
|
|||||||
"failedToRestore": "无法从 localStorage 恢复",
|
"failedToRestore": "无法从 localStorage 恢复",
|
||||||
"failedToPersist": "卸载前无法持久化状态",
|
"failedToPersist": "卸载前无法持久化状态",
|
||||||
"failedToExport": "获取图表数据时出错",
|
"failedToExport": "获取图表数据时出错",
|
||||||
"failedToLoadExample": "加载示例图片时出错"
|
"failedToLoadExample": "加载示例图片时出错",
|
||||||
|
"failedToRecordFeedback": "记录您的反馈失败。请重试。",
|
||||||
|
"storageUpdateFailed": "聊天已清除,但无法更新浏览器存储"
|
||||||
},
|
},
|
||||||
"quota": {
|
"quota": {
|
||||||
"dailyLimit": "已达每日配额",
|
"dailyLimit": "已达每日配额",
|
||||||
@@ -186,6 +191,21 @@
|
|||||||
"thoughtFor": "思考了 {duration} 秒",
|
"thoughtFor": "思考了 {duration} 秒",
|
||||||
"thoughtBrief": "思考了几秒钟"
|
"thoughtBrief": "思考了几秒钟"
|
||||||
},
|
},
|
||||||
|
"dev": {
|
||||||
|
"title": "开发:XML 流式模拟器",
|
||||||
|
"preset": "预设:",
|
||||||
|
"selectPreset": "选择预设...",
|
||||||
|
"clear": "清除",
|
||||||
|
"placeholder": "在此粘贴 mxCell XML 或选择预设...",
|
||||||
|
"interval": "间隔:",
|
||||||
|
"chars": "字符:",
|
||||||
|
"streaming": "流式传输中...",
|
||||||
|
"simulate": "模拟",
|
||||||
|
"stop": "停止",
|
||||||
|
"testQuotaToast": "测试配额提示",
|
||||||
|
"simulatingMessage": "[开发] 模拟 XML 流式传输",
|
||||||
|
"successMessage": "成功显示图表。"
|
||||||
|
},
|
||||||
"about": {
|
"about": {
|
||||||
"modelChange": "模型变更与用量限制",
|
"modelChange": "模型变更与用量限制",
|
||||||
"walletCrying": "(别名:我的钱包顶不住了)",
|
"walletCrying": "(别名:我的钱包顶不住了)",
|
||||||
|
|||||||
Reference in New Issue
Block a user