[Init] Initial commit - NetMesh terminal manager
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
This commit is contained in:
152
components/ai/AgentActivityGroup.tsx
Normal file
152
components/ai/AgentActivityGroup.tsx
Normal file
@@ -0,0 +1,152 @@
|
||||
import {
|
||||
Activity,
|
||||
AlertTriangle,
|
||||
Check,
|
||||
CheckCircle2,
|
||||
ChevronDown,
|
||||
ChevronRight,
|
||||
Circle,
|
||||
FileDiff,
|
||||
Loader2,
|
||||
Search,
|
||||
} from 'lucide-react';
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import type { AgentActivity, AgentUsage } from '../../domain/agentActivity';
|
||||
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '../ui/collapsible';
|
||||
|
||||
interface AgentActivityGroupProps {
|
||||
activities?: AgentActivity[];
|
||||
usage?: AgentUsage;
|
||||
isStreaming?: boolean;
|
||||
t: (key: string) => string;
|
||||
}
|
||||
|
||||
function statusLabel(status: 'running' | 'completed' | 'failed', t: AgentActivityGroupProps['t']): string {
|
||||
return t(`ai.chat.activity.status.${status}`);
|
||||
}
|
||||
|
||||
function formatTokens(value: number | undefined): string {
|
||||
return new Intl.NumberFormat().format(Math.max(0, value ?? 0));
|
||||
}
|
||||
|
||||
const AgentActivityGroup: React.FC<AgentActivityGroupProps> = ({
|
||||
activities = [],
|
||||
usage,
|
||||
isStreaming = false,
|
||||
t,
|
||||
}) => {
|
||||
const [open, setOpen] = useState(isStreaming);
|
||||
|
||||
useEffect(() => {
|
||||
setOpen(isStreaming);
|
||||
}, [isStreaming]);
|
||||
|
||||
const visibleActivities = useMemo(
|
||||
() => activities.filter((activity) => activity.type !== 'web_search' || activity.query),
|
||||
[activities],
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="my-1.5 space-y-1.5 text-xs">
|
||||
{visibleActivities.length > 0 && (
|
||||
<Collapsible open={open} onOpenChange={setOpen}>
|
||||
<CollapsibleTrigger className="flex w-full items-center gap-1.5 rounded-md px-2 py-1.5 text-left text-muted-foreground hover:bg-muted/30 hover:text-foreground transition-colors">
|
||||
{open ? <ChevronDown size={12} /> : <ChevronRight size={12} />}
|
||||
<Activity size={13} />
|
||||
<span>{t('ai.chat.activity.title')}</span>
|
||||
<span className="ml-auto tabular-nums text-muted-foreground/70">{visibleActivities.length}</span>
|
||||
</CollapsibleTrigger>
|
||||
<CollapsibleContent className="mt-1 space-y-1 rounded-md border border-border/30 bg-muted/10 p-2">
|
||||
{visibleActivities.map((activity) => {
|
||||
if (activity.type === 'plan_update') {
|
||||
return (
|
||||
<div key={activity.id} className="space-y-1.5">
|
||||
<div className="flex items-center gap-1.5 font-medium text-foreground/80">
|
||||
{activity.status === 'running'
|
||||
? <Loader2 size={12} className="animate-spin" />
|
||||
: <Check size={12} />}
|
||||
<span>{t('ai.chat.activity.plan')}</span>
|
||||
<span className="font-normal text-muted-foreground">
|
||||
· {statusLabel(activity.status, t)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="space-y-1 pl-0.5">
|
||||
{activity.items.map((item, index) => (
|
||||
<div key={`${activity.id}-${index}`} className="flex items-start gap-1.5 text-muted-foreground">
|
||||
{item.completed
|
||||
? <CheckCircle2 size={12} className="mt-0.5 shrink-0 text-emerald-500" />
|
||||
: <Circle size={12} className="mt-0.5 shrink-0" />}
|
||||
<span className={item.completed ? 'line-through opacity-70' : ''}>{item.text}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (activity.type === 'web_search') {
|
||||
return (
|
||||
<div key={activity.id} className="flex items-start gap-1.5 text-muted-foreground">
|
||||
{activity.status === 'running'
|
||||
? <Loader2 size={12} className="mt-0.5 shrink-0 animate-spin" />
|
||||
: <Search size={12} className="mt-0.5 shrink-0" />}
|
||||
<div className="min-w-0">
|
||||
<span className="font-medium text-foreground/80">{t('ai.chat.activity.webSearch')}: </span>
|
||||
<span className="break-words">{activity.query}</span>
|
||||
<span className="ml-1 text-muted-foreground/70">
|
||||
· {statusLabel(activity.status, t)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (activity.type === 'file_change') {
|
||||
return (
|
||||
<div key={activity.id} className="space-y-1.5">
|
||||
<div className="flex items-center gap-1.5 font-medium text-foreground/80">
|
||||
<FileDiff size={12} />
|
||||
<span>{t('ai.chat.activity.fileChanges')}</span>
|
||||
<span className={activity.status === 'failed' ? 'text-destructive' : 'text-muted-foreground'}>
|
||||
· {statusLabel(activity.status, t)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="space-y-1 pl-0.5">
|
||||
{activity.changes.map((change, index) => (
|
||||
<div key={`${activity.id}-${change.path}-${index}`} className="flex min-w-0 items-start gap-1.5">
|
||||
<span className="w-12 shrink-0 uppercase text-[10px] text-muted-foreground">
|
||||
{t(`ai.chat.activity.file.${change.kind}`)}
|
||||
</span>
|
||||
<code className="break-all text-[11px] text-foreground/75">{change.path}</code>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div key={activity.id} className="flex items-start gap-1.5 text-amber-600 dark:text-amber-400">
|
||||
<AlertTriangle size={12} className="mt-0.5 shrink-0" />
|
||||
<span className="break-words">{activity.message}</span>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</CollapsibleContent>
|
||||
</Collapsible>
|
||||
)}
|
||||
|
||||
{usage && (
|
||||
<div className="flex flex-wrap items-center gap-x-2 gap-y-0.5 px-2 text-[10px] text-muted-foreground/70 tabular-nums">
|
||||
<span>{usage.estimated ? `${t('ai.chat.activity.usage')} ~` : `${t('ai.chat.activity.usage')} `}{formatTokens(usage.totalTokens)}</span>
|
||||
<span>{t('ai.chat.activity.usage.input')} {formatTokens(usage.inputTokens)}</span>
|
||||
<span>{t('ai.chat.activity.usage.output')} {formatTokens(usage.outputTokens)}</span>
|
||||
{!!usage.cachedInputTokens && <span>{t('ai.chat.activity.usage.cached')} {formatTokens(usage.cachedInputTokens)}</span>}
|
||||
{!!usage.reasoningTokens && <span>{t('ai.chat.activity.usage.reasoning')} {formatTokens(usage.reasoningTokens)}</span>}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AgentActivityGroup;
|
||||
Reference in New Issue
Block a user