summaryrefslogtreecommitdiffhomepage
path: root/packages/app/src/components
diff options
context:
space:
mode:
Diffstat (limited to 'packages/app/src/components')
-rw-r--r--packages/app/src/components/debug-bar.tsx70
-rw-r--r--packages/app/src/components/dialog-select-file.tsx2
-rw-r--r--packages/app/src/components/dialog-select-server.tsx4
-rw-r--r--packages/app/src/components/server/server-row.tsx4
-rw-r--r--packages/app/src/components/session/session-header.tsx54
-rw-r--r--packages/app/src/components/session/session-sortable-terminal-tab.tsx7
-rw-r--r--packages/app/src/components/settings-keybinds.tsx2
-rw-r--r--packages/app/src/components/terminal.tsx2
8 files changed, 76 insertions, 69 deletions
diff --git a/packages/app/src/components/debug-bar.tsx b/packages/app/src/components/debug-bar.tsx
index 6fde71f3b..cbb24f77b 100644
--- a/packages/app/src/components/debug-bar.tsx
+++ b/packages/app/src/components/debug-bar.tsx
@@ -2,6 +2,7 @@ import { useIsRouting, useLocation } from "@solidjs/router"
import { batch, createEffect, onCleanup, onMount } from "solid-js"
import { createStore } from "solid-js/store"
import { Tooltip } from "@opencode-ai/ui/tooltip"
+import { useLanguage } from "@/context/language"
type Mem = Performance & {
memory?: {
@@ -27,17 +28,17 @@ type Obs = PerformanceObserverInit & {
const span = 5000
const ms = (n?: number, d = 0) => {
- if (n === undefined || Number.isNaN(n)) return "n/a"
+ if (n === undefined || Number.isNaN(n)) return
return `${n.toFixed(d)}ms`
}
const time = (n?: number) => {
- if (n === undefined || Number.isNaN(n)) return "n/a"
+ if (n === undefined || Number.isNaN(n)) return
return `${Math.round(n)}`
}
const mb = (n?: number) => {
- if (n === undefined || Number.isNaN(n)) return "n/a"
+ if (n === undefined || Number.isNaN(n)) return
const v = n / 1024 / 1024
return `${v >= 1024 ? v.toFixed(0) : v.toFixed(1)}MB`
}
@@ -74,6 +75,7 @@ function Cell(props: { bad?: boolean; dim?: boolean; label: string; tip: string;
}
export function DebugBar() {
+ const language = useLanguage()
const location = useLocation()
const routing = useIsRouting()
const [state, setState] = createStore({
@@ -98,14 +100,15 @@ export function DebugBar() {
},
})
+ const na = () => language.t("debugBar.na")
const heap = () => (state.heap.limit ? (state.heap.used ?? 0) / state.heap.limit : undefined)
const heapv = () => {
const value = heap()
- if (value === undefined) return "n/a"
+ if (value === undefined) return na()
return `${Math.round(value * 100)}%`
}
- const longv = () => (state.long.count === undefined ? "n/a" : `${time(state.long.block)}/${state.long.count}`)
- const navv = () => (state.nav.pending ? "..." : time(state.nav.dur))
+ const longv = () => (state.long.count === undefined ? na() : `${time(state.long.block) ?? na()}/${state.long.count}`)
+ const navv = () => (state.nav.pending ? "..." : (time(state.nav.dur) ?? na()))
let prev = ""
let start = 0
@@ -359,7 +362,7 @@ export function DebugBar() {
return (
<aside
- aria-label="Development performance diagnostics"
+ aria-label={language.t("debugBar.ariaLabel")}
class="pointer-events-auto fixed bottom-3 right-3 z-50 w-[308px] max-w-[calc(100vw-1.5rem)] overflow-hidden rounded-xl border p-0.5 text-text-on-interactive-base shadow-[var(--shadow-lg-border-base)] sm:bottom-4 sm:right-4 sm:w-[324px]"
style={{
"background-color": "color-mix(in srgb, var(--icon-interactive-base) 42%, black)",
@@ -368,67 +371,70 @@ export function DebugBar() {
>
<div class="grid grid-cols-5 gap-px font-mono">
<Cell
- label="NAV"
- tip="Last completed route transition touching a session page, measured from router start until the first paint after it settles."
+ label={language.t("debugBar.nav.label")}
+ tip={language.t("debugBar.nav.tip")}
value={navv()}
bad={bad(state.nav.dur, 400)}
dim={state.nav.dur === undefined && !state.nav.pending}
/>
<Cell
- label="FPS"
- tip="Rolling frames per second over the last 5 seconds."
- value={state.fps === undefined ? "n/a" : `${Math.round(state.fps)}`}
+ label={language.t("debugBar.fps.label")}
+ tip={language.t("debugBar.fps.tip")}
+ value={state.fps === undefined ? na() : `${Math.round(state.fps)}`}
bad={bad(state.fps, 50, true)}
dim={state.fps === undefined}
/>
<Cell
- label="FRAME"
- tip="Worst frame time over the last 5 seconds."
- value={time(state.gap)}
+ label={language.t("debugBar.frame.label")}
+ tip={language.t("debugBar.frame.tip")}
+ value={time(state.gap) ?? na()}
bad={bad(state.gap, 50)}
dim={state.gap === undefined}
/>
<Cell
- label="JANK"
- tip="Frames over 32ms in the last 5 seconds."
- value={state.jank === undefined ? "n/a" : `${state.jank}`}
+ label={language.t("debugBar.jank.label")}
+ tip={language.t("debugBar.jank.tip")}
+ value={state.jank === undefined ? na() : `${state.jank}`}
bad={bad(state.jank, 8)}
dim={state.jank === undefined}
/>
<Cell
- label="LONG"
- tip={`Blocked time and long-task count in the last 5 seconds. Max task: ${ms(state.long.max)}.`}
+ label={language.t("debugBar.long.label")}
+ tip={language.t("debugBar.long.tip", { max: ms(state.long.max) ?? na() })}
value={longv()}
bad={bad(state.long.block, 200)}
dim={state.long.count === undefined}
/>
<Cell
- label="DELAY"
- tip="Worst observed input delay in the last 5 seconds."
- value={time(state.delay)}
+ label={language.t("debugBar.delay.label")}
+ tip={language.t("debugBar.delay.tip")}
+ value={time(state.delay) ?? na()}
bad={bad(state.delay, 100)}
dim={state.delay === undefined}
/>
<Cell
- label="INP"
- tip="Approximate interaction duration over the last 5 seconds. This is INP-like, not the official Web Vitals INP."
- value={time(state.inp)}
+ label={language.t("debugBar.inp.label")}
+ tip={language.t("debugBar.inp.tip")}
+ value={time(state.inp) ?? na()}
bad={bad(state.inp, 200)}
dim={state.inp === undefined}
/>
<Cell
- label="CLS"
- tip="Cumulative layout shift for the current app lifetime."
- value={state.cls === undefined ? "n/a" : state.cls.toFixed(2)}
+ label={language.t("debugBar.cls.label")}
+ tip={language.t("debugBar.cls.tip")}
+ value={state.cls === undefined ? na() : state.cls.toFixed(2)}
bad={bad(state.cls, 0.1)}
dim={state.cls === undefined}
/>
<Cell
- label="MEM"
+ label={language.t("debugBar.mem.label")}
tip={
state.heap.used === undefined
- ? "Used JS heap vs heap limit. Chromium only."
- : `Used JS heap vs heap limit. ${mb(state.heap.used)} of ${mb(state.heap.limit)}.`
+ ? language.t("debugBar.mem.tipUnavailable")
+ : language.t("debugBar.mem.tip", {
+ used: mb(state.heap.used) ?? na(),
+ limit: mb(state.heap.limit) ?? na(),
+ })
}
value={heapv()}
bad={bad(heap(), 0.8)}
diff --git a/packages/app/src/components/dialog-select-file.tsx b/packages/app/src/components/dialog-select-file.tsx
index 3d4dbecbd..e21be77fb 100644
--- a/packages/app/src/components/dialog-select-file.tsx
+++ b/packages/app/src/components/dialog-select-file.tsx
@@ -426,7 +426,7 @@ export function DialogSelectFile(props: { mode?: DialogSelectFileMode; onOpenFil
</Show>
</div>
<Show when={item.keybind}>
- <Keybind class="rounded-[4px]">{formatKeybind(item.keybind ?? "")}</Keybind>
+ <Keybind class="rounded-[4px]">{formatKeybind(item.keybind ?? "", language.t)}</Keybind>
</Show>
</div>
</Match>
diff --git a/packages/app/src/components/dialog-select-server.tsx b/packages/app/src/components/dialog-select-server.tsx
index 655aba0b0..eb039c14d 100644
--- a/packages/app/src/components/dialog-select-server.tsx
+++ b/packages/app/src/components/dialog-select-server.tsx
@@ -149,7 +149,7 @@ function ServerForm(props: ServerFormProps) {
<TextField
type="text"
label={language.t("dialog.server.add.username")}
- placeholder="username"
+ placeholder={language.t("dialog.server.add.usernamePlaceholder")}
value={props.username}
disabled={props.busy}
onChange={props.onUsernameChange}
@@ -158,7 +158,7 @@ function ServerForm(props: ServerFormProps) {
<TextField
type="password"
label={language.t("dialog.server.add.password")}
- placeholder="password"
+ placeholder={language.t("dialog.server.add.passwordPlaceholder")}
value={props.password}
disabled={props.busy}
onChange={props.onPasswordChange}
diff --git a/packages/app/src/components/server/server-row.tsx b/packages/app/src/components/server/server-row.tsx
index 8a4b7be4d..63a40bac2 100644
--- a/packages/app/src/components/server/server-row.tsx
+++ b/packages/app/src/components/server/server-row.tsx
@@ -10,6 +10,7 @@ import {
type ParentProps,
Show,
} from "solid-js"
+import { useLanguage } from "@/context/language"
import { type ServerConnection, serverName } from "@/context/server"
import type { ServerHealth } from "@/utils/server-health"
@@ -25,6 +26,7 @@ interface ServerRowProps extends ParentProps {
}
export function ServerRow(props: ServerRowProps) {
+ const language = useLanguage()
const [truncated, setTruncated] = createSignal(false)
let nameRef: HTMLSpanElement | undefined
let versionRef: HTMLSpanElement | undefined
@@ -100,7 +102,7 @@ export function ServerRow(props: ServerRowProps) {
{conn().http.username ? (
<span class="text-text-weak">{conn().http.username}</span>
) : (
- <span class="text-text-weaker">no username</span>
+ <span class="text-text-weaker">{language.t("server.row.noUsername")}</span>
)}
</span>
{conn().http.password && <span class="text-text-weak">••••••••</span>}
diff --git a/packages/app/src/components/session/session-header.tsx b/packages/app/src/components/session/session-header.tsx
index 8cb704bf1..ae9d2800e 100644
--- a/packages/app/src/components/session/session-header.tsx
+++ b/packages/app/src/components/session/session-header.tsx
@@ -46,63 +46,63 @@ type OS = "macos" | "windows" | "linux" | "unknown"
const MAC_APPS = [
{
id: "vscode",
- label: "VS Code",
+ label: "session.header.open.app.vscode",
icon: "vscode",
openWith: "Visual Studio Code",
},
- { id: "cursor", label: "Cursor", icon: "cursor", openWith: "Cursor" },
- { id: "zed", label: "Zed", icon: "zed", openWith: "Zed" },
- { id: "textmate", label: "TextMate", icon: "textmate", openWith: "TextMate" },
+ { id: "cursor", label: "session.header.open.app.cursor", icon: "cursor", openWith: "Cursor" },
+ { id: "zed", label: "session.header.open.app.zed", icon: "zed", openWith: "Zed" },
+ { id: "textmate", label: "session.header.open.app.textmate", icon: "textmate", openWith: "TextMate" },
{
id: "antigravity",
- label: "Antigravity",
+ label: "session.header.open.app.antigravity",
icon: "antigravity",
openWith: "Antigravity",
},
- { id: "terminal", label: "Terminal", icon: "terminal", openWith: "Terminal" },
- { id: "iterm2", label: "iTerm2", icon: "iterm2", openWith: "iTerm" },
- { id: "ghostty", label: "Ghostty", icon: "ghostty", openWith: "Ghostty" },
- { id: "warp", label: "Warp", icon: "warp", openWith: "Warp" },
- { id: "xcode", label: "Xcode", icon: "xcode", openWith: "Xcode" },
+ { id: "terminal", label: "session.header.open.app.terminal", icon: "terminal", openWith: "Terminal" },
+ { id: "iterm2", label: "session.header.open.app.iterm2", icon: "iterm2", openWith: "iTerm" },
+ { id: "ghostty", label: "session.header.open.app.ghostty", icon: "ghostty", openWith: "Ghostty" },
+ { id: "warp", label: "session.header.open.app.warp", icon: "warp", openWith: "Warp" },
+ { id: "xcode", label: "session.header.open.app.xcode", icon: "xcode", openWith: "Xcode" },
{
id: "android-studio",
- label: "Android Studio",
+ label: "session.header.open.app.androidStudio",
icon: "android-studio",
openWith: "Android Studio",
},
{
id: "sublime-text",
- label: "Sublime Text",
+ label: "session.header.open.app.sublimeText",
icon: "sublime-text",
openWith: "Sublime Text",
},
] as const
const WINDOWS_APPS = [
- { id: "vscode", label: "VS Code", icon: "vscode", openWith: "code" },
- { id: "cursor", label: "Cursor", icon: "cursor", openWith: "cursor" },
- { id: "zed", label: "Zed", icon: "zed", openWith: "zed" },
+ { id: "vscode", label: "session.header.open.app.vscode", icon: "vscode", openWith: "code" },
+ { id: "cursor", label: "session.header.open.app.cursor", icon: "cursor", openWith: "cursor" },
+ { id: "zed", label: "session.header.open.app.zed", icon: "zed", openWith: "zed" },
{
id: "powershell",
- label: "PowerShell",
+ label: "session.header.open.app.powershell",
icon: "powershell",
openWith: "powershell",
},
{
id: "sublime-text",
- label: "Sublime Text",
+ label: "session.header.open.app.sublimeText",
icon: "sublime-text",
openWith: "Sublime Text",
},
] as const
const LINUX_APPS = [
- { id: "vscode", label: "VS Code", icon: "vscode", openWith: "code" },
- { id: "cursor", label: "Cursor", icon: "cursor", openWith: "cursor" },
- { id: "zed", label: "Zed", icon: "zed", openWith: "zed" },
+ { id: "vscode", label: "session.header.open.app.vscode", icon: "vscode", openWith: "code" },
+ { id: "cursor", label: "session.header.open.app.cursor", icon: "cursor", openWith: "cursor" },
+ { id: "zed", label: "session.header.open.app.zed", icon: "zed", openWith: "zed" },
{
id: "sublime-text",
- label: "Sublime Text",
+ label: "session.header.open.app.sublimeText",
icon: "sublime-text",
openWith: "Sublime Text",
},
@@ -160,9 +160,9 @@ export function SessionHeader() {
})
const fileManager = createMemo(() => {
- if (os() === "macos") return { label: "Finder", icon: "finder" as const }
- if (os() === "windows") return { label: "File Explorer", icon: "file-explorer" as const }
- return { label: "File Manager", icon: "finder" as const }
+ if (os() === "macos") return { label: "session.header.open.finder", icon: "finder" as const }
+ if (os() === "windows") return { label: "session.header.open.fileExplorer", icon: "file-explorer" as const }
+ return { label: "session.header.open.fileManager", icon: "finder" as const }
})
createEffect(() => {
@@ -187,8 +187,10 @@ export function SessionHeader() {
const options = createMemo(() => {
return [
- { id: "finder", label: fileManager().label, icon: fileManager().icon },
- ...apps().filter((app) => exists[app.id]),
+ { id: "finder", label: language.t(fileManager().label), icon: fileManager().icon },
+ ...apps()
+ .filter((app) => exists[app.id])
+ .map((app) => ({ ...app, label: language.t(app.label) })),
] as const
})
diff --git a/packages/app/src/components/session/session-sortable-terminal-tab.tsx b/packages/app/src/components/session/session-sortable-terminal-tab.tsx
index 4f49911c1..898958742 100644
--- a/packages/app/src/components/session/session-sortable-terminal-tab.tsx
+++ b/packages/app/src/components/session/session-sortable-terminal-tab.tsx
@@ -6,6 +6,7 @@ import { IconButton } from "@opencode-ai/ui/icon-button"
import { Tabs } from "@opencode-ai/ui/tabs"
import { DropdownMenu } from "@opencode-ai/ui/dropdown-menu"
import { Icon } from "@opencode-ai/ui/icon"
+import { isDefaultTitle as isDefaultTerminalTitle } from "@/context/terminal-title"
import { useTerminal, type LocalPTY } from "@/context/terminal"
import { useLanguage } from "@/context/language"
import { focusTerminalById } from "@/pages/session/helpers"
@@ -27,11 +28,7 @@ export function SortableTerminalTab(props: { terminal: LocalPTY; onClose?: () =>
const isDefaultTitle = () => {
const number = props.terminal.titleNumber
if (!Number.isFinite(number) || number <= 0) return false
- const match = props.terminal.title.match(/^Terminal (\d+)$/)
- if (!match) return false
- const parsed = Number(match[1])
- if (!Number.isFinite(parsed) || parsed <= 0) return false
- return parsed === number
+ return isDefaultTerminalTitle(props.terminal.title, number)
}
const label = () => {
diff --git a/packages/app/src/components/settings-keybinds.tsx b/packages/app/src/components/settings-keybinds.tsx
index 1e4244789..7e2a48110 100644
--- a/packages/app/src/components/settings-keybinds.tsx
+++ b/packages/app/src/components/settings-keybinds.tsx
@@ -239,7 +239,7 @@ function useKeyCapture(input: {
showToast({
title: input.language.t("settings.shortcuts.conflict.title"),
description: input.language.t("settings.shortcuts.conflict.description", {
- keybind: formatKeybind(next),
+ keybind: formatKeybind(next, input.language.t),
titles: [...conflicts.values()].join(", "),
}),
})
diff --git a/packages/app/src/components/terminal.tsx b/packages/app/src/components/terminal.tsx
index 840903293..ff455ebe2 100644
--- a/packages/app/src/components/terminal.tsx
+++ b/packages/app/src/components/terminal.tsx
@@ -519,7 +519,7 @@ export const Terminal = (props: TerminalProps) => {
if (event.code !== 1000) {
if (once.value) return
once.value = true
- local.onConnectError?.(new Error(`WebSocket closed abnormally: ${event.code}`))
+ local.onConnectError?.(new Error(language.t("terminal.connectionLost.abnormalClose", { code: event.code })))
}
}
socket.addEventListener("close", handleClose)