1
This commit is contained in:
@@ -4,11 +4,17 @@ import type {
|
||||
CustomWorldAgentSessionSnapshot,
|
||||
SendCustomWorldAgentMessageRequest,
|
||||
} from '../../../packages/shared/src/contracts/customWorldAgent';
|
||||
import { CustomWorldAgentComposer } from './CustomWorldAgentComposer';
|
||||
import { CustomWorldAgentHeader } from './CustomWorldAgentHeader';
|
||||
import { CustomWorldAgentOperationBanner } from './CustomWorldAgentOperationBanner';
|
||||
import { CustomWorldAgentThread } from './CustomWorldAgentThread';
|
||||
import { EightAnchorProgressBar } from './EightAnchorProgressBar';
|
||||
import {
|
||||
createCreationAgentClientMessageId,
|
||||
isCreationAgentOperationBusy,
|
||||
} from '../../services/creation-agent';
|
||||
import {
|
||||
type CreationAgentAnchorView,
|
||||
type CreationAgentOperationView,
|
||||
type CreationAgentSessionView,
|
||||
type CreationAgentTheme,
|
||||
CreationAgentWorkspace,
|
||||
} from '../creation-agent';
|
||||
|
||||
type CustomWorldAgentWorkspaceProps = {
|
||||
session: CustomWorldAgentSessionSnapshot | null;
|
||||
@@ -20,15 +26,132 @@ type CustomWorldAgentWorkspaceProps = {
|
||||
onExecuteAction: (payload: CustomWorldAgentActionRequest) => void;
|
||||
};
|
||||
|
||||
function createClientMessageId() {
|
||||
if (
|
||||
typeof crypto !== 'undefined' &&
|
||||
typeof crypto.randomUUID === 'function'
|
||||
) {
|
||||
return crypto.randomUUID();
|
||||
const CUSTOM_WORLD_AGENT_THEME: CreationAgentTheme = {
|
||||
accentTextClass: 'text-emerald-100/86',
|
||||
accentBgClass: 'bg-emerald-300',
|
||||
accentButtonClass: 'bg-emerald-200 shadow-emerald-950/20',
|
||||
userBubbleClass:
|
||||
'border border-[var(--platform-cool-border)] bg-[var(--platform-cool-bg)] text-[var(--platform-text-strong)]',
|
||||
heroClass:
|
||||
'border border-emerald-100/18 bg-[radial-gradient(circle_at_top_left,rgba(52,211,153,0.2),transparent_32%),linear-gradient(135deg,rgba(6,78,59,0.95),rgba(24,33,39,0.96))]',
|
||||
anchorGridClass: 'grid gap-2 sm:grid-cols-2 xl:grid-cols-4',
|
||||
};
|
||||
|
||||
function stringifyAnchorValue(value: unknown): string {
|
||||
if (!value) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return `client-message-${Date.now()}`;
|
||||
if (typeof value === 'string') {
|
||||
return value;
|
||||
}
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
return value
|
||||
.map((item): string => stringifyAnchorValue(item))
|
||||
.filter((item): item is string => Boolean(item))
|
||||
.join(' / ');
|
||||
}
|
||||
|
||||
if (typeof value !== 'object') {
|
||||
return String(value);
|
||||
}
|
||||
|
||||
return Object.values(value as Record<string, unknown>)
|
||||
.map((item): string => stringifyAnchorValue(item))
|
||||
.filter((item): item is string => Boolean(item))
|
||||
.join(' / ');
|
||||
}
|
||||
|
||||
function buildCustomWorldAnchor(
|
||||
key: string,
|
||||
label: string,
|
||||
value: unknown,
|
||||
): CreationAgentAnchorView {
|
||||
const text = stringifyAnchorValue(value);
|
||||
|
||||
return {
|
||||
key,
|
||||
label,
|
||||
value: text,
|
||||
status: text ? 'confirmed' : 'missing',
|
||||
};
|
||||
}
|
||||
|
||||
function mapCustomWorldSession(
|
||||
session: CustomWorldAgentSessionSnapshot,
|
||||
): CreationAgentSessionView {
|
||||
return {
|
||||
sessionId: session.sessionId,
|
||||
title: '世界共创',
|
||||
assistantSummary:
|
||||
session.lastAssistantReply ||
|
||||
'先说一个你最想让玩家记住的世界方向,我会帮你收束成可生成草稿的锚点。',
|
||||
currentTurn: session.currentTurn,
|
||||
progressPercent: session.progressPercent,
|
||||
anchors: [
|
||||
buildCustomWorldAnchor(
|
||||
'worldPromise',
|
||||
'世界承诺',
|
||||
session.anchorContent.worldPromise,
|
||||
),
|
||||
buildCustomWorldAnchor(
|
||||
'playerFantasy',
|
||||
'玩家幻想',
|
||||
session.anchorContent.playerFantasy,
|
||||
),
|
||||
buildCustomWorldAnchor(
|
||||
'themeBoundary',
|
||||
'主题边界',
|
||||
session.anchorContent.themeBoundary,
|
||||
),
|
||||
buildCustomWorldAnchor(
|
||||
'playerEntryPoint',
|
||||
'开局切入',
|
||||
session.anchorContent.playerEntryPoint,
|
||||
),
|
||||
buildCustomWorldAnchor(
|
||||
'coreConflict',
|
||||
'核心冲突',
|
||||
session.anchorContent.coreConflict,
|
||||
),
|
||||
buildCustomWorldAnchor(
|
||||
'keyRelationships',
|
||||
'关键关系',
|
||||
session.anchorContent.keyRelationships,
|
||||
),
|
||||
buildCustomWorldAnchor(
|
||||
'hiddenLines',
|
||||
'暗线',
|
||||
session.anchorContent.hiddenLines,
|
||||
),
|
||||
buildCustomWorldAnchor(
|
||||
'iconicElements',
|
||||
'标志元素',
|
||||
session.anchorContent.iconicElements,
|
||||
),
|
||||
],
|
||||
messages: session.messages,
|
||||
recommendedReplies: session.recommendedReplies,
|
||||
};
|
||||
}
|
||||
|
||||
function mapCustomWorldOperation(
|
||||
operation: CustomWorldAgentOperationRecord | null,
|
||||
): CreationAgentOperationView | null {
|
||||
if (!operation || operation.type === 'process_message') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
operationId: operation.operationId,
|
||||
type: operation.type,
|
||||
status: operation.status,
|
||||
phaseLabel: operation.phaseLabel,
|
||||
phaseDetail: operation.phaseDetail,
|
||||
progress: operation.progress,
|
||||
error: operation.error,
|
||||
};
|
||||
}
|
||||
|
||||
export function CustomWorldAgentWorkspace({
|
||||
@@ -40,22 +163,12 @@ export function CustomWorldAgentWorkspace({
|
||||
onSubmitMessage,
|
||||
onExecuteAction,
|
||||
}: CustomWorldAgentWorkspaceProps) {
|
||||
if (!session) {
|
||||
return (
|
||||
<div className="platform-remap-surface platform-subpanel mx-auto flex h-full w-full max-w-4xl items-center justify-center rounded-[1.75rem] px-6 py-8 text-center text-sm text-[var(--platform-text-soft)]">
|
||||
正在恢复
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const isBusy =
|
||||
activeOperation?.status === 'queued' ||
|
||||
activeOperation?.status === 'running' ||
|
||||
isStreamingReply;
|
||||
isCreationAgentOperationBusy(activeOperation) || isStreamingReply;
|
||||
|
||||
const submitMessage = (text: string, quickFillRequested = false) => {
|
||||
onSubmitMessage({
|
||||
clientMessageId: createClientMessageId(),
|
||||
clientMessageId: createCreationAgentClientMessageId('custom-world'),
|
||||
text,
|
||||
quickFillRequested,
|
||||
focusCardId: null,
|
||||
@@ -64,48 +177,44 @@ export function CustomWorldAgentWorkspace({
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="mx-auto flex h-full min-h-0 w-full max-w-5xl flex-col gap-3 overflow-hidden px-1 sm:px-0">
|
||||
<CustomWorldAgentHeader onBack={onBack} />
|
||||
|
||||
<EightAnchorProgressBar
|
||||
currentTurn={session.currentTurn}
|
||||
progressPercent={session.progressPercent}
|
||||
disabled={isBusy}
|
||||
onSummaryClick={() => {
|
||||
submitMessage('请总结一下当前已经成形的世界设定。');
|
||||
}}
|
||||
onQuickFill={() => {
|
||||
<CreationAgentWorkspace
|
||||
session={session ? mapCustomWorldSession(session) : null}
|
||||
theme={CUSTOM_WORLD_AGENT_THEME}
|
||||
loadingText="正在恢复"
|
||||
composerPlaceholder="输入消息"
|
||||
primaryActionLabel="生成游戏设定草稿"
|
||||
activeOperation={mapCustomWorldOperation(activeOperation)}
|
||||
streamingReplyText={streamingReplyText}
|
||||
isStreamingReply={isStreamingReply}
|
||||
isBusy={isBusy}
|
||||
quickActions={[
|
||||
{
|
||||
key: 'summarize',
|
||||
label: '总结当前设定',
|
||||
},
|
||||
{
|
||||
key: 'quickFill',
|
||||
label: '补全剩余设定',
|
||||
minTurn: 2,
|
||||
},
|
||||
]}
|
||||
onBack={onBack}
|
||||
onSubmitText={(text) => {
|
||||
submitMessage(text);
|
||||
}}
|
||||
onPrimaryAction={() => {
|
||||
onExecuteAction({
|
||||
action: 'draft_foundation',
|
||||
});
|
||||
}}
|
||||
onQuickAction={(action) => {
|
||||
if (action.key === 'quickFill') {
|
||||
submitMessage('请补全剩余设定。', true);
|
||||
}}
|
||||
onGenerateDraft={() => {
|
||||
onExecuteAction({
|
||||
action: 'draft_foundation',
|
||||
});
|
||||
}}
|
||||
/>
|
||||
return;
|
||||
}
|
||||
|
||||
{activeOperation?.type !== 'process_message' ? (
|
||||
<CustomWorldAgentOperationBanner operation={activeOperation} />
|
||||
) : null}
|
||||
|
||||
<div className="min-h-0 flex-1 overflow-hidden">
|
||||
<div className="h-full min-h-[18rem] lg:min-h-0">
|
||||
<CustomWorldAgentThread
|
||||
messages={session.messages}
|
||||
recommendedReplies={session.recommendedReplies}
|
||||
onRecommendedReply={(text) => {
|
||||
submitMessage(text);
|
||||
}}
|
||||
streamingReplyText={streamingReplyText}
|
||||
isStreamingReply={isStreamingReply}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<CustomWorldAgentComposer
|
||||
disabled={isBusy}
|
||||
onSubmit={onSubmitMessage}
|
||||
/>
|
||||
</div>
|
||||
submitMessage('请总结一下当前已经成形的世界设定。');
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user