Files
tutti/packages/agent/gui/shared/WorkspaceAgentSessionThinkingDisclosure.tsx
agent-radar 2ed3415fb7
Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
2026-09-01 20:17:27 +08:00

134 lines
4.7 KiB
TypeScript

import { useState, type JSX } from "react";
import { ChevronDown, ChevronRight } from "lucide-react";
import { BrainIcon } from "../app/renderer/components/icons/BrainIcon";
import { translate } from "../i18n/index";
import { CollapsibleReveal } from "./agentConversation/components/CollapsibleReveal";
import { AgentMessageMarkdown } from "./AgentMessageMarkdown";
import type { WorkspaceAgentSessionDetailThinking } from "./workspaceAgentSessionDetailViewModel";
import { ToolActivityKindIcon } from "./toolActivityKindIcons";
interface WorkspaceAgentSessionThinkingDisclosureProps {
thinking: WorkspaceAgentSessionDetailThinking;
label: string;
onLinkClick?: (href: string) => void;
}
export function WorkspaceAgentSessionThinkingDisclosure({
thinking,
label,
onLinkClick
}: WorkspaceAgentSessionThinkingDisclosureProps): JSX.Element {
"use memo";
const [expanded, setExpanded] = useState(false);
const isActive =
thinking.statusKind === "working" || thinking.statusKind === "waiting";
const statusLabel =
thinking.statusKind === "working"
? null
: thinkingStatusLabel(thinking.statusKind);
return (
<div className="workspace-agents-status-panel__detail-thinking-disclosure flex w-full min-w-0 flex-col items-start gap-[7px]">
<button
type="button"
className={[
"group/thinking-disclosure inline-flex w-fit max-w-full min-w-0 items-center gap-1.5 border-0 bg-transparent p-0 text-left font-[var(--tsh-font-ui)] text-[13px] leading-[1.3] font-normal text-[var(--text-secondary)] transition-colors duration-150 hover:text-[var(--tsh-shell-body)]",
isActive ? "tsh-inline-scanlight-group" : ""
]
.filter(Boolean)
.join(" ")}
aria-expanded={expanded}
data-active={isActive ? "true" : undefined}
onClick={() => {
setExpanded((value) => !value);
}}
>
{isActive ? (
<BrainIcon
size={16}
active
aria-hidden="true"
className="tsh-inline-scanlight-icon shrink-0 opacity-[0.86]"
/>
) : (
<ToolActivityKindIcon
kind="thinking"
width={16}
height={16}
className="tsh-inline-scanlight-icon shrink-0 opacity-[0.72]"
/>
)}
<span className={isActive ? "tsh-inline-scanlight-line" : undefined}>
<span className="font-semibold">{label}</span>
{statusLabel ? (
<span className="ml-1.5">
{formatInlineStatusLabel(statusLabel)}
</span>
) : null}
{isActive ? <LoadingEllipsis /> : null}
</span>
{expanded ? (
<ChevronDown
size={12}
strokeWidth={2.2}
aria-hidden="true"
className="shrink-0 text-[var(--text-tertiary)] opacity-0 transition-opacity duration-150 group-hover/thinking-disclosure:opacity-100 group-focus-visible/thinking-disclosure:opacity-100"
/>
) : (
<ChevronRight
size={12}
strokeWidth={2.2}
aria-hidden="true"
className="shrink-0 text-[var(--text-tertiary)] opacity-0 transition-opacity duration-150 group-hover/thinking-disclosure:opacity-100 group-focus-visible/thinking-disclosure:opacity-100"
/>
)}
</button>
<CollapsibleReveal expanded={expanded} className="w-full">
<div className="box-border w-full min-w-0 rounded-[6px] bg-[var(--transparency-block)] p-3">
<AgentMessageMarkdown
content={thinking.body}
onLinkClick={onLinkClick}
className="text-[11px] text-[var(--text-secondary)] opacity-92 [&_p]:mb-1"
enableImageZoom
/>
</div>
</CollapsibleReveal>
</div>
);
}
function LoadingEllipsis(): JSX.Element {
"use memo";
return (
<span className="tsh-inline-loading-ellipsis" aria-hidden="true">
<span />
<span />
<span />
</span>
);
}
function thinkingStatusLabel(
statusKind: WorkspaceAgentSessionDetailThinking["statusKind"]
): string | null {
switch (statusKind) {
case "working":
return translate("agentHost.agentTool.statusWorking");
case "completed":
return translate("agentHost.agentTool.statusCompleted");
case "failed":
return translate("agentHost.agentTool.statusFailed");
case "canceled":
return translate("agentHost.agentTool.statusCanceled");
case "waiting":
return translate("agentHost.agentTool.statusWaiting");
default:
return null;
}
}
function formatInlineStatusLabel(label: string): string {
return /^[A-Z][a-z]+$/.test(label) ? label.toLowerCase() : label;
}