Files
Genarrative/src/components/match3d-creation/Match3DAgentWorkspace.tsx
五香丸子 2f76367108
Some checks failed
CI / verify (push) Has been cancelled
抓大鹅B3实现
2026-04-30 21:29:36 +08:00

216 lines
6.1 KiB
TypeScript

import { useState } from 'react';
import type {
ExecuteMatch3DActionRequest,
Match3DAgentSessionSnapshot,
Match3DAnchorItemResponse,
SendMatch3DMessageRequest,
} from '../../../packages/shared/src/contracts/match3dAgent';
import {
buildCreationAgentChatMessage,
createCreationAgentChatQuickActions,
createCreationAgentClientMessageId,
resolveCreationAgentQuickActionMessage,
} from '../../services/creation-agent';
import {
type CreationAgentAnchorView,
type CreationAgentSessionView,
type CreationAgentTheme,
CreationAgentWorkspace,
} from '../creation-agent';
type Match3DAgentWorkspaceProps = {
session: Match3DAgentSessionSnapshot | null;
streamingReplyText?: string;
isStreamingReply?: boolean;
isBusy?: boolean;
error?: string | null;
onBack: () => void;
onSubmitMessage: (payload: SendMatch3DMessageRequest) => void;
onExecuteAction: (payload: ExecuteMatch3DActionRequest) => void;
};
type Match3DReferenceImageState = {
src: string;
label: string;
};
const MATCH3D_AGENT_THEME: CreationAgentTheme = {
accentTextClass: 'text-lime-100/86',
accentBgClass: 'bg-lime-200',
accentButtonClass: 'bg-lime-200 shadow-emerald-950/20',
userBubbleClass: 'bg-emerald-600 text-white',
heroClass:
'border border-lime-100/18 bg-[radial-gradient(circle_at_top_left,rgba(190,242,100,0.24),transparent_34%),radial-gradient(circle_at_bottom_right,rgba(251,146,60,0.2),transparent_32%),linear-gradient(135deg,rgba(20,83,45,0.96),rgba(39,39,42,0.96))]',
anchorGridClass: 'grid gap-2 sm:grid-cols-3',
};
const MATCH3D_QUICK_ACTIONS = [
...createCreationAgentChatQuickActions(),
{
key: 'match3d-auto-config',
label: '自动配置',
},
];
function readMatch3DReferenceImageAsDataUrl(file: File) {
return new Promise<string>((resolve, reject) => {
if (!file.type.startsWith('image/')) {
reject(new Error('请选择图片文件。'));
return;
}
const reader = new FileReader();
reader.onerror = () => reject(new Error('参考图读取失败,请重试。'));
reader.onload = () => resolve(String(reader.result || ''));
reader.readAsDataURL(file);
});
}
function mapMatch3DAnchor(
anchor: Match3DAnchorItemResponse,
): CreationAgentAnchorView {
return {
key: anchor.key,
label: anchor.label,
value: anchor.value,
status: anchor.status,
};
}
function mapMatch3DSession(
session: Match3DAgentSessionSnapshot,
): CreationAgentSessionView {
// 中文注释:抓大鹅 F1 只展示聊天与配置锚点,草稿结果交给后续结果页承接。
const chatMessages = session.messages.filter(
(message) =>
message.kind === 'chat' ||
message.kind === 'summary' ||
message.kind === 'warning',
);
return {
sessionId: session.sessionId,
title: null,
assistantSummary: null,
currentTurn: session.currentTurn,
progressPercent: session.progressPercent,
anchors: [
session.anchorPack.theme,
session.anchorPack.clearCount,
session.anchorPack.difficulty,
].map(mapMatch3DAnchor),
messages: chatMessages,
recommendedReplies: [],
};
}
function buildMatch3DChatPayload({
text,
quickFillRequested = false,
referenceImageSrc,
}: {
text: string;
quickFillRequested?: boolean;
referenceImageSrc?: string | null;
}) {
return buildCreationAgentChatMessage<{
referenceImageSrc?: string | null;
}>({
clientMessageId: createCreationAgentClientMessageId('match3d'),
text,
quickFillRequested,
extraPayload: {
referenceImageSrc: referenceImageSrc || null,
},
});
}
export function Match3DAgentWorkspace({
session,
streamingReplyText = '',
isStreamingReply = false,
isBusy = false,
error = null,
onBack,
onSubmitMessage,
onExecuteAction,
}: Match3DAgentWorkspaceProps) {
const [referenceImage, setReferenceImage] =
useState<Match3DReferenceImageState | null>(null);
const [referenceImageError, setReferenceImageError] = useState<string | null>(
null,
);
return (
<CreationAgentWorkspace
session={session ? mapMatch3DSession(session) : null}
theme={MATCH3D_AGENT_THEME}
loadingText="正在准备抓大鹅共创工作区..."
composerPlaceholder="题材、消除次数、难度..."
primaryActionLabel="生成结果页"
streamingReplyText={streamingReplyText}
isStreamingReply={isStreamingReply}
isBusy={isBusy}
error={error}
quickActions={MATCH3D_QUICK_ACTIONS}
referenceImagePreviewSrc={referenceImage?.src ?? null}
referenceImageLabel={referenceImage?.label ?? null}
referenceImageError={referenceImageError}
onBack={onBack}
onSubmitText={(text) => {
onSubmitMessage(
buildMatch3DChatPayload({
text,
referenceImageSrc: referenceImage?.src ?? null,
}),
);
}}
onPrimaryAction={() => {
onExecuteAction({ action: 'match3d_compile_draft' });
}}
onQuickAction={(action) => {
const quickActionMessage =
action.key === 'match3d-auto-config'
? {
text: '自动配置',
quickFillRequested: true,
}
: resolveCreationAgentQuickActionMessage(
action.key,
'请总结一下当前抓大鹅设定。',
);
onSubmitMessage(
buildMatch3DChatPayload({
...quickActionMessage,
referenceImageSrc: referenceImage?.src ?? null,
}),
);
}}
onReferenceImageChange={async (file) => {
try {
const dataUrl = await readMatch3DReferenceImageAsDataUrl(file);
setReferenceImage({
src: dataUrl,
label: file.name.trim() || '本地参考图',
});
setReferenceImageError(null);
} catch (caughtError) {
setReferenceImageError(
caughtError instanceof Error
? caughtError.message
: '参考图读取失败,请重试。',
);
}
}}
onClearReferenceImage={() => {
setReferenceImage(null);
setReferenceImageError(null);
}}
/>
);
}
export default Match3DAgentWorkspace;