This commit is contained in:
2026-04-29 20:56:59 +08:00
parent fb6f455530
commit 730f485f48
200 changed files with 9881 additions and 2221 deletions

View File

@@ -25,6 +25,7 @@ import type {
} from '../../../packages/shared/src/contracts/puzzleAgentActions';
import type { PuzzleResultDraft } from '../../../packages/shared/src/contracts/puzzleAgentDraft';
import type {
CreatePuzzleAgentSessionRequest,
PuzzleAgentSessionSnapshot,
SendPuzzleAgentMessageRequest,
} from '../../../packages/shared/src/contracts/puzzleAgentSession';
@@ -98,12 +99,20 @@ import {
} from '../../services/puzzle-gallery';
import {
advanceLocalPuzzleNextLevel,
dragPuzzlePieceOrGroup,
getPuzzleRun,
startPuzzleRun,
submitPuzzleLeaderboard,
swapPuzzlePieces,
updatePuzzleRunPause,
usePuzzleRuntimeProp as consumePuzzleRuntimeProp,
} from '../../services/puzzle-runtime';
import {
applyLocalPuzzleFreezeTime,
dragLocalPuzzlePiece,
isLocalPuzzleRun,
refreshLocalPuzzleTimer,
setLocalPuzzlePaused,
startLocalPuzzleRun,
submitLocalPuzzleLeaderboard,
swapLocalPuzzlePieces,
@@ -114,8 +123,8 @@ import { rpgCreationPreviewAdapter } from '../../services/rpg-creation/rpgCreati
import {
deleteRpgEntryWorldProfile,
getRpgEntryWorldGalleryDetailByCode,
remixRpgEntryWorldGallery,
recordRpgEntryWorldGalleryPlay,
remixRpgEntryWorldGallery,
} from '../../services/rpg-entry/rpgEntryLibraryClient';
import { getRpgProfilePlayStats } from '../../services/rpg-entry/rpgProfileClient';
import type { CustomWorldProfile } from '../../types';
@@ -230,6 +239,33 @@ function mapBigFishWorkToPublicWorkDetail(
return mapBigFishWorkToPlatformGalleryCard(item);
}
function mapPublicWorkDetailToPuzzleWork(
entry: PlatformPublicGalleryCard,
): PuzzleWorkSummary | null {
if (!isPuzzleGalleryEntry(entry)) {
return null;
}
return {
workId: entry.workId,
profileId: entry.profileId,
ownerUserId: entry.ownerUserId,
sourceSessionId: null,
authorDisplayName: entry.authorDisplayName,
levelName: entry.worldName,
summary: entry.summaryText,
themeTags: entry.themeTags,
coverImageSrc: entry.coverImageSrc,
publicationStatus: 'published',
updatedAt: entry.updatedAt,
publishedAt: entry.publishedAt,
playCount: entry.playCount ?? 0,
remixCount: entry.remixCount ?? 0,
likeCount: entry.likeCount ?? 0,
publishReady: true,
};
}
function mapPublicWorkDetailToBigFishWork(
entry: PlatformPublicGalleryCard,
): BigFishWorkSummary | null {
@@ -265,6 +301,26 @@ function mapPublicWorkDetailToBigFishWork(
};
}
async function resolvePublicWorkAuthorSummary(
entry: PlatformPublicGalleryCard,
): Promise<PublicUserSummary | null> {
if ('authorPublicUserCode' in entry && entry.authorPublicUserCode?.trim()) {
try {
return await getPublicAuthUserByCode(entry.authorPublicUserCode);
} catch {
if (!entry.ownerUserId.trim()) {
return null;
}
}
}
if (entry.ownerUserId.trim()) {
return getPublicAuthUserById(entry.ownerUserId);
}
return null;
}
function readProfileTextField(
profile: CustomWorldProfile | null,
paths: string[],
@@ -400,6 +456,18 @@ function buildPuzzleResultProfileId(sessionId: string | null | undefined) {
return `puzzle-profile-${stableSuffix}`;
}
function buildPuzzleCompileActionFromFormPayload(
payload: CreatePuzzleAgentSessionRequest | null,
): PuzzleAgentActionRequest {
return {
action: 'compile_puzzle_draft',
promptText:
payload?.pictureDescription?.trim() || payload?.seedText?.trim(),
referenceImageSrc: payload?.referenceImageSrc || null,
candidateCount: 1,
};
}
const CustomWorldGenerationView = lazy(async () => {
const module = await import('../CustomWorldGenerationView');
return {
@@ -505,6 +573,9 @@ export function PlatformEntryFlowShellImpl({
useState<CustomWorldLibraryEntry<CustomWorldProfile> | null>(null);
const [selectedPublicWorkDetail, setSelectedPublicWorkDetail] =
useState<PlatformPublicGalleryCard | null>(null);
const [selectedPublicWorkAuthor, setSelectedPublicWorkAuthor] =
useState<PublicUserSummary | null>(null);
const publicWorkAuthorRequestKeyRef = useRef(0);
const [publicWorkDetailError, setPublicWorkDetailError] = useState<
string | null
>(null);
@@ -529,8 +600,9 @@ export function PlatformEntryFlowShellImpl({
const [isBigFishLoadingLibrary, setIsBigFishLoadingLibrary] = useState(false);
const [bigFishGenerationState, setBigFishGenerationState] =
useState<MiniGameDraftGenerationState | null>(null);
const [puzzleOperation, setPuzzleOperation] =
useState<PuzzleAgentOperationRecord | null>(null);
const [, setPuzzleOperation] = useState<PuzzleAgentOperationRecord | null>(
null,
);
const [puzzleWorks, setPuzzleWorks] = useState<PuzzleWorkSummary[]>([]);
const [puzzleGalleryEntries, setPuzzleGalleryEntries] = useState<
PuzzleWorkSummary[]
@@ -544,9 +616,12 @@ export function PlatformEntryFlowShellImpl({
const [isPuzzleLeaderboardBusy, setIsPuzzleLeaderboardBusy] = useState(false);
const submittedPuzzleLeaderboardKeysRef = useRef(new Set<string>());
const [puzzleRun, setPuzzleRun] = useState<PuzzleRunSnapshot | null>(null);
const puzzleRunRef = useRef<PuzzleRunSnapshot | null>(null);
const [isPuzzleLoadingLibrary, setIsPuzzleLoadingLibrary] = useState(false);
const [puzzleGenerationState, setPuzzleGenerationState] =
useState<MiniGameDraftGenerationState | null>(null);
const [puzzleFormDraftPayload, setPuzzleFormDraftPayload] =
useState<CreatePuzzleAgentSessionRequest | null>(null);
const [isPuzzleNextLevelGenerating, setIsPuzzleNextLevelGenerating] =
useState(false);
const [isSearchingPublicCode, setIsSearchingPublicCode] = useState(false);
@@ -984,7 +1059,7 @@ export function PlatformEntryFlowShellImpl({
const puzzleFlow = usePlatformCreationAgentFlowController<
PuzzleAgentSessionSnapshot,
Record<string, never>,
CreatePuzzleAgentSessionRequest,
{ session: PuzzleAgentSessionSnapshot },
SendPuzzleAgentMessageRequest,
PuzzleAgentActionRequest,
@@ -1097,7 +1172,6 @@ export function PlatformEntryFlowShellImpl({
const setPuzzleError = puzzleFlow.setError;
const isPuzzleBusy = puzzleFlow.isBusy;
const setIsPuzzleBusy = puzzleFlow.setIsBusy;
const streamingPuzzleReplyText = puzzleFlow.streamingReplyText;
const isStreamingPuzzleReply = puzzleFlow.isStreamingReply;
const resetRpgSessionViewState = sessionController.resetSessionViewState;
const setRpgGeneratedCustomWorldProfile =
@@ -1106,6 +1180,11 @@ export function PlatformEntryFlowShellImpl({
const persistRpgAgentUiState = sessionController.persistAgentUiState;
const resetAutoSaveTrackingToIdle =
autosaveCoordinator.resetAutoSaveTrackingToIdle;
useEffect(() => {
puzzleRunRef.current = puzzleRun;
}, [puzzleRun]);
const openBigFishAgentWorkspace = useCallback(async () => {
setBigFishRun(null);
await bigFishFlow.openWorkspace();
@@ -1114,8 +1193,32 @@ export function PlatformEntryFlowShellImpl({
const openPuzzleAgentWorkspace = useCallback(async () => {
setPuzzleRun(null);
setPuzzleOperation(null);
await puzzleFlow.openWorkspace();
}, [puzzleFlow]);
setPuzzleGenerationState(null);
setPuzzleFormDraftPayload(null);
puzzleFlow.setSession(null);
puzzleFlow.setError(null);
puzzleFlow.setStreamingReplyText('');
puzzleFlow.setIsStreamingReply(false);
enterCreateTab();
setShowCreationTypeModal(false);
setSelectionStage('puzzle-agent-workspace');
}, [enterCreateTab, puzzleFlow, setSelectionStage]);
const createPuzzleDraftFromForm = useCallback(
async (payload: CreatePuzzleAgentSessionRequest) => {
setPuzzleFormDraftPayload(payload);
const nextSession = await puzzleFlow.openWorkspace(payload);
if (!nextSession) {
return;
}
await puzzleFlow.executeAction(
buildPuzzleCompileActionFromFormPayload(payload),
nextSession,
);
},
[puzzleFlow],
);
useEffect(() => {
if (platformBootstrap.canReadProtectedData) {
@@ -1325,6 +1428,8 @@ export function PlatformEntryFlowShellImpl({
async (
profileId: string,
returnStage: PuzzleRuntimeReturnStage = 'work-detail',
detailItem?: PuzzleWorkSummary,
mirrorErrorToPublicDetail = false,
) => {
if (isPuzzleBusy) {
return;
@@ -1334,7 +1439,8 @@ export function PlatformEntryFlowShellImpl({
setPuzzleError(null);
try {
const { item } = await getPuzzleGalleryDetail(profileId);
const item =
detailItem ?? (await getPuzzleGalleryDetail(profileId)).item;
const { run } = await startPuzzleRun({ profileId: item.profileId });
setSelectedPuzzleDetail(item);
setPuzzleRun(run);
@@ -1347,12 +1453,22 @@ export function PlatformEntryFlowShellImpl({
),
);
} catch (error) {
setPuzzleError(resolvePuzzleErrorMessage(error, '启动拼图玩法失败。'));
const message = resolvePuzzleErrorMessage(error, '启动拼图玩法失败。');
setPuzzleError(message);
if (mirrorErrorToPublicDetail) {
setPublicWorkDetailError(message);
}
} finally {
setIsPuzzleBusy(false);
}
},
[isPuzzleBusy, resolvePuzzleErrorMessage, setSelectionStage],
[
isPuzzleBusy,
resolvePuzzleErrorMessage,
setIsPuzzleBusy,
setPuzzleError,
setSelectionStage,
],
);
const buildPuzzleTestWork = useCallback(
@@ -1449,9 +1565,20 @@ export function PlatformEntryFlowShellImpl({
}
setPuzzleError(null);
setPuzzleRun(swapLocalPuzzlePieces(puzzleRun, payload));
if (isLocalPuzzleRun(puzzleRun)) {
setPuzzleRun(swapLocalPuzzlePieces(puzzleRun, payload));
return;
}
void swapPuzzlePieces(puzzleRun.runId, payload)
.then(({ run }) => {
setPuzzleRun(run);
})
.catch((error) => {
setPuzzleError(resolvePuzzleErrorMessage(error, '交换拼图块失败。'));
});
},
[isPuzzleBusy, puzzleRun],
[isPuzzleBusy, puzzleRun, resolvePuzzleErrorMessage, setPuzzleError],
);
const dragPuzzlePiece = useCallback(
@@ -1461,9 +1588,126 @@ export function PlatformEntryFlowShellImpl({
}
setPuzzleError(null);
setPuzzleRun(dragLocalPuzzlePiece(puzzleRun, payload));
if (isLocalPuzzleRun(puzzleRun)) {
setPuzzleRun(dragLocalPuzzlePiece(puzzleRun, payload));
return;
}
void dragPuzzlePieceOrGroup(puzzleRun.runId, payload)
.then(({ run }) => {
setPuzzleRun(run);
})
.catch((error) => {
setPuzzleError(resolvePuzzleErrorMessage(error, '拖动拼图块失败。'));
});
},
[isPuzzleBusy, puzzleRun],
[isPuzzleBusy, puzzleRun, resolvePuzzleErrorMessage, setPuzzleError],
);
useEffect(() => {
if (selectionStage !== 'puzzle-runtime' || !puzzleRun?.currentLevel) {
return;
}
if (puzzleRun.currentLevel.status !== 'playing') {
return;
}
const timerId = window.setInterval(() => {
if (!isLocalPuzzleRun(puzzleRun)) {
return;
}
setPuzzleRun((currentRun) =>
currentRun ? refreshLocalPuzzleTimer(currentRun) : currentRun,
);
}, 250);
return () => window.clearInterval(timerId);
}, [puzzleRun, selectionStage]);
const setPuzzleRuntimePaused = useCallback(
async (paused: boolean) => {
if (!puzzleRun?.currentLevel) {
return;
}
if (isLocalPuzzleRun(puzzleRun)) {
setPuzzleRun((currentRun) =>
currentRun ? setLocalPuzzlePaused(currentRun, paused) : currentRun,
);
return;
}
try {
const { run } = await updatePuzzleRunPause(puzzleRun.runId, {
paused,
});
setPuzzleRun(run);
void platformBootstrap.refreshProfileDashboard();
} catch (error) {
setPuzzleError(
resolvePuzzleErrorMessage(error, '更新拼图计时状态失败。'),
);
}
},
[platformBootstrap, puzzleRun, resolvePuzzleErrorMessage, setPuzzleError],
);
const syncPuzzleRuntimeTimeout = useCallback(async () => {
if (
!puzzleRun?.currentLevel ||
puzzleRun.currentLevel.status !== 'playing'
) {
return;
}
if (isLocalPuzzleRun(puzzleRun)) {
setPuzzleRun((currentRun) =>
currentRun ? refreshLocalPuzzleTimer(currentRun) : currentRun,
);
return;
}
try {
const { run } = await getPuzzleRun(puzzleRun.runId);
setPuzzleRun(run);
} catch (error) {
setPuzzleError(
resolvePuzzleErrorMessage(error, '同步拼图失败状态失败。'),
);
}
}, [puzzleRun, resolvePuzzleErrorMessage, setPuzzleError]);
const usePuzzleProp = useCallback(
async (propKind: 'hint' | 'reference' | 'freezeTime') => {
if (
!puzzleRun?.currentLevel ||
puzzleRun.currentLevel.status !== 'playing'
) {
return null;
}
if (isLocalPuzzleRun(puzzleRun)) {
const currentRun = puzzleRunRef.current ?? puzzleRun;
if (!currentRun.currentLevel) {
return null;
}
const nextRun =
propKind === 'freezeTime'
? applyLocalPuzzleFreezeTime(currentRun)
: setLocalPuzzlePaused(currentRun, propKind === 'reference');
puzzleRunRef.current = nextRun;
setPuzzleRun(nextRun);
return nextRun;
}
const { run } = await consumePuzzleRuntimeProp(puzzleRun.runId, {
propKind,
});
setPuzzleRun(run);
void platformBootstrap.refreshProfileDashboard();
return run;
},
[platformBootstrap, puzzleRun],
);
useEffect(() => {
@@ -1622,34 +1866,6 @@ export function PlatformEntryFlowShellImpl({
});
}, [handleCustomWorldSelect, runProtectedAction, selectedDetailEntry]);
const handleExperienceRpgWork = useCallback(
(work: (typeof creationHubItems)[number]) => {
if (!work.profileId) {
return;
}
runProtectedAction(() => {
const matchedEntry = platformBootstrap.savedCustomWorldEntries.find(
(entry) => entry.profileId === work.profileId,
);
if (!matchedEntry) {
platformBootstrap.setPlatformError(
'未找到可体验的作品,请刷新后重试。',
);
return;
}
handleCustomWorldSelect(matchedEntry.profile);
});
},
[
handleCustomWorldSelect,
platformBootstrap,
platformBootstrap.savedCustomWorldEntries,
runProtectedAction,
],
);
const handleDeleteLibraryEntry = useCallback(
(entry: CustomWorldLibraryEntry<CustomWorldProfile>) => {
if (!entry.profileId || deletingCreationWorkId) {
@@ -1815,6 +2031,32 @@ export function PlatformEntryFlowShellImpl({
],
);
const clearSelectedPublicWorkAuthor = useCallback(() => {
publicWorkAuthorRequestKeyRef.current += 1;
setSelectedPublicWorkAuthor(null);
}, []);
const loadSelectedPublicWorkAuthor = useCallback(
(entry: PlatformPublicGalleryCard) => {
const requestKey = publicWorkAuthorRequestKeyRef.current + 1;
publicWorkAuthorRequestKeyRef.current = requestKey;
setSelectedPublicWorkAuthor(null);
void resolvePublicWorkAuthorSummary(entry)
.then((author) => {
if (publicWorkAuthorRequestKeyRef.current === requestKey) {
setSelectedPublicWorkAuthor(author);
}
})
.catch(() => {
if (publicWorkAuthorRequestKeyRef.current === requestKey) {
setSelectedPublicWorkAuthor(null);
}
});
},
[],
);
const openPublicWorkDetail = useCallback(
(entry: PlatformPublicGalleryCard) => {
setSelectedPublicWorkDetail(entry);
@@ -1829,19 +2071,44 @@ export function PlatformEntryFlowShellImpl({
[setSelectionStage],
);
useEffect(() => {
const detailEntry =
selectionStage === 'work-detail'
? selectedPublicWorkDetail
: selectionStage === 'detail' &&
selectedDetailEntry &&
selectedDetailEntry.visibility !== 'draft'
? mapRpgGalleryCardToPublicWorkDetail(selectedDetailEntry)
: null;
if (!detailEntry) {
clearSelectedPublicWorkAuthor();
return;
}
loadSelectedPublicWorkAuthor(detailEntry);
}, [
clearSelectedPublicWorkAuthor,
loadSelectedPublicWorkAuthor,
selectedDetailEntry,
selectedPublicWorkDetail,
selectionStage,
]);
const openRpgPublicWorkDetail = useCallback(
async (entry: CustomWorldGalleryCard) => {
setIsPublicWorkDetailBusy(true);
setPublicWorkDetailError(null);
clearSelectedPublicWorkAuthor();
setSelectedPublicWorkDetail(entry);
setSelectionStage('work-detail');
try {
const detailEntry =
await detailNavigation.loadGalleryDetailEntry(entry);
setSelectedDetailEntry(detailEntry);
setSelectedPublicWorkDetail(
mapRpgGalleryCardToPublicWorkDetail(detailEntry),
);
const detailCard = mapRpgGalleryCardToPublicWorkDetail(detailEntry);
setSelectedPublicWorkDetail(detailCard);
if (detailEntry.publicWorkCode?.trim()) {
pushAppHistoryPath(
buildPublicWorkStagePath('work-detail', detailEntry.publicWorkCode),
@@ -1856,7 +2123,12 @@ export function PlatformEntryFlowShellImpl({
setIsPublicWorkDetailBusy(false);
}
},
[detailNavigation, setSelectedDetailEntry, setSelectionStage],
[
clearSelectedPublicWorkAuthor,
detailNavigation,
setSelectedDetailEntry,
setSelectionStage,
],
);
const openPuzzlePublicWorkDetail = useCallback(
@@ -2004,7 +2276,13 @@ export function PlatformEntryFlowShellImpl({
}
if (isPuzzleGalleryEntry(selectedPublicWorkDetail)) {
void startPuzzleRunFromProfile(selectedPublicWorkDetail.profileId);
const work = mapPublicWorkDetailToPuzzleWork(selectedPublicWorkDetail);
if (!work) {
setPublicWorkDetailError('当前拼图作品信息不完整,暂时无法进入玩法。');
return;
}
setPublicWorkDetailError(null);
void startPuzzleRunFromProfile(work.profileId, 'work-detail', work, true);
return;
}
@@ -2106,7 +2384,7 @@ export function PlatformEntryFlowShellImpl({
(entry) => entry.profileId !== nextEntry.profileId,
),
]);
detailNavigation.openSavedCustomWorldEditor(nextEntry);
void detailNavigation.openSavedCustomWorldEditor(nextEntry);
})
.catch((error) => {
setPublicWorkDetailError(
@@ -2272,7 +2550,7 @@ export function PlatformEntryFlowShellImpl({
setSearchedPublicUser(user);
} catch (error) {
setPublicSearchError(
resolveRpgCreationErrorMessage(error, '未找到对应的叙世号或作品号。'),
resolveRpgCreationErrorMessage(error, '未找到对应的陶泥号或作品号。'),
);
} finally {
setIsSearchingPublicCode(false);
@@ -2539,9 +2817,6 @@ export function PlatformEntryFlowShellImpl({
handleDeletePublishedWork(item);
}}
deletingWorkId={deletingCreationWorkId}
onExperienceRpg={(item) => {
handleExperienceRpgWork(item);
}}
rpgLibraryEntries={platformBootstrap.savedCustomWorldEntries}
bigFishItems={isBigFishCreationVisible ? bigFishWorks : []}
onOpenBigFishDetail={
@@ -2553,15 +2828,6 @@ export function PlatformEntryFlowShellImpl({
}
: undefined
}
onExperienceBigFish={
isBigFishCreationVisible
? (item) => {
runProtectedAction(() => {
void startBigFishRunFromWork(item, 'platform');
});
}
: null
}
onDeleteBigFish={
isBigFishCreationVisible
? (item) => {
@@ -2575,11 +2841,6 @@ export function PlatformEntryFlowShellImpl({
void openPuzzleDraft(item);
});
}}
onExperiencePuzzle={(profileId) => {
runProtectedAction(() => {
void startPuzzleRunFromProfile(profileId, 'platform');
});
}}
onDeletePuzzle={(item) => {
handleDeletePuzzleWork(item);
}}
@@ -2648,7 +2909,7 @@ export function PlatformEntryFlowShellImpl({
}}
onOpenLibraryDetail={(entry) => {
runProtectedAction(() => {
detailNavigation.openLibraryDetail(entry);
void detailNavigation.openLibraryDetail(entry);
});
}}
onDeleteLibraryEntry={(entry) => {
@@ -2691,10 +2952,12 @@ export function PlatformEntryFlowShellImpl({
>
<PlatformWorkDetailView
entry={selectedPublicWorkDetail}
authorAvatarUrl={selectedPublicWorkAuthor?.avatarUrl ?? null}
isBusy={isPublicWorkDetailBusy || isPuzzleBusy || isBigFishBusy}
error={publicWorkDetailError}
onBack={() => {
setPublicWorkDetailError(null);
clearSelectedPublicWorkAuthor();
setSelectionStage('platform');
}}
onStart={startSelectedPublicWork}
@@ -2720,10 +2983,12 @@ export function PlatformEntryFlowShellImpl({
) : selectedDetailEntry.visibility !== 'draft' ? (
<PlatformWorkDetailView
entry={mapRpgGalleryCardToPublicWorkDetail(selectedDetailEntry)}
authorAvatarUrl={selectedPublicWorkAuthor?.avatarUrl ?? null}
isBusy={detailNavigation.isMutatingDetail}
error={detailNavigation.detailError}
onBack={() => {
detailNavigation.setDetailError(null);
clearSelectedPublicWorkAuthor();
entryNavigation.backToPlatformHome();
}}
onStart={handleStartSelectedWorld}
@@ -2747,7 +3012,7 @@ export function PlatformEntryFlowShellImpl({
detailNavigation.isSelectedWorldOwned
? () => {
runProtectedAction(() => {
detailNavigation.openSavedCustomWorldEditor(
void detailNavigation.openSavedCustomWorldEditor(
selectedDetailEntry,
);
});
@@ -2988,9 +3253,6 @@ export function PlatformEntryFlowShellImpl({
>
<PuzzleAgentWorkspace
session={puzzleSession}
activeOperation={puzzleOperation}
streamingReplyText={streamingPuzzleReplyText}
isStreamingReply={isStreamingPuzzleReply}
isBusy={isPuzzleBusy || isStreamingPuzzleReply}
error={puzzleError}
onBack={leavePuzzleFlow}
@@ -3000,6 +3262,10 @@ export function PlatformEntryFlowShellImpl({
onExecuteAction={(payload) => {
void executePuzzleAction(payload);
}}
initialFormPayload={puzzleFormDraftPayload}
onCreateFromForm={(payload) => {
void createPuzzleDraftFromForm(payload);
}}
/>
</Suspense>
</motion.div>
@@ -3033,7 +3299,11 @@ export function PlatformEntryFlowShellImpl({
setSelectionStage('puzzle-agent-workspace');
}}
onRetry={() => {
void executePuzzleAction({ action: 'compile_puzzle_draft' });
void executePuzzleAction(
buildPuzzleCompileActionFromFormPayload(
puzzleFormDraftPayload,
),
);
}}
onInterrupt={undefined}
backLabel="返回创作中心"
@@ -3117,6 +3387,7 @@ export function PlatformEntryFlowShellImpl({
void startPuzzleRunFromProfile(
selectedPuzzleDetail.profileId,
'puzzle-gallery-detail',
selectedPuzzleDetail,
);
}}
/>
@@ -3155,6 +3426,9 @@ export function PlatformEntryFlowShellImpl({
onAdvanceNextLevel={() => {
void advancePuzzleLevel();
}}
onPauseChange={setPuzzleRuntimePaused}
onUseProp={usePuzzleProp}
onTimeExpired={syncPuzzleRuntimeTimeout}
/>
</Suspense>
{isPuzzleNextLevelGenerating ? (
@@ -3474,7 +3748,7 @@ export function PlatformEntryFlowShellImpl({
{searchedPublicUser.displayName}
</div>
<div className="mt-2 text-sm text-[var(--platform-text-soft)]">
{searchedPublicUser.publicUserCode}
{searchedPublicUser.publicUserCode}
</div>
</div>
) : null}