setExpanded((e) => !e)}
>
- {expanded() ? "Show less" : "Show more"}
+ {expanded() ? messages.show_less : messages.show_more}
)}
diff --git a/packages/web/src/components/share/content-text.tsx b/packages/web/src/components/share/content-text.tsx
index 5db12a537..b549b74a4 100644
--- a/packages/web/src/components/share/content-text.tsx
+++ b/packages/web/src/components/share/content-text.tsx
@@ -1,6 +1,6 @@
import style from "./content-text.module.css"
import { createSignal } from "solid-js"
-import { createOverflow } from "./common"
+import { createOverflow, useShareMessages } from "./common"
import { CopyButton } from "./copy-button"
interface Props {
@@ -11,6 +11,7 @@ interface Props {
export function ContentText(props: Props) {
const [expanded, setExpanded] = createSignal(false)
const overflow = createOverflow()
+ const messages = useShareMessages()
return (
setExpanded((e) => !e)}
>
- {expanded() ? "Show less" : "Show more"}
+ {expanded() ? messages.show_less : messages.show_more}
)}
diff --git a/packages/web/src/components/share/copy-button.tsx b/packages/web/src/components/share/copy-button.tsx
index 892d5553f..6c5097a99 100644
--- a/packages/web/src/components/share/copy-button.tsx
+++ b/packages/web/src/components/share/copy-button.tsx
@@ -1,5 +1,6 @@
import { createSignal } from "solid-js"
import { IconClipboard, IconCheckCircle } from "../icons"
+import { useShareMessages } from "./common"
import styles from "./copy-button.module.css"
interface CopyButtonProps {
@@ -8,6 +9,7 @@ interface CopyButtonProps {
export function CopyButton(props: CopyButtonProps) {
const [copied, setCopied] = createSignal(false)
+ const messages = useShareMessages()
function handleCopyClick() {
if (props.text) {
@@ -20,7 +22,13 @@ export function CopyButton(props: CopyButtonProps) {
return (
-
diff --git a/packages/web/src/components/share/part.tsx b/packages/web/src/components/share/part.tsx
index f7a6a9304..45bd97fe3 100644
--- a/packages/web/src/components/share/part.tsx
+++ b/packages/web/src/components/share/part.tsx
@@ -25,7 +25,7 @@ import { ContentDiff } from "./content-diff"
import { ContentText } from "./content-text"
import { ContentBash } from "./content-bash"
import { ContentError } from "./content-error"
-import { formatDuration } from "../share/common"
+import { formatCount, formatDuration, formatNumber, normalizeLocale, useShareMessages } from "../share/common"
import { ContentMarkdown } from "./content-markdown"
import type { MessageV2 } from "opencode/session/message-v2"
import type { Diagnostic } from "vscode-languageserver-types"
@@ -44,6 +44,7 @@ export interface PartProps {
export function Part(props: PartProps) {
const [copied, setCopied] = createSignal(false)
const id = createMemo(() => props.message.id + "-" + props.index)
+ const messages = useShareMessages()
return (
-
@@ -143,11 +144,13 @@ export function Part(props: PartProps) {
{props.last && props.message.role === "assistant" && props.message.time.completed && (
)}
@@ -155,13 +158,13 @@ export function Part(props: PartProps) {
{props.message.role === "assistant" && props.part.type === "reasoning" && (
- Thinking
+ {messages.thinking}
@@ -170,13 +173,7 @@ export function Part(props: PartProps) {
)}
{props.message.role === "user" && props.part.type === "file" && (
-
Attachment
-
{props.part.filename}
-
- )}
- {props.message.role === "user" && props.part.type === "file" && (
-
-
Attachment
+
{messages.attachment}
{props.part.filename}
)}
@@ -188,7 +185,7 @@ export function Part(props: PartProps) {
)}
{props.part.type === "tool" && props.part.state.status === "error" && (
- {formatErrorString(props.part.state.error)}
+ {formatErrorString(props.part.state.error, messages.error)}
)}
@@ -343,43 +340,45 @@ function getShikiLang(filename: string) {
return type ? (overrides[type] ?? type) : "plaintext"
}
-function getDiagnostics(diagnosticsByFile: Record, currentFile: string): JSX.Element[] {
+function getDiagnostics(
+ diagnosticsByFile: Record,
+ currentFile: string,
+ label: string,
+): JSX.Element[] {
const result: JSX.Element[] = []
if (diagnosticsByFile === undefined || diagnosticsByFile[currentFile] === undefined) return result
- for (const diags of Object.values(diagnosticsByFile)) {
- for (const d of diags) {
- if (d.severity !== 1) continue
-
- const line = d.range.start.line + 1
- const column = d.range.start.character + 1
-
- result.push(
-
-
- Error
-
-
- [{line}:{column}]
-
- {d.message}
- ,
- )
- }
+ for (const d of diagnosticsByFile[currentFile]) {
+ if (d.severity !== 1) continue
+
+ const line = d.range.start.line + 1
+ const column = d.range.start.character + 1
+
+ result.push(
+
+
+ {label}
+
+
+ [{line}:{column}]
+
+ {d.message}
+ ,
+ )
}
return result
}
-function formatErrorString(error: string): JSX.Element {
+function formatErrorString(error: string, label: string): JSX.Element {
const errorMarker = "Error: "
const startsWithError = error.startsWith(errorMarker)
return startsWithError ? (
- Error
+ {label}
{error.slice(errorMarker.length)}
@@ -391,6 +390,7 @@ function formatErrorString(error: string): JSX.Element {
}
export function TodoWriteTool(props: ToolProps) {
+ const messages = useShareMessages()
const priority: Record = {
in_progress: 0,
pending: 1,
@@ -406,9 +406,9 @@ export function TodoWriteTool(props: ToolProps) {
<>
-
- Creating plan
- Completing plan
+
+ {messages.creating_plan}
+ {messages.completing_plan}
@@ -429,6 +429,8 @@ export function TodoWriteTool(props: ToolProps) {
}
export function GrepTool(props: ToolProps) {
+ const messages = useShareMessages()
+
return (
<>
@@ -439,7 +441,12 @@ export function GrepTool(props: ToolProps) {
0}>
@@ -482,6 +489,8 @@ export function ListTool(props: ToolProps) {
}
export function WebFetchTool(props: ToolProps) {
+ const messages = useShareMessages()
+
return (
<>
@@ -491,7 +500,7 @@ export function WebFetchTool(props: ToolProps) {
- {formatErrorString(props.state.output)}
+ {formatErrorString(props.state.output, messages.error)}
@@ -505,6 +514,7 @@ export function WebFetchTool(props: ToolProps) {
}
export function ReadTool(props: ToolProps) {
+ const messages = useShareMessages()
const filePath = createMemo(() => stripWorkingDirectory(props.state.input?.filePath, props.message.path.cwd))
return (
@@ -518,10 +528,10 @@ export function ReadTool(props: ToolProps) {
- {formatErrorString(props.state.output)}
+ {formatErrorString(props.state.output, messages.error)}
-
+
@@ -537,8 +547,11 @@ export function ReadTool(props: ToolProps) {
}
export function WriteTool(props: ToolProps) {
+ const messages = useShareMessages()
const filePath = createMemo(() => stripWorkingDirectory(props.state.input?.filePath, props.message.path.cwd))
- const diagnostics = createMemo(() => getDiagnostics(props.state.metadata?.diagnostics, props.state.input.filePath))
+ const diagnostics = createMemo(() =>
+ getDiagnostics(props.state.metadata?.diagnostics, props.state.input.filePath, messages.error),
+ )
return (
<>
@@ -554,10 +567,10 @@ export function WriteTool(props: ToolProps) {
- {formatErrorString(props.state.output)}
+ {formatErrorString(props.state.output, messages.error)}
-
+
@@ -568,8 +581,11 @@ export function WriteTool(props: ToolProps) {
}
export function EditTool(props: ToolProps) {
+ const messages = useShareMessages()
const filePath = createMemo(() => stripWorkingDirectory(props.state.input.filePath, props.message.path.cwd))
- const diagnostics = createMemo(() => getDiagnostics(props.state.metadata?.diagnostics, props.state.input.filePath))
+ const diagnostics = createMemo(() =>
+ getDiagnostics(props.state.metadata?.diagnostics, props.state.input.filePath, messages.error),
+ )
return (
<>
@@ -582,7 +598,7 @@ export function EditTool(props: ToolProps) {
- {formatErrorString(props.state.metadata?.message || "")}
+ {formatErrorString(props.state.metadata?.message || "", messages.error)}
@@ -609,6 +625,8 @@ export function BashTool(props: ToolProps) {
}
export function GlobTool(props: ToolProps) {
+ const messages = useShareMessages()
+
return (
<>
@@ -619,7 +637,12 @@ export function GlobTool(props: ToolProps) {
0}>
@@ -639,11 +662,12 @@ interface ResultsButtonProps extends ParentProps {
}
function ResultsButton(props: ResultsButtonProps) {
const [show, setShow] = createSignal(false)
+ const messages = useShareMessages()
return (
<>
setShow((e) => !e)}>
- {show() ? props.hideCopy || "Hide results" : props.showCopy || "Show results"}
+ {show() ? props.hideCopy || messages.hide_results : props.showCopy || messages.show_results}
}>
@@ -668,10 +692,19 @@ function Footer(props: ParentProps<{ title: string }>) {
}
function ToolFooter(props: { time: number }) {
- return props.time > MIN_DURATION &&
+ const messages = useShareMessages()
+ return (
+ props.time > MIN_DURATION && (
+
+ )
+ )
}
function TaskTool(props: ToolProps) {
+ const messages = useShareMessages()
+
return (
<>
@@ -679,7 +712,7 @@ function TaskTool(props: ToolProps) {
{props.state.input.description}
“{props.state.input.prompt}”
-
+
@@ -700,7 +733,7 @@ export function FallbackTool(props: ToolProps) {
<>
{arg[0]}
- {arg[1]}
+ {String(arg[1] ?? "")}
>
)}
@@ -720,10 +753,11 @@ export function FallbackTool(props: ToolProps) {
// Converts nested objects/arrays into [path, value] pairs.
// E.g. {a:{b:{c:1}}, d:[{e:2}, 3]} => [["a.b.c",1], ["d[0].e",2], ["d[1]",3]]
-function flattenToolArgs(obj: any, prefix: string = ""): Array<[string, any]> {
- const entries: Array<[string, any]> = []
+function flattenToolArgs(obj: unknown, prefix: string = ""): Array<[string, unknown]> {
+ const entries: Array<[string, unknown]> = []
+ if (typeof obj !== "object" || obj === null) return entries
- for (const [key, value] of Object.entries(obj)) {
+ for (const [key, value] of Object.entries(obj as Record)) {
const path = prefix ? `${prefix}.${key}` : key
if (value !== null && typeof value === "object") {
--
cgit v1.2.3