fix(i18n): Wave 3 完成 - thinking-terminal + 翻譯補充

- thinking-terminal.tsx: 所有 hardcode 改用 useTranslations
  - DependencyPathVisualizer: blastRadius/rootCauseChain
  - ServiceChainVisualizer: upstreamImpact/downstreamDependencies
  - FinOpsVisualizer: finopsAnalysis/wastedPerMonth/realizable/freed
  - ThinkingTerminal: title/executing/initiate/waiting/stream/events
- live-host-card.tsx: 移除未使用的 baselineLabel 預設值
- zh-TW.json/en.json: 新增 terminal 區塊翻譯

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
OG T
2026-03-29 16:34:03 +08:00
parent 9747bd43a2
commit e9bed212de
4 changed files with 51 additions and 24 deletions

View File

@@ -163,15 +163,25 @@
"terminal": {
"title": "AWOOOI Terminal",
"version": "Version",
"waiting": "Waiting for command...",
"waiting": "> Waiting for command...",
"initiate": "INITIATE SYNC",
"executing": "EXECUTING...",
"events": "events",
"stream": "STREAM",
"executing": ">_ EXECUTING...",
"events": "{count} events",
"stream": "STREAM: /agent/thinking",
"waitingForData": "Waiting for decision chain data...",
"steps": "Steps",
"streaming": "Streaming",
"paused": "Paused"
"paused": "Paused",
"blastRadius": "[ BLAST RADIUS ]",
"rootCauseChain": "[ ROOT CAUSE CHAIN ]",
"upstreamImpact": "[ UPSTREAM IMPACT ]",
"downstreamDependencies": "[ DOWNSTREAM DEPENDENCIES ]",
"dependsOn": "depends on",
"calls": "calls",
"finopsAnalysis": "[ FINOPS ANALYSIS ]",
"wastedPerMonth": "Wasted/mo",
"realizable": "Realizable",
"freed": "Freed"
},
"omniTerminal": {
"title": "OMNI-TERMINAL",

View File

@@ -163,15 +163,25 @@
"terminal": {
"title": "AWOOOI 終端機",
"version": "版本",
"waiting": "等待指令...",
"waiting": "> 等待指令...",
"initiate": "啟動同步",
"executing": "執行中...",
"events": "事件",
"stream": "串流",
"executing": ">_ 執行中...",
"events": "{count} 事件",
"stream": "串流: /agent/thinking",
"waitingForData": "等待決策鏈資料...",
"steps": "步驟",
"streaming": "串流中",
"paused": "已暫停"
"paused": "已暫停",
"blastRadius": "[ 爆炸半徑 ]",
"rootCauseChain": "[ 根因分析鏈 ]",
"upstreamImpact": "[ 上游影響 ]",
"downstreamDependencies": "[ 下游依賴 ]",
"dependsOn": "依賴",
"calls": "呼叫",
"finopsAnalysis": "[ FINOPS 分析 ]",
"wastedPerMonth": "每月浪費",
"realizable": "可實現",
"freed": "已釋放"
},
"omniTerminal": {
"title": "OMNI-TERMINAL",

View File

@@ -9,6 +9,7 @@
*/
import { useEffect, useMemo } from 'react'
import { useTranslations } from 'next-intl'
import { cn } from '@/lib/utils'
import {
useAgentStore,
@@ -50,12 +51,13 @@ function DependencyPathVisualizer({
paths: string[]
direction: 'upstream' | 'downstream'
}) {
const t = useTranslations('terminal')
if (paths.length === 0) return null
return (
<div className="mt-2 p-3 bg-nothing-gray-800 rounded border border-nothing-gray-700">
<div className="text-xs text-nothing-gray-500 mb-2">
{direction === 'upstream' ? '[ BLAST RADIUS ]' : '[ ROOT CAUSE CHAIN ]'}
{direction === 'upstream' ? t('blastRadius') : t('rootCauseChain')}
</div>
<div className="font-mono text-sm space-y-1">
{paths.map((path, i) => (
@@ -84,13 +86,14 @@ function ServiceChainVisualizer({
target: string
type: 'blast_radius' | 'root_cause'
}) {
const t = useTranslations('terminal')
// 建構 ASCII 風格的依賴圖
const isBlastRadius = type === 'blast_radius'
return (
<div className="mt-2 p-3 bg-nothing-gray-800 rounded border border-nothing-gray-700">
<div className="text-xs text-nothing-gray-500 mb-2">
{isBlastRadius ? '[ UPSTREAM IMPACT ]' : '[ DOWNSTREAM DEPENDENCIES ]'}
{isBlastRadius ? t('upstreamImpact') : t('downstreamDependencies')}
</div>
{/* ASCII Art 風格圖形 */}
@@ -103,7 +106,7 @@ function ServiceChainVisualizer({
{' │ '}{services.slice(0, 3).join(', ').padEnd(19)}{'│'}
</div>
<div className="text-nothing-gray-600">{' └─────────┬───────────┘'}</div>
<div className="text-nothing-gray-600">{'depends on'}</div>
<div className="text-nothing-gray-600">{`${t('dependsOn')}`}</div>
<div className="text-nothing-gray-600">{' ▼'}</div>
<div className="text-nothing-gray-600">{' ┌─────────────────────┐'}</div>
<div className="text-status-critical">
@@ -119,7 +122,7 @@ function ServiceChainVisualizer({
{' │ '}{target.padEnd(19)}{'│ '}<span className="text-status-warning">!</span>
</div>
<div className="text-nothing-gray-600">{' └─────────┬───────────┘'}</div>
<div className="text-nothing-gray-600">{'calls'}</div>
<div className="text-nothing-gray-600">{`${t('calls')}`}</div>
<div className="text-nothing-gray-600">{' ▼'}</div>
<div className="text-nothing-gray-600">{' ┌─────────────────────┐'}</div>
<div className="text-status-critical">
@@ -157,9 +160,10 @@ function ServiceChainVisualizer({
// ==================== FinOps 視覺化 ====================
function FinOpsVisualizer({ data }: { data: NonNullable<ThinkingStep['finopsData']> }) {
const t = useTranslations('terminal')
return (
<div className="mt-2 p-3 bg-nothing-gray-800 rounded border border-nothing-gray-700">
<div className="text-xs text-nothing-gray-500 mb-2">[ FINOPS ANALYSIS ]</div>
<div className="text-xs text-nothing-gray-500 mb-2">{t('finopsAnalysis')}</div>
{/* 成本摘要 */}
<div className="grid grid-cols-3 gap-2 mb-3">
@@ -167,19 +171,19 @@ function FinOpsVisualizer({ data }: { data: NonNullable<ThinkingStep['finopsData
<div className="text-lg font-bold text-status-critical">
${data.totalWastedUsd.toFixed(0)}
</div>
<div className="text-xs text-nothing-gray-500">Wasted/mo</div>
<div className="text-xs text-nothing-gray-500">{t('wastedPerMonth')}</div>
</div>
<div className="text-center p-2 bg-nothing-gray-900 rounded">
<div className="text-lg font-bold text-status-healthy">
${data.realizableSavingsUsd.toFixed(0)}
</div>
<div className="text-xs text-nothing-gray-500">Realizable</div>
<div className="text-xs text-nothing-gray-500">{t('realizable')}</div>
</div>
<div className="text-center p-2 bg-nothing-gray-900 rounded">
<div className="text-lg font-bold text-status-warning">
${data.freedResourcesUsd.toFixed(0)}
</div>
<div className="text-xs text-nothing-gray-500">Freed</div>
<div className="text-xs text-nothing-gray-500">{t('freed')}</div>
</div>
</div>
@@ -305,6 +309,7 @@ export function ThinkingTerminal({
className,
maxHeight = '300px',
}: ThinkingTerminalProps) {
const t = useTranslations('terminal')
const thinkingStream = useAgentStore(selectThinkingStream)
const status = useAgentStore(selectAgentStatus)
const error = useAgentStore(selectError)
@@ -331,7 +336,7 @@ export function ThinkingTerminal({
<div className="w-3 h-3 rounded-full bg-status-thinking" />
<div className="w-3 h-3 rounded-full bg-status-healthy" />
</div>
<h2 className="font-display text-display-sm">AWOOOI Terminal</h2>
<h2 className="font-display text-display-sm">{t('title')}</h2>
</div>
<span className="font-mono text-xs text-nothing-gray-500">
v0.1.0 | SSE
@@ -349,7 +354,7 @@ export function ThinkingTerminal({
: 'bg-nothing-white text-nothing-black border-transparent hover:bg-nothing-gray-200'
)}
>
{isStreaming ? '>_ EXECUTING...' : 'INITIATE SYNC'}
{isStreaming ? t('executing') : t('initiate')}
</button>
{/* Terminal Output */}
@@ -359,7 +364,7 @@ export function ThinkingTerminal({
>
{thinkingStream.length === 0 && !isStreaming && !error && (
<div className="text-nothing-gray-600">
{'>'} Waiting for command...
{t('waiting')}
</div>
)}
@@ -379,10 +384,10 @@ export function ThinkingTerminal({
{/* Footer */}
<div className="flex items-center justify-between pt-2 border-t border-nothing-gray-800">
<p className="font-mono text-xs text-nothing-gray-600">
STREAM: /agent/thinking
{t('stream')}
</p>
<p className="font-mono text-xs text-nothing-gray-600">
{thinkingStream.length} events
{t('events', { count: thinkingStream.length })}
</p>
</div>
</div>

View File

@@ -324,7 +324,9 @@ function MetricBar({
criticalThreshold = 80,
collectingLabel = '-',
baselineValue,
baselineLabel = '基準線',
// Note: baselineLabel prop kept for future use but not used currently
// eslint-disable-next-line @typescript-eslint/no-unused-vars
baselineLabel,
}: MetricBarProps) {
// Handle null/undefined values - show collecting state
if (value === null || value === undefined) {