Files
tutti/packages/agent/gui/shared/WorkspaceAgentSessionDetail.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

130 lines
4.2 KiB
TypeScript

import { useMemo, type JSX } from "react";
import type { WorkspaceLinkAction } from "../contexts/workspace/presentation/renderer/actions/workspaceLinkActions";
import { translate } from "../i18n/index";
import type { AgentMessageMarkdownAgentTarget } from "./AgentTargetPresentationContext";
import { AgentConversationFlow } from "./agentConversation/components/AgentConversationFlow";
import type { AgentConversationParticipantPresentation } from "./agentConversation/contracts/agentConversationParticipantPresentation";
import { useProjectedAgentConversation } from "./agentConversation/projection/useProjectedAgentConversation";
import type { WorkspaceAgentSessionDetailViewModel } from "./workspaceAgentSessionDetailViewModel";
export interface WorkspaceAgentSessionDetailProps {
detail: WorkspaceAgentSessionDetailViewModel;
avoidGroupingEdits?: boolean;
isLoading: boolean;
timelineItemCount: number;
agentTargets?: readonly AgentMessageMarkdownAgentTarget[];
onLinkAction?: (action: WorkspaceLinkAction) => void;
toolCallsLabel: (count: number) => string;
thinkingLabel?: string;
loadingLabel?: string;
rawTimelineJsonLabel?: string;
showRawTimelineJson?: boolean;
participantPresentation?: AgentConversationParticipantPresentation;
}
export function WorkspaceAgentSessionDetail({
detail,
avoidGroupingEdits = false,
isLoading,
timelineItemCount,
agentTargets,
onLinkAction,
toolCallsLabel,
thinkingLabel = translate("agentHost.workspaceAgentSessionDetailThinking"),
loadingLabel = translate("common.loading"),
rawTimelineJsonLabel,
showRawTimelineJson = false,
participantPresentation
}: WorkspaceAgentSessionDetailProps): JSX.Element {
const conversation = useProjectedAgentConversation({
detail,
avoidGroupingEdits
});
const showLoadingSkeleton =
detail.turns.length === 0 &&
(isLoading ||
detail.activity.status === "waiting" ||
detail.activity.status === "working");
const emptySummary =
detail.activity.latestActivitySummary ||
(timelineItemCount > 0
? translate("agentHost.workspaceAgentSessionDetailEmptyWithTimeline")
: translate("agentHost.workspaceAgentSessionDetailEmptyNoTimeline"));
const flowLabels = useMemo(
() => ({
thinkingLabel,
toolCallsLabel,
processing: translate("agentHost.agentGui.processing"),
turnSummary: translate("agentHost.agentGui.turnSummary"),
rawTimelineJson: rawTimelineJsonLabel
}),
[rawTimelineJsonLabel, thinkingLabel, toolCallsLabel]
);
const emptyState = useMemo(
() => (
<div className="workspace-agents-status-panel__detail-empty">
{emptySummary}
</div>
),
[emptySummary]
);
return (
<div className="workspace-agents-status-panel__detail">
<AgentConversationFlow
conversation={detail.turns.length > 0 ? conversation : null}
agentTargets={agentTargets}
isLoading={showLoadingSkeleton}
loadingLabel={loadingLabel}
empty={emptyState}
onLinkAction={onLinkAction}
showRawTimelineJson={showRawTimelineJson}
participantPresentation={participantPresentation}
labels={flowLabels}
/>
</div>
);
}
export function WorkspaceAgentSessionDetailSkeleton({
loading = true,
loadingLabel = translate("common.loading")
}: {
loading?: boolean;
loadingLabel?: string;
}): JSX.Element {
const flowLabels = useMemo(
() => ({
thinkingLabel: translate("agentHost.workspaceAgentSessionDetailThinking"),
toolCallsLabel: (count: number) =>
translate("agentHost.workspaceAgentSessionDetailToolCalls", { count }),
processing: translate("agentHost.agentGui.processing"),
turnSummary: translate("agentHost.agentGui.turnSummary")
}),
[]
);
if (!loading) {
return <></>;
}
return (
<div
className="workspace-agents-status-panel__detail-skeleton"
data-testid="workspace-agents-status-panel-detail-skeleton"
role="status"
aria-busy="true"
aria-label={loadingLabel}
>
<AgentConversationFlow
conversation={null}
isLoading
loadingLabel={loadingLabel}
loadingTestId="workspace-agents-status-panel-detail-skeleton"
empty={null}
labels={flowLabels}
/>
</div>
);
}