Merge remote-tracking branch 'origin/codex/tiaoyitiao' into codex/tiaoyitiao
# Conflicts: # docs/prd/【玩法创作】跳一跳俯视角玩法模板PRD-2026-05-19.md # server-rs/crates/spacetime-client/src/jump_hop.rs # server-rs/crates/spacetime-client/src/mapper/runtime.rs # server-rs/crates/spacetime-client/src/wooden_fish.rs # src/components/jump-hop-result/JumpHopResultView.test.tsx # src/components/jump-hop-runtime/JumpHopRuntimeShell.test.tsx # src/components/platform-entry/PlatformEntryFlowShellImpl.tsx # src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx # src/components/unified-creation/workspaces/JumpHopCreationWorkspace.tsx # src/components/unified-creation/workspaces/JumpHopWorkspace.test.tsx
This commit is contained in:
@@ -38,7 +38,10 @@ import type {
|
||||
BabyObjectMatchDraft,
|
||||
CreateBabyObjectMatchDraftRequest,
|
||||
} from '../../../packages/shared/src/contracts/edutainmentBabyObject';
|
||||
import type { JumpHopWorkSummaryResponse } from '../../../packages/shared/src/contracts/jumpHop';
|
||||
import type {
|
||||
JumpHopJumpRequest,
|
||||
JumpHopWorkSummaryResponse,
|
||||
} from '../../../packages/shared/src/contracts/jumpHop';
|
||||
import type {
|
||||
CreateMatch3DSessionRequest,
|
||||
ExecuteMatch3DActionRequest,
|
||||
@@ -108,6 +111,7 @@ import type {
|
||||
VisualNovelWorkDetail,
|
||||
VisualNovelWorkSummary,
|
||||
} from '../../../packages/shared/src/contracts/visualNovel';
|
||||
import type { WoodenFishWorkSummaryResponse } from '../../../packages/shared/src/contracts/woodenFish';
|
||||
import { buildCustomWorldPlayableCharacters } from '../../data/characterPresets';
|
||||
import {
|
||||
buildPublicWorkStagePath,
|
||||
@@ -187,6 +191,7 @@ import {
|
||||
jumpHopClient,
|
||||
type JumpHopGalleryCardResponse,
|
||||
type JumpHopRunResponse,
|
||||
type JumpHopRuntimeRequestOptions,
|
||||
type JumpHopSessionResponse,
|
||||
type JumpHopSessionSnapshotResponse,
|
||||
JumpHopWorkProfileResponse,
|
||||
@@ -350,7 +355,6 @@ import {
|
||||
type WoodenFishWorkProfileResponse,
|
||||
type WoodenFishWorkspaceCreateRequest,
|
||||
} from '../../services/wooden-fish/woodenFishClient';
|
||||
import type { WoodenFishWorkSummaryResponse } from '../../../packages/shared/src/contracts/woodenFish';
|
||||
import type { CustomWorldProfile } from '../../types';
|
||||
import { useAuthUi } from '../auth/AuthUiContext';
|
||||
import { PublishShareModal } from '../common/PublishShareModal';
|
||||
@@ -438,11 +442,11 @@ import {
|
||||
PlatformErrorDialog,
|
||||
type PlatformErrorDialogPayload,
|
||||
} from './PlatformErrorDialog';
|
||||
import { PlatformFeedbackView } from './PlatformFeedbackView';
|
||||
import {
|
||||
PlatformTaskCompletionDialog,
|
||||
type PlatformTaskCompletionDialogPayload,
|
||||
} from './PlatformTaskCompletionDialog';
|
||||
import { PlatformFeedbackView } from './PlatformFeedbackView';
|
||||
import { PlatformWorkDetailView } from './PlatformWorkDetailView';
|
||||
import { usePlatformCreationAgentFlowController } from './usePlatformCreationAgentFlowController';
|
||||
import { usePlatformEntryBootstrap } from './usePlatformEntryBootstrap';
|
||||
@@ -489,6 +493,30 @@ type PuzzleBackgroundCompileTask = {
|
||||
error: string | null;
|
||||
};
|
||||
|
||||
type MiniGameGenerationProgressTickStateMap = Partial<
|
||||
Record<MiniGameDraftGenerationKind, MiniGameDraftGenerationState | null>
|
||||
>;
|
||||
|
||||
export function resolveMiniGameGenerationProgressTickState(
|
||||
selectionStage: SelectionStage,
|
||||
states: MiniGameGenerationProgressTickStateMap,
|
||||
) {
|
||||
const stageKindMap: Partial<
|
||||
Record<SelectionStage, MiniGameDraftGenerationKind>
|
||||
> = {
|
||||
'puzzle-generating': 'puzzle',
|
||||
'big-fish-generating': 'big-fish',
|
||||
'square-hole-generating': 'square-hole',
|
||||
'match3d-generating': 'match3d',
|
||||
'baby-object-match-generating': 'baby-object-match',
|
||||
'jump-hop-generating': 'jump-hop',
|
||||
'wooden-fish-generating': 'wooden-fish',
|
||||
};
|
||||
const kind = stageKindMap[selectionStage];
|
||||
|
||||
return kind ? (states[kind] ?? null) : null;
|
||||
}
|
||||
|
||||
type PuzzleDetailReturnTarget = {
|
||||
tab: PlatformHomeTab;
|
||||
};
|
||||
@@ -591,11 +619,11 @@ const AGENT_RESULT_STRUCTURAL_BLOCKER_CODES = new Set([
|
||||
'publish_missing_main_chapter',
|
||||
'publish_missing_first_act',
|
||||
]);
|
||||
const RECOMMEND_RUNTIME_BACKGROUND_AUTH_OPTIONS =
|
||||
const RECOMMEND_RUNTIME_BACKGROUND_AUTH_OPTIONS: JumpHopRuntimeRequestOptions =
|
||||
BACKGROUND_AUTH_REQUEST_OPTIONS;
|
||||
const RECOMMEND_PUZZLE_BACKGROUND_AUTH_OPTIONS =
|
||||
const RECOMMEND_PUZZLE_BACKGROUND_AUTH_OPTIONS: JumpHopRuntimeRequestOptions =
|
||||
RECOMMEND_RUNTIME_BACKGROUND_AUTH_OPTIONS;
|
||||
async function buildRecommendRuntimeGuestOptions() {
|
||||
async function buildRecommendRuntimeGuestOptions(): Promise<JumpHopRuntimeRequestOptions> {
|
||||
const { token } = await ensureRuntimeGuestToken();
|
||||
return {
|
||||
...RECOMMEND_RUNTIME_BACKGROUND_AUTH_OPTIONS,
|
||||
@@ -610,9 +638,9 @@ function shouldUseRecommendRuntimeGuestAuth(
|
||||
async function buildRecommendRuntimeAuthOptions(
|
||||
authUi: { user?: { id?: string } | null } | null | undefined,
|
||||
embedded?: boolean,
|
||||
) {
|
||||
): Promise<JumpHopRuntimeRequestOptions> {
|
||||
if (!embedded) {
|
||||
return {};
|
||||
return RECOMMEND_RUNTIME_BACKGROUND_AUTH_OPTIONS;
|
||||
}
|
||||
|
||||
if (shouldUseRecommendRuntimeGuestAuth(authUi)) {
|
||||
@@ -1982,6 +2010,7 @@ function buildJumpHopPendingSession(
|
||||
templateId: 'jump-hop',
|
||||
templateName: '跳一跳',
|
||||
profileId: item.profileId,
|
||||
themeText: item.themeText || item.workTitle,
|
||||
workTitle: item.workTitle,
|
||||
workDescription: item.workDescription,
|
||||
themeTags: item.themeTags,
|
||||
@@ -2788,6 +2817,7 @@ function buildPendingJumpHopWorks(
|
||||
profileId: `jump-hop-profile-${sessionId}`,
|
||||
ownerUserId: '',
|
||||
sourceSessionId: sessionId,
|
||||
themeText: '跳一跳',
|
||||
workTitle: '跳一跳草稿',
|
||||
workDescription:
|
||||
state.status === 'failed'
|
||||
@@ -3611,6 +3641,8 @@ export function PlatformEntryFlowShellImpl({
|
||||
const [jumpHopRun, setJumpHopRun] = useState<
|
||||
JumpHopRunResponse['run'] | null
|
||||
>(null);
|
||||
const [jumpHopRuntimeRequestOptions, setJumpHopRuntimeRequestOptions] =
|
||||
useState<JumpHopRuntimeRequestOptions | null>(null);
|
||||
const [jumpHopWork, setJumpHopWork] =
|
||||
useState<JumpHopWorkProfileResponse | null>(null);
|
||||
const [jumpHopGalleryEntries, setJumpHopGalleryEntries] = useState<
|
||||
@@ -5390,22 +5422,18 @@ export function PlatformEntryFlowShellImpl({
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
const activeGenerationState =
|
||||
selectionStage === 'puzzle-generating'
|
||||
? puzzleGenerationState
|
||||
: selectionStage === 'match3d-generating'
|
||||
? match3dGenerationState
|
||||
: selectionStage === 'big-fish-generating'
|
||||
? bigFishGenerationState
|
||||
: selectionStage === 'square-hole-generating'
|
||||
? squareHoleGenerationState
|
||||
: selectionStage === 'jump-hop-generating'
|
||||
? jumpHopGenerationState
|
||||
: selectionStage === 'wooden-fish-generating'
|
||||
? woodenFishGenerationState
|
||||
: selectionStage === 'baby-object-match-generating'
|
||||
? babyObjectMatchGenerationState
|
||||
: null;
|
||||
const activeGenerationState = resolveMiniGameGenerationProgressTickState(
|
||||
selectionStage,
|
||||
{
|
||||
puzzle: puzzleGenerationState,
|
||||
'big-fish': bigFishGenerationState,
|
||||
'square-hole': squareHoleGenerationState,
|
||||
match3d: match3dGenerationState,
|
||||
'baby-object-match': babyObjectMatchGenerationState,
|
||||
'jump-hop': jumpHopGenerationState,
|
||||
'wooden-fish': woodenFishGenerationState,
|
||||
},
|
||||
);
|
||||
const shouldTickProgress =
|
||||
selectionStage === 'visual-novel-generating'
|
||||
? visualNovelGenerationStartedAtMs != null &&
|
||||
@@ -7339,6 +7367,7 @@ export function PlatformEntryFlowShellImpl({
|
||||
setJumpHopSession(null);
|
||||
setJumpHopWork(null);
|
||||
setJumpHopRun(null);
|
||||
setJumpHopRuntimeRequestOptions(null);
|
||||
setJumpHopGenerationState(null);
|
||||
enterCreateTab();
|
||||
setShowCreationTypeModal(false);
|
||||
@@ -8471,6 +8500,7 @@ export function PlatformEntryFlowShellImpl({
|
||||
setJumpHopRuntimeReturnStage('jump-hop-result');
|
||||
setJumpHopGenerationState(null);
|
||||
setJumpHopSession(null);
|
||||
setJumpHopRuntimeRequestOptions(null);
|
||||
setJumpHopError(null);
|
||||
returnToCreationFlowSource();
|
||||
}, [returnToCreationFlowSource]);
|
||||
@@ -9494,6 +9524,7 @@ export function PlatformEntryFlowShellImpl({
|
||||
);
|
||||
setJumpHopWork(null);
|
||||
setJumpHopRun(null);
|
||||
setJumpHopRuntimeRequestOptions(null);
|
||||
setJumpHopGenerationState(generationState);
|
||||
setIsJumpHopBusy(true);
|
||||
setSelectionStage('jump-hop-generating');
|
||||
@@ -9508,6 +9539,8 @@ export function PlatformEntryFlowShellImpl({
|
||||
created.session.sessionId,
|
||||
{
|
||||
actionType: 'compile-draft',
|
||||
themeText:
|
||||
payload?.themeText ?? created.session.draft?.themeText,
|
||||
workTitle: payload?.workTitle ?? created.session.draft?.workTitle,
|
||||
workDescription:
|
||||
payload?.workDescription ??
|
||||
@@ -9622,7 +9655,7 @@ export function PlatformEntryFlowShellImpl({
|
||||
}, [compileJumpHopSession, jumpHopSession, setSelectionStage]);
|
||||
|
||||
const regenerateJumpHopAsset = useCallback(
|
||||
async (actionType: 'regenerate-character' | 'regenerate-tiles') => {
|
||||
async (actionType: 'regenerate-tiles') => {
|
||||
if (!jumpHopSession?.sessionId) {
|
||||
setSelectionStage('jump-hop-workspace');
|
||||
return;
|
||||
@@ -9638,6 +9671,9 @@ export function PlatformEntryFlowShellImpl({
|
||||
jumpHopSession.sessionId,
|
||||
{
|
||||
actionType,
|
||||
profileId:
|
||||
jumpHopWork?.summary.profileId ?? jumpHopSession.draft?.profileId,
|
||||
themeText: jumpHopSession.draft?.themeText,
|
||||
workTitle: jumpHopSession.draft?.workTitle,
|
||||
workDescription: jumpHopSession.draft?.workDescription,
|
||||
themeTags: jumpHopSession.draft?.themeTags,
|
||||
@@ -9663,9 +9699,7 @@ export function PlatformEntryFlowShellImpl({
|
||||
} catch (error) {
|
||||
const errorMessage = resolveRpgCreationErrorMessage(
|
||||
error,
|
||||
actionType === 'regenerate-character'
|
||||
? '重新生成跳一跳角色失败。'
|
||||
: '重新生成跳一跳地块失败。',
|
||||
'重新生成跳一跳地块失败。',
|
||||
);
|
||||
setJumpHopError(errorMessage);
|
||||
setJumpHopGenerationState(
|
||||
@@ -9741,7 +9775,9 @@ export function PlatformEntryFlowShellImpl({
|
||||
setJumpHopError(null);
|
||||
setJumpHopRuntimeReturnStage('jump-hop-result');
|
||||
try {
|
||||
const response = await jumpHopClient.startRun(profileId);
|
||||
const response = await jumpHopClient.startRun(profileId, {
|
||||
runtimeMode: 'draft',
|
||||
});
|
||||
setJumpHopRun(response.run);
|
||||
setSelectionStage('jump-hop-runtime');
|
||||
} catch (error) {
|
||||
@@ -9772,13 +9808,30 @@ export function PlatformEntryFlowShellImpl({
|
||||
setJumpHopError(null);
|
||||
setJumpHopRuntimeReturnStage(options.returnStage ?? 'work-detail');
|
||||
try {
|
||||
const runtimeGuestOptions = await buildRecommendRuntimeAuthOptions(
|
||||
authUi,
|
||||
options.embedded,
|
||||
const runtimeGuestOptions =
|
||||
options.embedded || shouldUseRecommendRuntimeGuestAuth(authUi)
|
||||
? await buildRecommendRuntimeAuthOptions(authUi, true)
|
||||
: RECOMMEND_RUNTIME_BACKGROUND_AUTH_OPTIONS;
|
||||
setJumpHopRuntimeRequestOptions(
|
||||
runtimeGuestOptions.runtimeGuestToken?.trim()
|
||||
? {
|
||||
runtimeGuestToken: runtimeGuestOptions.runtimeGuestToken,
|
||||
authImpact: runtimeGuestOptions.authImpact,
|
||||
skipAuth: runtimeGuestOptions.skipAuth,
|
||||
skipRefresh: runtimeGuestOptions.skipRefresh,
|
||||
notifyAuthStateChange:
|
||||
runtimeGuestOptions.notifyAuthStateChange,
|
||||
clearAuthOnUnauthorized:
|
||||
runtimeGuestOptions.clearAuthOnUnauthorized,
|
||||
}
|
||||
: null,
|
||||
);
|
||||
const [detail, runResponse] = await Promise.all([
|
||||
jumpHopClient.getWorkDetail(normalizedProfileId).catch(() => null),
|
||||
jumpHopClient.startRun(normalizedProfileId, runtimeGuestOptions),
|
||||
jumpHopClient.startRun(normalizedProfileId, {
|
||||
...runtimeGuestOptions,
|
||||
runtimeMode: 'published',
|
||||
}),
|
||||
]);
|
||||
if (detail?.item) {
|
||||
setJumpHopWork(detail.item);
|
||||
@@ -9816,7 +9869,10 @@ export function PlatformEntryFlowShellImpl({
|
||||
setIsJumpHopBusy(true);
|
||||
setJumpHopError(null);
|
||||
try {
|
||||
const response = await jumpHopClient.restartRun(runId);
|
||||
const response = await jumpHopClient.restartRun(
|
||||
runId,
|
||||
jumpHopRuntimeRequestOptions ?? undefined,
|
||||
);
|
||||
setJumpHopRun(response.run);
|
||||
} catch (error) {
|
||||
setJumpHopError(
|
||||
@@ -9825,16 +9881,29 @@ export function PlatformEntryFlowShellImpl({
|
||||
} finally {
|
||||
setIsJumpHopBusy(false);
|
||||
}
|
||||
}, [jumpHopRun?.runId, startJumpHopTestRunFromProfile]);
|
||||
}, [
|
||||
jumpHopRun?.runId,
|
||||
jumpHopRuntimeRequestOptions,
|
||||
startJumpHopTestRunFromProfile,
|
||||
]);
|
||||
|
||||
const submitJumpHopJumpAction = useCallback(
|
||||
async (payload: { chargeMs: number }) => {
|
||||
async (
|
||||
payload: Pick<
|
||||
JumpHopJumpRequest,
|
||||
'dragDistance' | 'dragVectorX' | 'dragVectorY'
|
||||
>,
|
||||
) => {
|
||||
const runId = jumpHopRun?.runId;
|
||||
if (!runId) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const response = await jumpHopClient.submitJump(runId, payload);
|
||||
const response = await jumpHopClient.submitJump(
|
||||
runId,
|
||||
payload,
|
||||
jumpHopRuntimeRequestOptions ?? undefined,
|
||||
);
|
||||
setJumpHopRun(response.run);
|
||||
} catch (error) {
|
||||
setJumpHopError(
|
||||
@@ -9842,7 +9911,7 @@ export function PlatformEntryFlowShellImpl({
|
||||
);
|
||||
}
|
||||
},
|
||||
[jumpHopRun?.runId],
|
||||
[jumpHopRun?.runId, jumpHopRuntimeRequestOptions],
|
||||
);
|
||||
|
||||
const compileWoodenFishSession = useCallback(
|
||||
@@ -14217,6 +14286,14 @@ export function PlatformEntryFlowShellImpl({
|
||||
return;
|
||||
}
|
||||
|
||||
if (isJumpHopGalleryEntry(selectedPublicWorkDetail)) {
|
||||
setPublicWorkDetailError(null);
|
||||
void startJumpHopRunFromProfile(selectedPublicWorkDetail.profileId, {
|
||||
returnStage: 'work-detail',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
runProtectedAction(() => {
|
||||
if (isBigFishGalleryEntry(selectedPublicWorkDetail)) {
|
||||
const work = mapPublicWorkDetailToBigFishWork(selectedPublicWorkDetail);
|
||||
@@ -14251,14 +14328,6 @@ export function PlatformEntryFlowShellImpl({
|
||||
return;
|
||||
}
|
||||
|
||||
if (isJumpHopGalleryEntry(selectedPublicWorkDetail)) {
|
||||
setPublicWorkDetailError(null);
|
||||
void startJumpHopRunFromProfile(selectedPublicWorkDetail.profileId, {
|
||||
returnStage: 'work-detail',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (isWoodenFishGalleryEntry(selectedPublicWorkDetail)) {
|
||||
setPublicWorkDetailError(null);
|
||||
void startWoodenFishRunFromProfile(selectedPublicWorkDetail.profileId, {
|
||||
@@ -14761,37 +14830,15 @@ export function PlatformEntryFlowShellImpl({
|
||||
run={jumpHopRun}
|
||||
isBusy={isJumpHopBusy}
|
||||
error={jumpHopError}
|
||||
runtimeRequestOptions={jumpHopRuntimeRequestOptions ?? undefined}
|
||||
onBack={() => {
|
||||
setActiveRecommendRuntimeKind(null);
|
||||
}}
|
||||
onRestart={() => {
|
||||
if (!jumpHopRun?.runId || isJumpHopBusy) {
|
||||
return;
|
||||
}
|
||||
|
||||
setIsJumpHopBusy(true);
|
||||
setJumpHopError(null);
|
||||
void jumpHopClient
|
||||
.restartRun(jumpHopRun.runId)
|
||||
.then((response) => {
|
||||
setJumpHopRun(response.run);
|
||||
})
|
||||
.catch((error) => {
|
||||
setJumpHopError(
|
||||
resolveRpgCreationErrorMessage(error, '重新开始跳一跳失败。'),
|
||||
);
|
||||
})
|
||||
.finally(() => {
|
||||
setIsJumpHopBusy(false);
|
||||
});
|
||||
void restartJumpHopRuntimeRun();
|
||||
}}
|
||||
onJump={async (payload) => {
|
||||
const runId = jumpHopRun?.runId;
|
||||
if (!runId) {
|
||||
throw new Error('跳一跳运行态缺少 runId。');
|
||||
}
|
||||
const response = await jumpHopClient.submitJump(runId, payload);
|
||||
setJumpHopRun(response.run);
|
||||
await submitJumpHopJumpAction(payload);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
@@ -16741,6 +16788,7 @@ export function PlatformEntryFlowShellImpl({
|
||||
)}
|
||||
progress={buildMiniGameDraftGenerationProgress(
|
||||
bigFishGenerationState,
|
||||
miniGameGenerationProgressNowMs,
|
||||
)}
|
||||
isGenerating={isBigFishBusy}
|
||||
error={bigFishError}
|
||||
@@ -17351,6 +17399,7 @@ export function PlatformEntryFlowShellImpl({
|
||||
)}
|
||||
progress={buildMiniGameDraftGenerationProgress(
|
||||
squareHoleGenerationState,
|
||||
miniGameGenerationProgressNowMs,
|
||||
)}
|
||||
isGenerating={isSquareHoleBusy}
|
||||
error={squareHoleError}
|
||||
@@ -17564,6 +17613,7 @@ export function PlatformEntryFlowShellImpl({
|
||||
)}
|
||||
progress={buildMiniGameDraftGenerationProgress(
|
||||
jumpHopGenerationState,
|
||||
miniGameGenerationProgressNowMs,
|
||||
)}
|
||||
isGenerating={isJumpHopBusy}
|
||||
error={jumpHopError}
|
||||
@@ -17605,9 +17655,6 @@ export function PlatformEntryFlowShellImpl({
|
||||
}}
|
||||
onStartTestRun={startJumpHopTestRunFromProfile}
|
||||
onPublish={publishJumpHopDraft}
|
||||
onRegenerateCharacter={() => {
|
||||
void regenerateJumpHopAsset('regenerate-character');
|
||||
}}
|
||||
onRegenerateTiles={() => {
|
||||
void regenerateJumpHopAsset('regenerate-tiles');
|
||||
}}
|
||||
@@ -17643,6 +17690,7 @@ export function PlatformEntryFlowShellImpl({
|
||||
profile={jumpHopWork}
|
||||
isBusy={isJumpHopBusy}
|
||||
error={jumpHopError}
|
||||
runtimeRequestOptions={jumpHopRuntimeRequestOptions ?? undefined}
|
||||
onBack={() => {
|
||||
setSelectionStage(jumpHopRuntimeReturnStage);
|
||||
}}
|
||||
@@ -17704,6 +17752,7 @@ export function PlatformEntryFlowShellImpl({
|
||||
)}
|
||||
progress={buildMiniGameDraftGenerationProgress(
|
||||
woodenFishGenerationState,
|
||||
miniGameGenerationProgressNowMs,
|
||||
)}
|
||||
isGenerating={isWoodenFishBusy}
|
||||
error={woodenFishError}
|
||||
|
||||
Reference in New Issue
Block a user