merge(fix3): wire useTranslations in Traffic Inspector components (Group A)

This commit is contained in:
diegosouzapw
2026-05-28 11:40:29 -03:00
7 changed files with 70 additions and 46 deletions

View File

@@ -1,6 +1,7 @@
"use client";
import { useState } from "react";
import { useTranslations } from "next-intl";
import { cn } from "@/shared/utils/cn";
import { CustomHostsManager } from "./CustomHostsManager";
import { HttpProxySnippetCard } from "./HttpProxySnippetCard";
@@ -17,6 +18,7 @@ interface CaptureModesToolbarProps {
}
export function CaptureModesToolbar({ customHostCount }: CaptureModesToolbarProps) {
const t = useTranslations("trafficInspector");
const [modes, setModes] = useState<CaptureModeState>({
agentBridge: true,
customHosts: false,
@@ -39,18 +41,18 @@ export function CaptureModesToolbar({ customHostCount }: CaptureModesToolbarProp
warn?: boolean;
extra?: React.ReactNode;
}> = [
{ key: "agentBridge", label: "AgentBridge", alwaysOn: true },
{ key: "agentBridge", label: t("agentBridgeMode"), alwaysOn: true },
{
key: "customHosts",
label: `Custom hosts (${customHostCount})`,
label: `${t("customHostsMode")} (${customHostCount})`,
},
{
key: "httpProxy",
label: `HTTP_PROXY :${proxyPort}`,
label: `${t("httpProxyMode")} :${proxyPort}`,
},
{
key: "systemWide",
label: "System-wide",
label: t("systemWideMode"),
warn: true,
},
];
@@ -94,14 +96,14 @@ export function CaptureModesToolbar({ customHostCount }: CaptureModesToolbarProp
onClick={() => setShowHosts(true)}
className="text-xs text-text-muted hover:text-text-main focus-ring rounded"
>
Manage hosts
{t("manageHosts")}
</button>
<button
type="button"
onClick={() => setShowProxy(true)}
className="text-xs text-text-muted hover:text-text-main focus-ring rounded"
>
Copy proxy snippet
{t("copySnippet")}
</button>
</div>
</div>

View File

@@ -1,6 +1,7 @@
"use client";
import { useEffect, useState } from "react";
import { useTranslations } from "next-intl";
import { z } from "zod";
interface CustomHost {
@@ -15,6 +16,7 @@ interface CustomHostsManagerProps {
}
export function CustomHostsManager({ onClose }: CustomHostsManagerProps) {
const t = useTranslations("trafficInspector");
const [hosts, setHosts] = useState<CustomHost[]>([]);
const [input, setInput] = useState("");
const [error, setError] = useState<string | null>(null);
@@ -80,7 +82,7 @@ export function CustomHostsManager({ onClose }: CustomHostsManagerProps) {
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50">
<div className="w-full max-w-md rounded-xl border border-border bg-surface shadow-xl p-5">
<div className="flex items-center justify-between mb-4">
<h2 className="text-base font-semibold text-text-main">Custom Hosts</h2>
<h2 className="text-base font-semibold text-text-main">{t("customHostsTitle")}</h2>
<button
type="button"
onClick={onClose}
@@ -97,7 +99,7 @@ export function CustomHostsManager({ onClose }: CustomHostsManagerProps) {
value={input}
onChange={(e) => setInput(e.target.value)}
onKeyDown={(e) => e.key === "Enter" && addHost()}
placeholder="api.openai.com"
placeholder={t("hostPlaceholder")}
className="flex-1 rounded border border-border bg-bg-subtle px-3 py-1.5 text-sm text-text-main focus:outline-none focus:ring-1 focus:ring-blue-500"
/>
<button
@@ -105,15 +107,15 @@ export function CustomHostsManager({ onClose }: CustomHostsManagerProps) {
onClick={addHost}
className="rounded border border-border bg-blue-600 px-3 py-1.5 text-sm text-white hover:bg-blue-700 focus-ring"
>
Add
{t("addHost")}
</button>
</div>
{error && <p className="text-xs text-red-400 mb-2">{error}</p>}
<div className="space-y-1 max-h-60 overflow-y-auto">
{loading && <p className="text-sm text-text-muted">Loading</p>}
{loading && <p className="text-sm text-text-muted">{t("loading")}</p>}
{!loading && hosts.length === 0 && (
<p className="text-sm text-text-muted italic">No custom hosts added yet.</p>
<p className="text-sm text-text-muted italic">{t("noHostsYet")}</p>
)}
{hosts.map((h) => (
<div

View File

@@ -1,6 +1,7 @@
"use client";
import { useState } from "react";
import { useTranslations } from "next-intl";
import { cn } from "@/shared/utils/cn";
interface HttpProxySnippetCardProps {
@@ -11,6 +12,7 @@ interface HttpProxySnippetCardProps {
type Lang = "bash" | "python" | "node";
export function HttpProxySnippetCard({ port, onClose }: HttpProxySnippetCardProps) {
const t = useTranslations("trafficInspector");
const [lang, setLang] = useState<Lang>("bash");
const [copied, setCopied] = useState(false);
@@ -31,7 +33,7 @@ export function HttpProxySnippetCard({ port, onClose }: HttpProxySnippetCardProp
<div className="w-full max-w-lg rounded-xl border border-border bg-surface shadow-xl p-5">
<div className="flex items-center justify-between mb-4">
<h2 className="text-base font-semibold text-text-main">
HTTP Proxy Snippet port {port}
{t("httpProxyTitle", { port })}
</h2>
<button
type="button"
@@ -74,7 +76,7 @@ export function HttpProxySnippetCard({ port, onClose }: HttpProxySnippetCardProp
<span className="material-symbols-outlined text-[14px]" aria-hidden="true">
{copied ? "check" : "content_copy"}
</span>
{copied ? "Copied!" : "Copy"}
{copied ? t("copied") : t("copy")}
</button>
</div>
</div>

View File

@@ -1,5 +1,6 @@
"use client";
import { useTranslations } from "next-intl";
import type { ListFilters } from "@/mitm/inspector/types";
import type { AgentId } from "@/mitm/types";
import { cn } from "@/shared/utils/cn";
@@ -9,6 +10,9 @@ import type { SessionInfo } from "../hooks/useSessionRecorder";
type Profile = "llm" | "custom" | "all";
// PROFILES labels are resolved inside the component via useTranslations
const PROFILE_IDS: Profile[] = ["llm", "custom", "all"];
interface TopBarControlsProps {
filters: ListFilters;
onProfileChange: (p: Profile) => void;
@@ -34,11 +38,6 @@ interface TopBarControlsProps {
onSessionDelete: (id: string) => void;
}
const PROFILES: Array<{ id: Profile; label: string }> = [
{ id: "llm", label: "LLM only" },
{ id: "custom", label: "Custom" },
{ id: "all", label: "All" },
];
export function TopBarControls({
filters,
@@ -63,8 +62,15 @@ export function TopBarControls({
onSessionSelect,
onSessionDelete,
}: TopBarControlsProps) {
const t = useTranslations("trafficInspector");
const profile: Profile = (filters.profile as Profile) ?? "llm";
const profileLabels: Record<Profile, string> = {
llm: t("profileLlmOnly"),
custom: t("profileCustom"),
all: t("profileAll"),
};
return (
<div className="flex flex-wrap items-center gap-2 border-b border-border bg-bg-subtle px-3 py-2">
{/* Profile selector */}
@@ -73,21 +79,21 @@ export function TopBarControls({
aria-label="Traffic profile"
className="flex items-center gap-1 rounded border border-border bg-surface p-0.5"
>
{PROFILES.map((p) => (
{PROFILE_IDS.map((id) => (
<button
key={p.id}
key={id}
type="button"
role="radio"
aria-checked={profile === p.id}
onClick={() => onProfileChange(p.id)}
aria-checked={profile === id}
onClick={() => onProfileChange(id)}
className={cn(
"px-2 py-0.5 text-xs rounded focus-ring",
profile === p.id
profile === id
? "bg-blue-600 text-white"
: "text-text-muted hover:text-text-main"
)}
>
{p.label}
{profileLabels[id]}
</button>
))}
</div>
@@ -95,7 +101,7 @@ export function TopBarControls({
{/* Host filter */}
<input
type="text"
placeholder="Filter host"
placeholder={t("filterHost")}
defaultValue={filters.host ?? ""}
onChange={(e) => onHostChange(e.target.value || undefined)}
className="rounded border border-border bg-bg-subtle px-2 py-1 text-xs text-text-main w-32 focus:outline-none focus:ring-1 focus:ring-blue-500"
@@ -109,7 +115,7 @@ export function TopBarControls({
}
className="rounded border border-border bg-bg-subtle px-2 py-1 text-xs text-text-main focus:outline-none focus:ring-1 focus:ring-blue-500"
>
<option value="">Any status</option>
<option value="">{t("anyStatus")}</option>
<option value="2xx">2xx</option>
<option value="3xx">3xx</option>
<option value="4xx">4xx</option>
@@ -122,36 +128,36 @@ export function TopBarControls({
type="button"
onClick={paused ? onResume : onPause}
className="inline-flex items-center gap-1 rounded border border-border px-2 py-1 text-xs text-text-muted hover:text-text-main focus-ring"
title={paused ? "Resume streaming" : "Pause streaming"}
title={paused ? t("resumeBtn") : t("pauseBtn")}
>
<span className="material-symbols-outlined text-[14px]" aria-hidden="true">
{paused ? "play_arrow" : "pause"}
</span>
{paused ? "Resume" : "Pause"}
{paused ? t("resumeBtn") : t("pauseBtn")}
</button>
<button
type="button"
onClick={onClear}
className="inline-flex items-center gap-1 rounded border border-border px-2 py-1 text-xs text-text-muted hover:text-red-400 focus-ring"
title="Clear all requests"
title={t("clearBtn")}
>
<span className="material-symbols-outlined text-[14px]" aria-hidden="true">
delete_sweep
</span>
Clear
{t("clearBtn")}
</button>
<button
type="button"
onClick={onExport}
className="inline-flex items-center gap-1 rounded border border-border px-2 py-1 text-xs text-text-muted hover:text-text-main focus-ring"
title="Export as .har"
title={t("exportHar")}
>
<span className="material-symbols-outlined text-[14px]" aria-hidden="true">
download
</span>
.har
{t("exportHar")}
</button>
{/* Session controls */}
@@ -178,7 +184,7 @@ export function TopBarControls({
connected ? "bg-green-400 animate-pulse" : "bg-gray-500"
)}
/>
{connected ? "live" : "offline"}
{connected ? t("liveBadge") : t("offlineBadge")}
<span className="text-text-muted font-mono">
{total}/{maxSize}
</span>

View File

@@ -1,5 +1,6 @@
"use client";
import { useTranslations } from "next-intl";
import { cn } from "@/shared/utils/cn";
import type { SessionInfo } from "../../hooks/useSessionRecorder";
@@ -26,6 +27,7 @@ export function SessionRecorderBar({
onStart,
onStop,
}: SessionRecorderBarProps) {
const t = useTranslations("trafficInspector");
return (
<div
className={cn(
@@ -47,19 +49,19 @@ export function SessionRecorderBar({
onClick={onStop}
className="ml-auto rounded border border-red-500/50 px-2 py-0.5 text-xs hover:bg-red-800/30 focus-ring"
>
Stop
{t("stopSession")}
</button>
</>
) : (
<>
<span className="inline-block h-2 w-2 rounded-full bg-gray-500" />
<span className="text-xs">Not recording</span>
<span className="text-xs">{t("notRecording")}</span>
<button
type="button"
onClick={() => onStart()}
className="ml-auto rounded border border-border px-2 py-0.5 text-xs hover:bg-surface focus-ring"
>
REC
{t("recordSession")}
</button>
</>
)}

View File

@@ -877,8 +877,8 @@
"cliToolsSubtitle": "Configure CLI runtimes",
"agentsSubtitle": "Manage local agents",
"cloudAgentsSubtitle": "Manage cloud-based agents",
"agentBridge": "AgentBridge",
"agentBridgeSubtitle": "MITM interceptor for IDE agents",
"agentBridge": "Agent Bridge",
"agentBridgeSubtitle": "Intercept IDE agent traffic",
"apiEndpointsSubtitle": "Expose custom endpoints",
"proxySubtitle": "HTTP proxy settings",
"mitmProxySubtitle": "MITM interception",
@@ -933,8 +933,6 @@
"docsSubtitle": "Documentation",
"issuesSubtitle": "Report a bug",
"changelogSubtitle": "Release notes",
"agentBridge": "Agent Bridge",
"agentBridgeSubtitle": "Intercept IDE agent traffic",
"trafficInspector": "Traffic Inspector",
"trafficInspectorSubtitle": "Monitor LLM calls + debug any HTTPS traffic"
},
@@ -7452,6 +7450,13 @@
"llmStream": "Stream",
"llmMappedTo": "Mapped to",
"llmCostEstimate": "Cost estimate",
"systemProxyExitWarning": "System-wide proxy still active — leave page anyway?"
"systemProxyExitWarning": "System-wide proxy still active — leave page anyway?",
"customHostsTitle": "Custom Hosts",
"loading": "Loading…",
"copied": "Copied!",
"copy": "Copy",
"httpProxyTitle": "HTTP Proxy Snippet — port {port}",
"notRecording": "Not recording",
"anyStatus": "Any status"
}
}

View File

@@ -877,8 +877,8 @@
"cliToolsSubtitle": "Configurar runtimes CLI",
"agentsSubtitle": "Gerenciar agentes locais",
"cloudAgentsSubtitle": "Gerenciar agentes na nuvem",
"agentBridge": "AgentBridge",
"agentBridgeSubtitle": "Interceptador MITM para agentes de IDE",
"agentBridge": "Agent Bridge",
"agentBridgeSubtitle": "Interceptar tráfego de agentes IDE",
"apiEndpointsSubtitle": "Expor endpoints customizados",
"proxySubtitle": "Configurações do proxy HTTP",
"mitmProxySubtitle": "Interceptação MITM",
@@ -933,8 +933,6 @@
"docsSubtitle": "Documentação",
"issuesSubtitle": "Reportar um bug",
"changelogSubtitle": "Notas de versão",
"agentBridge": "Agent Bridge",
"agentBridgeSubtitle": "Interceptar tráfego de agentes IDE",
"trafficInspector": "Inspector de Tráfego",
"trafficInspectorSubtitle": "Monitorar chamadas LLM + debugar tráfego HTTPS"
},
@@ -7442,6 +7440,13 @@
"llmStream": "Stream",
"llmMappedTo": "Mapeado para",
"llmCostEstimate": "Estimativa de custo",
"systemProxyExitWarning": "Proxy do sistema ainda está ativo — sair mesmo assim?"
"systemProxyExitWarning": "Proxy do sistema ainda está ativo — sair mesmo assim?",
"customHostsTitle": "Hosts Personalizados",
"loading": "Carregando…",
"copied": "Copiado!",
"copy": "Copiar",
"httpProxyTitle": "Snippet de Proxy HTTP — porta {port}",
"notRecording": "Sem gravação",
"anyStatus": "Qualquer status"
}
}