mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-31 04:12:10 +03:00
merge(fix3): wire useTranslations in Traffic Inspector components (Group A)
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user