﻿import { ArrowRight, Loader2, Sparkles } from 'lucide-react';
import { AnimatePresence, motion } from 'motion/react';
import {
  type Dispatch,
  lazy,
  type SetStateAction,
  Suspense,
  useCallback,
  useEffect,
  useMemo,
  useRef,
  useState,
} from 'react';

import type { PublicUserSummary } from '../../../packages/shared/src/contracts/auth';
import type {
  BigFishRuntimeSnapshotResponse,
  BigFishSessionSnapshotResponse,
  ExecuteBigFishActionRequest,
  SendBigFishMessageRequest,
  SubmitBigFishInputRequest,
} from '../../../packages/shared/src/contracts/bigFish';
import type { BigFishWorkSummary } from '../../../packages/shared/src/contracts/bigFishWorkSummary';
import type {
  CreativeAgentSessionSnapshot,
  CreativeAgentSseEvent,
  CreativeDraftEditResult,
  StreamCreativeAgentMessageRequest,
} from '../../../packages/shared/src/contracts/creativeAgent';
import type { CustomWorldWorkSummary } from '../../../packages/shared/src/contracts/customWorldWorkSummary';
import type {
  CreateMatch3DSessionRequest,
  ExecuteMatch3DActionRequest,
  Match3DActionResponse,
  Match3DAgentSessionSnapshot,
  Match3DSessionResponse,
  SendMatch3DMessageRequest,
} from '../../../packages/shared/src/contracts/match3dAgent';
import type { Match3DRunSnapshot } from '../../../packages/shared/src/contracts/match3dRuntime';
import type {
  Match3DWorkProfile,
  Match3DWorkSummary,
} from '../../../packages/shared/src/contracts/match3dWorks';
import type {
  PuzzleAgentActionRequest,
  PuzzleAgentOperationRecord,
} from '../../../packages/shared/src/contracts/puzzleAgentActions';
import type {
  PuzzleDraftLevel,
  PuzzleResultDraft,
} from '../../../packages/shared/src/contracts/puzzleAgentDraft';
import type {
  CreatePuzzleAgentSessionRequest,
  PuzzleAgentSessionSnapshot,
  SendPuzzleAgentMessageRequest,
} from '../../../packages/shared/src/contracts/puzzleAgentSession';
import type { PuzzleCreativeTemplateSelection } from '../../../packages/shared/src/contracts/puzzleCreativeTemplate';
import type {
  PuzzleRunSnapshot,
  PuzzleRuntimePropKind,
  SubmitPuzzleLeaderboardRequest,
} from '../../../packages/shared/src/contracts/puzzleRuntimeSession';
import type { PuzzleWorkSummary } from '../../../packages/shared/src/contracts/puzzleWorkSummary';
import type {
  CustomWorldGalleryCard,
  CustomWorldLibraryEntry,
  ProfilePlayedWorkSummary,
  ProfilePlayStatsResponse,
  ProfileSaveArchiveResumeResponse,
  ProfileSaveArchiveSummary,
} from '../../../packages/shared/src/contracts/runtime';
import type {
  CreateSquareHoleSessionRequest,
  ExecuteSquareHoleActionRequest,
  SendSquareHoleMessageRequest,
  SquareHoleActionResponse,
  SquareHoleSessionResponse,
  SquareHoleSessionSnapshot,
} from '../../../packages/shared/src/contracts/squareHoleAgent';
import type { SquareHoleRunSnapshot } from '../../../packages/shared/src/contracts/squareHoleRuntime';
import type {
  SquareHoleWorkProfile,
  SquareHoleWorkSummary,
} from '../../../packages/shared/src/contracts/squareHoleWorks';
import type {
  CreateVisualNovelSessionRequest,
  ExecuteVisualNovelAgentActionRequest,
  SendVisualNovelMessageRequest,
  VisualNovelAgentSessionSnapshot,
  VisualNovelResultDraft,
  VisualNovelRunSnapshot,
  VisualNovelRuntimeActionRequest,
  VisualNovelSessionResponse,
  VisualNovelWorkDetail,
  VisualNovelWorkSummary,
} from '../../../packages/shared/src/contracts/visualNovel';
import { buildCustomWorldPlayableCharacters } from '../../data/characterPresets';
import {
  buildPublicWorkStagePath,
  pushAppHistoryPath,
} from '../../routing/appPageRoutes';
import {
  ApiClientError,
  BACKGROUND_AUTH_REQUEST_OPTIONS,
} from '../../services/apiClient';
import {
  getPublicAuthUserByCode,
  getPublicAuthUserById,
} from '../../services/authService';
import {
  createBigFishCreationSession,
  executeBigFishCreationAction,
  getBigFishCreationSession,
  streamBigFishCreationMessage,
} from '../../services/big-fish-creation';
import {
  likeBigFishGalleryWork,
  listBigFishGallery,
  remixBigFishGalleryWork,
} from '../../services/big-fish-gallery';
import {
  recordBigFishPlay,
  startBigFishRun as startBigFishRuntimeRun,
  submitBigFishInput as submitBigFishRuntimeInput,
} from '../../services/big-fish-runtime';
import {
  deleteBigFishWork,
  listBigFishWorks,
} from '../../services/big-fish-works';
import {
  cancelCreativeAgentSession,
  confirmCreativePuzzleTemplate,
  createCreativeAgentSession,
  streamCreativeAgentMessage,
  streamCreativeDraftEdit,
} from '../../services/creative-agent';
import {
  readCustomWorldAgentUiState,
  shouldRestoreCustomWorldAgentUiState,
} from '../../services/customWorldAgentUiState';
import { match3dCreationClient } from '../../services/match3d-creation';
import {
  clickMatch3DItem,
  finishMatch3DTimeUp,
  restartMatch3DRun,
  startMatch3DRun,
  stopMatch3DRun,
} from '../../services/match3d-runtime';
import {
  deleteMatch3DWork,
  getMatch3DWorkDetail,
  listMatch3DGallery,
  listMatch3DWorks,
} from '../../services/match3d-works';
import {
  buildBigFishGenerationAnchorEntries,
  buildMiniGameDraftGenerationProgress,
  buildPuzzleGenerationAnchorEntries,
  buildSquareHoleGenerationAnchorEntries,
  createMiniGameDraftGenerationState,
  type MiniGameDraftGenerationState,
} from '../../services/miniGameDraftGenerationProgress';
import { getPlatformProfileDashboard } from '../../services/platform-entry/platformProfileClient';
import {
  buildBigFishPublicWorkCode,
  buildMatch3DPublicWorkCode,
  buildPuzzlePublicWorkCode,
  buildSquareHolePublicWorkCode,
  buildVisualNovelPublicWorkCode,
  isSameBigFishPublicWorkCode,
  isSameMatch3DPublicWorkCode,
  isSamePuzzlePublicWorkCode,
  isSameSquareHolePublicWorkCode,
  isSameVisualNovelPublicWorkCode,
} from '../../services/publicWorkCode';
import {
  createPuzzleAgentSession,
  executePuzzleAgentAction,
  getPuzzleAgentSession,
  streamPuzzleAgentMessage,
} from '../../services/puzzle-agent';
import {
  getPuzzleGalleryDetail,
  likePuzzleGalleryWork,
  listPuzzleGallery,
  remixPuzzleGalleryWork,
} from '../../services/puzzle-gallery';
import {
  generatePuzzleOnboardingWork,
  savePuzzleOnboardingWork,
} from '../../services/puzzle-onboarding';
import {
  advancePuzzleNextLevel,
  startPuzzleRun,
  submitPuzzleLeaderboard,
} from '../../services/puzzle-runtime';
import {
  advanceLocalPuzzleLevel,
  applyLocalPuzzleFreezeTime,
  dragLocalPuzzlePiece,
  extendLocalPuzzleTime,
  isLocalPuzzleRun,
  refreshLocalPuzzleTimer,
  resolvePuzzleRestartLevelId,
  restartLocalPuzzleLevel,
  setLocalPuzzlePaused,
  startLocalPuzzleRun,
  submitLocalPuzzleLeaderboard,
  swapLocalPuzzlePieces,
} from '../../services/puzzle-runtime/puzzleLocalRuntime';
import {
  claimPuzzleWorkPointIncentive,
  deletePuzzleWork,
  listPuzzleWorks,
  updatePuzzleWork,
} from '../../services/puzzle-works';
import { deleteRpgCreationAgentSession } from '../../services/rpg-creation';
import { rpgCreationPreviewAdapter } from '../../services/rpg-creation/rpgCreationPreviewAdapter';
import {
  deleteRpgEntryWorldProfile,
  getRpgEntryWorldGalleryDetailByCode,
  likeRpgEntryWorldGallery,
  recordRpgEntryWorldGalleryPlay,
  remixRpgEntryWorldGallery,
} from '../../services/rpg-entry/rpgEntryLibraryClient';
import {
  getRpgProfilePlayStats,
  submitRpgProfileFeedback,
} from '../../services/rpg-entry/rpgProfileClient';
import { requestRpgRuntimeJson } from '../../services/rpg-runtime/rpgRuntimeRequest';
import { squareHoleCreationClient } from '../../services/square-hole-creation';
import {
  dropSquareHoleShape,
  finishSquareHoleTimeUp,
  restartSquareHoleRun,
  startSquareHoleRun,
  stopSquareHoleRun,
} from '../../services/square-hole-runtime';
import {
  deleteSquareHoleWork,
  getSquareHoleWorkDetail,
  listSquareHoleGallery,
  listSquareHoleWorks,
} from '../../services/square-hole-works';
import {
  compileVisualNovelWorkProfile,
  createVisualNovelSession,
  executeVisualNovelAction,
  getVisualNovelSession,
  streamVisualNovelMessage,
} from '../../services/visual-novel-creation';
import {
  listVisualNovelGallery,
  startVisualNovelRun,
  streamVisualNovelRuntimeAction,
} from '../../services/visual-novel-runtime';
import {
  deleteVisualNovelWork,
  getVisualNovelWorkDetail,
  listVisualNovelWorks,
  publishVisualNovelWork,
  updateVisualNovelWork,
} from '../../services/visual-novel-works';
import type { CustomWorldProfile } from '../../types';
import { useAuthUi } from '../auth/AuthUiContext';
import { PublishShareModal } from '../common/PublishShareModal';
import type { PublishShareModalPayload } from '../common/publishShareModalModel';
import { UnifiedModal } from '../common/UnifiedModal';
import { resolveCreativeAgentTargetSelectionStage } from '../creative-agent/creativeAgentViewModel';
import {
  isBigFishGalleryEntry,
  isMatch3DGalleryEntry,
  isPuzzleGalleryEntry,
  isSquareHoleGalleryEntry,
  isVisualNovelGalleryEntry,
  mapBigFishWorkToPlatformGalleryCard,
  mapMatch3DWorkToPlatformGalleryCard,
  mapPuzzleWorkToPlatformGalleryCard,
  mapSquareHoleWorkToPlatformGalleryCard,
  mapVisualNovelWorkToPlatformGalleryCard,
  type PlatformPublicGalleryCard,
} from '../rpg-entry/rpgEntryWorldPresentation';
import { useRpgCreationAgentOperationPolling } from '../rpg-entry/useRpgCreationAgentOperationPolling';
import { useRpgCreationEnterWorld } from '../rpg-entry/useRpgCreationEnterWorld';
import { useRpgCreationResultAutosave } from '../rpg-entry/useRpgCreationResultAutosave';
import { useRpgCreationSessionController } from '../rpg-entry/useRpgCreationSessionController';
import { createMockVisualNovelRunFromDraft } from '../visual-novel-runtime/visualNovelMockData';
import { PlatformEntryCreationTypeModal } from './PlatformEntryCreationTypeModal';
import { PlatformFeedbackView } from './PlatformFeedbackView';
import type { PlatformCreationTypeId } from './platformEntryCreationTypes';
import {
  getVisiblePlatformCreationTypes,
  isPlatformCreationTypeVisible,
} from './platformEntryCreationTypes';
import {
  PlatformEntryHomeView,
  type PlatformHomeTab,
} from './PlatformEntryHomeView';
import {
  buildCreationHubFallbackItems,
  resolveRpgCreationErrorMessage,
} from './platformEntryShared';
import type { PlatformEntryFlowShellProps } from './platformEntryTypes';
import { PlatformEntryWorldDetailView } from './PlatformEntryWorldDetailView';
import { PlatformWorkDetailView } from './PlatformWorkDetailView';
import { usePlatformCreationAgentFlowController } from './usePlatformCreationAgentFlowController';
import { usePlatformEntryBootstrap } from './usePlatformEntryBootstrap';
import { usePlatformEntryLibraryDetail } from './usePlatformEntryLibraryDetail';
import { usePlatformEntryNavigation } from './usePlatformEntryNavigation';

type AgentResultPublishGateView = {
  blockers: string[];
  publishReady: boolean;
};

type PuzzleDetailReturnTarget = {
  tab: PlatformHomeTab;
};

type PuzzleRuntimeReturnStage =
  | 'puzzle-result'
  | 'puzzle-gallery-detail'
  | 'work-detail'
  | 'platform';
type PuzzleRuntimeAuthMode = 'default' | 'isolated';

type PuzzleOnboardingPhase = 'input' | 'generating' | 'generated';

type PuzzleOnboardingDraft = {
  promptText: string;
  item: PuzzleWorkSummary;
};

type BigFishRuntimeReturnStage = 'big-fish-result' | 'work-detail' | 'platform';
type BigFishRuntimeSessionSource = 'draft' | 'work' | null;
type RecommendRuntimeKind =
  | 'big-fish'
  | 'match3d'
  | 'puzzle'
  | 'square-hole'
  | 'visual-novel'
  | 'rpg';
type SquareHoleRuntimeReturnStage =
  | 'square-hole-result'
  | 'work-detail'
  | 'platform';
type VisualNovelRuntimeReturnStage =
  | 'visual-novel-result'
  | 'visual-novel-gallery-detail'
  | 'work-detail'
  | 'platform';

type PuzzleSaveArchiveState = {
  runtimeKind?: unknown;
  entryProfileId?: unknown;
  currentProfileId?: unknown;
  currentLevelId?: unknown;
};

type DeleteCreationWorkConfirmation = {
  id: string;
  title: string;
  detail: string;
  run: () => void;
};

async function resumePuzzleProfileSaveArchiveRaw(worldKey: string) {
  return requestRpgRuntimeJson<
    ProfileSaveArchiveResumeResponse<PuzzleSaveArchiveState>
  >(
    `/profile/save-archives/${encodeURIComponent(worldKey)}`,
    { method: 'POST' },
    '恢复拼图存档失败',
  );
}

type AgentResultBlockerView = {
  code?: string;
  message: string;
};

const AGENT_RESULT_STRUCTURAL_BLOCKER_CODES = new Set([
  'publish_missing_world_hook',
  'publish_missing_player_premise',
  'publish_missing_core_conflict',
  'publish_missing_main_chapter',
  'publish_missing_first_act',
]);
const RECOMMEND_RUNTIME_BACKGROUND_AUTH_OPTIONS =
  BACKGROUND_AUTH_REQUEST_OPTIONS;
const PUBLIC_PUZZLE_RUNTIME_AUTH_OPTIONS =
  RECOMMEND_RUNTIME_BACKGROUND_AUTH_OPTIONS;

function getPlatformPublicGalleryEntryTime(entry: PlatformPublicGalleryCard) {
  const rawTime = entry.publishedAt ?? entry.updatedAt;
  const timestamp = new Date(rawTime).getTime();
  return Number.isNaN(timestamp) ? 0 : timestamp;
}

function getPlatformPublicGalleryEntryKey(entry: PlatformPublicGalleryCard) {
  const kind = isBigFishGalleryEntry(entry)
    ? 'big-fish'
    : isPuzzleGalleryEntry(entry)
      ? 'puzzle'
      : isMatch3DGalleryEntry(entry)
        ? 'match3d'
        : isSquareHoleGalleryEntry(entry)
          ? 'square-hole'
          : isVisualNovelGalleryEntry(entry)
            ? 'visual-novel'
          : 'rpg';
  return `${kind}:${entry.ownerUserId}:${entry.profileId}`;
}

function getPlatformRecommendRuntimeKind(
  entry: PlatformPublicGalleryCard,
): RecommendRuntimeKind {
  if (isBigFishGalleryEntry(entry)) {
    return 'big-fish';
  }

  if (isPuzzleGalleryEntry(entry)) {
    return 'puzzle';
  }

  if (isMatch3DGalleryEntry(entry)) {
    return 'match3d';
  }

  if (isSquareHoleGalleryEntry(entry)) {
    return 'square-hole';
  }

  if (isVisualNovelGalleryEntry(entry)) {
    return 'visual-novel';
  }

  return 'rpg';
}

function isSamePlatformPublicGalleryEntry(
  left: PlatformPublicGalleryCard,
  right: PlatformPublicGalleryCard,
) {
  return (
    getPlatformPublicGalleryEntryKey(left) ===
    getPlatformPublicGalleryEntryKey(right)
  );
}

function mergePlatformPublicGalleryEntries(
  rpgEntries: CustomWorldGalleryCard[],
  puzzleEntries: PlatformPublicGalleryCard[],
) {
  const entryMap = new Map<string, PlatformPublicGalleryCard>();

  [...rpgEntries, ...puzzleEntries].forEach((entry) => {
    entryMap.set(getPlatformPublicGalleryEntryKey(entry), entry);
  });

  return Array.from(entryMap.values()).sort(
    (left, right) =>
      getPlatformPublicGalleryEntryTime(right) -
      getPlatformPublicGalleryEntryTime(left),
  );
}

function mapRpgGalleryCardToPublicWorkDetail(
  entry: CustomWorldGalleryCard,
): PlatformPublicGalleryCard {
  return entry;
}

function mapPuzzleWorkToPublicWorkDetail(
  item: PuzzleWorkSummary,
): PlatformPublicGalleryCard {
  return mapPuzzleWorkToPlatformGalleryCard(item);
}

function resolveVisiblePuzzleDetailCoverCount(
  entry: PlatformPublicGalleryCard | null,
  run: PuzzleRunSnapshot | null,
) {
  if (!entry || !isPuzzleGalleryEntry(entry)) {
    return 1;
  }

  if (run?.entryProfileId !== entry.profileId) {
    return 1;
  }

  // 中文注释：封面首图永远公开，后续封面跟随当前玩家本次 run 的通关进度即时解锁。
  return Math.max(1, run.clearedLevelCount + 1);
}

function mapMatch3DWorkToPublicWorkDetail(
  item: Match3DWorkSummary,
): PlatformPublicGalleryCard {
  return mapMatch3DWorkToPlatformGalleryCard(item);
}

function mapSquareHoleWorkToPublicWorkDetail(
  item: SquareHoleWorkSummary,
): PlatformPublicGalleryCard {
  return mapSquareHoleWorkToPlatformGalleryCard(item);
}

function mapBigFishWorkToPublicWorkDetail(
  item: BigFishWorkSummary,
): PlatformPublicGalleryCard {
  return mapBigFishWorkToPlatformGalleryCard(item);
}

function mapVisualNovelWorkToPublicWorkDetail(
  item: VisualNovelWorkSummary,
): PlatformPublicGalleryCard {
  return mapVisualNovelWorkToPlatformGalleryCard(item);
}

function mapVisualNovelWorkDetailToSession(
  work: VisualNovelWorkDetail,
): VisualNovelAgentSessionSnapshot {
  return {
    sessionId: work.sourceSessionId?.trim() || work.workId,
    ownerUserId: work.summary.ownerUserId,
    sourceMode: work.draft.sourceMode,
    status: 'ready',
    messages: [],
    draft: work.draft,
    pendingAction: null,
    createdAt: work.createdAt,
    updatedAt: work.summary.updatedAt,
  };
}

function mapPublicWorkDetailToMatch3DWork(
  entry: PlatformPublicGalleryCard,
): Match3DWorkSummary | null {
  if (!isMatch3DGalleryEntry(entry)) {
    return null;
  }

  return {
    workId: entry.workId,
    profileId: entry.profileId,
    ownerUserId: entry.ownerUserId,
    sourceSessionId:
      'sourceSessionId' in entry && typeof entry.sourceSessionId === 'string'
        ? entry.sourceSessionId
        : null,
    gameName: entry.worldName,
    themeText: entry.themeTags[0] ?? '经典消除',
    summary: entry.summaryText,
    tags: entry.themeTags,
    coverImageSrc: entry.coverImageSrc,
    referenceImageSrc: null,
    clearCount: 12,
    difficulty: 4,
    publicationStatus: 'published',
    playCount: entry.playCount ?? 0,
    updatedAt: entry.updatedAt,
    publishedAt: entry.publishedAt,
    publishReady: true,
  };
}

function buildMatch3DProfileFromSession(
  session: Match3DAgentSessionSnapshot | null,
): Match3DWorkProfile | null {
  const draft = session?.draft;
  if (!session || !draft?.profileId) {
    return null;
  }

  const now = session.updatedAt || new Date().toISOString();
  return {
    workId: draft.profileId,
    profileId: draft.profileId,
    ownerUserId: 'current-user',
    sourceSessionId: session.sessionId,
    gameName: draft.gameName,
    themeText: draft.themeText,
    summary: draft.summary ?? draft.summaryText ?? '',
    tags: draft.tags,
    coverImageSrc: draft.coverImageSrc ?? draft.referenceImageSrc ?? null,
    referenceImageSrc: draft.referenceImageSrc ?? null,
    clearCount: draft.clearCount,
    difficulty: draft.difficulty,
    publicationStatus: 'draft',
    playCount: 0,
    updatedAt: now,
    publishedAt: null,
    publishReady: Boolean(draft.publishReady),
  };
}

function mapPublicWorkDetailToPuzzleWork(
  entry: PlatformPublicGalleryCard,
): PuzzleWorkSummary | null {
  if (!isPuzzleGalleryEntry(entry)) {
    return null;
  }

  return {
    workId: entry.workId,
    profileId: entry.profileId,
    ownerUserId: entry.ownerUserId,
    sourceSessionId:
      'sourceSessionId' in entry && typeof entry.sourceSessionId === 'string'
        ? entry.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,
    pointIncentiveTotalHalfPoints: 0,
    pointIncentiveClaimedPoints: 0,
    pointIncentiveTotalPoints: 0,
    pointIncentiveClaimablePoints: 0,
    publishReady: true,
    levels:
      entry.coverSlides?.map((slide, index) => ({
        levelId: slide.id || `puzzle-level-${index + 1}`,
        levelName: slide.label,
        pictureDescription: entry.summaryText,
        candidates: [],
        selectedCandidateId: null,
        coverImageSrc: slide.imageSrc,
        coverAssetId: null,
        generationStatus: 'ready' as const,
      })) ?? [],
  };
}

function mapPublicWorkDetailToBigFishWork(
  entry: PlatformPublicGalleryCard,
): BigFishWorkSummary | null {
  if (!isBigFishGalleryEntry(entry)) {
    return null;
  }

  const levelCount = Number.parseInt(
    entry.themeTags.find((tag) => /^\d+级$/u.test(tag))?.replace('级', '') ??
      '0',
    10,
  );

  return {
    workId: entry.workId,
    sourceSessionId: entry.profileId,
    ownerUserId: entry.ownerUserId,
    authorDisplayName: entry.authorDisplayName,
    title: entry.worldName,
    subtitle: entry.subtitle,
    summary: entry.summaryText,
    coverImageSrc: entry.coverImageSrc,
    status: 'published',
    updatedAt: entry.updatedAt,
    publishedAt: entry.publishedAt,
    publishReady: true,
    levelCount: Number.isNaN(levelCount) ? 0 : levelCount,
    levelMainImageReadyCount: 0,
    levelMotionReadyCount: 0,
    backgroundReady: Boolean(entry.coverImageSrc),
    playCount: entry.playCount ?? 0,
    remixCount: entry.remixCount ?? 0,
    likeCount: entry.likeCount ?? 0,
  };
}

function mapPublicWorkDetailToSquareHoleWork(
  entry: PlatformPublicGalleryCard,
): SquareHoleWorkSummary | null {
  if (!isSquareHoleGalleryEntry(entry)) {
    return null;
  }

  return {
    workId: entry.workId,
    profileId: entry.profileId,
    ownerUserId: entry.ownerUserId,
    sourceSessionId:
      'sourceSessionId' in entry && typeof entry.sourceSessionId === 'string'
        ? entry.sourceSessionId
        : null,
    gameName: entry.worldName,
    themeText: entry.themeTags[0] ?? '方洞挑战',
    twistRule: entry.subtitle,
    summary: entry.summaryText,
    tags: entry.themeTags,
    coverImageSrc: entry.coverImageSrc,
    backgroundPrompt: entry.backgroundPrompt ?? '方洞挑战运行背景',
    backgroundImageSrc: entry.backgroundImageSrc ?? null,
    shapeOptions: entry.shapeOptions ?? [],
    holeOptions: entry.holeOptions ?? [],
    shapeCount: entry.shapeCount ?? 8,
    difficulty: entry.difficulty ?? 4,
    publicationStatus: 'published',
    playCount: entry.playCount ?? 0,
    updatedAt: entry.updatedAt,
    publishedAt: entry.publishedAt,
    publishReady: true,
  };
}

function buildSquareHoleProfileFromSession(
  session: SquareHoleSessionSnapshot | null,
): SquareHoleWorkProfile | null {
  const draft = session?.draft;
  if (!session || !draft?.profileId) {
    return null;
  }

  const now = session.updatedAt || new Date().toISOString();
  return {
    workId: draft.profileId,
    profileId: draft.profileId,
    ownerUserId: 'current-user',
    sourceSessionId: session.sessionId,
    gameName: draft.gameName,
    themeText: draft.themeText,
    twistRule: draft.twistRule,
    summary: draft.summary,
    tags: draft.tags,
    coverImageSrc: draft.coverImageSrc ?? null,
    backgroundPrompt: draft.backgroundPrompt,
    backgroundImageSrc: draft.backgroundImageSrc ?? null,
    shapeOptions: draft.shapeOptions,
    holeOptions: draft.holeOptions,
    shapeCount: draft.shapeCount,
    difficulty: draft.difficulty,
    publicationStatus: 'draft',
    playCount: 0,
    updatedAt: now,
    publishedAt: null,
    publishReady: Boolean(draft.publishReady),
  };
}

function mergePuzzleWorkSummary(
  current: PuzzleWorkSummary,
  updated: PuzzleWorkSummary,
): PuzzleWorkSummary {
  return current.profileId === updated.profileId ? updated : current;
}

const PUZZLE_ONBOARDING_FIRST_VISIT_STORAGE_KEY =
  'genarrative.puzzle-onboarding.first-visit.v1';
const PUZZLE_ONBOARDING_COPY = '待定待定待定';
const PUZZLE_ONBOARDING_CLEAR_COPY = '只差一步，就可以永久保留你的梦';
const PUZZLE_ONBOARDING_GENERATED_DELAY_MS = 700;

function escapePuzzleOnboardingSvgText(value: string) {
  return value
    .replace(/&/gu, '&amp;')
    .replace(/</gu, '&lt;')
    .replace(/>/gu, '&gt;')
    .replace(/"/gu, '&quot;');
}

function buildPuzzleOnboardingFallbackImage(promptText: string) {
  const trimmedPrompt = promptText.trim();
  const displayPrompt = escapePuzzleOnboardingSvgText(
    trimmedPrompt.slice(0, 12) || '百梦拼图',
  );
  return (
    'data:image/svg+xml;utf8,' +
    encodeURIComponent(`
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
  <defs>
    <linearGradient id="sky" x1="0" y1="0" x2="1" y2="1">
      <stop offset="0" stop-color="#fde68a" />
      <stop offset="0.48" stop-color="#38bdf8" />
      <stop offset="1" stop-color="#312e81" />
    </linearGradient>
    <radialGradient id="glow" cx="34%" cy="30%" r="42%">
      <stop offset="0" stop-color="#fff7ed" stop-opacity="0.86" />
      <stop offset="1" stop-color="#fff7ed" stop-opacity="0" />
    </radialGradient>
  </defs>
  <rect width="1024" height="1024" fill="url(#sky)" />
  <circle cx="352" cy="292" r="248" fill="url(#glow)" />
  <path d="M0 654 C156 570 296 680 452 610 C626 532 782 598 1024 500 V1024 H0 Z" fill="#0f172a" opacity="0.88" />
  <path d="M0 802 C196 710 332 838 540 750 C704 682 846 724 1024 670 V1024 H0 Z" fill="#111827" opacity="0.72" />
  <path d="M160 344 C242 268 340 282 430 350 C532 428 646 388 752 322 C844 266 918 302 958 362" fill="none" stroke="#fff7ed" stroke-width="18" stroke-linecap="round" opacity="0.72" />
  <path d="M188 538 H836" stroke="#ffffff" stroke-width="16" stroke-linecap="round" opacity="0.26" />
  <path d="M260 620 H764" stroke="#ffffff" stroke-width="12" stroke-linecap="round" opacity="0.2" />
  <text x="512" y="868" text-anchor="middle" font-family="sans-serif" font-size="54" font-weight="800" fill="#ffffff" opacity="0.84">${displayPrompt}</text>
</svg>`)
  );
}

function buildPuzzleOnboardingFallbackWork(
  promptText: string,
): PuzzleWorkSummary {
  const now = new Date().toISOString();
  const seed = Date.now();
  const coverImageSrc = buildPuzzleOnboardingFallbackImage(promptText);
  const level: PuzzleDraftLevel = {
    levelId: 'onboarding-local-level-1',
    levelName: '梦境拼图',
    pictureDescription: promptText,
    pictureReference: null,
    candidates: [
      {
        candidateId: 'onboarding-local-candidate-1',
        imageSrc: coverImageSrc,
        assetId: 'onboarding-local-asset-1',
        prompt: promptText,
        actualPrompt: promptText,
        sourceType: 'generated',
        selected: true,
      },
    ],
    selectedCandidateId: 'onboarding-local-candidate-1',
    coverImageSrc,
    coverAssetId: 'onboarding-local-asset-1',
    generationStatus: 'ready',
  };

  return {
    workId: `onboarding-local-work-${seed}`,
    profileId: `onboarding-local-profile-${seed}`,
    ownerUserId: 'onboarding-guest',
    sourceSessionId: null,
    authorDisplayName: '百梦主',
    workTitle: '梦境拼图',
    workDescription: promptText,
    levelName: level.levelName,
    summary: promptText,
    themeTags: ['新手引导', '拼图'],
    coverImageSrc,
    coverAssetId: level.coverAssetId,
    publicationStatus: 'draft',
    updatedAt: now,
    publishedAt: null,
    playCount: 0,
    remixCount: 0,
    likeCount: 0,
    publishReady: true,
    levels: [level],
  };
}

function shouldUseLocalPuzzleOnboardingFallback(error: unknown) {
  return (
    error instanceof ApiClientError &&
    error.status === 404 &&
    (error.code === 'NOT_FOUND' || error.message.includes('资源不存在'))
  );
}

function isMissingPuzzleWorkError(error: unknown) {
  return (
    (error instanceof ApiClientError &&
      error.status === 404 &&
      (error.code === 'NOT_FOUND' ||
        error.message.includes('资源不存在') ||
        error.message.includes('未找到'))) ||
    (error instanceof Error &&
      (error.message.includes('资源不存在') ||
        error.message.includes('未找到拼图作品')))
  );
}

function hasSeenPuzzleOnboarding() {
  if (typeof window === 'undefined') {
    return true;
  }

  try {
    return (
      window.localStorage.getItem(PUZZLE_ONBOARDING_FIRST_VISIT_STORAGE_KEY) ===
      '1'
    );
  } catch {
    return false;
  }
}

function markPuzzleOnboardingSeen() {
  if (typeof window === 'undefined') {
    return;
  }

  try {
    window.localStorage.setItem(PUZZLE_ONBOARDING_FIRST_VISIT_STORAGE_KEY, '1');
  } catch {
    // 中文注释：localStorage 不可写时只降级为本次会话展示，不影响主流程。
  }
}

function PuzzleOnboardingView({
  prompt,
  phase,
  error,
  onPromptChange,
  onSubmit,
  onSkip,
}: {
  prompt: string;
  phase: PuzzleOnboardingPhase;
  error: string | null;
  onPromptChange: (value: string) => void;
  onSubmit: () => void;
  onSkip: () => void;
}) {
  const isGenerating = phase === 'generating';
  const isGenerated = phase === 'generated';
  const canSubmit = Boolean(prompt.trim()) && !isGenerating && !isGenerated;

  return (
    <div className="relative flex min-h-screen items-center justify-center overflow-hidden bg-[radial-gradient(circle_at_30%_15%,rgba(251,191,36,0.22),transparent_30%),linear-gradient(135deg,#0f172a,#111827_46%,#1e1b4b)] px-4 py-8 text-white">
      <div className="absolute inset-0 bg-[linear-gradient(rgba(255,255,255,0.045)_1px,transparent_1px),linear-gradient(90deg,rgba(255,255,255,0.04)_1px,transparent_1px)] bg-[length:38px_38px] opacity-30" />
      <button
        type="button"
        disabled={isGenerating}
        onClick={onSkip}
        className="absolute right-4 top-4 z-10 inline-flex min-h-10 items-center justify-center rounded-full border border-white/14 bg-black/24 px-4 text-sm font-black text-white/86 shadow-[0_12px_28px_rgba(0,0,0,0.22)] backdrop-blur transition hover:border-amber-200/45 hover:text-amber-100 disabled:cursor-not-allowed disabled:opacity-45 sm:right-6 sm:top-6"
      >
        跳过
      </button>
      <section className="relative flex w-full max-w-[34rem] flex-col items-center gap-5 text-center">
        <div className="grid h-14 w-14 place-items-center rounded-[1.2rem] border border-amber-200/32 bg-amber-200/14 text-amber-100 shadow-[0_18px_48px_rgba(251,191,36,0.18)]">
          {isGenerating ? (
            <Loader2 className="h-6 w-6 animate-spin" />
          ) : (
            <Sparkles className="h-6 w-6" />
          )}
        </div>
        <h1 className="text-[2rem] font-black leading-tight sm:text-[2.85rem]">
          {PUZZLE_ONBOARDING_COPY}
        </h1>
        <form
          className="flex w-full flex-col gap-3"
          onSubmit={(event) => {
            event.preventDefault();
            onSubmit();
          }}
        >
          <textarea
            value={prompt}
            disabled={isGenerating || isGenerated}
            onChange={(event) => onPromptChange(event.target.value)}
            placeholder="把你的梦讲给我听吧"
            rows={4}
            className="min-h-32 w-full resize-none rounded-[1.25rem] border border-white/14 bg-black/28 px-4 py-4 text-base font-semibold leading-7 text-white shadow-[0_18px_50px_rgba(0,0,0,0.24)] outline-none backdrop-blur placeholder:text-white/42 focus:border-amber-200/70 focus:ring-2 focus:ring-amber-200/20 disabled:opacity-70"
          />
          <button
            type="submit"
            disabled={!canSubmit}
            className="inline-flex min-h-12 items-center justify-center gap-2 rounded-[1rem] bg-amber-200 px-5 text-sm font-black text-slate-950 transition hover:bg-amber-100 disabled:cursor-not-allowed disabled:opacity-45"
          >
            {isGenerating ? (
              <>
                <Loader2 className="h-4 w-4 animate-spin" />
                生成
              </>
            ) : (
              '生成'
            )}
          </button>
        </form>
        {error ? (
          <div className="w-full rounded-[1rem] border border-red-300/30 bg-red-500/14 px-4 py-3 text-sm font-semibold text-red-50">
            {error}
          </div>
        ) : null}
      </section>
    </div>
  );
}

function PuzzleOnboardingLoginOverlay({
  isSaving,
  error,
  onLogin,
}: {
  isSaving: boolean;
  error: string | null;
  onLogin: () => void;
}) {
  return (
    <div className="fixed inset-0 z-[110] flex items-center justify-center bg-slate-950/72 px-4 py-6 text-white backdrop-blur-md">
      <section className="flex w-full max-w-[24rem] flex-col items-center gap-5 rounded-[1.35rem] border border-white/14 bg-slate-950/94 px-5 py-6 text-center shadow-[0_28px_90px_rgba(0,0,0,0.5)]">
        <div className="grid h-12 w-12 place-items-center rounded-[1rem] bg-amber-200 text-slate-950">
          {isSaving ? (
            <Loader2 className="h-5 w-5 animate-spin" />
          ) : (
            <Sparkles className="h-5 w-5" />
          )}
        </div>
        <h2 className="text-2xl font-black leading-tight">
          {PUZZLE_ONBOARDING_CLEAR_COPY}
        </h2>
        <button
          type="button"
          disabled={isSaving}
          onClick={onLogin}
          className="inline-flex min-h-12 w-full items-center justify-center gap-2 rounded-[1rem] bg-amber-200 px-5 text-sm font-black text-slate-950 transition hover:bg-amber-100 disabled:cursor-not-allowed disabled:opacity-45"
        >
          {isSaving ? (
            <>
              <Loader2 className="h-4 w-4 animate-spin" />
              注册账号 / 登录
            </>
          ) : (
            '注册账号 / 登录'
          )}
        </button>
        {error ? (
          <div className="w-full rounded-[1rem] border border-red-300/30 bg-red-500/14 px-4 py-3 text-sm font-semibold text-red-50">
            {error}
          </div>
        ) : null}
      </section>
    </div>
  );
}

function mergeBigFishWorkSummary(
  current: BigFishWorkSummary,
  updated: BigFishWorkSummary,
): BigFishWorkSummary {
  return current.sourceSessionId === updated.sourceSessionId
    ? updated
    : current;
}

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[],
) {
  for (const path of paths) {
    let current: unknown = profile;
    for (const segment of path.split('.')) {
      if (!current || typeof current !== 'object') {
        current = null;
        break;
      }
      current = (current as Record<string, unknown>)[segment];
    }
    if (typeof current === 'string' && current.trim()) {
      return current.trim();
    }
  }
  return null;
}

function hasProfileTextArray(profile: CustomWorldProfile | null, key: string) {
  const value = profile
    ? (profile as unknown as Record<string, unknown>)[key]
    : null;
  return Array.isArray(value)
    ? value.some((entry) => typeof entry === 'string' && entry.trim())
    : false;
}

function hasProfileArray(profile: CustomWorldProfile | null, key: string) {
  const value = profile
    ? (profile as unknown as Record<string, unknown>)[key]
    : null;
  return Array.isArray(value) && value.length > 0;
}

function hasSceneAct(profile: CustomWorldProfile | null) {
  const rawProfile = profile as unknown as Record<string, unknown> | null;
  const chapters =
    rawProfile &&
    (Array.isArray(rawProfile.sceneChapterBlueprints)
      ? rawProfile.sceneChapterBlueprints
      : Array.isArray(rawProfile.sceneChapters)
        ? rawProfile.sceneChapters
        : []);
  return Array.isArray(chapters)
    ? chapters.some((chapter) => {
        const acts =
          chapter && typeof chapter === 'object'
            ? (chapter as Record<string, unknown>).acts
            : null;
        return Array.isArray(acts) && acts.length > 0;
      })
    : false;
}

function isAgentResultStructuralBlockerResolved(
  profile: CustomWorldProfile,
  code: string | undefined,
) {
  if (!code || !AGENT_RESULT_STRUCTURAL_BLOCKER_CODES.has(code)) {
    return false;
  }

  if (code === 'publish_missing_world_hook') {
    return Boolean(
      readProfileTextField(profile, [
        'worldHook',
        'creatorIntent.worldHook',
        'anchorContent.worldPromise',
        'anchorContent.worldPromise.hook',
        'settingText',
      ]),
    );
  }
  if (code === 'publish_missing_player_premise') {
    return Boolean(
      readProfileTextField(profile, [
        'playerPremise',
        'creatorIntent.playerPremise',
        'anchorContent.playerEntryPoint',
        'anchorContent.playerEntryPoint.openingIdentity',
        'anchorContent.playerEntryPoint.openingProblem',
        'anchorContent.playerEntryPoint.entryMotivation',
      ]),
    );
  }
  if (code === 'publish_missing_core_conflict') {
    return hasProfileTextArray(profile, 'coreConflicts');
  }
  if (code === 'publish_missing_main_chapter') {
    return (
      hasProfileArray(profile, 'chapters') ||
      hasProfileArray(profile, 'sceneChapterBlueprints') ||
      hasProfileArray(profile, 'sceneChapters')
    );
  }
  return hasSceneAct(profile);
}

function buildAgentResultPublishGateView(
  profile: CustomWorldProfile | null,
  fallbackBlockers: AgentResultBlockerView[],
  fallbackPublishReady: boolean,
): AgentResultPublishGateView {
  if (!profile) {
    return {
      blockers: fallbackBlockers.map((entry) => entry.message),
      publishReady: fallbackPublishReady,
    };
  }

  const blockers = fallbackBlockers
    .filter(
      (entry) => !isAgentResultStructuralBlockerResolved(profile, entry.code),
    )
    .map((entry) => entry.message);

  return {
    blockers,
    publishReady: blockers.length === 0,
  };
}

function buildPuzzleResultProfileId(sessionId: string | null | undefined) {
  const normalizedSessionId = sessionId?.trim();
  if (!normalizedSessionId) {
    return null;
  }
  const stableSuffix = normalizedSessionId.startsWith('puzzle-session-')
    ? normalizedSessionId.slice('puzzle-session-'.length)
    : normalizedSessionId;
  return `puzzle-profile-${stableSuffix}`;
}

function buildPuzzleCompileActionFromFormPayload(
  payload: CreatePuzzleAgentSessionRequest | null,
): PuzzleAgentActionRequest {
  const pictureDescription =
    payload?.pictureDescription?.trim() || payload?.seedText?.trim();

  return {
    action: 'compile_puzzle_draft',
    promptText: pictureDescription,
    ...(pictureDescription ? { pictureDescription } : {}),
    referenceImageSrc: payload?.referenceImageSrc || null,
    imageModel: payload?.imageModel ?? null,
    aiRedraw: payload?.aiRedraw ?? true,
    candidateCount: 1,
  };
}

function buildPuzzleFormPayloadFromSession(
  session: PuzzleAgentSessionSnapshot,
): CreatePuzzleAgentSessionRequest {
  const formDraft = session.draft?.formDraft;
  const pictureDescription =
    formDraft?.pictureDescription?.trim() ||
    session.draft?.levels?.[0]?.pictureDescription?.trim() ||
    session.anchorPack.visualSubject.value.trim() ||
    session.seedText?.trim() ||
    '';

  return {
    seedText: pictureDescription,
    pictureDescription,
    referenceImageSrc: null,
    imageModel: null,
    aiRedraw: true,
  };
}

function buildPuzzleFormPayloadFromAction(
  payload: PuzzleAgentActionRequest,
): CreatePuzzleAgentSessionRequest | null {
  if (
    payload.action !== 'compile_puzzle_draft' &&
    payload.action !== 'save_puzzle_form_draft'
  ) {
    return null;
  }

  const workTitle = payload.workTitle?.trim() ?? '';
  const workDescription = payload.workDescription?.trim() ?? '';
  const pictureDescription =
    payload.pictureDescription?.trim() || payload.promptText?.trim() || '';

  return {
    seedText: pictureDescription,
    ...(workTitle ? { workTitle } : {}),
    ...(workDescription ? { workDescription } : {}),
    pictureDescription,
    referenceImageSrc:
      payload.action === 'compile_puzzle_draft'
        ? (payload.referenceImageSrc ?? null)
        : (payload.referenceImageSrc ?? null),
    imageModel:
      payload.action === 'compile_puzzle_draft'
        ? (payload.imageModel ?? null)
        : (payload.imageModel ?? null),
    aiRedraw:
      payload.action === 'compile_puzzle_draft'
        ? (payload.aiRedraw ?? true)
        : (payload.aiRedraw ?? true),
  };
}

function isPuzzleFormOnlyDraft(session: PuzzleAgentSessionSnapshot | null) {
  return Boolean(
    session?.stage === 'collecting_anchors' && session.draft?.formDraft,
  );
}

function isEmptyPuzzleFormOnlyDraft(
  session: PuzzleAgentSessionSnapshot | null,
) {
  if (!isPuzzleFormOnlyDraft(session)) {
    return false;
  }

  const formDraft = session?.draft?.formDraft;
  return !(
    session?.seedText?.trim() ||
    formDraft?.workTitle?.trim() ||
    formDraft?.workDescription?.trim() ||
    formDraft?.pictureDescription?.trim()
  );
}

const CustomWorldGenerationView = lazy(async () => {
  const module = await import('../CustomWorldGenerationView');
  return {
    default: module.CustomWorldGenerationView,
  };
});

const RpgCreationResultView = lazy(async () => {
  const module = await import('../rpg-creation-result/RpgCreationResultView');
  return {
    default: module.RpgCreationResultView,
  };
});

const CustomWorldAgentWorkspace = lazy(async () => {
  const module = await import(
    '../custom-world-agent/CustomWorldAgentWorkspace'
  );
  return {
    default: module.CustomWorldAgentWorkspace,
  };
});

const BigFishAgentWorkspace = lazy(async () => {
  const module = await import('../big-fish-creation/BigFishAgentWorkspace');
  return {
    default: module.BigFishAgentWorkspace,
  };
});

const BigFishResultView = lazy(async () => {
  const module = await import('../big-fish-result/BigFishResultView');
  return {
    default: module.BigFishResultView,
  };
});

const BigFishRuntimeShell = lazy(async () => {
  const module = await import('../big-fish-runtime/BigFishRuntimeShell');
  return {
    default: module.BigFishRuntimeShell,
  };
});

const Match3DAgentWorkspace = lazy(async () => {
  const module = await import('../match3d-creation/Match3DAgentWorkspace');
  return {
    default: module.Match3DAgentWorkspace,
  };
});

const Match3DResultView = lazy(async () => {
  const module = await import('../match3d-result/Match3DResultView');
  return {
    default: module.Match3DResultView,
  };
});

const Match3DRuntimeShell = lazy(async () => {
  const module = await import('../match3d-runtime/Match3DRuntimeShell');
  return {
    default: module.Match3DRuntimeShell,
  };
});

const SquareHoleAgentWorkspace = lazy(async () => {
  const module = await import(
    '../square-hole-creation/SquareHoleAgentWorkspace'
  );
  return {
    default: module.SquareHoleAgentWorkspace,
  };
});

const SquareHoleResultView = lazy(async () => {
  const module = await import('../square-hole-result/SquareHoleResultView');
  return {
    default: module.SquareHoleResultView,
  };
});

const SquareHoleRuntimeShell = lazy(async () => {
  const module = await import('../square-hole-runtime/SquareHoleRuntimeShell');
  return {
    default: module.SquareHoleRuntimeShell,
  };
});

const CustomWorldCreationHub = lazy(async () => {
  const module = await import('../custom-world-home/CustomWorldCreationHub');
  return {
    default: module.CustomWorldCreationHub,
  };
});

const PuzzleAgentWorkspace = lazy(async () => {
  const module = await import('../puzzle-agent/PuzzleAgentWorkspace');
  return {
    default: module.PuzzleAgentWorkspace,
  };
});

const CreativeAgentWorkspace = lazy(async () => {
  const module = await import('../creative-agent/CreativeAgentWorkspace');
  return {
    default: module.CreativeAgentWorkspace,
  };
});

const VisualNovelAgentWorkspace = lazy(async () => {
  const module = await import(
    '../visual-novel-creation/VisualNovelAgentWorkspace'
  );
  return {
    default: module.VisualNovelAgentWorkspace,
  };
});

const VisualNovelResultView = lazy(async () => {
  const module = await import('../visual-novel-result/VisualNovelResultView');
  return {
    default: module.VisualNovelResultView,
  };
});

const VisualNovelRuntimeShell = lazy(async () => {
  const module = await import('../visual-novel-runtime/VisualNovelRuntimeShell');
  return {
    default: module.VisualNovelRuntimeShell,
  };
});

const PuzzleResultView = lazy(async () => {
  const module = await import('../puzzle-result/PuzzleResultView');
  return {
    default: module.PuzzleResultView,
  };
});

const PuzzleGalleryDetailView = lazy(async () => {
  const module = await import('../puzzle-gallery/PuzzleGalleryDetailView');
  return {
    default: module.PuzzleGalleryDetailView,
  };
});

const PuzzleRuntimeShell = lazy(async () => {
  const module = await import('../puzzle-runtime/PuzzleRuntimeShell');
  return {
    default: module.PuzzleRuntimeShell,
  };
});

function LazyPanelFallback({ label }: { label: string }) {
  return (
    <div className="flex h-full min-h-0 items-center justify-center">
      <div className="platform-subpanel rounded-2xl px-5 py-4 text-sm text-[var(--platform-text-base)]">
        {label}
      </div>
    </div>
  );
}

function mergePuzzleServiceRuntimeState(
  currentRun: PuzzleRunSnapshot,
  serviceRun: PuzzleRunSnapshot,
): PuzzleRunSnapshot {
  if (!currentRun.currentLevel || !serviceRun.currentLevel) {
    return currentRun;
  }

  const serviceLevel = serviceRun.currentLevel;
  const leaderboardEntries =
    serviceLevel.leaderboardEntries.length > 0
      ? serviceLevel.leaderboardEntries
      : serviceRun.leaderboardEntries;

  // 中文注释：拼块布局和通关状态由前端即时裁决；后端快照只合并榜单与下一关 handoff。
  return {
    ...currentRun,
    runId: serviceRun.runId,
    entryProfileId: serviceRun.entryProfileId,
    clearedLevelCount: Math.max(
      currentRun.clearedLevelCount,
      serviceRun.clearedLevelCount,
    ),
    recommendedNextProfileId: serviceRun.recommendedNextProfileId,
    nextLevelMode: serviceRun.nextLevelMode,
    nextLevelProfileId: serviceRun.nextLevelProfileId,
    nextLevelId: serviceRun.nextLevelId,
    recommendedNextWorks: serviceRun.recommendedNextWorks,
    leaderboardEntries,
    currentLevel: {
      ...currentRun.currentLevel,
      leaderboardEntries:
        leaderboardEntries.length > 0
          ? leaderboardEntries
          : currentRun.currentLevel.leaderboardEntries,
    },
  };
}

export function PlatformEntryFlowShellImpl({
  selectionStage,
  setSelectionStage,
  hasSavedGame,
  savedSnapshot,
  handleContinueGame,
  handleStartNewGame,
  handleCustomWorldSelect,
  initialPublicWorkCode,
}: PlatformEntryFlowShellProps) {
  const authUi = useAuthUi();
  const platformThemeClass =
    authUi?.platformTheme === 'dark'
      ? 'platform-theme--dark'
      : 'platform-theme--light';
  const [showCreationTypeModal, setShowCreationTypeModal] = useState(false);
  const [selectedDetailEntry, setSelectedDetailEntry] =
    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);
  const [isPublicWorkDetailBusy, setIsPublicWorkDetailBusy] = useState(false);
  const [bigFishWorks, setBigFishWorks] = useState<BigFishWorkSummary[]>([]);
  const [bigFishGalleryEntries, setBigFishGalleryEntries] = useState<
    BigFishWorkSummary[]
  >([]);
  const [match3dWorks, setMatch3DWorks] = useState<Match3DWorkSummary[]>([]);
  const [match3dGalleryEntries, setMatch3DGalleryEntries] = useState<
    Match3DWorkSummary[]
  >([]);
  const [match3dProfile, setMatch3DProfile] =
    useState<Match3DWorkProfile | null>(null);
  const [match3dRun, setMatch3DRun] = useState<Match3DRunSnapshot | null>(null);
  const [match3dRuntimeReturnStage, setMatch3DRuntimeReturnStage] = useState<
    'match3d-result' | 'work-detail'
  >('match3d-result');
  const [isMatch3DLoadingLibrary, setIsMatch3DLoadingLibrary] = useState(false);
  const [squareHoleWorks, setSquareHoleWorks] = useState<
    SquareHoleWorkSummary[]
  >([]);
  const [squareHoleGalleryEntries, setSquareHoleGalleryEntries] = useState<
    SquareHoleWorkSummary[]
  >([]);
  const [squareHoleProfile, setSquareHoleProfile] =
    useState<SquareHoleWorkProfile | null>(null);
  const [squareHoleRun, setSquareHoleRun] =
    useState<SquareHoleRunSnapshot | null>(null);
  const [squareHoleRuntimeReturnStage, setSquareHoleRuntimeReturnStage] =
    useState<SquareHoleRuntimeReturnStage>('square-hole-result');
  const [isSquareHoleLoadingLibrary, setIsSquareHoleLoadingLibrary] =
    useState(false);
  const [squareHoleGenerationState, setSquareHoleGenerationState] =
    useState<MiniGameDraftGenerationState | null>(null);
  const [bigFishRun, setBigFishRun] =
    useState<BigFishRuntimeSnapshotResponse | null>(null);
  const [bigFishRuntimeShare, setBigFishRuntimeShare] = useState<{
    title: string;
    publicWorkCode: string;
  } | null>(null);
  const [_bigFishRuntimeWork, setBigFishRuntimeWork] =
    useState<BigFishWorkSummary | null>(null);
  const [bigFishRuntimeStartedAt, setBigFishRuntimeStartedAt] = useState<
    number | null
  >(null);
  const [_bigFishRuntimeSessionSource, setBigFishRuntimeSessionSource] =
    useState<BigFishRuntimeSessionSource>(null);
  const [bigFishRuntimeReturnStage, setBigFishRuntimeReturnStage] =
    useState<BigFishRuntimeReturnStage>('platform');
  const bigFishInputInFlightRef = useRef(false);
  const [isBigFishLoadingLibrary, setIsBigFishLoadingLibrary] = useState(false);
  const [bigFishGenerationState, setBigFishGenerationState] =
    useState<MiniGameDraftGenerationState | null>(null);
  const [activeRecommendEntryKey, setActiveRecommendEntryKey] = useState<
    string | null
  >(null);
  const [activeRecommendRuntimeKind, setActiveRecommendRuntimeKind] =
    useState<RecommendRuntimeKind | null>(null);
  const [activeRecommendRuntimeError, setActiveRecommendRuntimeError] =
    useState<string | null>(null);
  const [isStartingRecommendEntry, setIsStartingRecommendEntry] =
    useState(false);
  const recommendRuntimeStartRequestRef = useRef(0);
  const [, setPuzzleOperation] = useState<PuzzleAgentOperationRecord | null>(
    null,
  );
  const [puzzleWorks, setPuzzleWorks] = useState<PuzzleWorkSummary[]>([]);
  const [puzzleGalleryEntries, setPuzzleGalleryEntries] = useState<
    PuzzleWorkSummary[]
  >([]);
  const [selectedPuzzleDetail, setSelectedPuzzleDetail] =
    useState<PuzzleWorkSummary | null>(null);
  const [puzzleDetailReturnTarget, setPuzzleDetailReturnTarget] =
    useState<PuzzleDetailReturnTarget | null>(null);
  const [puzzleRuntimeReturnStage, setPuzzleRuntimeReturnStage] =
    useState<PuzzleRuntimeReturnStage>('puzzle-gallery-detail');
  const [puzzleRuntimeAuthMode, setPuzzleRuntimeAuthMode] =
    useState<PuzzleRuntimeAuthMode>('default');
  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 errorSetterRefNoop = useMemo(
    () =>
      ((_: SetStateAction<string | null>) => undefined) as Dispatch<
        SetStateAction<string | null>
      >,
    [],
  );
  const bigFishErrorSetterRef = useRef(errorSetterRefNoop);
  const match3DErrorSetterRef = useRef(errorSetterRefNoop);
  const squareHoleErrorSetterRef = useRef(errorSetterRefNoop);
  const puzzleErrorSetterRef = useRef(errorSetterRefNoop);
  const visualNovelErrorSetterRef = useRef(errorSetterRefNoop);
  const [isPuzzleLoadingLibrary, setIsPuzzleLoadingLibrary] = useState(false);
  const [puzzleShelfError, setPuzzleShelfError] = useState<string | null>(null);
  const [puzzleCreationError, setPuzzleCreationError] = useState<string | null>(
    null,
  );
  const [puzzleGenerationState, setPuzzleGenerationState] =
    useState<MiniGameDraftGenerationState | null>(null);
  const [puzzleGenerationProgressNowMs, setPuzzleGenerationProgressNowMs] =
    useState(() => Date.now());
  const [puzzleFormDraftPayload, setPuzzleFormDraftPayload] =
    useState<CreatePuzzleAgentSessionRequest | null>(null);
  const [puzzleOnboardingPrompt, setPuzzleOnboardingPrompt] = useState('');
  const [puzzleOnboardingPhase, setPuzzleOnboardingPhase] =
    useState<PuzzleOnboardingPhase>('input');
  const [puzzleOnboardingDraft, setPuzzleOnboardingDraft] =
    useState<PuzzleOnboardingDraft | null>(null);
  const [puzzleOnboardingError, setPuzzleOnboardingError] = useState<
    string | null
  >(null);
  const [isPuzzleOnboardingSaving, setIsPuzzleOnboardingSaving] =
    useState(false);
  const [creativeAgentSession, setCreativeAgentSession] =
    useState<CreativeAgentSessionSnapshot | null>(null);
  const [creativeAgentError, setCreativeAgentError] = useState<string | null>(
    null,
  );
  const [isCreativeAgentBusy, setIsCreativeAgentBusy] = useState(false);
  const [isCreativeAgentStreaming, setIsCreativeAgentStreaming] =
    useState(false);
  const [creativeAgentEvents, setCreativeAgentEvents] = useState<
    CreativeAgentSseEvent[]
  >([]);
  const [activeCreativeAgentSessionId, setActiveCreativeAgentSessionId] =
    useState<string | null>(null);
  const [creativeDraftEditError, setCreativeDraftEditError] = useState<
    string | null
  >(null);
  const [isCreativeDraftEditBusy, setIsCreativeDraftEditBusy] = useState(false);
  const [visualNovelWork, setVisualNovelWork] =
    useState<VisualNovelWorkDetail | null>(null);
  const [visualNovelWorks, setVisualNovelWorks] = useState<
    VisualNovelWorkSummary[]
  >([]);
  const [visualNovelGalleryEntries, setVisualNovelGalleryEntries] = useState<
    VisualNovelWorkSummary[]
  >([]);
  const [visualNovelRun, setVisualNovelRun] =
    useState<VisualNovelRunSnapshot | null>(null);
  const [visualNovelRuntimeReturnStage, setVisualNovelRuntimeReturnStage] =
    useState<VisualNovelRuntimeReturnStage>('visual-novel-result');
  const [isVisualNovelLoadingLibrary, setIsVisualNovelLoadingLibrary] =
    useState(false);
  const [isPuzzleNextLevelGenerating, setIsPuzzleNextLevelGenerating] =
    useState(false);
  const [isSearchingPublicCode, setIsSearchingPublicCode] = useState(false);
  const [publicSearchError, setPublicSearchError] = useState<string | null>(
    null,
  );
  const [searchedPublicUser, setSearchedPublicUser] =
    useState<PublicUserSummary | null>(null);
  const [deletingCreationWorkId, setDeletingCreationWorkId] = useState<
    string | null
  >(null);
  const [pendingDeleteCreationWork, setPendingDeleteCreationWork] =
    useState<DeleteCreationWorkConfirmation | null>(null);
  const [
    claimingPuzzlePointIncentiveProfileId,
    setClaimingPuzzlePointIncentiveProfileId,
  ] = useState<string | null>(null);
  const [publishSharePayload, setPublishSharePayload] =
    useState<PublishShareModalPayload | null>(null);
  const isBigFishCreationVisible = isPlatformCreationTypeVisible('big-fish');
  const [profilePlayStats, setProfilePlayStats] =
    useState<ProfilePlayStatsResponse | null>(null);
  const [profilePlayStatsError, setProfilePlayStatsError] = useState<
    string | null
  >(null);
  const [isProfilePlayStatsLoading, setIsProfilePlayStatsLoading] =
    useState(false);
  const [isProfilePlayStatsOpen, setIsProfilePlayStatsOpen] = useState(false);
  const hadReadableProtectedDataRef = useRef(false);
  const hasInitialAgentSession = Boolean(
    readCustomWorldAgentUiState().activeSessionId &&
      shouldRestoreCustomWorldAgentUiState(),
  );
  const handledInitialPublicWorkCodeRef = useRef<string | null>(null);

  const platformBootstrap = usePlatformEntryBootstrap({
    user: authUi?.user,
    canAccessProtectedData: authUi?.canAccessProtectedData,
    getProfileDashboard: getPlatformProfileDashboard,
    handleContinueGame,
    hasInitialAgentSession,
  });
  const entryNavigation = usePlatformEntryNavigation({
    setSelectionStage,
    setSelectedDetailEntry,
  });
  const { setPlatformTab } = platformBootstrap;

  useEffect(() => {
    if (selectionStage === 'profile-feedback') {
      setPlatformTab('profile');
    }
  }, [selectionStage, setPlatformTab]);

  const openProfileFeedback = useCallback(() => {
    if (!authUi?.user) {
      authUi?.openLoginModal();
      return;
    }

    setPlatformTab('profile');
    setSelectionStage('profile-feedback');
  }, [authUi, setPlatformTab, setSelectionStage]);

  const enterCreateTab = useCallback(() => {
    // 只依赖稳定的 setter，避免把 bootstrap 对象的 render 级引用变化
    // 传导成 Agent session 恢复 effect 的重复触发。
    setPlatformTab('create');
  }, [setPlatformTab]);
  const enterDraftTab = useCallback(() => {
    setPlatformTab('saves');
  }, [setPlatformTab]);

  const resolveBigFishErrorMessage = useCallback(
    (error: unknown, fallback: string) =>
      resolveRpgCreationErrorMessage(error, fallback),
    [],
  );
  const resolvePuzzleErrorMessage = useCallback(
    (error: unknown, fallback: string) =>
      resolveRpgCreationErrorMessage(error, fallback),
    [],
  );
  const resolveMatch3DErrorMessage = useCallback(
    (error: unknown, fallback: string) =>
      resolveRpgCreationErrorMessage(error, fallback),
    [],
  );
  const resolveSquareHoleErrorMessage = useCallback(
    (error: unknown, fallback: string) =>
      resolveRpgCreationErrorMessage(error, fallback),
    [],
  );

  const refreshBigFishShelf = useCallback(async () => {
    setIsBigFishLoadingLibrary(true);

    try {
      const worksResponse = await listBigFishWorks();
      setBigFishWorks(worksResponse.items);
      bigFishErrorSetterRef.current(null);
    } catch (error) {
      bigFishErrorSetterRef.current(
        resolveBigFishErrorMessage(error, '读取大鱼吃小鱼作品列表失败。'),
      );
    } finally {
      setIsBigFishLoadingLibrary(false);
    }
  }, [resolveBigFishErrorMessage]);

  const refreshBigFishGallery = useCallback(async () => {
    try {
      const galleryResponse = await listBigFishGallery();
      setBigFishGalleryEntries(galleryResponse.items);
      return galleryResponse.items;
    } catch (error) {
      setBigFishGalleryEntries([]);
      bigFishErrorSetterRef.current(
        resolveBigFishErrorMessage(error, '读取大鱼吃小鱼广场失败。'),
      );
      return [];
    }
  }, [resolveBigFishErrorMessage]);

  const refreshMatch3DShelf = useCallback(async () => {
    setIsMatch3DLoadingLibrary(true);

    try {
      const worksResponse = await listMatch3DWorks();
      setMatch3DWorks(worksResponse.items);
      match3DErrorSetterRef.current(null);
    } catch (error) {
      match3DErrorSetterRef.current(
        resolveMatch3DErrorMessage(error, '读取抓大鹅作品列表失败。'),
      );
    } finally {
      setIsMatch3DLoadingLibrary(false);
    }
  }, [resolveMatch3DErrorMessage]);

  const refreshMatch3DGallery = useCallback(async () => {
    try {
      const galleryResponse = await listMatch3DGallery();
      setMatch3DGalleryEntries(galleryResponse.items);
      return galleryResponse.items;
    } catch {
      // 中文注释：公开广场是首页展示数据，失败时只降级为空列表；
      // 不写入创作错误态，避免挡住抓大鹅共创入口。
      setMatch3DGalleryEntries([]);
      return [];
    }
  }, []);

  const refreshSquareHoleShelf = useCallback(async () => {
    setIsSquareHoleLoadingLibrary(true);

    try {
      const worksResponse = await listSquareHoleWorks();
      setSquareHoleWorks(worksResponse.items);
      squareHoleErrorSetterRef.current(null);
    } catch (error) {
      squareHoleErrorSetterRef.current(
        resolveSquareHoleErrorMessage(error, '读取方洞挑战作品列表失败。'),
      );
    } finally {
      setIsSquareHoleLoadingLibrary(false);
    }
  }, [resolveSquareHoleErrorMessage]);

  const refreshSquareHoleGallery = useCallback(async () => {
    try {
      const galleryResponse = await listSquareHoleGallery();
      setSquareHoleGalleryEntries(galleryResponse.items);
      return galleryResponse.items;
    } catch {
      setSquareHoleGalleryEntries([]);
      return [];
    }
  }, []);

  const refreshPuzzleShelf = useCallback(async () => {
    setIsPuzzleLoadingLibrary(true);

    try {
      const worksResponse = await listPuzzleWorks();
      setPuzzleWorks(worksResponse.items);
      setPuzzleShelfError(null);
    } catch (error) {
      setPuzzleShelfError(
        resolvePuzzleErrorMessage(error, '读取拼图作品列表失败。'),
      );
    } finally {
      setIsPuzzleLoadingLibrary(false);
    }
  }, [resolvePuzzleErrorMessage]);

  const refreshPuzzleGallery = useCallback(async () => {
    try {
      const galleryResponse = await listPuzzleGallery();
      setPuzzleGalleryEntries(galleryResponse.items);
      return galleryResponse.items;
    } catch (error) {
      setPuzzleGalleryEntries([]);
      puzzleErrorSetterRef.current(
        resolvePuzzleErrorMessage(error, '读取拼图广场失败。'),
      );
      return [];
    }
  }, [resolvePuzzleErrorMessage]);

  const refreshVisualNovelShelf = useCallback(async () => {
    setIsVisualNovelLoadingLibrary(true);

    try {
      const worksResponse = await listVisualNovelWorks();
      setVisualNovelWorks(worksResponse.works);
      visualNovelErrorSetterRef.current(null);
      return worksResponse.works;
    } catch (error) {
      visualNovelErrorSetterRef.current(
        resolvePuzzleErrorMessage(error, '读取视觉小说作品列表失败。'),
      );
      return [];
    } finally {
      setIsVisualNovelLoadingLibrary(false);
    }
  }, [resolvePuzzleErrorMessage]);

  const refreshVisualNovelGallery = useCallback(async () => {
    try {
      const galleryResponse = await listVisualNovelGallery();
      setVisualNovelGalleryEntries(galleryResponse.works);
      return galleryResponse.works;
    } catch (error) {
      setVisualNovelGalleryEntries([]);
      visualNovelErrorSetterRef.current(
        resolvePuzzleErrorMessage(error, '读取视觉小说广场失败。'),
      );
      return [];
    }
  }, [resolvePuzzleErrorMessage]);

  const sessionController = useRpgCreationSessionController({
    userId: authUi?.user?.id,
    openLoginModal: authUi?.openLoginModal,
    selectionStage,
    setSelectionStage,
    enterCreateTab,
    onSessionOpened: () => {
      setShowCreationTypeModal(false);
    },
  });

  useRpgCreationAgentOperationPolling({
    activeAgentSessionId: sessionController.activeAgentSessionId,
    activeAgentOperationId: sessionController.activeAgentOperationId,
    userId: authUi?.user?.id,
    setAgentOperation: sessionController.setAgentOperation,
    persistAgentUiState: sessionController.persistAgentUiState,
    syncAgentSessionSnapshot: sessionController.syncAgentSessionSnapshot,
  });

  const autosaveCoordinator = useRpgCreationResultAutosave({
    selectionStage,
    activeAgentSessionId: sessionController.activeAgentSessionId,
    generatedCustomWorldProfile: sessionController.generatedCustomWorldProfile,
    isAgentDraftResultView: sessionController.isAgentDraftResultView,
    userId: authUi?.user?.id,
    setGeneratedCustomWorldProfile:
      sessionController.setGeneratedCustomWorldProfile,
    setAgentOperation: sessionController.setAgentOperation,
    setSavedCustomWorldEntries: platformBootstrap.setSavedCustomWorldEntries,
    setSelectedDetailEntry,
    refreshCustomWorldWorks: platformBootstrap.refreshCustomWorldWorks,
    persistAgentUiState: sessionController.persistAgentUiState,
    syncAgentSessionSnapshot: sessionController.syncAgentSessionSnapshot,
    syncAgentCreationResultView: sessionController.syncAgentCreationResultView,
    buildDraftResultProfile: (view) =>
      rpgCreationPreviewAdapter.buildPreviewFromResultView(view),
  });

  const detailNavigation = usePlatformEntryLibraryDetail({
    userId: authUi?.user?.id,
    selectedDetailEntry,
    setSelectedDetailEntry,
    savedCustomWorldEntries: platformBootstrap.savedCustomWorldEntries,
    setSavedCustomWorldEntries: platformBootstrap.setSavedCustomWorldEntries,
    setGeneratedCustomWorldProfile:
      sessionController.setGeneratedCustomWorldProfile,
    setCustomWorldError: sessionController.setCustomWorldError,
    setCustomWorldAutoSaveError:
      autosaveCoordinator.setCustomWorldAutoSaveError,
    setCustomWorldAutoSaveState:
      autosaveCoordinator.setCustomWorldAutoSaveState,
    setCustomWorldGenerationViewSource:
      sessionController.setCustomWorldGenerationViewSource,
    setCustomWorldResultViewSource:
      sessionController.setCustomWorldResultViewSource,
    setSelectionStage,
    setPlatformTabToCreate: enterCreateTab,
    setPlatformTabToDraft: enterDraftTab,
    setPlatformError: platformBootstrap.setPlatformError,
    appendBrowseHistoryEntry: platformBootstrap.appendBrowseHistoryEntry,
    refreshCustomWorldWorks: platformBootstrap.refreshCustomWorldWorks,
    refreshPublishedGallery: platformBootstrap.refreshPublishedGallery,
    persistAgentUiState: sessionController.persistAgentUiState,
    syncAgentCreationResultView: sessionController.syncAgentCreationResultView,
    buildDraftResultProfile: (view) =>
      rpgCreationPreviewAdapter.buildPreviewFromResultView(view),
    suppressAgentDraftResultAutoOpen:
      sessionController.suppressAgentDraftResultAutoOpen,
    releaseAgentDraftResultAutoOpenSuppression:
      sessionController.releaseAgentDraftResultAutoOpenSuppression,
    resetAutoSaveTrackingToIdle:
      autosaveCoordinator.resetAutoSaveTrackingToIdle,
    markAutoSavedProfile: autosaveCoordinator.markAutoSavedProfile,
  });

  const enterWorldCoordinator = useRpgCreationEnterWorld({
    isAgentDraftResultView: sessionController.isAgentDraftResultView,
    activeAgentSessionId: sessionController.activeAgentSessionId,
    generatedCustomWorldProfile: sessionController.generatedCustomWorldProfile,
    handleCustomWorldSelect,
    syncAgentDraftResultProfile:
      autosaveCoordinator.syncAgentDraftResultProfile,
    executePublishWorld: async () => {
      const latestSession = await autosaveCoordinator.executeAgentActionAndWait(
        {
          action: 'publish_world',
        },
      );
      // 发布动作会在后端同步 gallery 投影；前端发布完成后立即刷新首页/分类页共用的公开作品列表。
      await Promise.allSettled([
        platformBootstrap.refreshPublishedGallery(),
        platformBootstrap.refreshCustomWorldWorks(),
        isBigFishCreationVisible
          ? refreshBigFishGallery()
          : Promise.resolve([] as BigFishWorkSummary[]),
        refreshMatch3DGallery(),
        refreshPuzzleGallery(),
      ]);
      return latestSession;
    },
    syncAgentCreationResultView: sessionController.syncAgentCreationResultView,
    setGeneratedCustomWorldProfile:
      sessionController.setGeneratedCustomWorldProfile,
  });

  const previewCustomWorldCharacters = useMemo(
    () =>
      sessionController.generatedCustomWorldProfile
        ? buildCustomWorldPlayableCharacters(
            sessionController.generatedCustomWorldProfile,
          )
        : [],
    [sessionController.generatedCustomWorldProfile],
  );
  const agentResultPreview =
    sessionController.agentSession?.resultPreview ?? null;
  const agentResultPreviewBlockers = useMemo(
    () => agentResultPreview?.blockers ?? [],
    [agentResultPreview],
  );
  const agentResultPublishGateView = useMemo(
    () =>
      buildAgentResultPublishGateView(
        sessionController.generatedCustomWorldProfile,
        agentResultPreviewBlockers,
        Boolean(agentResultPreview?.publishReady),
      ),
    [
      agentResultPreview?.publishReady,
      agentResultPreviewBlockers,
      sessionController.generatedCustomWorldProfile,
    ],
  );
  const agentResultPreviewQualityFindings = useMemo(
    () => agentResultPreview?.qualityFindings ?? [],
    [agentResultPreview],
  );

  const openPublishShareModal = useCallback(
    (payload: PublishShareModalPayload) => {
      const publicWorkCode = payload.publicWorkCode.trim();
      if (!publicWorkCode) {
        return;
      }

      setPublishSharePayload({
        ...payload,
        publicWorkCode,
        title: payload.title.trim() || '我的作品',
      });
    },
    [],
  );

  const openRpgPublishShareModal = useCallback(
    async (profile: CustomWorldProfile | null | undefined) => {
      const profileId = profile?.id?.trim();
      if (!profileId) {
        return;
      }
      const profileName = profile?.name?.trim() || '我的作品';

      const galleryEntries = await platformBootstrap
        .refreshPublishedGallery()
        .catch(() => [] as CustomWorldGalleryCard[]);
      const galleryEntry = galleryEntries.find(
        (entry) => entry.profileId === profileId,
      );
      const publicWorkCode = galleryEntry?.publicWorkCode?.trim();
      if (!publicWorkCode) {
        return;
      }

      openPublishShareModal({
        title: galleryEntry?.worldName || profileName,
        publicWorkCode,
        stage: 'work-detail',
      });
    },
    [openPublishShareModal, platformBootstrap],
  );
  const agentResultPreviewSourceLabel = useMemo(() => {
    if (!agentResultPreview?.source) {
      return null;
    }
    if (agentResultPreview.source === 'published_profile') {
      return '已发布世界';
    }
    if (agentResultPreview.source === 'session_preview') {
      return '会话预览';
    }
    return '服务端预览';
  }, [agentResultPreview]);

  const featuredGalleryEntries = useMemo(() => {
    const bigFishPublicEntries = isBigFishCreationVisible
      ? bigFishGalleryEntries.map(mapBigFishWorkToPlatformGalleryCard)
      : [];
    const match3dPublicEntries = match3dGalleryEntries.map(
      mapMatch3DWorkToPlatformGalleryCard,
    );
    const puzzlePublicEntries = puzzleGalleryEntries.map(
      mapPuzzleWorkToPlatformGalleryCard,
    );
    const squareHolePublicEntries = squareHoleGalleryEntries.map(
      mapSquareHoleWorkToPlatformGalleryCard,
    );
    const visualNovelPublicEntries = visualNovelGalleryEntries.map(
      mapVisualNovelWorkToPlatformGalleryCard,
    );
    return mergePlatformPublicGalleryEntries(
      platformBootstrap.publishedGalleryEntries,
      [
        ...bigFishPublicEntries,
        ...match3dPublicEntries,
        ...puzzlePublicEntries,
        ...squareHolePublicEntries,
        ...visualNovelPublicEntries,
      ],
    ).slice(0, 6);
  }, [
    isBigFishCreationVisible,
    bigFishGalleryEntries,
    match3dGalleryEntries,
    platformBootstrap.publishedGalleryEntries,
    puzzleGalleryEntries,
    squareHoleGalleryEntries,
    visualNovelGalleryEntries,
  ]);
  const latestGalleryEntries = useMemo(
    () =>
      mergePlatformPublicGalleryEntries(
        platformBootstrap.publishedGalleryEntries,
        [
          ...(isBigFishCreationVisible
            ? bigFishGalleryEntries.map(mapBigFishWorkToPlatformGalleryCard)
            : []),
          ...match3dGalleryEntries.map(mapMatch3DWorkToPlatformGalleryCard),
          ...puzzleGalleryEntries.map(mapPuzzleWorkToPlatformGalleryCard),
          ...squareHoleGalleryEntries.map(
            mapSquareHoleWorkToPlatformGalleryCard,
          ),
          ...visualNovelGalleryEntries.map(
            mapVisualNovelWorkToPlatformGalleryCard,
          ),
        ],
      ),
    [
      isBigFishCreationVisible,
      bigFishGalleryEntries,
      match3dGalleryEntries,
      platformBootstrap.publishedGalleryEntries,
      puzzleGalleryEntries,
      squareHoleGalleryEntries,
      visualNovelGalleryEntries,
    ],
  );
  const recommendRuntimeEntries = useMemo(
    () => {
      const entryMap = new Map<string, PlatformPublicGalleryCard>();
      [...featuredGalleryEntries, ...latestGalleryEntries].forEach((entry) => {
        entryMap.set(getPlatformPublicGalleryEntryKey(entry), entry);
      });
      return Array.from(entryMap.values());
    },
    [featuredGalleryEntries, latestGalleryEntries],
  );

  const creationHubItems = useMemo<CustomWorldWorkSummary[]>(
    () =>
      platformBootstrap.customWorldWorkEntries.length > 0
        ? platformBootstrap.customWorldWorkEntries
        : buildCreationHubFallbackItems(
            platformBootstrap.savedCustomWorldEntries,
          ),
    [
      platformBootstrap.customWorldWorkEntries,
      platformBootstrap.savedCustomWorldEntries,
    ],
  );
  const resultViewError =
    autosaveCoordinator.customWorldAutoSaveError ??
    sessionController.customWorldError;
  const isSelectedPublicWorkOwned = Boolean(
    authUi?.user?.id &&
      selectedPublicWorkDetail?.ownerUserId === authUi.user.id,
  );
  const selectedPublicWorkActionMode = isSelectedPublicWorkOwned
    ? 'edit'
    : 'remix';

  useEffect(() => {
    if (
      selectionStage === 'custom-world-result' &&
      !sessionController.generatedCustomWorldProfile
    ) {
      setSelectionStage(selectedDetailEntry ? 'detail' : 'platform');
    }
  }, [
    selectedDetailEntry,
    selectionStage,
    sessionController.generatedCustomWorldProfile,
    setSelectionStage,
  ]);

  useEffect(() => {
    const shouldTickPuzzleProgress =
      selectionStage === 'puzzle-generating' &&
      puzzleGenerationState != null &&
      puzzleGenerationState.phase !== 'ready' &&
      puzzleGenerationState.phase !== 'failed';

    if (!shouldTickPuzzleProgress) {
      return undefined;
    }

    setPuzzleGenerationProgressNowMs(Date.now());
    const timerId = window.setInterval(() => {
      setPuzzleGenerationProgressNowMs(Date.now());
    }, 500);

    return () => window.clearInterval(timerId);
  }, [puzzleGenerationState, selectionStage]);

  const runProtectedAction = useCallback(
    (action: () => void) => {
      if (!authUi?.requireAuth) {
        action();
        return;
      }

      authUi.requireAuth(action);
    },
    [authUi],
  );

  const savePuzzleOnboardingDraft = useCallback(async () => {
    if (!puzzleOnboardingDraft || isPuzzleOnboardingSaving) {
      return;
    }

    setIsPuzzleOnboardingSaving(true);
    setPuzzleOnboardingError(null);
    try {
      const response = await savePuzzleOnboardingWork({
        promptText: puzzleOnboardingDraft.promptText,
        item: puzzleOnboardingDraft.item,
      });
      setPuzzleWorks((current) => [response.item, ...current]);
      setSelectedPuzzleDetail(null);
      setPuzzleRun(null);
      setPuzzleRuntimeAuthMode('default');
      setPuzzleOnboardingDraft(null);
      setPuzzleOnboardingPrompt('');
      setPuzzleOnboardingPhase('input');
      platformBootstrap.setPlatformTab('home');
      setSelectionStage('platform');
      void refreshPuzzleShelf();
    } catch (error) {
      setPuzzleOnboardingError(
        resolvePuzzleErrorMessage(error, '保存新手引导拼图失败。'),
      );
    } finally {
      setIsPuzzleOnboardingSaving(false);
    }
  }, [
    isPuzzleOnboardingSaving,
    platformBootstrap,
    puzzleOnboardingDraft,
    refreshPuzzleShelf,
    resolvePuzzleErrorMessage,
    setSelectionStage,
  ]);

  const requestPuzzleOnboardingLogin = useCallback(() => {
    if (isPuzzleOnboardingSaving) {
      return;
    }
    authUi?.openLoginModal(() => {
      void savePuzzleOnboardingDraft();
    });
  }, [authUi, isPuzzleOnboardingSaving, savePuzzleOnboardingDraft]);

  const skipPuzzleOnboarding = useCallback(() => {
    markPuzzleOnboardingSeen();
    setPuzzleOnboardingDraft(null);
    setPuzzleOnboardingPrompt('');
    setPuzzleOnboardingPhase('input');
    setPuzzleOnboardingError(null);
    setPuzzleRun(null);
    setPuzzleRuntimeAuthMode('default');
    setSelectedPuzzleDetail(null);
    platformBootstrap.setPlatformTab(authUi?.user ? 'home' : 'category');
    setSelectionStage('platform');
  }, [authUi?.user, platformBootstrap, setSelectionStage]);

  useEffect(() => {
    if (
      !authUi ||
      authUi?.user ||
      selectionStage !== 'platform' ||
      hasSeenPuzzleOnboarding()
    ) {
      return;
    }

    setPuzzleOnboardingPhase('input');
    setPuzzleOnboardingError(null);
    setSelectionStage('puzzle-onboarding');
  }, [authUi, authUi?.user, selectionStage, setSelectionStage]);

  const submitPuzzleOnboardingPrompt = useCallback(async () => {
    const promptText = puzzleOnboardingPrompt.trim();
    if (!promptText || puzzleOnboardingPhase === 'generating') {
      return;
    }

    setPuzzleOnboardingPhase('generating');
    setPuzzleOnboardingError(null);
    try {
      let item: PuzzleWorkSummary;
      try {
        const response = await generatePuzzleOnboardingWork({ promptText });
        item = {
          ...response.item,
          levels:
            response.item.levels && response.item.levels.length > 0
              ? response.item.levels
              : [response.level],
        };
      } catch (error) {
        if (!shouldUseLocalPuzzleOnboardingFallback(error)) {
          throw error;
        }

        // 中文注释：旧后端或资源路由尚未更新时，首访体验继续用前端临时图面兜底，不写入作品库。
        item = buildPuzzleOnboardingFallbackWork(promptText);
      }
      setPuzzleOnboardingDraft({ promptText, item });
      setSelectedPuzzleDetail(item);
      setPuzzleOnboardingPhase('generated');
      markPuzzleOnboardingSeen();
      window.setTimeout(() => {
        setPuzzleRun(startLocalPuzzleRun(item));
        setPuzzleRuntimeAuthMode('default');
        setPuzzleRuntimeReturnStage('platform');
        setSelectionStage('puzzle-runtime');
      }, PUZZLE_ONBOARDING_GENERATED_DELAY_MS);
    } catch (error) {
      setPuzzleOnboardingPhase('input');
      setPuzzleOnboardingError(
        resolvePuzzleErrorMessage(error, '生成新手引导拼图失败。'),
      );
    }
  }, [
    puzzleOnboardingPhase,
    puzzleOnboardingPrompt,
    resolvePuzzleErrorMessage,
    setSelectionStage,
  ]);

  const requestDeleteCreationWork = useCallback(
    (confirmation: DeleteCreationWorkConfirmation) => {
      if (deletingCreationWorkId) {
        return;
      }

      runProtectedAction(() => {
        setPendingDeleteCreationWork(confirmation);
      });
    },
    [deletingCreationWorkId, runProtectedAction],
  );

  const closeDeleteCreationWorkConfirmation = useCallback(() => {
    if (deletingCreationWorkId) {
      return;
    }

    setPendingDeleteCreationWork(null);
  }, [deletingCreationWorkId]);

  const confirmDeleteCreationWork = useCallback(() => {
    const confirmation = pendingDeleteCreationWork;
    if (!confirmation || deletingCreationWorkId) {
      return;
    }

    setPendingDeleteCreationWork(null);
    confirmation.run();
  }, [deletingCreationWorkId, pendingDeleteCreationWork]);

  const prepareCreationLaunch = useCallback(() => {
    if (sessionController.isCreatingAgentSession) {
      return false;
    }

    if (!hasSavedGame) {
      handleStartNewGame();
    }

    sessionController.setCreationTypeError(null);
    return true;
  }, [handleStartNewGame, hasSavedGame, sessionController]);

  const openCreationTypePicker = useCallback(() => {
    if (!prepareCreationLaunch()) {
      return;
    }

    setShowCreationTypeModal(true);
  }, [prepareCreationLaunch]);

  const bigFishFlow = usePlatformCreationAgentFlowController<
    BigFishSessionSnapshotResponse,
    Record<string, never>,
    { session: BigFishSessionSnapshotResponse },
    SendBigFishMessageRequest,
    ExecuteBigFishActionRequest,
    { session: BigFishSessionSnapshotResponse }
  >({
    client: {
      createSession: createBigFishCreationSession,
      getSession: getBigFishCreationSession,
      streamMessage: streamBigFishCreationMessage,
      executeAction: executeBigFishCreationAction,
      selectSession: (response) => response.session,
    },
    createPayload: {},
    workspaceStage: 'big-fish-agent-workspace',
    resultStage: 'big-fish-result',
    platformStage: 'platform',
    isCompileAction: (payload) => payload.action === 'big_fish_compile_draft',
    resolveErrorMessage: resolveBigFishErrorMessage,
    errorMessages: {
      open: '开启大鱼吃小鱼共创工作台失败。',
      restoreMissingSession: '这份大鱼吃小鱼草稿缺少会话信息，请重新开始创作。',
      restore: '读取大鱼吃小鱼创作草稿失败。',
      submit: '发送大鱼吃小鱼共创消息失败。',
      execute: '执行大鱼吃小鱼操作失败。',
    },
    enterCreateTab,
    setSelectionStage,
    onSessionOpened: () => {
      setShowCreationTypeModal(false);
    },
    onActionComplete: ({ payload, response, setSession }) => {
      setSession(response.session);
      if (payload.action === 'big_fish_publish_game') {
        void refreshBigFishShelf();
        void refreshBigFishGallery();
        openPublishShareModal({
          title: response.session.draft?.title ?? '大鱼吃小鱼',
          publicWorkCode: buildBigFishPublicWorkCode(
            response.session.sessionId,
          ),
          stage: 'big-fish-runtime',
        });
      }
      if (payload.action !== 'big_fish_compile_draft') {
        return;
      }
      setBigFishGenerationState((current) =>
        current
          ? {
              ...current,
              phase: 'ready',
              completedAssetCount: response.session.assetSlots.filter(
                (slot) => slot.status === 'ready',
              ).length,
              totalAssetCount: response.session.assetSlots.length,
            }
          : current,
      );
    },
    beforeExecuteAction: ({ payload }) => {
      if (payload.action !== 'big_fish_compile_draft') {
        return;
      }
      setSelectionStage('big-fish-generating');
      setBigFishGenerationState(createMiniGameDraftGenerationState('big-fish'));
    },
    onActionError: ({ payload, errorMessage }) => {
      if (payload.action !== 'big_fish_compile_draft') {
        return;
      }
      setBigFishGenerationState((current) =>
        current
          ? {
              ...current,
              phase: 'failed',
              error: errorMessage,
            }
          : current,
      );
    },
  });

  const match3dFlow = usePlatformCreationAgentFlowController<
    Match3DAgentSessionSnapshot,
    CreateMatch3DSessionRequest,
    Match3DSessionResponse,
    SendMatch3DMessageRequest,
    ExecuteMatch3DActionRequest,
    Match3DActionResponse
  >({
    client: {
      createSession: match3dCreationClient.createSession,
      getSession: match3dCreationClient.getSession,
      streamMessage: match3dCreationClient.streamMessage,
      executeAction: match3dCreationClient.executeAction,
      selectSession: (response) => response.session,
    },
    createPayload: {},
    workspaceStage: 'match3d-agent-workspace',
    resultStage: 'match3d-result',
    platformStage: 'platform',
    isCompileAction: (payload) => payload.action === 'match3d_compile_draft',
    resolveErrorMessage: resolveMatch3DErrorMessage,
    errorMessages: {
      open: '开启抓大鹅共创工作台失败。',
      restoreMissingSession: '这份抓大鹅草稿缺少会话信息，请重新开始创作。',
      restore: '读取抓大鹅创作草稿失败。',
      submit: '发送抓大鹅共创消息失败。',
      execute: '执行抓大鹅操作失败。',
    },
    enterCreateTab,
    setSelectionStage,
    onSessionOpened: () => {
      setShowCreationTypeModal(false);
    },
    onActionComplete: async ({ payload, response, setSession }) => {
      setSession(response.session);
      if (payload.action !== 'match3d_compile_draft') {
        return;
      }

      const profileId = response.session.draft?.profileId;
      if (!profileId) {
        setMatch3DProfile(null);
        return;
      }

      try {
        const { item } = await getMatch3DWorkDetail(profileId);
        setMatch3DProfile(item);
        await refreshMatch3DShelf().catch(() => undefined);
      } catch {
        setMatch3DProfile(buildMatch3DProfileFromSession(response.session));
      }
    },
  });

  const squareHoleFlow = usePlatformCreationAgentFlowController<
    SquareHoleSessionSnapshot,
    CreateSquareHoleSessionRequest,
    SquareHoleSessionResponse,
    SendSquareHoleMessageRequest,
    ExecuteSquareHoleActionRequest,
    SquareHoleActionResponse
  >({
    client: {
      createSession: squareHoleCreationClient.createSession,
      getSession: squareHoleCreationClient.getSession,
      streamMessage: squareHoleCreationClient.streamMessage,
      executeAction: squareHoleCreationClient.executeAction,
      selectSession: (response) => response.session,
    },
    createPayload: {},
    workspaceStage: 'square-hole-agent-workspace',
    resultStage: 'square-hole-result',
    platformStage: 'platform',
    isCompileAction: () => false,
    resolveErrorMessage: resolveSquareHoleErrorMessage,
    errorMessages: {
      open: '开启方洞挑战共创工作台失败。',
      restoreMissingSession: '这份方洞挑战草稿缺少会话信息，请重新开始创作。',
      restore: '读取方洞挑战创作草稿失败。',
      submit: '发送方洞挑战共创消息失败。',
      execute: '执行方洞挑战操作失败。',
    },
    enterCreateTab,
    setSelectionStage,
    onSessionOpened: () => {
      setShowCreationTypeModal(false);
    },
    beforeExecuteAction: ({ payload }) => {
      if (payload.action === 'square_hole_compile_draft') {
        setSquareHoleGenerationState(
          createMiniGameDraftGenerationState('square-hole'),
        );
        setSelectionStage('square-hole-generating');
      }
      if (payload.action === 'square_hole_generate_visual_assets') {
        setSquareHoleGenerationState((current) => ({
          ...(current ?? createMiniGameDraftGenerationState('square-hole')),
          phase: 'square-hole-cover',
          completedAssetCount: 0,
          totalAssetCount: 0,
          error: null,
        }));
        setSelectionStage('square-hole-generating');
      }
    },
    onActionComplete: async ({ payload, response, setSession }) => {
      setSession(response.session);
      if (
        payload.action !== 'square_hole_compile_draft' &&
        payload.action !== 'square_hole_generate_visual_assets'
      ) {
        return;
      }

      const profileId = response.session.draft?.profileId;
      if (!profileId) {
        setSquareHoleProfile(null);
        return;
      }

      if (payload.action === 'square_hole_compile_draft') {
        try {
          const assetResponse = await squareHoleCreationClient.executeAction(
            response.session.sessionId,
            {
              action: 'square_hole_generate_visual_assets',
            },
          );
          setSession(assetResponse.session);
          const assetProfileId = assetResponse.session.draft?.profileId;
          if (!assetProfileId) {
            setSquareHoleProfile(
              buildSquareHoleProfileFromSession(assetResponse.session),
            );
            setSelectionStage('square-hole-result');
            return;
          }
          const { item } = await getSquareHoleWorkDetail(assetProfileId);
          setSquareHoleProfile(item);
          setSquareHoleGenerationState((current) => ({
            ...(current ?? createMiniGameDraftGenerationState('square-hole')),
            phase: 'ready',
            completedAssetCount:
              item.shapeOptions.length + item.holeOptions.length + 2,
            totalAssetCount:
              item.shapeOptions.length + item.holeOptions.length + 2,
            error: null,
          }));
          await refreshSquareHoleShelf().catch(() => undefined);
          setSelectionStage('square-hole-result');
        } catch (error) {
          const errorMessage = resolveSquareHoleErrorMessage(
            error,
            '生成方洞挑战图片失败。',
          );
          setSquareHoleError(errorMessage);
          setSquareHoleGenerationState((current) => ({
            ...(current ?? createMiniGameDraftGenerationState('square-hole')),
            phase: 'failed',
            error: errorMessage,
          }));
          setSquareHoleProfile(
            buildSquareHoleProfileFromSession(response.session),
          );
          setSelectionStage('square-hole-result');
        }
        return;
      }

      try {
        const { item } = await getSquareHoleWorkDetail(profileId);
        setSquareHoleProfile(item);
        setSquareHoleGenerationState((current) => ({
          ...(current ?? createMiniGameDraftGenerationState('square-hole')),
          phase: 'ready',
          completedAssetCount:
            item.shapeOptions.length + item.holeOptions.length + 2,
          totalAssetCount:
            item.shapeOptions.length + item.holeOptions.length + 2,
          error: null,
        }));
        await refreshSquareHoleShelf().catch(() => undefined);
        setSelectionStage('square-hole-result');
      } catch {
        setSquareHoleProfile(
          buildSquareHoleProfileFromSession(response.session),
        );
        setSelectionStage('square-hole-result');
      }
    },
    onActionError: ({ payload, errorMessage }) => {
      if (
        payload.action === 'square_hole_compile_draft' ||
        payload.action === 'square_hole_generate_visual_assets'
      ) {
        setSquareHoleGenerationState((current) => ({
          ...(current ?? createMiniGameDraftGenerationState('square-hole')),
          phase: 'failed',
          error: errorMessage,
        }));
        setSelectionStage('square-hole-generating');
      }
    },
  });

  const puzzleFlow = usePlatformCreationAgentFlowController<
    PuzzleAgentSessionSnapshot,
    CreatePuzzleAgentSessionRequest,
    { session: PuzzleAgentSessionSnapshot },
    SendPuzzleAgentMessageRequest,
    PuzzleAgentActionRequest,
    {
      operation: PuzzleAgentOperationRecord;
      session: PuzzleAgentSessionSnapshot;
    }
  >({
    client: {
      createSession: createPuzzleAgentSession,
      getSession: getPuzzleAgentSession,
      streamMessage: streamPuzzleAgentMessage,
      executeAction: executePuzzleAgentAction,
      selectSession: (response) => response.session,
    },
    createPayload: {},
    workspaceStage: 'puzzle-agent-workspace',
    resultStage: 'puzzle-result',
    platformStage: 'platform',
    isCompileAction: (payload) => payload.action === 'compile_puzzle_draft',
    resolveErrorMessage: resolvePuzzleErrorMessage,
    errorMessages: {
      open: '开启拼图共创工作台失败。',
      restoreMissingSession: '这份拼图草稿缺少会话信息，请重新开始创作。',
      restore: '读取拼图创作草稿失败。',
      submit: '发送拼图共创消息失败。',
      execute: '执行拼图操作失败。',
    },
    enterCreateTab,
    setSelectionStage,
    onSessionOpened: () => {
      sessionController.setCreationTypeError(null);
      setPuzzleCreationError(null);
      setShowCreationTypeModal(false);
    },
    onOpenError: ({ errorMessage }) => {
      sessionController.setCreationTypeError(errorMessage);
      setPuzzleCreationError(errorMessage);
    },
    onActionComplete: async ({ payload, response, setSession }) => {
      setPuzzleOperation(response.operation);
      setSession(response.session);
      const formPayload = buildPuzzleFormPayloadFromAction(payload);
      if (formPayload) {
        setPuzzleFormDraftPayload(formPayload);
      }

      if (
        payload.action === 'publish_puzzle_work' ||
        payload.action === 'generate_puzzle_tags'
      ) {
        await Promise.allSettled([
          refreshPuzzleShelf(),
          refreshPuzzleGallery(),
        ]);
      }

      if (payload.action === 'compile_puzzle_draft') {
        setPuzzleGenerationState((current) =>
          current
            ? {
                ...current,
                phase: 'ready',
                completedAssetCount: 1,
                totalAssetCount: 1,
              }
            : current,
        );
      }

      if (
        payload.action === 'publish_puzzle_work' &&
        response.session.publishedProfileId
      ) {
        const galleryDetail = await getPuzzleGalleryDetail(
          response.session.publishedProfileId,
        );
        setSelectedPuzzleDetail(galleryDetail.item);
        const detailEntry = mapPuzzleWorkToPublicWorkDetail(galleryDetail.item);
        setSelectedPublicWorkDetail(detailEntry);
        setPublicWorkDetailError(null);
        setSelectionStage('work-detail');
        pushAppHistoryPath(
          buildPublicWorkStagePath(
            'work-detail',
            buildPuzzlePublicWorkCode(galleryDetail.item.profileId),
          ),
        );
        openPublishShareModal({
          title: galleryDetail.item.workTitle || galleryDetail.item.levelName,
          publicWorkCode: buildPuzzlePublicWorkCode(
            galleryDetail.item.profileId,
          ),
          stage: 'puzzle-gallery-detail',
        });
      }
    },
    beforeExecuteAction: ({ payload }) => {
      const formPayload = buildPuzzleFormPayloadFromAction(payload);
      if (formPayload) {
        setPuzzleFormDraftPayload(formPayload);
      }

      if (payload.action !== 'compile_puzzle_draft') {
        return;
      }
      setSelectionStage('puzzle-generating');
      setPuzzleGenerationState(createMiniGameDraftGenerationState('puzzle'));
    },
    onActionError: ({ payload, errorMessage }) => {
      if (payload.action !== 'compile_puzzle_draft') {
        return;
      }
      setPuzzleGenerationState((current) =>
        current
          ? {
              ...current,
              phase: 'failed',
              error: errorMessage,
            }
          : current,
      );
    },
  });

  const visualNovelFlow = usePlatformCreationAgentFlowController<
    VisualNovelAgentSessionSnapshot,
    CreateVisualNovelSessionRequest,
    VisualNovelSessionResponse,
    SendVisualNovelMessageRequest,
    ExecuteVisualNovelAgentActionRequest,
    VisualNovelSessionResponse
  >({
    client: {
      createSession: createVisualNovelSession,
      getSession: getVisualNovelSession,
      streamMessage: streamVisualNovelMessage,
      executeAction: executeVisualNovelAction,
      selectSession: (response) => response.session,
    },
    createPayload: {
      sourceMode: 'idea',
      seedText: null,
      sourceAssetIds: [],
    },
    workspaceStage: 'visual-novel-agent-workspace',
    resultStage: 'visual-novel-result',
    platformStage: 'platform',
    isCompileAction: (payload) => payload.kind === 'compile_work_profile',
    resolveErrorMessage: resolvePuzzleErrorMessage,
    errorMessages: {
      open: '开启视觉小说共创工作台失败。',
      restoreMissingSession: '这份视觉小说草稿缺少会话信息，请重新开始创作。',
      restore: '读取视觉小说创作草稿失败。',
      submit: '发送视觉小说共创消息失败。',
      execute: '执行视觉小说操作失败。',
    },
    enterCreateTab,
    setSelectionStage,
    onSessionOpened: () => {
      setShowCreationTypeModal(false);
    },
    onActionComplete: ({ response, setSession }) => {
      setSession(response.session);
    },
  });

  const bigFishSession = bigFishFlow.session;
  const bigFishError = bigFishFlow.error;
  const setBigFishError = bigFishFlow.setError;
  bigFishErrorSetterRef.current = setBigFishError;
  const isBigFishBusy = bigFishFlow.isBusy;
  const streamingBigFishReplyText = bigFishFlow.streamingReplyText;
  const isStreamingBigFishReply = bigFishFlow.isStreamingReply;

  const match3dSession = match3dFlow.session;
  const match3dError = match3dFlow.error;
  const setMatch3DSession = match3dFlow.setSession;
  const setMatch3DError = match3dFlow.setError;
  match3DErrorSetterRef.current = setMatch3DError;
  const isMatch3DBusy = match3dFlow.isBusy;
  const streamingMatch3DReplyText = match3dFlow.streamingReplyText;
  const setStreamingMatch3DReplyText = match3dFlow.setStreamingReplyText;
  const isStreamingMatch3DReply = match3dFlow.isStreamingReply;
  const setIsStreamingMatch3DReply = match3dFlow.setIsStreamingReply;

  const squareHoleSession = squareHoleFlow.session;
  const squareHoleError = squareHoleFlow.error;
  const setSquareHoleSession = squareHoleFlow.setSession;
  const setSquareHoleError = squareHoleFlow.setError;
  squareHoleErrorSetterRef.current = setSquareHoleError;
  const isSquareHoleBusy = squareHoleFlow.isBusy;
  const streamingSquareHoleReplyText = squareHoleFlow.streamingReplyText;
  const setStreamingSquareHoleReplyText = squareHoleFlow.setStreamingReplyText;
  const isStreamingSquareHoleReply = squareHoleFlow.isStreamingReply;
  const setIsStreamingSquareHoleReply = squareHoleFlow.setIsStreamingReply;

  const puzzleSession = puzzleFlow.session;
  const puzzleError = puzzleFlow.error;
  const setPuzzleError = puzzleFlow.setError;
  puzzleErrorSetterRef.current = setPuzzleError;
  const isPuzzleBusy = puzzleFlow.isBusy;
  const setIsPuzzleBusy = puzzleFlow.setIsBusy;
  const isStreamingPuzzleReply = puzzleFlow.isStreamingReply;
  const visualNovelSession = visualNovelFlow.session;
  const visualNovelError = visualNovelFlow.error;
  const setVisualNovelSession = visualNovelFlow.setSession;
  const setVisualNovelError = visualNovelFlow.setError;
  visualNovelErrorSetterRef.current = setVisualNovelError;
  const isVisualNovelBusy = visualNovelFlow.isBusy;
  const setIsVisualNovelBusy = visualNovelFlow.setIsBusy;
  const visualNovelStreamingReplyText = visualNovelFlow.streamingReplyText;
  const isVisualNovelStreamingReply = visualNovelFlow.isStreamingReply;
  const resetRpgSessionViewState = sessionController.resetSessionViewState;
  const setRpgGeneratedCustomWorldProfile =
    sessionController.setGeneratedCustomWorldProfile;
  const setRpgCustomWorldError = sessionController.setCustomWorldError;
  const persistRpgAgentUiState = sessionController.persistAgentUiState;
  const resetAutoSaveTrackingToIdle =
    autosaveCoordinator.resetAutoSaveTrackingToIdle;

  useEffect(() => {
    puzzleRunRef.current = puzzleRun;
  }, [puzzleRun]);

  const openBigFishAgentWorkspace = useCallback(async () => {
    setBigFishRun(null);
    await bigFishFlow.openWorkspace();
  }, [bigFishFlow]);

  const openMatch3DAgentWorkspace = useCallback(async () => {
    setMatch3DSession(null);
    setMatch3DProfile(null);
    setMatch3DRun(null);
    setMatch3DError(null);
    setStreamingMatch3DReplyText('');
    setIsStreamingMatch3DReply(false);
    await match3dFlow.openWorkspace();
  }, [
    match3dFlow,
    setIsStreamingMatch3DReply,
    setMatch3DError,
    setMatch3DProfile,
    setMatch3DRun,
    setMatch3DSession,
    setStreamingMatch3DReplyText,
  ]);

  const openSquareHoleAgentWorkspace = useCallback(async () => {
    setSquareHoleSession(null);
    setSquareHoleProfile(null);
    setSquareHoleRun(null);
    setSquareHoleError(null);
    setSquareHoleGenerationState(null);
    setSquareHoleRuntimeReturnStage('square-hole-result');
    setStreamingSquareHoleReplyText('');
    setIsStreamingSquareHoleReply(false);
    await squareHoleFlow.openWorkspace();
  }, [
    setIsStreamingSquareHoleReply,
    setSquareHoleError,
    setSquareHoleProfile,
    setSquareHoleRun,
    setSquareHoleSession,
    setStreamingSquareHoleReplyText,
    squareHoleFlow,
  ]);

  const openPuzzleAgentWorkspace = useCallback(async () => {
    setPuzzleRun(null);
    setPuzzleRuntimeAuthMode('default');
    setPuzzleOperation(null);
    setPuzzleGenerationState(null);
    setPuzzleFormDraftPayload(null);
    sessionController.setCreationTypeError(null);
    setPuzzleCreationError(null);
    const nextSession = await puzzleFlow.openWorkspace({});
    if (nextSession) {
      void refreshPuzzleShelf();
    }
  }, [puzzleFlow, refreshPuzzleShelf, sessionController]);

  const openVisualNovelAgentWorkspace = useCallback(() => {
    setVisualNovelWork(null);
    setVisualNovelRun(null);
    setVisualNovelRuntimeReturnStage('visual-novel-result');
    visualNovelFlow.resetTransientState();
    enterCreateTab();
    setShowCreationTypeModal(false);
    setSelectionStage('visual-novel-agent-workspace');
  }, [enterCreateTab, setSelectionStage, visualNovelFlow]);

  const leaveCreativeAgentWorkspace = useCallback(() => {
    const sessionId = creativeAgentSession?.sessionId?.trim();
    if (sessionId && creativeAgentSession?.stage !== 'target_ready') {
      void cancelCreativeAgentSession(sessionId).catch(() => undefined);
    }
    setCreativeAgentSession(null);
    setCreativeAgentEvents([]);
    setActiveCreativeAgentSessionId(null);
    setCreativeAgentError(null);
    setIsCreativeAgentBusy(false);
    setIsCreativeAgentStreaming(false);
    setCreativeDraftEditError(null);
    setIsCreativeDraftEditBusy(false);
    enterCreateTab();
    setSelectionStage('platform');
  }, [creativeAgentSession, enterCreateTab, setSelectionStage]);

  const openCreativeAgentWorkspace = useCallback(async () => {
    if (isCreativeAgentBusy || isCreativeAgentStreaming) {
      return;
    }

    setPuzzleRun(null);
    setPuzzleRuntimeAuthMode('default');
    setPuzzleOperation(null);
    setPuzzleGenerationState(null);
    setPuzzleFormDraftPayload(null);
    setCreativeAgentError(null);
    setCreativeAgentEvents([]);
    setIsCreativeAgentBusy(true);

    try {
      const response = await createCreativeAgentSession({
        entryContext: 'creation_home',
      });
      setCreativeAgentSession(response.session);
      setActiveCreativeAgentSessionId(response.session.sessionId);
      enterCreateTab();
      setShowCreationTypeModal(false);
      setSelectionStage('creative-agent-workspace');
    } catch (error) {
      setCreativeAgentError(
        resolvePuzzleErrorMessage(error, '开启智能创作工作区失败。'),
      );
    } finally {
      setIsCreativeAgentBusy(false);
    }
  }, [
    enterCreateTab,
    isCreativeAgentBusy,
    isCreativeAgentStreaming,
    resolvePuzzleErrorMessage,
    setSelectionStage,
  ]);

  const createPuzzleDraftFromForm = useCallback(
    async (payload: CreatePuzzleAgentSessionRequest) => {
      setPuzzleFormDraftPayload(payload);
      const nextSession =
        puzzleFlow.session && !isEmptyPuzzleFormOnlyDraft(puzzleFlow.session)
          ? puzzleFlow.session
          : await puzzleFlow.openWorkspace(payload);
      if (!nextSession) {
        return;
      }

      await puzzleFlow.executeAction(
        buildPuzzleCompileActionFromFormPayload(payload),
        nextSession,
      );
    },
    [puzzleFlow],
  );

  const savePuzzleFormDraft = useCallback(
    async (payload: CreatePuzzleAgentSessionRequest) => {
      const session = puzzleFlow.session;
      if (!session || session.stage !== 'collecting_anchors') {
        return;
      }

      setPuzzleFormDraftPayload(payload);

      try {
        const response = await executePuzzleAgentAction(session.sessionId, {
          action: 'save_puzzle_form_draft',
          promptText: payload.pictureDescription ?? null,
          pictureDescription: payload.pictureDescription ?? '',
          referenceImageSrc: payload.referenceImageSrc ?? null,
          imageModel: payload.imageModel ?? null,
          aiRedraw: payload.aiRedraw ?? true,
        });
        setPuzzleOperation(response.operation);
        puzzleFlow.setSession(response.session);
        setPuzzleError(null);
        void refreshPuzzleShelf();
      } catch (error) {
        setPuzzleError(
          resolvePuzzleErrorMessage(error, '保存拼图表单草稿失败。'),
        );
      }
    },
    [puzzleFlow, refreshPuzzleShelf, resolvePuzzleErrorMessage, setPuzzleError],
  );

  useEffect(() => {
    if (platformBootstrap.canReadProtectedData) {
      hadReadableProtectedDataRef.current = true;
      return;
    }

    if (authUi?.user || !hadReadableProtectedDataRef.current) {
      return;
    }

    hadReadableProtectedDataRef.current = false;

    // 创作中心只展示当前登录用户的私有作品。
    // 一旦退出登录或鉴权上下文被收回，三类作品缓存必须同步清空，不能等刷新页面。
    setShowCreationTypeModal(false);
    setSelectedDetailEntry(null);
    setSelectedPublicWorkDetail(null);
    setPublicWorkDetailError(null);
    setIsPublicWorkDetailBusy(false);
    setBigFishWorks([]);
    setBigFishRun(null);
    setBigFishRuntimeShare(null);
    setBigFishRuntimeWork(null);
    setBigFishRuntimeStartedAt(null);
    setBigFishRuntimeReturnStage('platform');
    setBigFishGenerationState(null);
    setBigFishError(null);
    setMatch3DSession(null);
    setMatch3DProfile(null);
    setMatch3DWorks([]);
    setMatch3DGalleryEntries([]);
    setMatch3DRun(null);
    setMatch3DRuntimeReturnStage('match3d-result');
    setMatch3DError(null);
    setStreamingMatch3DReplyText('');
    setIsStreamingMatch3DReply(false);
    setSquareHoleSession(null);
    setSquareHoleProfile(null);
    setSquareHoleWorks([]);
    setSquareHoleGalleryEntries([]);
    setSquareHoleRun(null);
    setSquareHoleRuntimeReturnStage('square-hole-result');
    setSquareHoleGenerationState(null);
    setSquareHoleError(null);
    setStreamingSquareHoleReplyText('');
    setIsStreamingSquareHoleReply(false);
    setCreativeAgentSession(null);
    setCreativeAgentEvents([]);
    setActiveCreativeAgentSessionId(null);
    setCreativeAgentError(null);
    setIsCreativeAgentBusy(false);
    setIsCreativeAgentStreaming(false);
    setCreativeDraftEditError(null);
    setIsCreativeDraftEditBusy(false);
    setPuzzleOperation(null);
    setPuzzleWorks([]);
    setSelectedPuzzleDetail(null);
    setPuzzleRuntimeReturnStage('puzzle-gallery-detail');
    setPuzzleRun(null);
    setPuzzleRuntimeAuthMode('default');
    setPuzzleGenerationState(null);
    setIsPuzzleNextLevelGenerating(false);
    setPuzzleShelfError(null);
    setPuzzleCreationError(null);
    setPuzzleError(null);
    setVisualNovelSession(null);
    setVisualNovelWork(null);
    setVisualNovelWorks([]);
    setVisualNovelGalleryEntries([]);
    setVisualNovelRun(null);
    setVisualNovelRuntimeReturnStage('visual-novel-result');
    setVisualNovelError(null);
    setDeletingCreationWorkId(null);
    setClaimingPuzzlePointIncentiveProfileId(null);
    setPublishSharePayload(null);
    setProfilePlayStats(null);
    setProfilePlayStatsError(null);
    setIsProfilePlayStatsOpen(false);
    resetRpgSessionViewState();
    setRpgGeneratedCustomWorldProfile(null);
    setRpgCustomWorldError(null);
    persistRpgAgentUiState(null, null);
    resetAutoSaveTrackingToIdle();

    if (
      selectionStage !== 'platform' &&
      selectionStage !== 'work-detail' &&
      selectionStage !== 'detail' &&
      selectionStage !== 'creative-agent-workspace' &&
      selectionStage !== 'puzzle-gallery-detail'
    ) {
      setSelectionStage('platform');
    }
  }, [
    authUi?.user,
    platformBootstrap.canReadProtectedData,
    persistRpgAgentUiState,
    resetAutoSaveTrackingToIdle,
    resetRpgSessionViewState,
    selectionStage,
    setBigFishError,
    setIsStreamingMatch3DReply,
    setIsStreamingSquareHoleReply,
    setMatch3DError,
    setMatch3DSession,
    setPuzzleError,
    setRpgCustomWorldError,
    setRpgGeneratedCustomWorldProfile,
    setSelectionStage,
    setSquareHoleError,
    setSquareHoleSession,
    setStreamingMatch3DReplyText,
    setStreamingSquareHoleReplyText,
    setVisualNovelError,
    setVisualNovelSession,
  ]);

  const handleCreationHubCreateType = useCallback(
    (type: PlatformCreationTypeId) => {
      if (type === 'airp') {
        return;
      }

      if (!prepareCreationLaunch()) {
        return;
      }

      if (type === 'rpg') {
        runProtectedAction(() => {
          void sessionController.openRpgAgentWorkspace();
        });
        return;
      }

      if (type === 'big-fish') {
        runProtectedAction(() => {
          void openBigFishAgentWorkspace();
        });
        return;
      }

      if (type === 'match3d') {
        runProtectedAction(() => {
          void openMatch3DAgentWorkspace();
        });
        return;
      }

      if (type === 'square-hole') {
        runProtectedAction(() => {
          void openSquareHoleAgentWorkspace();
        });
        return;
      }

      if (type === 'puzzle') {
        runProtectedAction(() => {
          void openPuzzleAgentWorkspace();
        });
        return;
      }

      if (type === 'visual-novel') {
        runProtectedAction(() => {
          openVisualNovelAgentWorkspace();
        });
        return;
      }
    },
    [
      openBigFishAgentWorkspace,
    openMatch3DAgentWorkspace,
    openPuzzleAgentWorkspace,
    openSquareHoleAgentWorkspace,
      openVisualNovelAgentWorkspace,
      prepareCreationLaunch,
      runProtectedAction,
      sessionController,
    ],
  );

  const leaveBigFishFlow = useCallback(() => {
    setBigFishRun(null);
    setBigFishRuntimeWork(null);
    setBigFishRuntimeStartedAt(null);
    setBigFishRuntimeReturnStage('platform');
    setBigFishGenerationState(null);
    bigFishFlow.leaveFlow();
  }, [bigFishFlow]);

  const leaveMatch3DFlow = useCallback(() => {
    setMatch3DRun(null);
    setMatch3DRuntimeReturnStage('match3d-result');
    match3dFlow.leaveFlow();
  }, [match3dFlow]);

  const leaveSquareHoleFlow = useCallback(() => {
    setSquareHoleRun(null);
    setSquareHoleRuntimeReturnStage('square-hole-result');
    setSquareHoleGenerationState(null);
    squareHoleFlow.leaveFlow();
  }, [squareHoleFlow]);

  const leavePuzzleFlow = useCallback(() => {
    setPuzzleOperation(null);
    setPuzzleRun(null);
    setPuzzleRuntimeAuthMode('default');
    setPuzzleGenerationState(null);
    setIsPuzzleNextLevelGenerating(false);
    setActiveCreativeAgentSessionId(null);
    setCreativeDraftEditError(null);
    puzzleFlow.leaveFlow();
  }, [puzzleFlow]);

  const leaveVisualNovelFlow = useCallback(() => {
    setVisualNovelWork(null);
    setVisualNovelRun(null);
    setVisualNovelRuntimeReturnStage('visual-novel-result');
    visualNovelFlow.leaveFlow();
  }, [visualNovelFlow]);

  const openVisualNovelResult = useCallback(
    (session: VisualNovelAgentSessionSnapshot) => {
      setVisualNovelSession(session);
      setVisualNovelWork(null);
      setVisualNovelRun(null);
      setVisualNovelRuntimeReturnStage('visual-novel-result');
      setSelectionStage('visual-novel-result');
    },
    [setSelectionStage, setVisualNovelSession],
  );

  const saveVisualNovelDraft = useCallback(
    async (draft: VisualNovelResultDraft) => {
      const currentSession = visualNovelSession;
      if (!currentSession || !currentSession.sessionId.trim()) {
        return;
      }

      setVisualNovelError(null);
      const now = new Date().toISOString();
      // 中文注释：VN-07 的保存草稿先保证结果页编辑不会丢失；
      // 后端 work profile 编译仍由“编译草稿”显式触发，避免把本地保存伪装成正式持久化。
      setVisualNovelSession({
        ...currentSession,
        draft,
        status: draft.publishReady ? 'ready' : currentSession.status,
        updatedAt: now,
      });
      if (!visualNovelWork) {
        return;
      }

      setIsVisualNovelBusy(true);
      try {
        const response = await updateVisualNovelWork(
          visualNovelWork.summary.profileId,
          { draft },
        );
        setVisualNovelWork(response.work);
      } catch (error) {
        setVisualNovelError(
          resolvePuzzleErrorMessage(error, '保存视觉小说草稿失败。'),
        );
      } finally {
        setIsVisualNovelBusy(false);
      }
    },
    [
      resolvePuzzleErrorMessage,
      setIsVisualNovelBusy,
      setVisualNovelError,
      setVisualNovelSession,
      visualNovelSession,
      visualNovelWork,
    ],
  );

  const compileVisualNovelDraft = useCallback(
    async (draft: VisualNovelResultDraft) => {
      const sessionId = visualNovelSession?.sessionId?.trim();
      if (!sessionId) {
        return;
      }

      setVisualNovelError(null);
      setIsVisualNovelBusy(true);
      try {
        const response = await compileVisualNovelWorkProfile(sessionId, {
          draft,
        });
        setVisualNovelSession(response.session);
        setVisualNovelWork(response.work);
        setSelectionStage('visual-novel-result');
      } catch (error) {
        setVisualNovelError(
          resolvePuzzleErrorMessage(error, '编译视觉小说草稿失败。'),
        );
      } finally {
        setIsVisualNovelBusy(false);
      }
    },
    [
      resolvePuzzleErrorMessage,
      setIsVisualNovelBusy,
      setSelectionStage,
      setVisualNovelError,
      setVisualNovelSession,
      visualNovelSession?.sessionId,
    ],
  );

  const publishVisualNovelDraft = useCallback(
    async (draft: VisualNovelResultDraft) => {
      const profileId = draft.profileId?.trim();
      if (!profileId) {
        return;
      }

      setVisualNovelError(null);
      setIsVisualNovelBusy(true);
      try {
        const { work } = await updateVisualNovelWork(profileId, {
          draft,
        });
        setVisualNovelWork(work);
        const publishedResponse = await publishVisualNovelWork(profileId);
        setVisualNovelWork(publishedResponse.work);
        setSelectedPublicWorkDetail(
          mapVisualNovelWorkToPublicWorkDetail(publishedResponse.work.summary),
        );
        await Promise.allSettled([
          refreshVisualNovelShelf(),
          refreshVisualNovelGallery(),
        ]);
        openPublishShareModal({
          title: publishedResponse.work.summary.title,
          publicWorkCode: buildVisualNovelPublicWorkCode(
            publishedResponse.work.summary.profileId,
          ),
          stage: 'work-detail',
        });
      } catch (error) {
        setVisualNovelError(
          resolvePuzzleErrorMessage(error, '发布视觉小说作品失败。'),
        );
      } finally {
        setIsVisualNovelBusy(false);
      }
    },
    [
      openPublishShareModal,
      refreshVisualNovelGallery,
      refreshVisualNovelShelf,
      resolvePuzzleErrorMessage,
      setIsVisualNovelBusy,
      setVisualNovelError,
    ],
  );

  const startVisualNovelTestRunFromDraft = useCallback(
    async (draft: VisualNovelResultDraft) => {
      const profileId =
        visualNovelWork?.summary.profileId?.trim() ?? draft.profileId?.trim();
      if (!profileId) {
        const run = createMockVisualNovelRunFromDraft(draft);
        setVisualNovelRun(run);
        setVisualNovelRuntimeReturnStage('visual-novel-result');
        setSelectionStage('visual-novel-runtime');
        return;
      }

      setVisualNovelError(null);
      setIsVisualNovelBusy(true);
      try {
        const { run } = await startVisualNovelRun(profileId, {
          profileId,
          mode: 'test',
        });
        setVisualNovelRun(run);
        setVisualNovelRuntimeReturnStage('visual-novel-result');
        setSelectionStage('visual-novel-runtime');
      } catch (error) {
        // 中文注释：VN-07 只要求结果页进入测试运行时；真实 runtime 接口未就绪时，
        // 使用当前 draft 生成本地 test run，不扩展到正式玩家运行链路。
        setVisualNovelRun(createMockVisualNovelRunFromDraft(draft));
        setVisualNovelRuntimeReturnStage('visual-novel-result');
        setSelectionStage('visual-novel-runtime');
        setVisualNovelError(
          resolvePuzzleErrorMessage(error, '已进入本地试玩，真实运行接口暂不可用。'),
        );
      } finally {
        setIsVisualNovelBusy(false);
      }
    },
    [
      resolvePuzzleErrorMessage,
      setIsVisualNovelBusy,
      setVisualNovelError,
      setSelectionStage,
      visualNovelWork?.summary.profileId,
    ],
  );

  const startVisualNovelRunFromProfile = useCallback(
    async (
      profileId: string,
      returnStage: VisualNovelRuntimeReturnStage = 'work-detail',
      options: { embedded?: boolean } = {},
    ) => {
      const targetProfileId = profileId.trim();
      if (!targetProfileId) {
        return false;
      }

      setVisualNovelError(null);
      setIsVisualNovelBusy(true);

      try {
        let workDetail =
          visualNovelWork?.summary.profileId === targetProfileId
            ? visualNovelWork
            : null;
        if (!workDetail) {
          const response = await getVisualNovelWorkDetail(
            targetProfileId,
            options.embedded ? RECOMMEND_RUNTIME_BACKGROUND_AUTH_OPTIONS : {},
          );
          workDetail = response.work;
        }
        const startRunPayload = {
          profileId: targetProfileId,
          mode: 'play' as const,
        };
        const { run } = options.embedded
          ? await startVisualNovelRun(
              targetProfileId,
              startRunPayload,
              RECOMMEND_RUNTIME_BACKGROUND_AUTH_OPTIONS,
            )
          : await startVisualNovelRun(targetProfileId, startRunPayload);
        setVisualNovelWork(workDetail);
        setVisualNovelRun(run);
        setVisualNovelRuntimeReturnStage(returnStage);
        if (!options.embedded) {
          setSelectionStage('visual-novel-runtime');
          pushAppHistoryPath(
            buildPublicWorkStagePath(
              'visual-novel-runtime',
              buildVisualNovelPublicWorkCode(targetProfileId),
            ),
          );
        }
        return true;
      } catch (error) {
        setVisualNovelError(
          resolvePuzzleErrorMessage(error, '启动视觉小说试玩失败。'),
        );
        return false;
      } finally {
        setIsVisualNovelBusy(false);
      }
    },
    [
      resolvePuzzleErrorMessage,
      setIsVisualNovelBusy,
      setSelectionStage,
      setVisualNovelError,
      visualNovelWork,
    ],
  );

  const submitVisualNovelRuntimeAction = useCallback(
    async (payload: VisualNovelRuntimeActionRequest) => {
      if (!visualNovelRun || isVisualNovelBusy) {
        return;
      }

      if (visualNovelRun.runId.startsWith('vn-run-local')) {
        return;
      }

      setVisualNovelError(null);
      setIsVisualNovelBusy(true);
      try {
        const nextRun = await streamVisualNovelRuntimeAction(
          visualNovelRun.runId,
          payload,
        );
        setVisualNovelRun(nextRun);
      } catch (error) {
        setVisualNovelError(
          resolvePuzzleErrorMessage(error, '推进视觉小说运行失败。'),
        );
      } finally {
        setIsVisualNovelBusy(false);
      }
    },
    [
      isVisualNovelBusy,
      resolvePuzzleErrorMessage,
      setIsVisualNovelBusy,
      setVisualNovelError,
      visualNovelRun,
    ],
  );

  const streamCreativeAgentPayloadForSession = useCallback(
    async (
      session: CreativeAgentSessionSnapshot,
      payload: StreamCreativeAgentMessageRequest,
    ) => {
      setCreativeAgentError(null);
      setIsCreativeAgentStreaming(true);
      setCreativeAgentEvents([]);
      setCreativeAgentSession((current) =>
        current
          ? {
              ...current,
              messages: [
                ...current.messages,
                {
                  id: payload.clientMessageId,
                  role: 'user',
                  kind: 'chat',
                  text: payload.content
                    .map((part) =>
                      part.type === 'input_text' ? part.text.trim() : '参考图',
                    )
                    .filter(Boolean)
                    .join(' / '),
                  createdAt: new Date().toISOString(),
                },
              ],
            }
          : current,
      );

      try {
        const nextSession = await streamCreativeAgentMessage(
          session.sessionId,
          payload,
          {
            onEvent: (event) => {
              setCreativeAgentEvents((current) => [...current, event]);
              if (event.event === 'stage') {
                setCreativeAgentSession((current) =>
                  current
                    ? {
                        ...current,
                        stage: event.data.stage,
                        updatedAt: new Date().toISOString(),
                      }
                    : current,
                );
              }
              if (event.event === 'session') {
                setCreativeAgentSession(event.data.session);
              }
            },
          },
        );
        setCreativeAgentSession(nextSession);
        if (nextSession.targetBinding) {
          setActiveCreativeAgentSessionId(nextSession.sessionId);
        }
      } catch (error) {
        setCreativeAgentError(
          resolvePuzzleErrorMessage(error, '发送智能创作消息失败。'),
        );
      } finally {
        setIsCreativeAgentStreaming(false);
      }
    },
    [resolvePuzzleErrorMessage],
  );

  const submitCreativeAgentMessage = useCallback(
    async (payload: StreamCreativeAgentMessageRequest) => {
      if (!creativeAgentSession || isCreativeAgentStreaming) {
        return;
      }

      await streamCreativeAgentPayloadForSession(creativeAgentSession, payload);
    },
    [
      creativeAgentSession,
      isCreativeAgentStreaming,
      streamCreativeAgentPayloadForSession,
    ],
  );

  const confirmCreativeTemplateSelection = useCallback(
    async (selection: PuzzleCreativeTemplateSelection) => {
      const sessionId = creativeAgentSession?.sessionId?.trim();
      if (!sessionId || isCreativeAgentBusy || isCreativeAgentStreaming) {
        return;
      }

      setIsCreativeAgentBusy(true);
      setCreativeAgentError(null);
      try {
        const response = await confirmCreativePuzzleTemplate(sessionId, {
          selection,
        });
        setCreativeAgentSession(response.session);
        if (response.session.targetBinding) {
          setActiveCreativeAgentSessionId(response.session.sessionId);
        }
      } catch (error) {
        setCreativeAgentError(
          resolvePuzzleErrorMessage(error, '确认拼图模板失败。'),
        );
      } finally {
        setIsCreativeAgentBusy(false);
      }
    },
    [
      creativeAgentSession?.sessionId,
      isCreativeAgentBusy,
      isCreativeAgentStreaming,
      resolvePuzzleErrorMessage,
    ],
  );

  const submitBigFishMessage = bigFishFlow.submitMessage;

  const submitMatch3DMessage = match3dFlow.submitMessage;

  const submitSquareHoleMessage = squareHoleFlow.submitMessage;

  const submitPuzzleMessage = puzzleFlow.submitMessage;

  const executeBigFishAction = bigFishFlow.executeAction;

  const executeMatch3DAction = match3dFlow.executeAction;

  const executeSquareHoleAction = squareHoleFlow.executeAction;

  const retrySquareHoleAssetGeneration = useCallback(() => {
    const session = squareHoleSession;
    if (!session?.draft?.profileId) {
      void executeSquareHoleAction({
        action: 'square_hole_compile_draft',
      });
      return;
    }

    void executeSquareHoleAction({
      action: 'square_hole_generate_visual_assets',
    });
  }, [executeSquareHoleAction, squareHoleSession]);

  const executePuzzleAction = puzzleFlow.executeAction;

  const retryPuzzleDraftGeneration = useCallback(() => {
    if (puzzleFormDraftPayload) {
      void createPuzzleDraftFromForm(puzzleFormDraftPayload);
      return;
    }

    void executePuzzleAction(
      buildPuzzleCompileActionFromFormPayload(puzzleFormDraftPayload),
    );
  }, [createPuzzleDraftFromForm, executePuzzleAction, puzzleFormDraftPayload]);

  const executePuzzleWorkspaceAction = useCallback(
    (payload: PuzzleAgentActionRequest) => {
      if (
        payload.action === 'compile_puzzle_draft' &&
        isEmptyPuzzleFormOnlyDraft(puzzleFlow.session)
      ) {
        const formPayload = buildPuzzleFormPayloadFromAction(payload);
        if (formPayload) {
          void createPuzzleDraftFromForm(formPayload);
          return;
        }
      }

      void executePuzzleAction(payload);
    },
    [createPuzzleDraftFromForm, executePuzzleAction, puzzleFlow.session],
  );

  const openCreativeAgentTarget = useCallback(async () => {
    const binding = creativeAgentSession?.targetBinding;
    if (!binding || isPuzzleBusy) {
      return;
    }

    setIsPuzzleBusy(true);
    setPuzzleError(null);
    try {
      const response = await getPuzzleAgentSession(binding.targetSessionId);
      puzzleFlow.setSession(response.session);
      setPuzzleOperation(null);
      setPuzzleRun(null);
      setPuzzleRuntimeAuthMode('default');
      enterCreateTab();
      setActiveCreativeAgentSessionId(creativeAgentSession.sessionId);
      setCreativeDraftEditError(null);
      setSelectionStage(resolveCreativeAgentTargetSelectionStage(binding.targetStage));
    } catch (error) {
      setCreativeAgentError(
        resolvePuzzleErrorMessage(error, '打开拼图草稿失败。'),
      );
    } finally {
      setIsPuzzleBusy(false);
    }
  }, [
    creativeAgentSession,
    enterCreateTab,
    isPuzzleBusy,
    puzzleFlow,
    resolvePuzzleErrorMessage,
    setIsPuzzleBusy,
    setPuzzleError,
    setSelectionStage,
  ]);

  const submitCreativeDraftEdit = useCallback(
    async ({
      instruction,
      currentDraft,
    }: {
      instruction: string;
      currentDraft: PuzzleResultDraft;
    }): Promise<CreativeDraftEditResult | null> => {
      const creativeSessionId =
        activeCreativeAgentSessionId ?? creativeAgentSession?.sessionId ?? null;
      const targetPuzzleSessionId = puzzleSession?.sessionId?.trim();
      if (
        !creativeSessionId ||
        !targetPuzzleSessionId ||
        isCreativeDraftEditBusy
      ) {
        return null;
      }

      setIsCreativeDraftEditBusy(true);
      setCreativeDraftEditError(null);
      try {
        const result = await streamCreativeDraftEdit(creativeSessionId, {
          clientMessageId: `creative-edit-${Date.now().toString(36)}`,
          instruction,
          targetPuzzleSessionId,
          currentDraft,
        });

        if ('puzzleSession' in result) {
          puzzleFlow.setSession(result.puzzleSession);
          setCreativeAgentSession(result.session);
          setActiveCreativeAgentSessionId(result.session.sessionId);
          return result;
        }

        setCreativeAgentSession(result);
        setActiveCreativeAgentSessionId(result.sessionId);
        const response = await getPuzzleAgentSession(targetPuzzleSessionId);
        puzzleFlow.setSession(response.session);
        return null;
      } catch (error) {
        setCreativeDraftEditError(
          resolvePuzzleErrorMessage(error, '智能修订拼图草稿失败。'),
        );
        return null;
      } finally {
        setIsCreativeDraftEditBusy(false);
      }
    },
    [
      activeCreativeAgentSessionId,
      creativeAgentSession?.sessionId,
      isCreativeDraftEditBusy,
      puzzleFlow,
      puzzleSession?.sessionId,
      resolvePuzzleErrorMessage,
    ],
  );

  useEffect(() => {
    if (selectionStage === 'big-fish-result' && !bigFishSession?.draft) {
      setSelectionStage(
        bigFishSession ? 'big-fish-agent-workspace' : 'platform',
      );
    }
    if (selectionStage === 'big-fish-runtime' && !bigFishRun) {
      setSelectionStage(bigFishSession?.draft ? 'big-fish-result' : 'platform');
    }
  }, [bigFishRun, bigFishSession, selectionStage, setSelectionStage]);

  useEffect(() => {
    if (selectionStage === 'match3d-result' && !match3dSession?.draft) {
      setSelectionStage(
        match3dSession ? 'match3d-agent-workspace' : 'platform',
      );
    }
    if (selectionStage === 'match3d-runtime' && !match3dRun) {
      setSelectionStage(match3dSession?.draft ? 'match3d-result' : 'platform');
    }
  }, [match3dRun, match3dSession, selectionStage, setSelectionStage]);

  useEffect(() => {
    if (selectionStage === 'square-hole-result' && !squareHoleSession?.draft) {
      setSelectionStage(
        squareHoleSession ? 'square-hole-agent-workspace' : 'platform',
      );
    }
    if (selectionStage === 'square-hole-runtime' && !squareHoleRun) {
      setSelectionStage(
        squareHoleSession?.draft ? 'square-hole-result' : 'platform',
      );
    }
  }, [selectionStage, setSelectionStage, squareHoleRun, squareHoleSession]);

  useEffect(() => {
    if (
      selectionStage === 'visual-novel-result' &&
      !visualNovelSession?.draft &&
      !visualNovelWork?.draft
    ) {
      setSelectionStage(
        visualNovelSession ? 'visual-novel-agent-workspace' : 'platform',
      );
    }
    if (selectionStage === 'visual-novel-runtime' && !visualNovelRun) {
      setSelectionStage(
        visualNovelSession?.draft || visualNovelWork?.draft
          ? 'visual-novel-result'
          : 'platform',
      );
    }
    if (selectionStage === 'visual-novel-gallery-detail' && !visualNovelWork) {
      setSelectionStage('platform');
    }
  }, [
    selectionStage,
    setSelectionStage,
    visualNovelRun,
    visualNovelSession,
    visualNovelWork,
  ]);

  const startBigFishRun = useCallback(async () => {
    if (!bigFishSession) {
      return;
    }

    const sessionId = bigFishSession.sessionId;
    setBigFishError(null);
    setBigFishRuntimeShare(null);
    setBigFishRuntimeWork(null);
    setBigFishRuntimeStartedAt(null);
    setBigFishRun(null);
    try {
      const { run } = await startBigFishRuntimeRun(sessionId);
      setBigFishRuntimeStartedAt(Date.now());
      setBigFishRuntimeSessionSource('draft');
      setBigFishRuntimeReturnStage('big-fish-result');
      setBigFishRun(run);
      setSelectionStage('big-fish-runtime');
      void recordBigFishPlay(sessionId, { elapsedMs: 0 }).catch((error) => {
        setBigFishError(
          resolveBigFishErrorMessage(error, '记录大鱼吃小鱼游玩失败。'),
        );
      });
      void refreshBigFishShelf();
    } catch (error) {
      setBigFishError(
        resolveBigFishErrorMessage(error, '启动大鱼吃小鱼玩法失败。'),
      );
    }
  }, [
    bigFishSession,
    refreshBigFishShelf,
    resolveBigFishErrorMessage,
    setBigFishError,
    setSelectionStage,
  ]);

  const restartBigFishRun = useCallback(async () => {
    if (!bigFishSession && !bigFishRun) {
      return;
    }

    const sessionId = bigFishSession?.sessionId ?? bigFishRun?.sessionId;
    if (!sessionId) {
      return;
    }

    setBigFishError(null);
    if (bigFishSession) {
      setBigFishRuntimeShare(null);
      setBigFishRuntimeReturnStage('big-fish-result');
    }
    setBigFishRuntimeStartedAt(null);
    setBigFishRun(null);
    try {
      const { run } = await startBigFishRuntimeRun(sessionId);
      setBigFishRuntimeStartedAt(Date.now());
      setBigFishRuntimeSessionSource(bigFishSession ? 'draft' : 'work');
      setBigFishRuntimeReturnStage(
        bigFishSession ? 'big-fish-result' : bigFishRuntimeReturnStage,
      );
      setBigFishRun(run);
      setSelectionStage('big-fish-runtime');
      void recordBigFishPlay(sessionId, { elapsedMs: 0 }).catch((error) => {
        setBigFishError(
          resolveBigFishErrorMessage(error, '记录大鱼吃小鱼游玩失败。'),
        );
      });
    } catch (error) {
      setBigFishError(
        resolveBigFishErrorMessage(error, '启动大鱼吃小鱼玩法失败。'),
      );
    }
  }, [
    bigFishRun,
    bigFishRuntimeReturnStage,
    bigFishSession,
    resolveBigFishErrorMessage,
    setBigFishError,
    setSelectionStage,
  ]);

  const startPuzzleRunFromProfile = useCallback(
    async (
      profileId: string,
      returnStage: PuzzleRuntimeReturnStage = 'work-detail',
      detailItem?: PuzzleWorkSummary,
      mirrorErrorToPublicDetail = false,
      levelId?: string | null,
      options: { embedded?: boolean; authMode?: PuzzleRuntimeAuthMode } = {},
    ) => {
      if (isPuzzleBusy) {
        return false;
      }

      setIsPuzzleBusy(true);
      setPuzzleError(null);

      try {
        const item =
          detailItem ?? (await getPuzzleGalleryDetail(profileId)).item;
        const startRunPayload = {
          profileId: item.profileId,
          levelId: levelId ?? null,
        };
        const authMode = options.embedded
          ? 'isolated'
          : (options.authMode ?? 'default');
        const { run } =
          authMode === 'isolated'
            ? await startPuzzleRun(
                startRunPayload,
                PUBLIC_PUZZLE_RUNTIME_AUTH_OPTIONS,
              )
            : await startPuzzleRun(startRunPayload);
        setSelectedPuzzleDetail(item);
        setPuzzleRun(run);
        setPuzzleRuntimeAuthMode(authMode);
        setPuzzleRuntimeReturnStage(returnStage);
        if (!options.embedded) {
          setSelectionStage('puzzle-runtime');
          pushAppHistoryPath(
            buildPublicWorkStagePath(
              'puzzle-runtime',
              buildPuzzlePublicWorkCode(item.profileId),
            ),
          );
        }
        return true;
      } catch (error) {
        if (isMissingPuzzleWorkError(error)) {
          setSelectedPuzzleDetail(null);
          setPuzzleDetailReturnTarget(null);
          setPuzzleRun(null);
          setPuzzleRuntimeAuthMode('default');
          setPuzzleError(null);
          setPublicWorkDetailError(null);
          setPlatformTab('home');
          setSelectionStage('platform');
          pushAppHistoryPath('/');
          return false;
        }

        const message = resolvePuzzleErrorMessage(error, '启动拼图玩法失败。');
        setPuzzleError(message);
        if (mirrorErrorToPublicDetail) {
          setPublicWorkDetailError(message);
        }
        return false;
      } finally {
        setIsPuzzleBusy(false);
      }
    },
    [
      isPuzzleBusy,
      resolvePuzzleErrorMessage,
      setIsPuzzleBusy,
      setPuzzleError,
      setPlatformTab,
      setSelectionStage,
    ],
  );

  const startMatch3DRunFromProfile = useCallback(
    async (
      profile: Match3DWorkProfile | Match3DWorkSummary,
      returnStage: 'match3d-result' | 'work-detail' = 'match3d-result',
      mirrorErrorToPublicDetail = false,
      options: { embedded?: boolean } = {},
    ) => {
      if (isMatch3DBusy) {
        return false;
      }

      match3dFlow.setIsBusy(true);
      setMatch3DError(null);

      try {
        const { run } = options.embedded
          ? await startMatch3DRun(
              profile.profileId,
              RECOMMEND_RUNTIME_BACKGROUND_AUTH_OPTIONS,
            )
          : await startMatch3DRun(profile.profileId);
        setMatch3DRun(run);
        setMatch3DRuntimeReturnStage(returnStage);
        if (!options.embedded) {
          setSelectionStage('match3d-runtime');
        }
        if (!options.embedded && profile.publicationStatus === 'published') {
          pushAppHistoryPath(
            buildPublicWorkStagePath(
              'work-detail',
              buildMatch3DPublicWorkCode(profile.profileId),
            ),
          );
        }
        return true;
      } catch (error) {
        const message = resolveMatch3DErrorMessage(
          error,
          '启动抓大鹅玩法失败。',
        );
        setMatch3DError(message);
        if (mirrorErrorToPublicDetail) {
          setPublicWorkDetailError(message);
        }
        return false;
      } finally {
        match3dFlow.setIsBusy(false);
      }
    },
    [
      isMatch3DBusy,
      match3dFlow,
      resolveMatch3DErrorMessage,
      setMatch3DError,
      setSelectionStage,
    ],
  );

  const startSquareHoleRunFromProfile = useCallback(
    async (
      profile: SquareHoleWorkProfile | SquareHoleWorkSummary,
      returnStage: SquareHoleRuntimeReturnStage = 'square-hole-result',
      mirrorErrorToPublicDetail = false,
      options: { embedded?: boolean } = {},
    ) => {
      if (isSquareHoleBusy) {
        return false;
      }

      squareHoleFlow.setIsBusy(true);
      setSquareHoleError(null);

      try {
        const { run } = options.embedded
          ? await startSquareHoleRun(
              profile.profileId,
              RECOMMEND_RUNTIME_BACKGROUND_AUTH_OPTIONS,
            )
          : await startSquareHoleRun(profile.profileId);
        setSquareHoleRun(run);
        setSquareHoleRuntimeReturnStage(returnStage);
        if (!options.embedded) {
          setSelectionStage('square-hole-runtime');
        }
        if (!options.embedded && profile.publicationStatus === 'published') {
          pushAppHistoryPath(
            buildPublicWorkStagePath(
              'work-detail',
              buildSquareHolePublicWorkCode(profile.profileId),
            ),
          );
        }
        return true;
      } catch (error) {
        const message = resolveSquareHoleErrorMessage(
          error,
          '启动方洞挑战玩法失败。',
        );
        setSquareHoleError(message);
        if (mirrorErrorToPublicDetail) {
          setPublicWorkDetailError(message);
        }
        return false;
      } finally {
        squareHoleFlow.setIsBusy(false);
      }
    },
    [
      isSquareHoleBusy,
      resolveSquareHoleErrorMessage,
      setSelectionStage,
      setSquareHoleError,
      squareHoleFlow,
    ],
  );

  const _buildPuzzleTestWork = useCallback(
    (draft: PuzzleResultDraft) => {
      const profileId =
        puzzleSession?.publishedProfileId ??
        `draft-${puzzleSession?.sessionId ?? 'puzzle'}-test`;
      const now = new Date().toISOString();

      return {
        workId: `test-${profileId}`,
        profileId,
        ownerUserId: authUi?.user?.id ?? 'current-user',
        sourceSessionId: puzzleSession?.sessionId ?? null,
        authorDisplayName: authUi?.user?.displayName ?? '玩家',
        workTitle: draft.workTitle || draft.levelName,
        workDescription: draft.workDescription || draft.summary,
        levelName: draft.levelName,
        summary: draft.summary,
        themeTags: draft.themeTags,
        coverImageSrc: draft.coverImageSrc,
        coverAssetId: draft.coverAssetId,
        publicationStatus: 'draft',
        updatedAt: now,
        publishedAt: null,
        playCount: 0,
        remixCount: 0,
        likeCount: 0,
        pointIncentiveTotalHalfPoints: 0,
        pointIncentiveClaimedPoints: 0,
        pointIncentiveTotalPoints: 0,
        pointIncentiveClaimablePoints: 0,
        publishReady: Boolean(puzzleSession?.resultPreview?.publishReady),
        levels: draft.levels,
      } satisfies PuzzleWorkSummary;
    },
    [
      authUi?.user?.displayName,
      authUi?.user?.id,
      puzzleSession?.publishedProfileId,
      puzzleSession?.resultPreview?.publishReady,
      puzzleSession?.sessionId,
    ],
  );

  const startPuzzleTestRunFromDraft = useCallback(
    async (draft: PuzzleResultDraft) => {
      if (isPuzzleBusy) {
        return;
      }
      if (!draft.coverImageSrc) {
        setPuzzleError('请先选择一张正式拼图图片。');
        return;
      }
      const profileId =
        puzzleSession?.publishedProfileId ??
        buildPuzzleResultProfileId(puzzleSession?.sessionId);
      if (!profileId) {
        setPuzzleError('这份拼图草稿缺少会话信息，请重新开始创作。');
        return;
      }

      setIsPuzzleBusy(true);
      setPuzzleError(null);
      try {
        const { item } = await updatePuzzleWork(profileId, {
          workTitle: draft.workTitle,
          workDescription: draft.workDescription,
          levelName: draft.levelName,
          summary: draft.summary,
          themeTags: draft.themeTags,
          coverImageSrc: draft.coverImageSrc,
          coverAssetId: draft.coverAssetId,
          levels: draft.levels ?? [],
        });
        const run = startLocalPuzzleRun(item);
        setSelectedPuzzleDetail(item);
        setPuzzleRun(run);
        setPuzzleRuntimeAuthMode('default');
        setPuzzleRuntimeReturnStage('puzzle-result');
        setSelectionStage('puzzle-runtime');
      } catch (error) {
        setPuzzleError(resolvePuzzleErrorMessage(error, '启动拼图试玩失败。'));
      } finally {
        setIsPuzzleBusy(false);
      }
    },
    [
      isPuzzleBusy,
      puzzleSession?.publishedProfileId,
      puzzleSession?.sessionId,
      resolvePuzzleErrorMessage,
      setIsPuzzleBusy,
      setPuzzleError,
      setSelectionStage,
    ],
  );

  const submitBigFishInput = useCallback(
    async (payload: SubmitBigFishInputRequest) => {
      if (
        !bigFishRun ||
        bigFishRun.status !== 'running' ||
        bigFishInputInFlightRef.current
      ) {
        return;
      }

      bigFishInputInFlightRef.current = true;
      try {
        const { run } = await submitBigFishRuntimeInput(
          bigFishRun.runId,
          payload,
        );
        setBigFishRun(run);
      } catch (error) {
        setBigFishError(
          resolveBigFishErrorMessage(error, '同步大鱼吃小鱼输入失败。'),
        );
      } finally {
        bigFishInputInFlightRef.current = false;
      }
    },
    [bigFishRun, resolveBigFishErrorMessage, setBigFishError],
  );

  const reportBigFishObservedPlayTime = useCallback(() => {
    const sessionId = bigFishRun?.sessionId?.trim();
    if (!sessionId || !bigFishRuntimeStartedAt) {
      return;
    }

    const elapsedMs = Math.max(1_000, Date.now() - bigFishRuntimeStartedAt);
    setBigFishRuntimeStartedAt(null);
    const reportPromise =
      activeRecommendRuntimeKind === 'big-fish'
        ? recordBigFishPlay(
            sessionId,
            { elapsedMs },
            RECOMMEND_RUNTIME_BACKGROUND_AUTH_OPTIONS,
          )
        : recordBigFishPlay(sessionId, { elapsedMs });
    void reportPromise.catch((error) => {
      setBigFishError(
        resolveBigFishErrorMessage(error, '记录大鱼吃小鱼游玩时长失败。'),
      );
    });
  }, [
    activeRecommendRuntimeKind,
    bigFishRun?.sessionId,
    bigFishRuntimeStartedAt,
    resolveBigFishErrorMessage,
    setBigFishError,
  ]);

  const swapPuzzlePiecesInRun = useCallback(
    async (payload: { firstPieceId: string; secondPieceId: string }) => {
      if (!puzzleRun || isPuzzleBusy) {
        return;
      }

      setIsPuzzleBusy(true);
      setPuzzleError(null);
      try {
        setPuzzleRun(
          swapLocalPuzzlePieces(
            puzzleRun,
            payload,
            isLocalPuzzleRun(puzzleRun) ? selectedPuzzleDetail : null,
          ),
        );
      } catch (error) {
        setPuzzleError(resolvePuzzleErrorMessage(error, '交换拼图块失败。'));
      } finally {
        setIsPuzzleBusy(false);
      }
    },
    [
      isPuzzleBusy,
      puzzleRun,
      resolvePuzzleErrorMessage,
      selectedPuzzleDetail,
      setIsPuzzleBusy,
      setPuzzleError,
    ],
  );

  const dragPuzzlePiece = useCallback(
    async (payload: {
      pieceId: string;
      targetRow: number;
      targetCol: number;
    }) => {
      if (!puzzleRun || isPuzzleBusy) {
        return;
      }

      setIsPuzzleBusy(true);
      setPuzzleError(null);
      try {
        setPuzzleRun(
          dragLocalPuzzlePiece(
            puzzleRun,
            payload,
            isLocalPuzzleRun(puzzleRun) ? selectedPuzzleDetail : null,
          ),
        );
      } catch (error) {
        setPuzzleError(resolvePuzzleErrorMessage(error, '拖动拼图块失败。'));
      } finally {
        setIsPuzzleBusy(false);
      }
    },
    [
      isPuzzleBusy,
      puzzleRun,
      resolvePuzzleErrorMessage,
      selectedPuzzleDetail,
      setIsPuzzleBusy,
      setPuzzleError,
    ],
  );

  useEffect(() => {
    if (selectionStage !== 'puzzle-runtime' || !puzzleRun?.currentLevel) {
      return;
    }
    if (puzzleRun.currentLevel.status !== 'playing') {
      return;
    }

    const timerId = window.setInterval(() => {
      // 中文注释：拼图运行态的棋盘交互和倒计时展示都在前端即时裁决，超时后只刷新本地失败态。
      setPuzzleRun((currentRun) =>
        currentRun ? refreshLocalPuzzleTimer(currentRun) : currentRun,
      );
    }, 250);

    return () => window.clearInterval(timerId);
  }, [puzzleRun, selectionStage]);

  const setPuzzleRuntimePaused = useCallback(
    async (paused: boolean) => {
      if (!puzzleRun?.currentLevel) {
        return;
      }

      setPuzzleRun((currentRun) =>
        currentRun ? setLocalPuzzlePaused(currentRun, paused) : currentRun,
      );
    },
    [puzzleRun],
  );

  const syncPuzzleRuntimeTimeout = useCallback(async () => {
    if (
      !puzzleRun?.currentLevel ||
      puzzleRun.currentLevel.status === 'cleared'
    ) {
      return;
    }

    setPuzzleRun((currentRun) =>
      currentRun ? refreshLocalPuzzleTimer(currentRun) : currentRun,
    );
  }, [puzzleRun]);

  const usePuzzleProp = useCallback(
    async (propKind: PuzzleRuntimePropKind) => {
      if (!puzzleRun?.currentLevel) {
        return null;
      }
      const canUseProp =
        propKind === 'extendTime'
          ? puzzleRun.currentLevel.status !== 'cleared'
          : puzzleRun.currentLevel.status === 'playing';
      if (!canUseProp) {
        return null;
      }

      const currentRun = puzzleRunRef.current ?? puzzleRun;
      if (!currentRun.currentLevel) {
        return null;
      }
      const nextRun =
        propKind === 'extendTime'
          ? extendLocalPuzzleTime(currentRun)
          : propKind === 'freezeTime'
            ? applyLocalPuzzleFreezeTime(currentRun)
            : setLocalPuzzlePaused(currentRun, propKind === 'reference');
      puzzleRunRef.current = nextRun;
      setPuzzleRun(nextRun);
      return nextRun;
    },
    [puzzleRun],
  );

  const restartPuzzleCurrentLevel = useCallback(async () => {
    const currentRun = puzzleRunRef.current ?? puzzleRun;
    const currentLevel = currentRun?.currentLevel ?? null;
    if (!currentRun || !currentLevel || isPuzzleBusy) {
      return;
    }

    setPuzzleError(null);
    setIsPuzzleBusy(true);
    try {
      if (isLocalPuzzleRun(currentRun)) {
        const nextRun = restartLocalPuzzleLevel(currentRun);
        puzzleRunRef.current = nextRun;
        setPuzzleRun(nextRun);
        return;
      }

      const detailItem =
        selectedPuzzleDetail?.profileId === currentLevel.profileId
          ? selectedPuzzleDetail
          : await getPuzzleGalleryDetail(currentLevel.profileId).then(
              (response) => response.item,
            );
      const startRunPayload = {
        profileId: currentLevel.profileId,
        levelId: resolvePuzzleRestartLevelId(currentRun, detailItem),
      };
      const { run } =
        puzzleRuntimeAuthMode === 'isolated'
          ? await startPuzzleRun(
              startRunPayload,
              PUBLIC_PUZZLE_RUNTIME_AUTH_OPTIONS,
            )
          : await startPuzzleRun(startRunPayload);
      setSelectedPuzzleDetail(detailItem);
      puzzleRunRef.current = run;
      setPuzzleRun(run);
    } catch (error) {
      setPuzzleError(
        resolvePuzzleErrorMessage(error, '重新开始拼图关卡失败。'),
      );
    } finally {
      setIsPuzzleBusy(false);
    }
  }, [
    isPuzzleBusy,
    puzzleRun,
    puzzleRuntimeAuthMode,
    resolvePuzzleErrorMessage,
    selectedPuzzleDetail,
    setIsPuzzleBusy,
    setPuzzleError,
  ]);

  const resumePuzzleSaveArchive = useCallback(
    async (entry: ProfileSaveArchiveSummary) => {
      if (isPuzzleBusy) {
        return;
      }

      setIsPuzzleBusy(true);
      setPuzzleError(null);
      platformBootstrap.setSaveError(null);

      try {
        const resumedArchive = await resumePuzzleProfileSaveArchiveRaw(
          entry.worldKey,
        );
        platformBootstrap.setSaveEntries((currentEntries) =>
          currentEntries.map((currentEntry) =>
            currentEntry.worldKey === resumedArchive.entry.worldKey
              ? resumedArchive.entry
              : currentEntry,
          ),
        );
        const gameState = resumedArchive.snapshot.gameState;
        const profileId =
          typeof gameState.currentProfileId === 'string' &&
          gameState.currentProfileId.trim()
            ? gameState.currentProfileId
            : typeof gameState.entryProfileId === 'string' &&
                gameState.entryProfileId.trim()
              ? gameState.entryProfileId
              : (entry.profileId ?? entry.worldKey.replace(/^puzzle:/u, ''));
        const levelId =
          typeof gameState.currentLevelId === 'string' &&
          gameState.currentLevelId.trim()
            ? gameState.currentLevelId
            : null;
        const item =
          selectedPuzzleDetail?.profileId === profileId
            ? selectedPuzzleDetail
            : await getPuzzleGalleryDetail(profileId).then(
                (response) => response.item,
              );
        await startPuzzleRunFromProfile(
          item.profileId,
          'platform',
          item,
          false,
          levelId,
        );
      } catch (error) {
        platformBootstrap.setSaveError(
          resolvePuzzleErrorMessage(error, '恢复拼图存档失败。'),
        );
      } finally {
        setIsPuzzleBusy(false);
      }
    },
    [
      isPuzzleBusy,
      platformBootstrap,
      selectedPuzzleDetail,
      resolvePuzzleErrorMessage,
      setIsPuzzleBusy,
      setPuzzleError,
      startPuzzleRunFromProfile,
    ],
  );

  useEffect(() => {
    const currentLevel = puzzleRun?.currentLevel ?? null;
    if (!puzzleRun || !currentLevel || currentLevel.status !== 'cleared') {
      return;
    }
    if (currentLevel.elapsedMs === null) {
      return;
    }
    if ((currentLevel.leaderboardEntries ?? []).length > 0) {
      return;
    }

    const submitKey = `${puzzleRun.runId}:${currentLevel.profileId}:${currentLevel.gridSize}:${currentLevel.elapsedMs}`;
    if (submittedPuzzleLeaderboardKeysRef.current.has(submitKey)) {
      return;
    }
    submittedPuzzleLeaderboardKeysRef.current.add(submitKey);
    setIsPuzzleLeaderboardBusy(true);

    const payload: SubmitPuzzleLeaderboardRequest = {
      profileId: currentLevel.profileId,
      gridSize: currentLevel.gridSize,
      elapsedMs: currentLevel.elapsedMs,
      nickname: authUi?.user?.displayName?.trim() || '玩家',
    };

    if (isLocalPuzzleRun(puzzleRun)) {
      setPuzzleRun(submitLocalPuzzleLeaderboard(puzzleRun, payload.nickname));
      setIsPuzzleLeaderboardBusy(false);
      return;
    }

    const submitLeaderboardPromise =
      puzzleRuntimeAuthMode === 'isolated'
        ? submitPuzzleLeaderboard(
            puzzleRun.runId,
            payload,
            PUBLIC_PUZZLE_RUNTIME_AUTH_OPTIONS,
          )
        : submitPuzzleLeaderboard(puzzleRun.runId, payload);

    void submitLeaderboardPromise
      .then(({ run }) => {
        setPuzzleRun((currentRun) => {
          if (!currentRun) {
            return currentRun;
          }
          return mergePuzzleServiceRuntimeState(currentRun, run);
        });
        void platformBootstrap.refreshSaveArchives();
      })
      .catch((error) => {
        submittedPuzzleLeaderboardKeysRef.current.delete(submitKey);
        setPuzzleError(
          resolvePuzzleErrorMessage(error, '提交拼图排行榜失败。'),
        );
      })
      .finally(() => {
        setIsPuzzleLeaderboardBusy(false);
      });
  }, [
    authUi?.user?.displayName,
    platformBootstrap,
    puzzleRun,
    puzzleRuntimeAuthMode,
    resolvePuzzleErrorMessage,
    setPuzzleError,
  ]);

  const advancePuzzleLevel = useCallback(
    async (_target?: { profileId?: string; levelId?: string | null }) => {
      if (!puzzleRun || isPuzzleBusy || isPuzzleLeaderboardBusy) {
        return;
      }
      const currentLevel = puzzleRun.currentLevel;
      if (!currentLevel || currentLevel.status !== 'cleared') {
        return;
      }

      setIsPuzzleBusy(true);
      setIsPuzzleNextLevelGenerating(true);
      setPuzzleError(null);

      try {
        if (isLocalPuzzleRun(puzzleRun)) {
          const nextRun = advanceLocalPuzzleLevel(
            puzzleRun,
            selectedPuzzleDetail,
            _target,
          );
          setPuzzleRun(nextRun);
          return;
        }

        const targetProfileId = _target?.profileId?.trim() ?? '';
        if (puzzleRun.nextLevelMode === 'similarWorks' && targetProfileId) {
          const itemPromise =
            selectedPuzzleDetail?.profileId === targetProfileId
              ? Promise.resolve(selectedPuzzleDetail)
              : getPuzzleGalleryDetail(targetProfileId).then(
                  (response) => response.item,
                );
          const advancePromise =
            puzzleRuntimeAuthMode === 'isolated'
              ? advancePuzzleNextLevel(
                  puzzleRun.runId,
                  {
                    targetProfileId,
                  },
                  PUBLIC_PUZZLE_RUNTIME_AUTH_OPTIONS,
                )
              : advancePuzzleNextLevel(puzzleRun.runId, {
                  targetProfileId,
                });
          const [{ run }, item] = await Promise.all([
            advancePromise,
            itemPromise,
          ]);
          setSelectedPuzzleDetail(item);
          setPuzzleRun(run);
          pushAppHistoryPath(
            buildPublicWorkStagePath(
              'puzzle-runtime',
              buildPuzzlePublicWorkCode(item.profileId),
            ),
          );
          return;
        }

        const { run } =
          puzzleRuntimeAuthMode === 'isolated'
            ? await advancePuzzleNextLevel(
                puzzleRun.runId,
                {},
                PUBLIC_PUZZLE_RUNTIME_AUTH_OPTIONS,
              )
            : await advancePuzzleNextLevel(puzzleRun.runId);
        setPuzzleRun(run);
      } catch (error) {
        setPuzzleError(resolvePuzzleErrorMessage(error, '准备下一关失败。'));
      } finally {
        setIsPuzzleNextLevelGenerating(false);
        setIsPuzzleBusy(false);
      }
    },
    [
      isPuzzleBusy,
      isPuzzleLeaderboardBusy,
      puzzleRun,
      puzzleRuntimeAuthMode,
      resolvePuzzleErrorMessage,
      selectedPuzzleDetail,
      setIsPuzzleBusy,
      setPuzzleError,
    ],
  );
  const remodelCurrentPuzzleRuntimeWork = useCallback(
    (profileId: string) => {
      const targetProfileId = profileId.trim();
      if (!targetProfileId || isPublicWorkDetailBusy || isPuzzleBusy) {
        return;
      }

      runProtectedAction(() => {
        setIsPublicWorkDetailBusy(true);
        setIsPuzzleBusy(true);
        setPuzzleError(null);
        setPublicWorkDetailError(null);

        void remixPuzzleGalleryWork(targetProfileId)
          .then((response) => {
            puzzleFlow.setSession(response.session);
            setPuzzleOperation(null);
            setPuzzleRun(null);
            setPuzzleRuntimeAuthMode('default');
            enterCreateTab();
            setSelectionStage('puzzle-result');
          })
          .catch((error) => {
            setPuzzleError(
              resolvePuzzleErrorMessage(error, '改造拼图作品失败。'),
            );
          })
          .finally(() => {
            setIsPublicWorkDetailBusy(false);
            setIsPuzzleBusy(false);
          });
      });
    },
    [
      enterCreateTab,
      isPublicWorkDetailBusy,
      isPuzzleBusy,
      puzzleFlow,
      resolvePuzzleErrorMessage,
      runProtectedAction,
      setIsPuzzleBusy,
      setPuzzleError,
      setSelectionStage,
    ],
  );

  const saveAndExitRecommendPuzzleRuntime = useCallback(async () => {
    if (activeRecommendRuntimeKind !== 'puzzle') {
      return;
    }

    const currentRun = puzzleRunRef.current;
    if (!currentRun) {
      setActiveRecommendRuntimeKind(null);
      return;
    }

    // 中文注释：推荐页嵌入拼图的“保存并退出”沿用现有运行态语义；
    // 正式 run 的每次交换/拖动/通关已写回后端，退出时只收口暂停和本地快照。
    const closedRun = currentRun.currentLevel
      ? setLocalPuzzlePaused(currentRun, false)
      : currentRun;
    puzzleRunRef.current = null;
    setPuzzleRun(null);
    setPuzzleRuntimeAuthMode('default');
    setActiveRecommendRuntimeKind(null);

    if (closedRun.currentLevel) {
      setPuzzleError(null);
    }
  }, [activeRecommendRuntimeKind, setPuzzleError]);

  const leaveAgentWorkspace = useCallback(() => {
    enterCreateTab();
    sessionController.resetSessionViewState();
    sessionController.setGeneratedCustomWorldProfile(null);
    autosaveCoordinator.resetAutoSaveTrackingToIdle();
    sessionController.persistAgentUiState(
      sessionController.activeAgentSessionId,
      null,
    );
    setSelectionStage('platform');
  }, [
    autosaveCoordinator,
    enterCreateTab,
    sessionController,
    setSelectionStage,
  ]);

  const leaveAgentDraftGeneration = useCallback(() => {
    if (sessionController.isActiveGenerationRunning) {
      return;
    }

    sessionController.setAgentDraftGenerationStartedAt(null);
    sessionController.setCustomWorldGenerationViewSource(null);
    setSelectionStage('agent-workspace');
  }, [sessionController, setSelectionStage]);

  const leaveAgentDraftResult = useCallback(() => {
    sessionController.suppressAgentDraftResultAutoOpen();
    sessionController.setGeneratedCustomWorldProfile(null);
    sessionController.setCustomWorldError(null);
    autosaveCoordinator.resetAutoSaveTrackingToIdle();
    sessionController.setCustomWorldGenerationViewSource(null);
    sessionController.setCustomWorldResultViewSource(null);
    enterCreateTab();
    setSelectionStage('platform');
  }, [
    autosaveCoordinator,
    enterCreateTab,
    sessionController,
    setSelectionStage,
  ]);

  const leaveCustomWorldResult = useCallback(() => {
    sessionController.setGeneratedCustomWorldProfile(null);
    sessionController.setCustomWorldError(null);
    autosaveCoordinator.resetAutoSaveTrackingToIdle();
    sessionController.setCustomWorldGenerationViewSource(null);
    sessionController.setCustomWorldResultViewSource(null);
    setSelectionStage(selectedDetailEntry ? 'detail' : 'platform');
  }, [
    autosaveCoordinator,
    selectedDetailEntry,
    sessionController,
    setSelectionStage,
  ]);

  const handleStartSelectedWorld = useCallback(() => {
    if (!selectedDetailEntry) {
      return;
    }

    runProtectedAction(() => {
      handleCustomWorldSelect(selectedDetailEntry.profile);
    });
  }, [handleCustomWorldSelect, runProtectedAction, selectedDetailEntry]);

  const handleDeleteLibraryEntry = useCallback(
    (entry: CustomWorldLibraryEntry<CustomWorldProfile>) => {
      if (!entry.profileId || deletingCreationWorkId) {
        return;
      }

      requestDeleteCreationWork({
        id: entry.profileId,
        title: entry.worldName,
        detail: '删除后会从你的作品列表和公开广场中移除。',
        run: () => {
          setDeletingCreationWorkId(entry.profileId);
          platformBootstrap.setPlatformError(null);

          void deleteRpgEntryWorldProfile(entry.profileId)
            .then(async (entries) => {
              platformBootstrap.setSavedCustomWorldEntries(entries);
              await platformBootstrap.refreshCustomWorldWorks().catch(() => []);
              await platformBootstrap.refreshPublishedGallery().catch(() => []);
            })
            .catch((error) => {
              platformBootstrap.setPlatformError(
                resolveRpgCreationErrorMessage(error, '删除自定义世界失败。'),
              );
            })
            .finally(() => {
              setDeletingCreationWorkId(null);
            });
        },
      });
    },
    [deletingCreationWorkId, platformBootstrap, requestDeleteCreationWork],
  );

  const handleDeletePublishedWork = useCallback(
    (work: (typeof creationHubItems)[number]) => {
      if (deletingCreationWorkId) {
        return;
      }

      requestDeleteCreationWork({
        id: work.workId,
        title: work.title,
        detail:
          work.status === 'published'
            ? '删除后会从你的作品列表和公开广场中移除。'
            : '删除后会从你的作品列表中移除。',
        run: () => {
          setDeletingCreationWorkId(work.workId);
          platformBootstrap.setPlatformError(null);

          const deleteTask =
            work.sourceType === 'published_profile' && work.profileId
              ? deleteRpgEntryWorldProfile(work.profileId).then(
                  async (entries) => {
                    platformBootstrap.setSavedCustomWorldEntries(entries);
                    await platformBootstrap
                      .refreshCustomWorldWorks()
                      .catch(() => []);
                  },
                )
              : work.sourceType === 'agent_session' && work.sessionId
                ? deleteRpgCreationAgentSession(work.sessionId).then(
                    (items) => {
                      platformBootstrap.setCustomWorldWorkEntries(items);
                    },
                  )
                : Promise.reject(new Error('当前 RPG 作品缺少可删除 ID。'));

          void deleteTask
            .then(async () => {
              await platformBootstrap.refreshPublishedGallery().catch(() => []);
            })
            .catch((error) => {
              platformBootstrap.setPlatformError(
                resolveRpgCreationErrorMessage(error, '删除自定义世界失败。'),
              );
            })
            .finally(() => {
              setDeletingCreationWorkId(null);
            });
        },
      });
    },
    [deletingCreationWorkId, platformBootstrap, requestDeleteCreationWork],
  );

  const handleDeleteBigFishWork = useCallback(
    (work: BigFishWorkSummary) => {
      if (deletingCreationWorkId) {
        return;
      }

      requestDeleteCreationWork({
        id: work.workId,
        title: work.title,
        detail:
          work.status === 'published'
            ? '删除后会从你的作品列表和公开广场中移除。'
            : '删除后会从你的作品列表中移除。',
        run: () => {
          setDeletingCreationWorkId(work.workId);
          setBigFishError(null);

          void deleteBigFishWork(work.sourceSessionId)
            .then(async (response) => {
              setBigFishWorks(response.items);
              await refreshBigFishGallery().catch(() => []);
            })
            .catch((error) => {
              setBigFishError(
                resolveBigFishErrorMessage(error, '删除大鱼吃小鱼作品失败。'),
              );
            })
            .finally(() => {
              setDeletingCreationWorkId(null);
            });
        },
      });
    },
    [
      deletingCreationWorkId,
      refreshBigFishGallery,
      requestDeleteCreationWork,
      resolveBigFishErrorMessage,
      setBigFishError,
    ],
  );

  const handleDeletePuzzleWork = useCallback(
    (work: PuzzleWorkSummary) => {
      if (deletingCreationWorkId) {
        return;
      }

      const displayName =
        work.workTitle?.trim() || work.levelName.trim() || '未命名拼图';
      requestDeleteCreationWork({
        id: work.workId,
        title: displayName,
        detail:
          work.publicationStatus === 'published'
            ? '删除后会从你的作品列表和公开广场中移除。'
            : '删除后会从你的作品列表中移除。',
        run: () => {
          setDeletingCreationWorkId(work.workId);
          setPuzzleFormDraftPayload(null);
          setPuzzleError(null);

          void deletePuzzleWork(work.profileId)
            .then((response) => {
              setPuzzleWorks(response.items);
              void refreshPuzzleGallery();
            })
            .catch((error) => {
              setPuzzleError(
                resolvePuzzleErrorMessage(error, '删除拼图作品失败。'),
              );
            })
            .finally(() => {
              setDeletingCreationWorkId(null);
            });
        },
      });
    },
    [
      deletingCreationWorkId,
      refreshPuzzleGallery,
      requestDeleteCreationWork,
      resolvePuzzleErrorMessage,
      setPuzzleError,
    ],
  );

  const handleDeleteMatch3DWork = useCallback(
    (work: Match3DWorkSummary) => {
      if (deletingCreationWorkId) {
        return;
      }

      requestDeleteCreationWork({
        id: work.workId,
        title: work.gameName,
        detail:
          work.publicationStatus === 'published'
            ? '删除后会从你的作品列表和公开广场中移除。'
            : '删除后会从你的作品列表中移除。',
        run: () => {
          setDeletingCreationWorkId(work.workId);
          setMatch3DError(null);

          void deleteMatch3DWork(work.profileId)
            .then((response) => {
              setMatch3DWorks(response.items);
              void refreshMatch3DGallery();
            })
            .catch((error) => {
              setMatch3DError(
                resolveMatch3DErrorMessage(error, '删除抓大鹅作品失败。'),
              );
            })
            .finally(() => {
              setDeletingCreationWorkId(null);
            });
        },
      });
    },
    [
      deletingCreationWorkId,
      refreshMatch3DGallery,
      requestDeleteCreationWork,
      resolveMatch3DErrorMessage,
      setMatch3DError,
    ],
  );

  const handleDeleteSquareHoleWork = useCallback(
    (work: SquareHoleWorkSummary) => {
      if (deletingCreationWorkId) {
        return;
      }

      requestDeleteCreationWork({
        id: work.workId,
        title: work.gameName,
        detail:
          work.publicationStatus === 'published'
            ? '删除后会从你的作品列表和公开广场中移除。'
            : '删除后会从你的作品列表中移除。',
        run: () => {
          setDeletingCreationWorkId(work.workId);
          setSquareHoleError(null);

          void deleteSquareHoleWork(work.profileId)
            .then((response) => {
              setSquareHoleWorks(response.items);
              void refreshSquareHoleGallery();
            })
            .catch((error) => {
              setSquareHoleError(
                resolveSquareHoleErrorMessage(error, '删除方洞挑战作品失败。'),
              );
            })
            .finally(() => {
              setDeletingCreationWorkId(null);
            });
        },
      });
    },
    [
      deletingCreationWorkId,
      refreshSquareHoleGallery,
      requestDeleteCreationWork,
      resolveSquareHoleErrorMessage,
      setSquareHoleError,
    ],
  );

  const handleDeleteVisualNovelWork = useCallback(
    (work: VisualNovelWorkSummary) => {
      if (deletingCreationWorkId) {
        return;
      }

      requestDeleteCreationWork({
        id: work.profileId,
        title: work.title || '未命名视觉小说',
        detail:
          work.publishStatus === 'published'
            ? '删除后会从你的作品列表和公开广场中移除。'
            : '删除后会从你的作品列表中移除。',
        run: () => {
          setDeletingCreationWorkId(work.profileId);
          setVisualNovelError(null);

          void deleteVisualNovelWork(work.profileId)
            .then(async (response) => {
              setVisualNovelWorks(response.works);
              await refreshVisualNovelGallery();
            })
            .catch((error) => {
              setVisualNovelError(
                resolvePuzzleErrorMessage(error, '删除视觉小说作品失败。'),
              );
            })
            .finally(() => {
              setDeletingCreationWorkId(null);
            });
        },
      });
    },
    [
      deletingCreationWorkId,
      refreshVisualNovelGallery,
      requestDeleteCreationWork,
      resolvePuzzleErrorMessage,
      setVisualNovelError,
    ],
  );

  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);
      setPublicWorkDetailError(null);
      setSelectionStage('work-detail');
      if (entry.publicWorkCode?.trim()) {
        pushAppHistoryPath(
          buildPublicWorkStagePath('work-detail', entry.publicWorkCode),
        );
      }
    },
    [setSelectionStage],
  );

  const syncUpdatedPublicWorkDetail = useCallback(
    (updatedEntry: PlatformPublicGalleryCard) => {
      setSelectedPublicWorkDetail((current) =>
        current && isSamePlatformPublicGalleryEntry(current, updatedEntry)
          ? updatedEntry
          : current,
      );
    },
    [],
  );

  const handleClaimPuzzlePointIncentive = useCallback(
    (work: PuzzleWorkSummary) => {
      if (claimingPuzzlePointIncentiveProfileId) {
        return;
      }

      runProtectedAction(() => {
        setClaimingPuzzlePointIncentiveProfileId(work.profileId);
        setPuzzleError(null);

        void claimPuzzleWorkPointIncentive(work.profileId)
          .then((response) => {
            const updatedWork = response.item;
            setPuzzleWorks((current) =>
              current.map((item) => mergePuzzleWorkSummary(item, updatedWork)),
            );
            setPuzzleGalleryEntries((current) =>
              current.map((item) => mergePuzzleWorkSummary(item, updatedWork)),
            );
            setSelectedPuzzleDetail((current) =>
              current ? mergePuzzleWorkSummary(current, updatedWork) : current,
            );
            syncUpdatedPublicWorkDetail(
              mapPuzzleWorkToPublicWorkDetail(updatedWork),
            );
            void platformBootstrap.refreshProfileDashboard();
          })
          .catch((error) => {
            setPuzzleError(
              resolvePuzzleErrorMessage(error, '领取拼图积分激励失败。'),
            );
          })
          .finally(() => {
            setClaimingPuzzlePointIncentiveProfileId(null);
          });
      });
    },
    [
      claimingPuzzlePointIncentiveProfileId,
      platformBootstrap,
      resolvePuzzleErrorMessage,
      runProtectedAction,
      setPuzzleError,
      syncUpdatedPublicWorkDetail,
    ],
  );

  const likePublicWork = useCallback(
    (entry: PlatformPublicGalleryCard) => {
      if (isPublicWorkDetailBusy) {
        return;
      }

      runProtectedAction(() => {
        setIsPublicWorkDetailBusy(true);
        setPublicWorkDetailError(null);

        if (isBigFishGalleryEntry(entry)) {
          void likeBigFishGalleryWork(entry.profileId)
            .then((response) => {
              const updatedWork = response.items.find(
                (item) => item.sourceSessionId === entry.profileId,
              );
              if (!updatedWork) {
                return;
              }
              setBigFishGalleryEntries((current) =>
                current.map((item) =>
                  mergeBigFishWorkSummary(item, updatedWork),
                ),
              );
              setBigFishWorks((current) =>
                current.map((item) =>
                  mergeBigFishWorkSummary(item, updatedWork),
                ),
              );
              syncUpdatedPublicWorkDetail(
                mapBigFishWorkToPublicWorkDetail(updatedWork),
              );
              setBigFishRuntimeWork((current) =>
                current
                  ? mergeBigFishWorkSummary(current, updatedWork)
                  : current,
              );
            })
            .catch((error) => {
              setPublicWorkDetailError(
                resolveBigFishErrorMessage(error, '点赞大鱼吃小鱼作品失败。'),
              );
            })
            .finally(() => {
              setIsPublicWorkDetailBusy(false);
            });
          return;
        }

        if (isPuzzleGalleryEntry(entry)) {
          void likePuzzleGalleryWork(entry.profileId)
            .then((response) => {
              const updatedWork = response.item;
              setPuzzleGalleryEntries((current) =>
                current.map((item) =>
                  mergePuzzleWorkSummary(item, updatedWork),
                ),
              );
              setPuzzleWorks((current) =>
                current.map((item) =>
                  mergePuzzleWorkSummary(item, updatedWork),
                ),
              );
              setSelectedPuzzleDetail((current) =>
                current
                  ? mergePuzzleWorkSummary(current, updatedWork)
                  : current,
              );
              syncUpdatedPublicWorkDetail(
                mapPuzzleWorkToPublicWorkDetail(updatedWork),
              );
            })
            .catch((error) => {
              setPublicWorkDetailError(
                resolvePuzzleErrorMessage(error, '点赞拼图作品失败。'),
              );
            })
            .finally(() => {
              setIsPublicWorkDetailBusy(false);
            });
          return;
        }

        if (isSquareHoleGalleryEntry(entry)) {
          setPublicWorkDetailError('方洞挑战点赞将在后续版本开放。');
          setIsPublicWorkDetailBusy(false);
          return;
        }

        if (isVisualNovelGalleryEntry(entry)) {
          setPublicWorkDetailError('视觉小说点赞将在后续版本开放。');
          setIsPublicWorkDetailBusy(false);
          return;
        }

        void likeRpgEntryWorldGallery(entry.ownerUserId, entry.profileId)
          .then((updatedEntry) => {
            setSelectedDetailEntry((current) =>
              current?.profileId === updatedEntry.profileId
                ? updatedEntry
                : current,
            );
            platformBootstrap.setPublishedGalleryEntries((current) =>
              current.map((item) =>
                item.profileId === updatedEntry.profileId ? updatedEntry : item,
              ),
            );
            syncUpdatedPublicWorkDetail(
              mapRpgGalleryCardToPublicWorkDetail(updatedEntry),
            );
          })
          .catch((error) => {
            setPublicWorkDetailError(
              resolveRpgCreationErrorMessage(error, '点赞 RPG 作品失败。'),
            );
          })
          .finally(() => {
            setIsPublicWorkDetailBusy(false);
          });
      });
    },
    [
      isPublicWorkDetailBusy,
      platformBootstrap,
      resolveBigFishErrorMessage,
      resolvePuzzleErrorMessage,
      runProtectedAction,
      syncUpdatedPublicWorkDetail,
    ],
  );

  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);
        const detailCard = mapRpgGalleryCardToPublicWorkDetail(detailEntry);
        setSelectedPublicWorkDetail(detailCard);
        if (detailEntry.publicWorkCode?.trim()) {
          pushAppHistoryPath(
            buildPublicWorkStagePath('work-detail', detailEntry.publicWorkCode),
          );
        }
      } catch (error) {
        setSelectedPublicWorkDetail(entry);
        setPublicWorkDetailError(
          resolveRpgCreationErrorMessage(error, '读取作品详情失败。'),
        );
      } finally {
        setIsPublicWorkDetailBusy(false);
      }
    },
    [
      clearSelectedPublicWorkAuthor,
      detailNavigation,
      setSelectedDetailEntry,
      setSelectionStage,
    ],
  );

  const openPuzzlePublicWorkDetail = useCallback(
    async (
      profileId: string,
      returnTarget: PuzzleDetailReturnTarget = {
        tab: platformBootstrap.platformTab,
      },
    ) => {
      setIsPuzzleBusy(true);
      setIsPublicWorkDetailBusy(true);
      setPuzzleError(null);
      setPublicWorkDetailError(null);
      setSelectionStage('work-detail');

      try {
        const { item } = await getPuzzleGalleryDetail(profileId);
        setSelectedPuzzleDetail(item);
        setPuzzleDetailReturnTarget(returnTarget);
        openPublicWorkDetail(mapPuzzleWorkToPublicWorkDetail(item));
      } catch (error) {
        if (isMissingPuzzleWorkError(error)) {
          setSelectedPuzzleDetail(null);
          setPuzzleDetailReturnTarget(null);
          setPuzzleRun(null);
          setPuzzleRuntimeAuthMode('default');
          setPuzzleError(null);
          setPublicWorkDetailError(null);
          setPlatformTab('home');
          setSelectionStage('platform');
          pushAppHistoryPath('/');
          return;
        }

        setPublicWorkDetailError(
          resolvePuzzleErrorMessage(error, '读取拼图详情失败。'),
        );
      } finally {
        setIsPuzzleBusy(false);
        setIsPublicWorkDetailBusy(false);
      }
    },
    [
      openPublicWorkDetail,
      platformBootstrap.platformTab,
      resolvePuzzleErrorMessage,
      setIsPuzzleBusy,
      setPuzzleError,
      setPlatformTab,
      setSelectionStage,
    ],
  );

  const openMatch3DPublicWorkDetail = useCallback(
    async (profileId: string) => {
      setIsPublicWorkDetailBusy(true);
      setMatch3DError(null);
      setPublicWorkDetailError(null);
      setSelectionStage('work-detail');

      try {
        const entries =
          match3dGalleryEntries.length > 0
            ? match3dGalleryEntries
            : await refreshMatch3DGallery();
        const matchedEntry = entries.find(
          (entry) => entry.profileId === profileId,
        );

        if (!matchedEntry) {
          throw new Error('未找到抓大鹅作品。');
        }

        openPublicWorkDetail(mapMatch3DWorkToPublicWorkDetail(matchedEntry));
      } catch (error) {
        setPublicWorkDetailError(
          resolveMatch3DErrorMessage(error, '读取抓大鹅详情失败。'),
        );
      } finally {
        setIsPublicWorkDetailBusy(false);
      }
    },
    [
      match3dGalleryEntries,
      openPublicWorkDetail,
      refreshMatch3DGallery,
      resolveMatch3DErrorMessage,
      setMatch3DError,
      setSelectionStage,
    ],
  );

  const openSquareHolePublicWorkDetail = useCallback(
    async (profileId: string) => {
      setIsPublicWorkDetailBusy(true);
      setSquareHoleError(null);
      setPublicWorkDetailError(null);
      setSelectionStage('work-detail');

      try {
        const entries =
          squareHoleGalleryEntries.length > 0
            ? squareHoleGalleryEntries
            : await refreshSquareHoleGallery();
        const matchedEntry = entries.find(
          (entry) => entry.profileId === profileId,
        );

        if (!matchedEntry) {
          throw new Error('未找到方洞挑战作品。');
        }

        openPublicWorkDetail(mapSquareHoleWorkToPublicWorkDetail(matchedEntry));
      } catch (error) {
        setPublicWorkDetailError(
          resolveSquareHoleErrorMessage(error, '读取方洞挑战详情失败。'),
        );
      } finally {
        setIsPublicWorkDetailBusy(false);
      }
    },
    [
      openPublicWorkDetail,
      refreshSquareHoleGallery,
      resolveSquareHoleErrorMessage,
      setSelectionStage,
      setSquareHoleError,
      squareHoleGalleryEntries,
    ],
  );

  const openVisualNovelPublicWorkDetail = useCallback(
    async (profileId: string) => {
      setIsPublicWorkDetailBusy(true);
      setVisualNovelError(null);
      setPublicWorkDetailError(null);
      setSelectionStage('work-detail');

      try {
        const entries =
          visualNovelGalleryEntries.length > 0
            ? visualNovelGalleryEntries
            : await refreshVisualNovelGallery();
        const matchedEntry = entries.find(
          (entry) => entry.profileId === profileId,
        );

        if (!matchedEntry) {
          throw new Error('未找到视觉小说作品。');
        }

        openPublicWorkDetail(mapVisualNovelWorkToPublicWorkDetail(matchedEntry));
      } catch (error) {
        setPublicWorkDetailError(
          resolvePuzzleErrorMessage(error, '读取视觉小说详情失败。'),
        );
      } finally {
        setIsPublicWorkDetailBusy(false);
      }
    },
    [
      openPublicWorkDetail,
      refreshVisualNovelGallery,
      resolvePuzzleErrorMessage,
      setSelectionStage,
      setVisualNovelError,
      visualNovelGalleryEntries,
    ],
  );

  const openPublicGalleryDetail = useCallback(
    (entry: PlatformPublicGalleryCard) => {
      if (isBigFishGalleryEntry(entry)) {
        openPublicWorkDetail(entry);
        return;
      }

      if (isPuzzleGalleryEntry(entry)) {
        void openPuzzlePublicWorkDetail(entry.profileId, {
          tab: platformBootstrap.platformTab,
        });
        return;
      }

      if (isMatch3DGalleryEntry(entry)) {
        openPublicWorkDetail(entry);
        return;
      }

      if (isSquareHoleGalleryEntry(entry)) {
        openPublicWorkDetail(entry);
        return;
      }

      if (isVisualNovelGalleryEntry(entry)) {
        void openVisualNovelPublicWorkDetail(entry.profileId);
        return;
      }

      void openRpgPublicWorkDetail(entry);
    },
    [
      openPuzzlePublicWorkDetail,
      openPublicWorkDetail,
      openRpgPublicWorkDetail,
      openVisualNovelPublicWorkDetail,
      platformBootstrap.platformTab,
    ],
  );

  const openRecommendGalleryDetail = useCallback(
    (entry: PlatformPublicGalleryCard) => {
      runProtectedAction(() => {
        openPublicGalleryDetail(entry);
      });
    },
    [openPublicGalleryDetail, runProtectedAction],
  );
  const openPuzzleDetail = useCallback(
    async (
      profileId: string,
      returnTarget: PuzzleDetailReturnTarget = {
        tab: platformBootstrap.platformTab,
      },
    ) => {
      setIsPuzzleBusy(true);
      setPuzzleError(null);

      try {
        const { item } = await getPuzzleGalleryDetail(profileId);
        setSelectedPuzzleDetail(item);
        setPuzzleDetailReturnTarget(returnTarget);
        setSelectionStage('puzzle-gallery-detail');
        pushAppHistoryPath(
          buildPublicWorkStagePath(
            'puzzle-gallery-detail',
            buildPuzzlePublicWorkCode(item.profileId),
          ),
        );
      } catch (error) {
        if (isMissingPuzzleWorkError(error)) {
          setSelectedPuzzleDetail(null);
          setPuzzleDetailReturnTarget(null);
          setPuzzleRun(null);
          setPuzzleRuntimeAuthMode('default');
          setPuzzleError(null);
          setPublicWorkDetailError(null);
          setPlatformTab('home');
          setSelectionStage('platform');
          pushAppHistoryPath('/');
          return;
        }

        setPuzzleError(resolvePuzzleErrorMessage(error, '读取拼图详情失败。'));
      } finally {
        setIsPuzzleBusy(false);
      }
    },
    [
      platformBootstrap.platformTab,
      resolvePuzzleErrorMessage,
      setIsPuzzleBusy,
      setPuzzleError,
      setPlatformTab,
      setSelectionStage,
    ],
  );

  const openPuzzleDraft = useCallback(
    async (item: PuzzleWorkSummary) => {
      setPuzzleOperation(null);
      setPuzzleRun(null);
      setPuzzleRuntimeAuthMode('default');
      setSelectedPuzzleDetail(null);
      if (!item.sourceSessionId?.trim()) {
        if (item.publicationStatus === 'published') {
          await openPuzzleDetail(item.profileId, { tab: 'create' });
          return;
        }

        setPuzzleError('这份拼图草稿缺少会话信息，请重新开始创作。');
        return;
      }

      const restoredSession = await puzzleFlow.restoreDraft(
        item.sourceSessionId,
      );
      if (!restoredSession) {
        await refreshPuzzleShelf().catch(() => undefined);
        return;
      }

      if (isPuzzleFormOnlyDraft(restoredSession)) {
        setPuzzleFormDraftPayload(
          buildPuzzleFormPayloadFromSession(restoredSession),
        );
        setSelectionStage('puzzle-agent-workspace');
      } else {
        setPuzzleFormDraftPayload(null);
      }
    },
    [
      openPuzzleDetail,
      puzzleFlow,
      refreshPuzzleShelf,
      setPuzzleError,
      setSelectionStage,
    ],
  );

  const openMatch3DDraft = useCallback(
    async (
      item: Match3DWorkSummary,
      options: { forceDraft?: boolean } = {},
    ) => {
      setMatch3DRun(null);
      setMatch3DError(null);
      setMatch3DProfile(null);

      if (item.publicationStatus === 'published' && !options.forceDraft) {
        openPublicWorkDetail(mapMatch3DWorkToPublicWorkDetail(item));
        return;
      }

      if (!item.sourceSessionId?.trim()) {
        setMatch3DError('这份抓大鹅草稿缺少会话信息，请重新开始创作。');
        return;
      }

      const restoredSession = await match3dFlow.restoreDraft(
        item.sourceSessionId,
      );
      if (!restoredSession) {
        await refreshMatch3DShelf().catch(() => undefined);
        return;
      }

      try {
        const { item: profile } = await getMatch3DWorkDetail(item.profileId);
        setMatch3DProfile(profile);
      } catch (error) {
        setMatch3DProfile(buildMatch3DProfileFromSession(restoredSession));
        setMatch3DError(
          resolveMatch3DErrorMessage(error, '读取抓大鹅作品详情失败。'),
        );
      }
    },
    [
      match3dFlow,
      openPublicWorkDetail,
      refreshMatch3DShelf,
      resolveMatch3DErrorMessage,
      setMatch3DError,
    ],
  );

  const openSquareHoleDraft = useCallback(
    async (
      item: SquareHoleWorkSummary,
      options: { forceDraft?: boolean } = {},
    ) => {
      setSquareHoleRun(null);
      setSquareHoleError(null);
      setSquareHoleProfile(null);

      if (item.publicationStatus === 'published' && !options.forceDraft) {
        openPublicWorkDetail(mapSquareHoleWorkToPublicWorkDetail(item));
        return;
      }

      if (!item.sourceSessionId?.trim()) {
        setSquareHoleError('这份方洞挑战草稿缺少会话信息，请重新开始创作。');
        return;
      }

      setSquareHoleGenerationState(null);
      const restoredSession = await squareHoleFlow.restoreDraft(
        item.sourceSessionId,
      );
      if (!restoredSession) {
        await refreshSquareHoleShelf().catch(() => undefined);
        return;
      }

      try {
        const { item: profile } = await getSquareHoleWorkDetail(item.profileId);
        setSquareHoleProfile(profile);
      } catch (error) {
        setSquareHoleProfile(
          buildSquareHoleProfileFromSession(restoredSession),
        );
        setSquareHoleError(
          resolveSquareHoleErrorMessage(error, '读取方洞挑战作品详情失败。'),
        );
      }
    },
    [
      openPublicWorkDetail,
      refreshSquareHoleShelf,
      resolveSquareHoleErrorMessage,
      setSquareHoleError,
      squareHoleFlow,
    ],
  );

  const openBigFishDraft = useCallback(
    async (item: BigFishWorkSummary) => {
      setBigFishRun(null);
      const restoredSession = await bigFishFlow.restoreDraft(
        item.sourceSessionId,
      );
      if (!restoredSession) {
        await refreshBigFishShelf().catch(() => undefined);
      }
    },
    [bigFishFlow, refreshBigFishShelf],
  );

  const openVisualNovelDraft = useCallback(
    async (
      item: VisualNovelWorkSummary,
      options: { forceDraft?: boolean } = {},
    ) => {
      if (item.publishStatus === 'published' && !options.forceDraft) {
        openPublicWorkDetail(mapVisualNovelWorkToPublicWorkDetail(item));
        return;
      }

      setVisualNovelWork(null);
      setVisualNovelRun(null);
      setVisualNovelRuntimeReturnStage('visual-novel-result');
      setVisualNovelError(null);
      setIsVisualNovelBusy(true);

      try {
        const { work } = await getVisualNovelWorkDetail(item.profileId);
        setVisualNovelWork(work);
        setVisualNovelSession(mapVisualNovelWorkDetailToSession(work));
        enterCreateTab();
        setSelectionStage('visual-novel-result');
      } catch (error) {
        setVisualNovelError(
          resolvePuzzleErrorMessage(error, '读取视觉小说作品详情失败。'),
        );
      } finally {
        setIsVisualNovelBusy(false);
      }
    },
    [
      enterCreateTab,
      openPublicWorkDetail,
      resolvePuzzleErrorMessage,
      setIsVisualNovelBusy,
      setSelectionStage,
      setVisualNovelError,
      setVisualNovelSession,
    ],
  );

  const startBigFishRunFromWork = useCallback(
    async (
      item: BigFishWorkSummary,
      returnStage: BigFishRuntimeReturnStage = 'work-detail',
      options: { embedded?: boolean } = {},
    ) => {
      const sessionId = item.sourceSessionId?.trim();
      if (!sessionId) {
        setBigFishError('当前作品缺少会话信息，暂时无法进入玩法。');
        return false;
      }

      const publicWorkCode = buildBigFishPublicWorkCode(item.sourceSessionId);
      setBigFishError(null);
      bigFishFlow.setSession(null);
      setBigFishRuntimeWork(item);
      setBigFishRuntimeShare({
        title: item.title,
        publicWorkCode,
      });
      setBigFishRuntimeStartedAt(null);
      setBigFishRuntimeSessionSource('work');
      setBigFishRuntimeReturnStage(returnStage);
      setBigFishRun(null);
      try {
        const { run } = options.embedded
          ? await startBigFishRuntimeRun(
              sessionId,
              RECOMMEND_RUNTIME_BACKGROUND_AUTH_OPTIONS,
            )
          : await startBigFishRuntimeRun(sessionId);
        setBigFishRuntimeStartedAt(Date.now());
        setBigFishRun(run);
        if (!options.embedded) {
          setSelectionStage('big-fish-runtime');
          pushAppHistoryPath(
            buildPublicWorkStagePath('big-fish-runtime', publicWorkCode),
          );
        }
        const recordPlayPromise = options.embedded
          ? recordBigFishPlay(
              sessionId,
              { elapsedMs: 0 },
              RECOMMEND_RUNTIME_BACKGROUND_AUTH_OPTIONS,
            )
          : recordBigFishPlay(sessionId, { elapsedMs: 0 });
        void recordPlayPromise.catch((error) => {
          setBigFishError(
            resolveBigFishErrorMessage(error, '记录大鱼吃小鱼游玩失败。'),
          );
        });
        return true;
      } catch (error) {
        setBigFishError(
          resolveBigFishErrorMessage(error, '启动大鱼吃小鱼玩法失败。'),
        );
        return false;
      }
    },
    [
      bigFishFlow,
      resolveBigFishErrorMessage,
      setBigFishError,
      setBigFishRuntimeReturnStage,
      setSelectionStage,
    ],
  );

  const startSelectedPublicWork = useCallback(() => {
    if (!selectedPublicWorkDetail || isPublicWorkDetailBusy) {
      return;
    }

    runProtectedAction(() => {
      if (isBigFishGalleryEntry(selectedPublicWorkDetail)) {
        const work = mapPublicWorkDetailToBigFishWork(selectedPublicWorkDetail);
        if (!work) {
          setPublicWorkDetailError('当前作品缺少会话信息，暂时无法进入玩法。');
          return;
        }
        startBigFishRunFromWork(work);
        return;
      }

      if (isPuzzleGalleryEntry(selectedPublicWorkDetail)) {
        const work =
          selectedPuzzleDetail?.profileId === selectedPublicWorkDetail.profileId
            ? selectedPuzzleDetail
            : mapPublicWorkDetailToPuzzleWork(selectedPublicWorkDetail);
        if (!work) {
          setPublicWorkDetailError(
            '当前拼图作品信息不完整，暂时无法进入玩法。',
          );
          return;
        }
        setPublicWorkDetailError(null);
        void startPuzzleRunFromProfile(
          work.profileId,
          'work-detail',
          work,
          true,
          null,
          { authMode: 'isolated' },
        );
        return;
      }

      if (isMatch3DGalleryEntry(selectedPublicWorkDetail)) {
        const work = mapPublicWorkDetailToMatch3DWork(selectedPublicWorkDetail);
        if (!work) {
          setPublicWorkDetailError(
            '当前抓大鹅作品信息不完整，暂时无法进入玩法。',
          );
          return;
        }
        setPublicWorkDetailError(null);
        void startMatch3DRunFromProfile(work, 'work-detail', true);
        return;
      }

      if (isSquareHoleGalleryEntry(selectedPublicWorkDetail)) {
        const work = mapPublicWorkDetailToSquareHoleWork(
          selectedPublicWorkDetail,
        );
        if (!work) {
          setPublicWorkDetailError(
            '当前方洞挑战作品信息不完整，暂时无法进入玩法。',
          );
          return;
        }
        setPublicWorkDetailError(null);
        void startSquareHoleRunFromProfile(work, 'work-detail', true);
        return;
      }

      if (isVisualNovelGalleryEntry(selectedPublicWorkDetail)) {
        setPublicWorkDetailError(null);
        void startVisualNovelRunFromProfile(
          selectedPublicWorkDetail.profileId,
          'work-detail',
        );
        return;
      }

      const launchEntry =
        selectedDetailEntry?.profileId === selectedPublicWorkDetail.profileId
          ? selectedDetailEntry
          : null;
      if (!launchEntry) {
        setPublicWorkDetailError('作品详情尚未读取完成。');
        return;
      }

      setIsPublicWorkDetailBusy(true);
      void recordRpgEntryWorldGalleryPlay(
        launchEntry.ownerUserId,
        launchEntry.profileId,
      )
        .then((updatedEntry) => {
          setSelectedDetailEntry(updatedEntry);
          setSelectedPublicWorkDetail(
            mapRpgGalleryCardToPublicWorkDetail(updatedEntry),
          );
          handleCustomWorldSelect(updatedEntry.profile);
        })
        .catch((error) => {
          setPublicWorkDetailError(
            resolveRpgCreationErrorMessage(error, '记录作品游玩失败。'),
          );
        })
        .finally(() => {
          setIsPublicWorkDetailBusy(false);
        });
    });
  }, [
    handleCustomWorldSelect,
    isPublicWorkDetailBusy,
    runProtectedAction,
    selectedDetailEntry,
    selectedPuzzleDetail,
    selectedPublicWorkDetail,
    startBigFishRunFromWork,
    startPuzzleRunFromProfile,
    startMatch3DRunFromProfile,
    startSquareHoleRunFromProfile,
    startVisualNovelRunFromProfile,
  ]);

  const selectRecommendRuntimeEntry = useCallback(
    async (entry: PlatformPublicGalleryCard) => {
      const entryKey = getPlatformPublicGalleryEntryKey(entry);
      const runtimeKind = getPlatformRecommendRuntimeKind(entry);
      const startRequestId = recommendRuntimeStartRequestRef.current + 1;
      recommendRuntimeStartRequestRef.current = startRequestId;
      const isCurrentStartRequest = () =>
        recommendRuntimeStartRequestRef.current === startRequestId;
      if (entryKey !== activeRecommendEntryKey) {
        await saveAndExitRecommendPuzzleRuntime();
        if (!isCurrentStartRequest()) {
          return;
        }
      }
      setActiveRecommendEntryKey(entryKey);
      setActiveRecommendRuntimeKind(runtimeKind);
      setActiveRecommendRuntimeError(null);
      setIsStartingRecommendEntry(true);

      try {
        let started = false;
        if (isBigFishGalleryEntry(entry)) {
          const work = mapPublicWorkDetailToBigFishWork(entry);
          if (!work) {
            setBigFishError('当前作品缺少会话信息，暂时无法进入玩法。');
          } else {
            started = await startBigFishRunFromWork(work, 'platform', {
              embedded: true,
            });
          }
        } else if (isPuzzleGalleryEntry(entry)) {
          const work =
            selectedPuzzleDetail?.profileId === entry.profileId
              ? selectedPuzzleDetail
              : mapPublicWorkDetailToPuzzleWork(entry);
          if (!work) {
            setPuzzleError('当前拼图作品信息不完整，暂时无法进入玩法。');
          } else {
            started = await startPuzzleRunFromProfile(
              work.profileId,
              'platform',
              work,
              false,
              null,
              { embedded: true },
            );
          }
        } else if (isMatch3DGalleryEntry(entry)) {
          const work = mapPublicWorkDetailToMatch3DWork(entry);
          if (!work) {
            setMatch3DError('当前抓大鹅作品信息不完整，暂时无法进入玩法。');
          } else {
            started = await startMatch3DRunFromProfile(
              work,
              'work-detail',
              false,
              { embedded: true },
            );
          }
        } else if (isSquareHoleGalleryEntry(entry)) {
          const work = mapPublicWorkDetailToSquareHoleWork(entry);
          if (!work) {
            setSquareHoleError('当前方洞挑战作品信息不完整，暂时无法进入玩法。');
          } else {
            started = await startSquareHoleRunFromProfile(
              work,
              'platform',
              false,
              { embedded: true },
            );
          }
        } else if (isVisualNovelGalleryEntry(entry)) {
          started = await startVisualNovelRunFromProfile(
            entry.profileId,
            'platform',
            { embedded: true },
          );
        } else {
          started = true;
        }

        if (!isCurrentStartRequest()) {
          return;
        }

        if (started) {
          setActiveRecommendRuntimeKind(runtimeKind);
          setActiveRecommendRuntimeError(null);
        } else {
          setActiveRecommendRuntimeKind(null);
          setActiveRecommendRuntimeError('作品暂时无法进入，请稍后再试。');
        }
      } catch (error) {
        if (!isCurrentStartRequest()) {
          return;
        }

        setActiveRecommendRuntimeKind(null);
        setActiveRecommendRuntimeError('作品暂时无法进入，请稍后再试。');
      } finally {
        if (isCurrentStartRequest()) {
          setIsStartingRecommendEntry(false);
        }
      }
    },
    [
      activeRecommendEntryKey,
      saveAndExitRecommendPuzzleRuntime,
      selectedPuzzleDetail,
      setBigFishError,
      setMatch3DError,
      setPuzzleError,
      setSquareHoleError,
      startBigFishRunFromWork,
      startMatch3DRunFromProfile,
      startPuzzleRunFromProfile,
      startSquareHoleRunFromProfile,
      startVisualNovelRunFromProfile,
    ],
  );
  const selectAdjacentRecommendRuntimeEntry = useCallback(
    (direction: 1 | -1) => {
      if (recommendRuntimeEntries.length === 0) {
        return;
      }

      const activeIndex = recommendRuntimeEntries.findIndex(
        (entry) =>
          getPlatformPublicGalleryEntryKey(entry) === activeRecommendEntryKey,
      );
      const baseIndex = activeIndex >= 0 ? activeIndex : 0;
      const nextIndex =
        (baseIndex + direction + recommendRuntimeEntries.length) %
        recommendRuntimeEntries.length;
      const nextEntry = recommendRuntimeEntries[nextIndex];
      if (!nextEntry) {
        return;
      }
      if (
        getPlatformPublicGalleryEntryKey(nextEntry) === activeRecommendEntryKey
      ) {
        return;
      }

      void selectRecommendRuntimeEntry(nextEntry);
    },
    [
      activeRecommendEntryKey,
      recommendRuntimeEntries,
      selectRecommendRuntimeEntry,
    ],
  );

  const recommendRuntimeContent = useMemo(() => {
    if (
      selectionStage !== 'platform' ||
      platformBootstrap.platformTab !== 'home' ||
      !activeRecommendRuntimeKind
    ) {
      return null;
    }

    const activeEntry =
      recommendRuntimeEntries.find(
        (entry) =>
          getPlatformPublicGalleryEntryKey(entry) === activeRecommendEntryKey,
      ) ?? null;
    if (!activeEntry) {
      return null;
    }

    if (activeRecommendRuntimeKind === 'big-fish') {
      return (
        <BigFishRuntimeShell
          run={bigFishRun}
          assetSlots={bigFishSession?.assetSlots ?? []}
          shareTitle={bigFishRuntimeShare?.title ?? null}
          sharePublicWorkCode={bigFishRuntimeShare?.publicWorkCode ?? null}
          isBusy={isBigFishBusy}
          error={bigFishError}
          embedded
          onBack={() => {
            reportBigFishObservedPlayTime();
            setActiveRecommendRuntimeKind(null);
          }}
          onRestart={() => {
            reportBigFishObservedPlayTime();
            void restartBigFishRun();
          }}
          onSubmitInput={submitBigFishInput}
        />
      );
    }

    if (activeRecommendRuntimeKind === 'match3d') {
      return (
        <Match3DRuntimeShell
          run={match3dRun}
          isBusy={isMatch3DBusy}
          error={match3dError}
          embedded
          onBack={() => {
            setActiveRecommendRuntimeKind(null);
          }}
          onRestart={() => {
            if (!match3dRun?.runId || isMatch3DBusy) {
              return;
            }

            match3dFlow.setIsBusy(true);
            setMatch3DError(null);
            void restartMatch3DRun(match3dRun.runId)
              .then(({ run }) => {
                setMatch3DRun(run);
              })
              .catch((error) => {
                setMatch3DError(
                  resolveMatch3DErrorMessage(
                    error,
                    '重新开始抓大鹅玩法失败。',
                  ),
                );
              })
              .finally(() => {
                match3dFlow.setIsBusy(false);
              });
          }}
          onOptimisticRunChange={setMatch3DRun}
          onClickItem={(payload) => {
            const runId = payload.runId ?? match3dRun?.runId;
            if (!runId) {
              return Promise.reject(new Error('抓大鹅运行态缺少 runId。'));
            }
            return clickMatch3DItem(runId, payload);
          }}
          onTimeExpired={() => {
            if (!match3dRun?.runId) {
              return;
            }

            void finishMatch3DTimeUp(match3dRun.runId)
              .then(({ run }) => {
                setMatch3DRun(run);
              })
              .catch((error) => {
                setMatch3DError(
                  resolveMatch3DErrorMessage(
                    error,
                    '同步抓大鹅倒计时失败。',
                  ),
                );
              });
          }}
        />
      );
    }

    if (activeRecommendRuntimeKind === 'puzzle') {
      return (
        <PuzzleRuntimeShell
          run={puzzleRun}
          isBusy={
            isPuzzleBusy ||
            isPuzzleNextLevelGenerating ||
            isPuzzleLeaderboardBusy
          }
          error={puzzleError}
          embedded
          hideBackButton
          hideExitControls
          onBack={() => {
            setActiveRecommendRuntimeKind(null);
          }}
          onRemodelWork={
            selectedPuzzleDetail?.publicationStatus === 'published'
              ? remodelCurrentPuzzleRuntimeWork
              : undefined
          }
          onSwapPieces={(payload) => {
            void swapPuzzlePiecesInRun(payload);
          }}
          onDragPiece={(payload) => {
            void dragPuzzlePiece(payload);
          }}
          onAdvanceNextLevel={(target) => {
            void advancePuzzleLevel(target);
          }}
          onRestartLevel={() => {
            void restartPuzzleCurrentLevel();
          }}
          onPauseChange={setPuzzleRuntimePaused}
          onUseProp={usePuzzleProp}
          onTimeExpired={syncPuzzleRuntimeTimeout}
        />
      );
    }

    if (activeRecommendRuntimeKind === 'square-hole') {
      return (
        <SquareHoleRuntimeShell
          run={squareHoleRun}
          isBusy={isSquareHoleBusy}
          error={squareHoleError}
          embedded
          onBack={() => {
            if (
              squareHoleRun?.runId &&
              squareHoleRun.status.toLowerCase() === 'running'
            ) {
              void stopSquareHoleRun(squareHoleRun.runId).catch(
                () => undefined,
              );
            }
            setActiveRecommendRuntimeKind(null);
          }}
          onRestart={() => {
            if (!squareHoleRun?.runId || isSquareHoleBusy) {
              return;
            }

            squareHoleFlow.setIsBusy(true);
            setSquareHoleError(null);
            void restartSquareHoleRun(squareHoleRun.runId)
              .then(({ run }) => {
                setSquareHoleRun(run);
              })
              .catch((error) => {
                setSquareHoleError(
                  resolveSquareHoleErrorMessage(
                    error,
                    '重新开始方洞挑战失败。',
                  ),
                );
              })
              .finally(() => {
                squareHoleFlow.setIsBusy(false);
              });
          }}
          onOptimisticRunChange={setSquareHoleRun}
          onDropShape={(payload) => {
            const runId = payload.runId ?? squareHoleRun?.runId;
            if (!runId) {
              return Promise.reject(new Error('方洞挑战运行态缺少 runId。'));
            }
            return dropSquareHoleShape(runId, payload);
          }}
          onTimeExpired={() => {
            if (!squareHoleRun?.runId) {
              return;
            }

            void finishSquareHoleTimeUp(squareHoleRun.runId)
              .then(({ run }) => {
                setSquareHoleRun(run);
              })
              .catch((error) => {
                setSquareHoleError(
                  resolveSquareHoleErrorMessage(
                    error,
                    '同步方洞挑战倒计时失败。',
                  ),
                );
              });
          }}
        />
      );
    }

    if (activeRecommendRuntimeKind === 'visual-novel') {
      return (
        <VisualNovelRuntimeShell
          draft={visualNovelWork?.draft ?? visualNovelSession?.draft}
          run={visualNovelRun}
          isBusy={isVisualNovelBusy}
          error={visualNovelError}
          embedded
          onBack={() => {
            setActiveRecommendRuntimeKind(null);
          }}
          onSubmitAction={(payload) => {
            void submitVisualNovelRuntimeAction(payload);
          }}
        />
      );
    }

    return (
      <div className={`platform-theme ${platformThemeClass} flex h-full min-h-0 items-center justify-center bg-[var(--platform-recommend-runtime-state-fill)] px-5 text-center text-sm font-semibold leading-6 text-[var(--platform-recommend-runtime-state-text)]`}>
        正在读取世界
      </div>
    );
  }, [
    activeRecommendEntryKey,
    activeRecommendRuntimeKind,
    bigFishError,
    bigFishRun,
    bigFishRuntimeShare,
    bigFishSession?.assetSlots,
    isBigFishBusy,
    isMatch3DBusy,
    isPuzzleBusy,
    isPuzzleLeaderboardBusy,
    isPuzzleNextLevelGenerating,
    isSquareHoleBusy,
    isVisualNovelBusy,
    match3dError,
    match3dFlow,
    match3dRun,
    platformThemeClass,
    puzzleError,
    puzzleRun,
    recommendRuntimeEntries,
    remodelCurrentPuzzleRuntimeWork,
    reportBigFishObservedPlayTime,
    restartBigFishRun,
    selectedPuzzleDetail,
    selectionStage,
    setMatch3DError,
    setMatch3DRun,
    setPuzzleRuntimePaused,
    setSquareHoleRun,
    squareHoleError,
    squareHoleFlow,
    squareHoleRun,
    submitBigFishInput,
    submitVisualNovelRuntimeAction,
    advancePuzzleLevel,
    dragPuzzlePiece,
    restartPuzzleCurrentLevel,
    setSquareHoleError,
    swapPuzzlePiecesInRun,
    syncPuzzleRuntimeTimeout,
    usePuzzleProp,
    visualNovelError,
    visualNovelRun,
    visualNovelSession,
    visualNovelWork,
  ]);

  useEffect(() => {
    if (
      selectionStage !== 'platform' ||
      platformBootstrap.platformTab !== 'home' ||
      !platformBootstrap.isAuthenticated ||
      !platformBootstrap.canReadProtectedData ||
      platformBootstrap.isLoadingPlatform
    ) {
      return;
    }

    if (recommendRuntimeEntries.length === 0) {
      setActiveRecommendEntryKey(null);
      setActiveRecommendRuntimeKind(null);
      setActiveRecommendRuntimeError(null);
      return;
    }

    const hasActiveEntry =
      activeRecommendEntryKey &&
      recommendRuntimeEntries.some(
        (entry) =>
          getPlatformPublicGalleryEntryKey(entry) === activeRecommendEntryKey,
      );
    if (hasActiveEntry || isStartingRecommendEntry) {
      return;
    }

    const firstRecommendEntry = recommendRuntimeEntries[0];
    if (firstRecommendEntry) {
      void selectRecommendRuntimeEntry(firstRecommendEntry);
    }
  }, [
    activeRecommendEntryKey,
    isStartingRecommendEntry,
    platformBootstrap.canReadProtectedData,
    platformBootstrap.isLoadingPlatform,
    platformBootstrap.isAuthenticated,
    platformBootstrap.platformTab,
    recommendRuntimeEntries,
    selectRecommendRuntimeEntry,
    selectionStage,
  ]);

  const remixPublicWork = useCallback(
    (entry: PlatformPublicGalleryCard) => {
      if (isPublicWorkDetailBusy) {
        return;
      }

      runProtectedAction(() => {
        setIsPublicWorkDetailBusy(true);
        setPublicWorkDetailError(null);

        if (isBigFishGalleryEntry(entry)) {
          void remixBigFishGalleryWork(entry.profileId)
            .then((response) => {
              bigFishFlow.setSession(response.session);
              enterCreateTab();
              setSelectionStage('big-fish-result');
            })
            .catch((error) => {
              setPublicWorkDetailError(
                resolveBigFishErrorMessage(error, 'Remix 大鱼吃小鱼作品失败。'),
              );
            })
            .finally(() => {
              setIsPublicWorkDetailBusy(false);
            });
          return;
        }

        if (isPuzzleGalleryEntry(entry)) {
          void remixPuzzleGalleryWork(entry.profileId)
            .then((response) => {
              puzzleFlow.setSession(response.session);
              setPuzzleOperation(null);
              enterCreateTab();
              setSelectionStage('puzzle-result');
            })
            .catch((error) => {
              setPublicWorkDetailError(
                resolvePuzzleErrorMessage(error, 'Remix 拼图作品失败。'),
              );
            })
            .finally(() => {
              setIsPublicWorkDetailBusy(false);
            });
          return;
        }

        if (isMatch3DGalleryEntry(entry)) {
          setPublicWorkDetailError('抓大鹅作品改造将在后续版本开放。');
          setIsPublicWorkDetailBusy(false);
          return;
        }

        if (isSquareHoleGalleryEntry(entry)) {
          setPublicWorkDetailError('方洞挑战作品改造将在后续版本开放。');
          setIsPublicWorkDetailBusy(false);
          return;
        }

        if (isVisualNovelGalleryEntry(entry)) {
          setPublicWorkDetailError('视觉小说作品改造将在后续版本开放。');
          setIsPublicWorkDetailBusy(false);
          return;
        }

        void remixRpgEntryWorldGallery(entry.ownerUserId, entry.profileId)
          .then((response) => {
            const nextEntry = response.entry;
            setSelectedDetailEntry(nextEntry);
            platformBootstrap.setSavedCustomWorldEntries([
              nextEntry,
              ...platformBootstrap.savedCustomWorldEntries.filter(
                (entry) => entry.profileId !== nextEntry.profileId,
              ),
            ]);
            void detailNavigation.openSavedCustomWorldEditor(nextEntry);
          })
          .catch((error) => {
            setPublicWorkDetailError(
              resolveRpgCreationErrorMessage(error, 'Remix RPG 作品失败。'),
            );
          })
          .finally(() => {
            setIsPublicWorkDetailBusy(false);
          });
      });
    },
    [
      bigFishFlow,
      detailNavigation,
      enterCreateTab,
      isPublicWorkDetailBusy,
      platformBootstrap,
      puzzleFlow,
      resolveBigFishErrorMessage,
      resolvePuzzleErrorMessage,
      runProtectedAction,
      setSelectionStage,
    ],
  );

  const editOwnedPublicWork = useCallback(
    (entry: PlatformPublicGalleryCard) => {
      if (isPublicWorkDetailBusy) {
        return;
      }

      runProtectedAction(() => {
        setPublicWorkDetailError(null);

        // 中文注释：自有公开作品必须恢复原草稿，不能复用 remix 复制链路。
        if (isBigFishGalleryEntry(entry)) {
          const work = mapPublicWorkDetailToBigFishWork(entry);
          if (!work?.sourceSessionId?.trim()) {
            setPublicWorkDetailError(
              '这份大鱼吃小鱼作品缺少原草稿会话，暂时无法编辑。',
            );
            return;
          }
          void openBigFishDraft(work);
          return;
        }

        if (isPuzzleGalleryEntry(entry)) {
          const work =
            selectedPuzzleDetail?.profileId === entry.profileId
              ? selectedPuzzleDetail
              : mapPublicWorkDetailToPuzzleWork(entry);
          if (!work?.sourceSessionId?.trim()) {
            setPublicWorkDetailError(
              '这份拼图作品缺少原草稿会话，暂时无法编辑。',
            );
            return;
          }
          void openPuzzleDraft(work);
          return;
        }

        if (isMatch3DGalleryEntry(entry)) {
          const work = mapPublicWorkDetailToMatch3DWork(entry);
          if (!work?.sourceSessionId?.trim()) {
            setPublicWorkDetailError(
              '这份抓大鹅作品缺少原草稿会话，暂时无法编辑。',
            );
            return;
          }
          void openMatch3DDraft(work, { forceDraft: true });
          return;
        }

        if (isSquareHoleGalleryEntry(entry)) {
          const work = mapPublicWorkDetailToSquareHoleWork(entry);
          if (!work?.sourceSessionId?.trim()) {
            setPublicWorkDetailError(
              '这份方洞挑战作品缺少原草稿会话，暂时无法编辑。',
            );
            return;
          }
          void openSquareHoleDraft(work, { forceDraft: true });
          return;
        }

        if (isVisualNovelGalleryEntry(entry)) {
          const matchedWork = visualNovelWorks.find(
            (work) => work.profileId === entry.profileId,
          );
          if (!matchedWork) {
            setPublicWorkDetailError('这份视觉小说缺少可编辑草稿。');
            return;
          }
          void openVisualNovelDraft(matchedWork, { forceDraft: true });
          return;
        }

        const editEntry =
          selectedDetailEntry?.profileId === entry.profileId
            ? selectedDetailEntry
            : null;
        if (!editEntry) {
          setPublicWorkDetailError('作品详情尚未读取完成。');
          return;
        }

        void detailNavigation.openSavedCustomWorldEditor(editEntry);
      });
    },
    [
      detailNavigation,
      isPublicWorkDetailBusy,
      openBigFishDraft,
      openMatch3DDraft,
      openPuzzleDraft,
      openSquareHoleDraft,
      openVisualNovelDraft,
      runProtectedAction,
      selectedDetailEntry,
      selectedPuzzleDetail,
      visualNovelWorks,
    ],
  );

  const remixSelectedPublicWork = useCallback(() => {
    if (!selectedPublicWorkDetail) {
      return;
    }
    if (isSelectedPublicWorkOwned) {
      editOwnedPublicWork(selectedPublicWorkDetail);
      return;
    }
    remixPublicWork(selectedPublicWorkDetail);
  }, [
    editOwnedPublicWork,
    isSelectedPublicWorkOwned,
    remixPublicWork,
    selectedPublicWorkDetail,
  ]);

  const handlePublicCodeSearch = useCallback(
    async (keyword: string) => {
      const normalizedKeyword = keyword.trim();
      if (!normalizedKeyword) {
        return;
      }

      setIsSearchingPublicCode(true);
      setPublicSearchError(null);
      setSearchedPublicUser(null);

      const upperKeyword = normalizedKeyword.toUpperCase();
      const shouldSearchUserIdFirst = /^user[_-][a-z0-9_-]+$/iu.test(
        normalizedKeyword,
      );
      const shouldSearchBigFishFirst = upperKeyword.startsWith('BF');
      const shouldSearchMatch3DFirst = upperKeyword.startsWith('M3');
      const shouldSearchPuzzleFirst = upperKeyword.startsWith('PZ');
      const shouldSearchSquareHoleFirst = upperKeyword.startsWith('SH');
      const shouldSearchVisualNovelFirst = upperKeyword.startsWith('VN');
      const shouldSearchWorkFirst =
        !shouldSearchUserIdFirst &&
        !shouldSearchBigFishFirst &&
        !shouldSearchMatch3DFirst &&
        !shouldSearchPuzzleFirst &&
        !shouldSearchSquareHoleFirst &&
        !shouldSearchVisualNovelFirst &&
        (upperKeyword.startsWith('CW') || /^\d{1,8}$/u.test(normalizedKeyword));
      const shouldSearchUserFirst =
        shouldSearchUserIdFirst ||
        upperKeyword.startsWith('SY') ||
        (!shouldSearchWorkFirst &&
          !shouldSearchBigFishFirst &&
          !shouldSearchMatch3DFirst &&
          !shouldSearchPuzzleFirst &&
          !shouldSearchSquareHoleFirst &&
          !shouldSearchVisualNovelFirst);

      const tryOpenGalleryEntry = async () => {
        const entry =
          await getRpgEntryWorldGalleryDetailByCode(normalizedKeyword);
        const card = {
          ownerUserId: entry.ownerUserId,
          profileId: entry.profileId,
          publicWorkCode: entry.publicWorkCode,
          authorPublicUserCode: entry.authorPublicUserCode,
          visibility: 'published',
          publishedAt: entry.publishedAt,
          updatedAt: entry.updatedAt,
          authorDisplayName: entry.authorDisplayName,
          worldName: entry.worldName,
          subtitle: entry.subtitle,
          summaryText: entry.summaryText,
          coverImageSrc: entry.coverImageSrc,
          themeMode: entry.themeMode,
          playableNpcCount: entry.playableNpcCount,
          landmarkCount: entry.landmarkCount,
          playCount: entry.playCount ?? 0,
          remixCount: entry.remixCount ?? 0,
          likeCount: entry.likeCount ?? 0,
        } satisfies CustomWorldGalleryCard;
        setSelectedDetailEntry(entry);
        openPublicWorkDetail(card);
      };
      const tryOpenPuzzleGalleryEntry = async () => {
        const entries =
          puzzleGalleryEntries.length > 0
            ? puzzleGalleryEntries
            : await refreshPuzzleGallery();
        const matchedEntry = entries.find((entry) =>
          isSamePuzzlePublicWorkCode(normalizedKeyword, entry.profileId),
        );

        if (!matchedEntry) {
          throw new Error('未找到拼图作品。');
        }

        await openPuzzlePublicWorkDetail(matchedEntry.profileId, {
          tab: platformBootstrap.platformTab,
        });
      };
      const tryOpenBigFishGalleryEntry = async () => {
        const entries =
          bigFishGalleryEntries.length > 0
            ? bigFishGalleryEntries
            : await refreshBigFishGallery();
        const matchedEntry = entries.find((entry) =>
          isSameBigFishPublicWorkCode(normalizedKeyword, entry.sourceSessionId),
        );

        if (!matchedEntry) {
          throw new Error('未找到大鱼吃小鱼作品。');
        }

        openPublicWorkDetail(mapBigFishWorkToPublicWorkDetail(matchedEntry));
      };
      const tryOpenMatch3DGalleryEntry = async () => {
        const entries =
          match3dGalleryEntries.length > 0
            ? match3dGalleryEntries
            : await refreshMatch3DGallery();
        const matchedEntry = entries.find((entry) =>
          isSameMatch3DPublicWorkCode(normalizedKeyword, entry.profileId),
        );

        if (!matchedEntry) {
          throw new Error('未找到抓大鹅作品。');
        }

        openPublicWorkDetail(mapMatch3DWorkToPublicWorkDetail(matchedEntry));
      };
      const tryOpenSquareHoleGalleryEntry = async () => {
        const entries =
          squareHoleGalleryEntries.length > 0
            ? squareHoleGalleryEntries
            : await refreshSquareHoleGallery();
        const matchedEntry = entries.find((entry) =>
          isSameSquareHolePublicWorkCode(normalizedKeyword, entry.profileId),
        );

        if (!matchedEntry) {
          throw new Error('未找到方洞挑战作品。');
        }

        openPublicWorkDetail(mapSquareHoleWorkToPublicWorkDetail(matchedEntry));
      };
      const tryOpenVisualNovelGalleryEntry = async () => {
        const entries =
          visualNovelGalleryEntries.length > 0
            ? visualNovelGalleryEntries
            : await refreshVisualNovelGallery();
        const matchedEntry = entries.find((entry) =>
          isSameVisualNovelPublicWorkCode(normalizedKeyword, entry.profileId),
        );

        if (!matchedEntry) {
          throw new Error('未找到视觉小说作品。');
        }

        openPublicWorkDetail(mapVisualNovelWorkToPublicWorkDetail(matchedEntry));
      };

      try {
        if (shouldSearchUserIdFirst) {
          const user = await getPublicAuthUserById(normalizedKeyword);
          setSearchedPublicUser(user);
          return;
        }

        if (shouldSearchPuzzleFirst) {
          await tryOpenPuzzleGalleryEntry();
          return;
        }

        if (shouldSearchBigFishFirst) {
          await tryOpenBigFishGalleryEntry();
          return;
        }

        if (shouldSearchMatch3DFirst) {
          await tryOpenMatch3DGalleryEntry();
          return;
        }

        if (shouldSearchSquareHoleFirst) {
          await tryOpenSquareHoleGalleryEntry();
          return;
        }

        if (shouldSearchVisualNovelFirst) {
          await tryOpenVisualNovelGalleryEntry();
          return;
        }

        if (shouldSearchWorkFirst) {
          try {
            await tryOpenGalleryEntry();
            return;
          } catch {
            // 作品号优先时允许继续回退到用户号搜索。
          }
        }

        if (shouldSearchUserFirst) {
          try {
            const user = await getPublicAuthUserByCode(normalizedKeyword);
            setSearchedPublicUser(user);
            return;
          } catch {
            // 用户号优先时允许继续回退到作品号搜索。
          }
        }

        if (!shouldSearchWorkFirst) {
          await tryOpenGalleryEntry();
          return;
        }

        const user = await getPublicAuthUserByCode(normalizedKeyword);
        setSearchedPublicUser(user);
      } catch (error) {
        setPublicSearchError(
          resolveRpgCreationErrorMessage(error, '未找到对应的百梦号或作品号。'),
        );
      } finally {
        setIsSearchingPublicCode(false);
      }
    },
    [
      bigFishGalleryEntries,
      match3dGalleryEntries,
      refreshMatch3DGallery,
      openPuzzlePublicWorkDetail,
      openPublicWorkDetail,
      platformBootstrap.platformTab,
      puzzleGalleryEntries,
      refreshBigFishGallery,
      refreshPuzzleGallery,
      refreshSquareHoleGallery,
      refreshVisualNovelGallery,
      squareHoleGalleryEntries,
      visualNovelGalleryEntries,
    ],
  );

  const openProfilePlayedWorks = useCallback(() => {
    setIsProfilePlayStatsOpen(true);
    setIsProfilePlayStatsLoading(true);
    setProfilePlayStatsError(null);

    void getRpgProfilePlayStats()
      .then(setProfilePlayStats)
      .catch((error) => {
        setProfilePlayStats(null);
        setProfilePlayStatsError(
          resolveRpgCreationErrorMessage(error, '读取玩过作品失败。'),
        );
      })
      .finally(() => {
        setIsProfilePlayStatsLoading(false);
      });
  }, []);

  const openPlayedWork = useCallback(
    (work: ProfilePlayedWorkSummary) => {
      const worldType = (work.worldType ?? '').toLowerCase();
      setIsProfilePlayStatsOpen(false);

      if (worldType === 'puzzle' || work.worldKey.startsWith('puzzle:')) {
        const profileId =
          work.profileId ?? work.worldKey.replace(/^puzzle:/u, '');
        if (profileId) {
          void openPuzzlePublicWorkDetail(profileId, { tab: 'profile' });
        }
        return;
      }

      if (
        worldType === 'match3d' ||
        worldType === 'match_3d' ||
        work.worldKey.startsWith('match3d:')
      ) {
        const profileId =
          work.profileId ?? work.worldKey.replace(/^match3d:/u, '');
        if (profileId) {
          void openMatch3DPublicWorkDetail(profileId);
        }
        return;
      }

      if (
        worldType === 'square-hole' ||
        worldType === 'square_hole' ||
        work.worldKey.startsWith('square-hole:')
      ) {
        const profileId =
          work.profileId ?? work.worldKey.replace(/^square-hole:/u, '');
        if (profileId) {
          void openSquareHolePublicWorkDetail(profileId);
        }
        return;
      }

      if (
        worldType === 'big_fish' ||
        worldType === 'big-fish' ||
        work.worldKey.startsWith('big-fish:')
      ) {
        const sessionId =
          work.profileId ?? work.worldKey.replace(/^big-fish:/u, '');
        if (!sessionId) {
          return;
        }
        void refreshBigFishGallery()
          .then((entries) => {
            const matchedEntry = entries.find(
              (entry) => entry.sourceSessionId === sessionId,
            );
            if (matchedEntry) {
              openPublicWorkDetail(
                mapBigFishWorkToPublicWorkDetail(matchedEntry),
              );
              return;
            }
            openPublicWorkDetail(
              mapBigFishWorkToPublicWorkDetail({
                workId: `big-fish:${sessionId}`,
                sourceSessionId: sessionId,
                ownerUserId: work.ownerUserId ?? '',
                authorDisplayName: work.worldSubtitle || '玩家',
                title: work.worldTitle,
                subtitle: work.worldSubtitle,
                summary: work.worldSubtitle,
                coverImageSrc: null,
                status: 'published',
                updatedAt: work.lastPlayedAt,
                publishReady: true,
                levelCount: 0,
                levelMainImageReadyCount: 0,
                levelMotionReadyCount: 0,
                backgroundReady: false,
              }),
            );
          })
          .catch((error) => {
            setBigFishError(
              resolveBigFishErrorMessage(error, '进入大鱼吃小鱼作品失败。'),
            );
          });
        return;
      }

      const profileId = work.profileId ?? work.worldKey;
      const ownerUserId = work.ownerUserId;
      if (!ownerUserId || !profileId) {
        return;
      }

      void openRpgPublicWorkDetail({
        ownerUserId,
        profileId,
        publicWorkCode: null,
        authorPublicUserCode: null,
        visibility: 'published',
        publishedAt: work.firstPlayedAt,
        updatedAt: work.lastPlayedAt,
        authorDisplayName: work.worldSubtitle,
        worldName: work.worldTitle,
        subtitle: work.worldSubtitle,
        summaryText: '',
        coverImageSrc: null,
        themeMode: 'martial',
        playableNpcCount: 0,
        landmarkCount: 0,
        playCount: 0,
        remixCount: 0,
        likeCount: 0,
      });
    },
    [
      openMatch3DPublicWorkDetail,
      openPuzzlePublicWorkDetail,
      openPublicWorkDetail,
      openRpgPublicWorkDetail,
      openSquareHolePublicWorkDetail,
      refreshBigFishGallery,
      resolveBigFishErrorMessage,
      setBigFishError,
    ],
  );

  useEffect(() => {
    const publicWorkCode = initialPublicWorkCode?.trim();
    if (
      !publicWorkCode ||
      handledInitialPublicWorkCodeRef.current === publicWorkCode
    ) {
      return;
    }

    handledInitialPublicWorkCodeRef.current = publicWorkCode;
    void handlePublicCodeSearch(publicWorkCode);
  }, [handlePublicCodeSearch, initialPublicWorkCode]);

  useEffect(() => {
    if (selectionStage === 'platform') {
      if (isBigFishCreationVisible) {
        void refreshBigFishGallery();
      }
      void refreshMatch3DGallery();
      void refreshPuzzleGallery();
      void refreshSquareHoleGallery();
      void refreshVisualNovelGallery();
    }
  }, [
    isBigFishCreationVisible,
    refreshBigFishGallery,
    refreshMatch3DGallery,
    refreshPuzzleGallery,
    refreshSquareHoleGallery,
    refreshVisualNovelGallery,
    selectionStage,
  ]);

  useEffect(() => {
    if (
      (platformBootstrap.platformTab === 'create' ||
        selectionStage === 'platform') &&
      platformBootstrap.canReadProtectedData
    ) {
      void refreshPuzzleShelf();
      void refreshMatch3DShelf();
      void refreshSquareHoleShelf();
      void refreshVisualNovelShelf();
    }
  }, [
    platformBootstrap.canReadProtectedData,
    platformBootstrap.platformTab,
    refreshMatch3DShelf,
    refreshPuzzleShelf,
    refreshSquareHoleShelf,
    refreshVisualNovelShelf,
    selectionStage,
  ]);

  useEffect(() => {
    if (
      isBigFishCreationVisible &&
      (platformBootstrap.platformTab === 'create' ||
        selectionStage === 'platform') &&
      platformBootstrap.canReadProtectedData
    ) {
      void refreshBigFishShelf();
    }
  }, [
    isBigFishCreationVisible,
    platformBootstrap.canReadProtectedData,
    platformBootstrap.platformTab,
    refreshBigFishShelf,
    selectionStage,
  ]);

  const renderCreationHubContent = (
    mode: 'start-only' | 'works-only',
    fallbackLabel: string,
  ) => (
    <Suspense fallback={<LazyPanelFallback label={fallbackLabel} />}>
      <CustomWorldCreationHub
        mode={mode}
        items={creationHubItems}
        loading={
          platformBootstrap.isLoadingPlatform ||
          isBigFishLoadingLibrary ||
          isMatch3DLoadingLibrary ||
          isSquareHoleLoadingLibrary ||
          isPuzzleLoadingLibrary ||
          isVisualNovelLoadingLibrary
        }
        error={
          platformBootstrap.isLoadingPlatform ||
          isBigFishLoadingLibrary ||
          isMatch3DLoadingLibrary ||
          isSquareHoleLoadingLibrary ||
          isPuzzleLoadingLibrary ||
          isVisualNovelLoadingLibrary
            ? null
            : (platformBootstrap.platformError ??
              sessionController.agentWorkspaceRestoreError ??
              bigFishError ??
              match3dError ??
              squareHoleError ??
              puzzleShelfError ??
              puzzleError ??
              visualNovelError)
        }
        onRetry={() => {
          platformBootstrap.setPlatformError(null);
          setBigFishError(null);
          setMatch3DError(null);
          setSquareHoleError(null);
          setPuzzleShelfError(null);
          setPuzzleCreationError(null);
          setPuzzleError(null);
          setVisualNovelError(null);
          void platformBootstrap.refreshCustomWorldWorks().catch((error) => {
            platformBootstrap.setPlatformError(
              resolveRpgCreationErrorMessage(error, '读取创作作品列表失败。'),
            );
          });
          if (isBigFishCreationVisible) {
            void refreshBigFishShelf();
          }
          void refreshMatch3DShelf();
          void refreshSquareHoleShelf();
          void refreshPuzzleShelf();
          void refreshVisualNovelShelf();
        }}
        createError={
          sessionController.creationTypeError ??
          bigFishError ??
          match3dError ??
          squareHoleError ??
          puzzleCreationError ??
          puzzleError ??
          visualNovelError
        }
        createBusy={
          sessionController.isCreatingAgentSession ||
          isCreativeAgentBusy ||
          isCreativeAgentStreaming ||
          isBigFishBusy ||
          isMatch3DBusy ||
          isSquareHoleBusy ||
          isPuzzleBusy ||
          isVisualNovelBusy ||
          isVisualNovelStreamingReply
        }
        onCreateType={handleCreationHubCreateType}
        onOpenDraft={(item) => {
          runProtectedAction(() => {
            void detailNavigation.handleOpenCreationWork(item);
          });
        }}
        onEnterPublished={(profileId) => {
          runProtectedAction(() => {
            const matchedWork = creationHubItems.find(
              (entry) => entry.profileId === profileId,
            );
            if (!matchedWork) {
              return;
            }
            void detailNavigation.handleOpenCreationWork(matchedWork);
          });
        }}
        onDeletePublished={(item) => {
          handleDeletePublishedWork(item);
        }}
        deletingWorkId={deletingCreationWorkId}
        rpgLibraryEntries={platformBootstrap.savedCustomWorldEntries}
        bigFishItems={isBigFishCreationVisible ? bigFishWorks : []}
        onOpenBigFishDetail={
          isBigFishCreationVisible
            ? (item) => {
                runProtectedAction(() => {
                  void openBigFishDraft(item);
                });
              }
            : undefined
        }
        onDeleteBigFish={
          isBigFishCreationVisible
            ? (item) => {
                handleDeleteBigFishWork(item);
              }
            : null
        }
        match3dItems={match3dWorks}
        onOpenMatch3DDetail={(item) => {
          runProtectedAction(() => {
            void openMatch3DDraft(item);
          });
        }}
        onDeleteMatch3D={(item) => {
          handleDeleteMatch3DWork(item);
        }}
        squareHoleItems={squareHoleWorks}
        onOpenSquareHoleDetail={(item) => {
          runProtectedAction(() => {
            void openSquareHoleDraft(item);
          });
        }}
        onDeleteSquareHole={(item) => {
          handleDeleteSquareHoleWork(item);
        }}
        puzzleItems={puzzleWorks}
        onOpenPuzzleDetail={(item) => {
          runProtectedAction(() => {
            void openPuzzleDraft(item);
          });
        }}
        onDeletePuzzle={(item) => {
          handleDeletePuzzleWork(item);
        }}
        onClaimPuzzlePointIncentive={(item) => {
          handleClaimPuzzlePointIncentive(item);
        }}
        claimingPuzzleProfileId={claimingPuzzlePointIncentiveProfileId}
        visualNovelItems={visualNovelWorks}
        onOpenVisualNovelDetail={(item) => {
          runProtectedAction(() => {
            void openVisualNovelDraft(item);
          });
        }}
        onDeleteVisualNovel={(item) => {
          handleDeleteVisualNovelWork(item);
        }}
      />
    </Suspense>
  );
  const creationStartContent = (
    <div className="platform-page-stage platform-remap-surface flex h-full min-h-0 flex-col overflow-hidden px-3 pb-3 pt-3 sm:px-4 sm:pt-4 xl:px-5 xl:pb-4 xl:pt-4">
      <div className="mx-auto flex h-full min-h-0 w-full max-w-5xl flex-col">
        <div className="shrink-0">
          <div
            className="flex snap-x gap-2 overflow-x-auto overscroll-x-contain pb-1 scrollbar-hide touch-pan-x [-webkit-overflow-scrolling:touch] sm:gap-3"
            role="tablist"
            aria-label="选择模板"
          >
            {getVisiblePlatformCreationTypes().map((item) => {
              const selected = item.id === 'puzzle';
              const disabled =
                item.locked ||
                sessionController.isCreatingAgentSession ||
                isCreativeAgentBusy ||
                isCreativeAgentStreaming ||
                isBigFishBusy ||
                isMatch3DBusy ||
                isSquareHoleBusy ||
                isPuzzleBusy ||
                isVisualNovelBusy ||
                isVisualNovelStreamingReply;

              return (
                <button
                  key={item.id}
                  type="button"
                  role="tab"
                  aria-label={item.title}
                  aria-selected={selected}
                  disabled={disabled}
                  onClick={() => {
                    if (item.id === 'puzzle') {
                      return;
                    }
                    handleCreationHubCreateType(item.id);
                  }}
                  className={`platform-creation-reference-card platform-interactive-card relative flex min-h-[4.75rem] w-[10.75rem] shrink-0 snap-start flex-col overflow-hidden rounded-[1.1rem] border p-0 text-left transition sm:min-h-[6.85rem] sm:w-[13.25rem] sm:rounded-[1.28rem] ${
                    selected
                      ? 'border-white/48 bg-black/12 text-white shadow-none ring-1 ring-inset ring-white/34'
                      : 'border-[var(--platform-subpanel-border)] bg-[var(--platform-subpanel-fill)] text-white hover:border-[var(--platform-surface-hover-border)]'
                  } ${disabled ? 'cursor-not-allowed opacity-55' : ''}`}
                >
                  <img
                    src={item.imageSrc}
                    alt=""
                    className="absolute inset-0 h-full w-full object-cover"
                    loading="lazy"
                  />
                  <span
                    className={`absolute inset-0 ${
                      selected
                        ? 'bg-[linear-gradient(90deg,rgba(3,7,18,0.58),rgba(3,7,18,0.08)),linear-gradient(180deg,rgba(3,7,18,0.02)_0%,rgba(3,7,18,0.16)_42%,rgba(3,7,18,0.84)_100%)]'
                        : item.locked
                          ? 'bg-[linear-gradient(90deg,rgba(3,7,18,0.58),rgba(3,7,18,0.16)),linear-gradient(180deg,rgba(3,7,18,0.08)_0%,rgba(3,7,18,0.2)_42%,rgba(3,7,18,0.84)_100%)]'
                          : 'bg-[linear-gradient(90deg,rgba(3,7,18,0.5),rgba(3,7,18,0.04)),linear-gradient(180deg,rgba(3,7,18,0.04)_0%,rgba(3,7,18,0.16)_42%,rgba(3,7,18,0.78)_100%)]'
                    }`}
                  />
                  <span className="relative z-10 flex min-h-5 items-center justify-end gap-2 px-3 pt-2.5 sm:px-4 sm:pt-3">
                    {item.locked ? (
                      <span className="platform-pill platform-pill--neutral border-white/30 bg-white/88 px-2.5 text-[10px] text-[var(--platform-text-soft)] sm:text-xs">
                        {item.badge}
                      </span>
                    ) : (
                      <ArrowRight className="h-4 w-4 text-white/82" />
                    )}
                  </span>
                  <span className="relative z-10 mt-auto block min-w-0 px-3 pb-2.5 pt-1 text-white [text-shadow:0_1px_8px_rgba(0,0,0,0.75)] sm:px-4 sm:pb-3 sm:pt-2.5">
                    <span className="block truncate text-base font-black leading-tight text-white sm:text-lg">
                      {item.title}
                    </span>
                    <span className="mt-0.5 block truncate text-[11px] font-semibold leading-4 text-white/90 sm:mt-1 sm:text-xs">
                      {item.subtitle}
                    </span>
                  </span>
                </button>
              );
            })}
          </div>
        </div>

        <div className="mt-3 min-h-0 flex-1 overflow-hidden">
          <Suspense fallback={<LazyPanelFallback label="正在加载拼图创作..." />}>
            <PuzzleAgentWorkspace
              session={puzzleSession}
              isBusy={isPuzzleBusy || isStreamingPuzzleReply}
              error={puzzleError}
              onBack={leavePuzzleFlow}
              onSubmitMessage={(payload) => {
                void submitPuzzleMessage(payload);
              }}
              onExecuteAction={(payload) => {
                executePuzzleWorkspaceAction(payload);
              }}
              initialFormPayload={puzzleFormDraftPayload}
              onCreateFromForm={(payload) => {
                runProtectedAction(() => {
                  void createPuzzleDraftFromForm(payload);
                });
              }}
              onAutoSaveForm={(payload) => {
                void savePuzzleFormDraft(payload);
              }}
              showBackButton={false}
              title={null}
            />
          </Suspense>
        </div>
      </div>
    </div>
  );
  const draftHubContent = renderCreationHubContent(
    'works-only',
    '正在加载草稿列表...',
  );

  return (
    <>
      <AnimatePresence mode="wait">
        {selectionStage === 'platform' && (
          <motion.div
            key="platform-home"
            initial={{ opacity: 0, y: 12 }}
            animate={{ opacity: 1, y: 0 }}
            exit={{ opacity: 0, y: -12 }}
            className="flex h-full min-h-0 min-w-0 flex-col overflow-hidden"
          >
            <PlatformEntryHomeView
              activeTab={platformBootstrap.platformTab}
              onTabChange={platformBootstrap.setPlatformTab}
              hasSavedGame={hasSavedGame}
              savedSnapshot={savedSnapshot}
              saveEntries={platformBootstrap.saveEntries}
              saveError={platformBootstrap.saveError}
              featuredEntries={featuredGalleryEntries}
              latestEntries={latestGalleryEntries}
              myEntries={platformBootstrap.savedCustomWorldEntries}
              historyEntries={platformBootstrap.historyEntries}
              profileDashboard={platformBootstrap.profileDashboard}
              isLoadingPlatform={platformBootstrap.isLoadingPlatform}
              isLoadingDashboard={platformBootstrap.isLoadingDashboard}
              isResumingSaveWorldKey={platformBootstrap.isResumingSaveWorldKey}
              platformError={
                platformBootstrap.isLoadingPlatform
                  ? null
                  : (platformBootstrap.platformError ??
                    sessionController.agentWorkspaceRestoreError)
              }
              dashboardError={
                platformBootstrap.isLoadingDashboard
                  ? null
                  : platformBootstrap.dashboardError
              }
              createTabContent={creationStartContent}
              draftTabContent={draftHubContent}
              onContinueGame={handleContinueGame}
              onResumeSave={(entry) => {
                if (
                  (entry.worldType ?? '').toLowerCase() === 'puzzle' ||
                  entry.worldKey.startsWith('puzzle:')
                ) {
                  void resumePuzzleSaveArchive(entry);
                  return;
                }
                void platformBootstrap.handleResumeSaveEntry(entry);
              }}
              onOpenCreateWorld={openCreationTypePicker}
              onOpenCreateTypePicker={openCreationTypePicker}
              onOpenGalleryDetail={openPublicGalleryDetail}
              onOpenRecommendGalleryDetail={openRecommendGalleryDetail}
              recommendRuntimeContent={recommendRuntimeContent}
              activeRecommendEntryKey={activeRecommendEntryKey}
              isStartingRecommendEntry={
                isStartingRecommendEntry ||
                isBigFishBusy ||
                isPuzzleBusy ||
                isMatch3DBusy ||
                isSquareHoleBusy ||
                isVisualNovelBusy
              }
              recommendRuntimeError={activeRecommendRuntimeError}
              onSelectNextRecommendEntry={() =>
                selectAdjacentRecommendRuntimeEntry(1)
              }
              onSelectPreviousRecommendEntry={() =>
                selectAdjacentRecommendRuntimeEntry(-1)
              }
              onOpenLibraryDetail={(entry) => {
                runProtectedAction(() => {
                  void detailNavigation.openLibraryDetail(entry);
                });
              }}
              onDeleteLibraryEntry={(entry) => {
                handleDeleteLibraryEntry(entry);
              }}
              deletingLibraryEntryId={deletingCreationWorkId}
              onSearchPublicCode={(keyword) => {
                void handlePublicCodeSearch(keyword);
              }}
              isSearchingPublicCode={isSearchingPublicCode}
              profilePlayStats={profilePlayStats}
              isProfilePlayStatsOpen={isProfilePlayStatsOpen}
              isProfilePlayStatsLoading={isProfilePlayStatsLoading}
              profilePlayStatsError={profilePlayStatsError}
              onCloseProfilePlayStats={() => {
                setIsProfilePlayStatsOpen(false);
              }}
              onOpenPlayedWork={openPlayedWork}
              onOpenFeedback={openProfileFeedback}
              onOpenProfileDashboardCard={(cardKey) => {
                if (cardKey === 'playedWorks') {
                  openProfilePlayedWorks();
                  return;
                }
                if (platformBootstrap.dashboardError) {
                  void platformBootstrap.refreshProfileDashboard();
                }
              }}
              onRechargeSuccess={platformBootstrap.refreshProfileDashboard}
            />
          </motion.div>
        )}

        {selectionStage === 'profile-feedback' && (
          <motion.div
            key="platform-profile-feedback"
            initial={{ opacity: 0, y: 12 }}
            animate={{ opacity: 1, y: 0 }}
            exit={{ opacity: 0, y: -12 }}
            className="flex h-full min-h-0 flex-col"
          >
            <PlatformFeedbackView
              onBack={() => {
                setPlatformTab('profile');
                setSelectionStage('platform');
              }}
              onSubmit={submitRpgProfileFeedback}
            />
          </motion.div>
        )}

        {selectionStage === 'work-detail' && selectedPublicWorkDetail && (
          <motion.div
            key="platform-work-detail"
            initial={{ opacity: 0, y: 12 }}
            animate={{ opacity: 1, y: 0 }}
            exit={{ opacity: 0, y: -12 }}
            className="flex h-full min-h-0 flex-col"
          >
            <PlatformWorkDetailView
              entry={selectedPublicWorkDetail}
              authorAvatarUrl={selectedPublicWorkAuthor?.avatarUrl ?? null}
              authorDisplayName={selectedPublicWorkAuthor?.displayName ?? null}
              isBusy={
                isPublicWorkDetailBusy ||
                isPuzzleBusy ||
                isBigFishBusy ||
                isMatch3DBusy ||
                isSquareHoleBusy ||
                isVisualNovelBusy
              }
              error={publicWorkDetailError}
              actionMode={selectedPublicWorkActionMode}
              visibleCoverCount={resolveVisiblePuzzleDetailCoverCount(
                selectedPublicWorkDetail,
                puzzleRun,
              )}
              onBack={() => {
                setPublicWorkDetailError(null);
                clearSelectedPublicWorkAuthor();
                setSelectionStage('platform');
              }}
              onLike={() => {
                likePublicWork(selectedPublicWorkDetail);
              }}
              onStart={startSelectedPublicWork}
              onRemix={remixSelectedPublicWork}
            />
          </motion.div>
        )}

        {selectionStage === 'detail' && (
          <motion.div
            key="platform-detail"
            initial={{ opacity: 0, y: 12 }}
            animate={{ opacity: 1, y: 0 }}
            exit={{ opacity: 0, y: -12 }}
            className="flex h-full min-h-0 flex-col"
          >
            {detailNavigation.isDetailLoading || !selectedDetailEntry ? (
              <div className="flex h-full items-center justify-center">
                <div className="platform-subpanel rounded-2xl px-5 py-4 text-sm text-[var(--platform-text-base)]">
                  {detailNavigation.detailError || '正在读取作品详情...'}
                </div>
              </div>
            ) : selectedDetailEntry.visibility !== 'draft' ? (
              <PlatformWorkDetailView
                entry={mapRpgGalleryCardToPublicWorkDetail(selectedDetailEntry)}
                authorAvatarUrl={selectedPublicWorkAuthor?.avatarUrl ?? null}
                authorDisplayName={
                  selectedPublicWorkAuthor?.displayName ?? null
                }
                isBusy={detailNavigation.isMutatingDetail}
                error={detailNavigation.detailError}
                actionMode={
                  detailNavigation.isSelectedWorldOwned ? 'edit' : 'remix'
                }
                onBack={() => {
                  detailNavigation.setDetailError(null);
                  clearSelectedPublicWorkAuthor();
                  entryNavigation.backToPlatformHome();
                }}
                onLike={() => {
                  likePublicWork(
                    mapRpgGalleryCardToPublicWorkDetail(selectedDetailEntry),
                  );
                }}
                onStart={handleStartSelectedWorld}
                onRemix={() => {
                  const publicWorkEntry =
                    mapRpgGalleryCardToPublicWorkDetail(selectedDetailEntry);
                  if (detailNavigation.isSelectedWorldOwned) {
                    editOwnedPublicWork(publicWorkEntry);
                    return;
                  }
                  remixPublicWork(publicWorkEntry);
                }}
              />
            ) : (
              <PlatformEntryWorldDetailView
                entry={selectedDetailEntry}
                isMutating={detailNavigation.isMutatingDetail}
                error={detailNavigation.detailError}
                onBack={() => {
                  detailNavigation.setDetailError(null);
                  entryNavigation.backToPlatformHome();
                }}
                onStartGame={handleStartSelectedWorld}
                onContinueEdit={
                  detailNavigation.isSelectedWorldOwned
                    ? () => {
                        runProtectedAction(() => {
                          void detailNavigation.openSavedCustomWorldEditor(
                            selectedDetailEntry,
                          );
                        });
                      }
                    : null
                }
                onPublish={
                  selectedDetailEntry.visibility === 'draft' &&
                  detailNavigation.isSelectedWorldOwned
                    ? () => {
                        runProtectedAction(() => {
                          void detailNavigation.handlePublishSelectedWorld();
                        });
                      }
                    : null
                }
                onUnpublish={
                  selectedDetailEntry.visibility !== 'draft' &&
                  detailNavigation.isSelectedWorldOwned
                    ? () => {
                        runProtectedAction(() => {
                          void detailNavigation.handleUnpublishSelectedWorld();
                        });
                      }
                    : null
                }
                onDelete={
                  detailNavigation.isSelectedWorldOwned
                    ? () => {
                        runProtectedAction(() => {
                          void detailNavigation.handleDeleteSelectedWorld();
                        });
                      }
                    : null
                }
              />
            )}
          </motion.div>
        )}

        {selectionStage === 'agent-workspace' && (
          <motion.div
            key="agent-workspace"
            initial={{ opacity: 0, y: 12 }}
            animate={{ opacity: 1, y: 0 }}
            exit={{ opacity: 0, y: -12 }}
            className="flex h-full min-h-0 flex-col"
          >
            <Suspense
              fallback={
                <LazyPanelFallback label="正在加载 Agent 共创工作区..." />
              }
            >
              {sessionController.agentSession ? (
                <CustomWorldAgentWorkspace
                  session={sessionController.agentSession}
                  activeOperation={sessionController.agentOperation}
                  streamingReplyText={sessionController.streamingAgentReplyText}
                  isStreamingReply={sessionController.isStreamingAgentReply}
                  onBack={leaveAgentWorkspace}
                  onSubmitMessage={(payload) => {
                    void sessionController.submitAgentMessage(payload);
                  }}
                  onExecuteAction={(payload) => {
                    void sessionController.executeAgentAction(payload);
                  }}
                />
              ) : (
                <div className="flex h-full items-center justify-center">
                  <div className="platform-subpanel rounded-2xl px-5 py-4 text-sm text-[var(--platform-text-base)]">
                    {sessionController.isLoadingAgentSession
                      ? '正在准备 Agent 共创工作区...'
                      : sessionController.agentWorkspaceRestoreError ||
                        '正在恢复创作工作区...'}
                  </div>
                </div>
              )}
            </Suspense>
          </motion.div>
        )}

        {selectionStage === 'big-fish-agent-workspace' && (
          <motion.div
            key="big-fish-agent-workspace"
            initial={{ opacity: 0, y: 12 }}
            animate={{ opacity: 1, y: 0 }}
            exit={{ opacity: 0, y: -12 }}
            className="flex h-full min-h-0 flex-col"
          >
            <Suspense
              fallback={
                <LazyPanelFallback label="正在加载大鱼吃小鱼共创工作区..." />
              }
            >
              <BigFishAgentWorkspace
                session={bigFishSession}
                streamingReplyText={streamingBigFishReplyText}
                isStreamingReply={isStreamingBigFishReply}
                isBusy={isBigFishBusy || isStreamingBigFishReply}
                error={bigFishError}
                onBack={leaveBigFishFlow}
                onSubmitMessage={(payload) => {
                  void submitBigFishMessage(payload);
                }}
                onExecuteAction={(payload) => {
                  void executeBigFishAction(payload);
                }}
              />
            </Suspense>
          </motion.div>
        )}

        {selectionStage === 'big-fish-generating' && (
          <motion.div
            key="big-fish-generating"
            initial={{ opacity: 0, y: 12 }}
            animate={{ opacity: 1, y: 0 }}
            exit={{ opacity: 0, y: -12 }}
            className="flex h-full min-h-0 flex-col"
          >
            <Suspense
              fallback={
                <LazyPanelFallback label="正在加载大鱼吃小鱼生成面板..." />
              }
            >
              <CustomWorldGenerationView
                settingText={
                  bigFishSession?.lastAssistantReply ?? '正在整理当前玩法草稿。'
                }
                anchorEntries={buildBigFishGenerationAnchorEntries(
                  bigFishSession,
                )}
                progress={buildMiniGameDraftGenerationProgress(
                  bigFishGenerationState,
                )}
                isGenerating={isBigFishBusy}
                error={bigFishError}
                onBack={leaveBigFishFlow}
                onEditSetting={() => {
                  setSelectionStage('big-fish-agent-workspace');
                }}
                onRetry={() => {
                  void executeBigFishAction({
                    action: 'big_fish_compile_draft',
                  });
                }}
                onInterrupt={undefined}
                backLabel="返回创作中心"
                settingActionLabel={null}
                retryLabel="重新生成草稿"
                settingTitle="当前玩法信息"
                settingDescription={null}
                progressTitle="大鱼吃小鱼草稿生成进度"
                activeBadgeLabel="草稿生成中"
                pausedBadgeLabel="草稿生成已暂停"
                idleBadgeLabel="等待返回工作区"
              />
            </Suspense>
          </motion.div>
        )}

        {selectionStage === 'big-fish-result' && bigFishSession?.draft && (
          <motion.div
            key="big-fish-result"
            initial={{ opacity: 0, y: 12 }}
            animate={{ opacity: 1, y: 0 }}
            exit={{ opacity: 0, y: -12 }}
            className="flex h-full min-h-0 flex-col"
          >
            <Suspense
              fallback={<LazyPanelFallback label="正在加载玩法结果页..." />}
            >
              <BigFishResultView
                session={bigFishSession}
                isBusy={isBigFishBusy}
                error={bigFishError}
                onBack={() => {
                  setSelectionStage('big-fish-agent-workspace');
                }}
                onDismissError={() => {
                  setBigFishError(null);
                }}
                onExecuteAction={(payload) => {
                  void executeBigFishAction(payload);
                }}
                onStartTestRun={() => {
                  void startBigFishRun();
                }}
              />
            </Suspense>
          </motion.div>
        )}

        {selectionStage === 'big-fish-runtime' && (
          <motion.div
            key="big-fish-runtime"
            initial={{ opacity: 0 }}
            animate={{ opacity: 1 }}
            exit={{ opacity: 0 }}
            className="fixed inset-0 z-[100]"
          >
            <Suspense
              fallback={<LazyPanelFallback label="正在加载大鱼吃小鱼玩法..." />}
            >
              <BigFishRuntimeShell
                run={bigFishRun}
                assetSlots={bigFishSession?.assetSlots ?? []}
                shareTitle={bigFishRuntimeShare?.title ?? null}
                sharePublicWorkCode={
                  bigFishRuntimeShare?.publicWorkCode ?? null
                }
                isBusy={isBigFishBusy}
                error={bigFishError}
                onBack={() => {
                  reportBigFishObservedPlayTime();
                  setSelectionStage(bigFishRuntimeReturnStage);
                }}
                onRestart={() => {
                  reportBigFishObservedPlayTime();
                  void restartBigFishRun();
                }}
                onSubmitInput={submitBigFishInput}
              />
            </Suspense>
          </motion.div>
        )}

        {selectionStage === 'match3d-agent-workspace' && (
          <motion.div
            key="match3d-agent-workspace"
            initial={{ opacity: 0, y: 12 }}
            animate={{ opacity: 1, y: 0 }}
            exit={{ opacity: 0, y: -12 }}
            className="flex h-full min-h-0 flex-col"
          >
            <Suspense
              fallback={
                <LazyPanelFallback label="正在加载抓大鹅共创工作区..." />
              }
            >
              <Match3DAgentWorkspace
                session={match3dSession}
                streamingReplyText={streamingMatch3DReplyText}
                isStreamingReply={isStreamingMatch3DReply}
                isBusy={isMatch3DBusy || isStreamingMatch3DReply}
                error={match3dError}
                onBack={leaveMatch3DFlow}
                onSubmitMessage={(payload) => {
                  void submitMatch3DMessage(payload);
                }}
                onExecuteAction={(payload) => {
                  void executeMatch3DAction(payload);
                }}
              />
            </Suspense>
          </motion.div>
        )}

        {selectionStage === 'match3d-result' && match3dSession?.draft && (
          <motion.div
            key="match3d-result"
            initial={{ opacity: 0, y: 12 }}
            animate={{ opacity: 1, y: 0 }}
            exit={{ opacity: 0, y: -12 }}
            className="flex h-full min-h-0 flex-col"
          >
            <Suspense
              fallback={<LazyPanelFallback label="正在加载抓大鹅结果..." />}
            >
              <Match3DResultView
                profile={
                  match3dProfile ??
                  buildMatch3DProfileFromSession(match3dSession)!
                }
                draft={match3dSession.draft}
                isBusy={isMatch3DBusy}
                error={match3dError}
                onBack={() => {
                  setSelectionStage('match3d-agent-workspace');
                }}
                onSaved={(profile) => {
                  setMatch3DProfile(profile);
                }}
                onPublished={(profile) => {
                  setMatch3DProfile(profile);
                  void Promise.allSettled([
                    refreshMatch3DShelf(),
                    refreshMatch3DGallery(),
                  ]);
                  openPublicWorkDetail(
                    mapMatch3DWorkToPublicWorkDetail(profile),
                  );
                  openPublishShareModal({
                    title: profile.gameName,
                    publicWorkCode: buildMatch3DPublicWorkCode(
                      profile.profileId,
                    ),
                    stage: 'work-detail',
                  });
                }}
                onStartTestRun={(profile) => {
                  setMatch3DProfile(profile);
                  void startMatch3DRunFromProfile(profile, 'match3d-result');
                }}
              />
            </Suspense>
          </motion.div>
        )}

        {selectionStage === 'match3d-runtime' && (
          <motion.div
            key="match3d-runtime"
            initial={{ opacity: 0 }}
            animate={{ opacity: 1 }}
            exit={{ opacity: 0 }}
            className="fixed inset-0 z-[100]"
          >
            <Suspense
              fallback={<LazyPanelFallback label="正在加载抓大鹅玩法..." />}
            >
              <Match3DRuntimeShell
                run={match3dRun}
                isBusy={isMatch3DBusy}
                error={match3dError}
                onBack={() => {
                  if (match3dRun?.runId && match3dRun.status === 'running') {
                    void stopMatch3DRun(match3dRun.runId).catch(
                      () => undefined,
                    );
                  }
                  setSelectionStage(match3dRuntimeReturnStage);
                }}
                onRestart={() => {
                  if (!match3dRun?.runId || isMatch3DBusy) {
                    return;
                  }

                  match3dFlow.setIsBusy(true);
                  setMatch3DError(null);
                  void restartMatch3DRun(match3dRun.runId)
                    .then(({ run }) => {
                      setMatch3DRun(run);
                    })
                    .catch((error) => {
                      setMatch3DError(
                        resolveMatch3DErrorMessage(
                          error,
                          '重新开始抓大鹅玩法失败。',
                        ),
                      );
                    })
                    .finally(() => {
                      match3dFlow.setIsBusy(false);
                    });
                }}
                onOptimisticRunChange={setMatch3DRun}
                onClickItem={(payload) => {
                  const runId = payload.runId ?? match3dRun?.runId;
                  if (!runId) {
                    return Promise.reject(
                      new Error('抓大鹅运行态缺少 runId。'),
                    );
                  }
                  return clickMatch3DItem(runId, payload);
                }}
                onTimeExpired={() => {
                  if (!match3dRun?.runId) {
                    return;
                  }

                  void finishMatch3DTimeUp(match3dRun.runId)
                    .then(({ run }) => {
                      setMatch3DRun(run);
                    })
                    .catch((error) => {
                      setMatch3DError(
                        resolveMatch3DErrorMessage(
                          error,
                          '同步抓大鹅倒计时失败。',
                        ),
                      );
                    });
                }}
              />
            </Suspense>
          </motion.div>
        )}

        {selectionStage === 'square-hole-agent-workspace' && (
          <motion.div
            key="square-hole-agent-workspace"
            initial={{ opacity: 0, y: 12 }}
            animate={{ opacity: 1, y: 0 }}
            exit={{ opacity: 0, y: -12 }}
            className="flex h-full min-h-0 flex-col"
          >
            <Suspense
              fallback={
                <LazyPanelFallback label="正在加载方洞挑战共创工作区..." />
              }
            >
              <SquareHoleAgentWorkspace
                session={squareHoleSession}
                streamingReplyText={streamingSquareHoleReplyText}
                isStreamingReply={isStreamingSquareHoleReply}
                isBusy={isSquareHoleBusy || isStreamingSquareHoleReply}
                error={squareHoleError}
                onBack={leaveSquareHoleFlow}
                onSubmitMessage={(payload) => {
                  void submitSquareHoleMessage(payload);
                }}
                onExecuteAction={(payload) => {
                  void executeSquareHoleAction(payload);
                }}
              />
            </Suspense>
          </motion.div>
        )}

        {selectionStage === 'square-hole-generating' && (
          <motion.div
            key="square-hole-generating"
            initial={{ opacity: 0, y: 12 }}
            animate={{ opacity: 1, y: 0 }}
            exit={{ opacity: 0, y: -12 }}
            className="flex h-full min-h-0 flex-col"
          >
            <Suspense
              fallback={
                <LazyPanelFallback label="正在加载方洞挑战生成面板..." />
              }
            >
              <CustomWorldGenerationView
                settingText={
                  squareHoleSession?.lastAssistantReply ??
                  '正在整理当前方洞挑战草稿。'
                }
                anchorEntries={buildSquareHoleGenerationAnchorEntries(
                  squareHoleSession,
                )}
                progress={buildMiniGameDraftGenerationProgress(
                  squareHoleGenerationState,
                )}
                isGenerating={isSquareHoleBusy}
                error={squareHoleError}
                onBack={leaveSquareHoleFlow}
                onEditSetting={() => {
                  setSelectionStage('square-hole-agent-workspace');
                }}
                onRetry={retrySquareHoleAssetGeneration}
                onInterrupt={undefined}
                backLabel="返回创作中心"
                settingActionLabel={null}
                retryLabel="重新生成图片"
                settingTitle="当前方洞挑战"
                settingDescription={null}
                progressTitle="方洞挑战图片生成进度"
                activeBadgeLabel="图片生成中"
                pausedBadgeLabel="图片生成已暂停"
                idleBadgeLabel="等待返回结果页"
              />
            </Suspense>
          </motion.div>
        )}

        {selectionStage === 'square-hole-result' &&
          squareHoleSession?.draft && (
            <motion.div
              key="square-hole-result"
              initial={{ opacity: 0, y: 12 }}
              animate={{ opacity: 1, y: 0 }}
              exit={{ opacity: 0, y: -12 }}
              className="flex h-full min-h-0 flex-col"
            >
              <Suspense
                fallback={<LazyPanelFallback label="正在加载方洞挑战结果..." />}
              >
                <SquareHoleResultView
                  profile={
                    squareHoleProfile ??
                    buildSquareHoleProfileFromSession(squareHoleSession)!
                  }
                  draft={squareHoleSession.draft}
                  isBusy={isSquareHoleBusy}
                  error={squareHoleError}
                  onBack={() => {
                    setSelectionStage('square-hole-agent-workspace');
                  }}
                  onSaved={(profile) => {
                    setSquareHoleProfile(profile);
                  }}
                  onPublished={(profile) => {
                    setSquareHoleProfile(profile);
                    void Promise.allSettled([
                      refreshSquareHoleShelf(),
                      refreshSquareHoleGallery(),
                    ]);
                    openPublicWorkDetail(
                      mapSquareHoleWorkToPublicWorkDetail(profile),
                    );
                    openPublishShareModal({
                      title: profile.gameName,
                      publicWorkCode: buildSquareHolePublicWorkCode(
                        profile.profileId,
                      ),
                      stage: 'work-detail',
                    });
                  }}
                  onStartTestRun={(profile) => {
                    setSquareHoleProfile(profile);
                    void startSquareHoleRunFromProfile(
                      profile,
                      'square-hole-result',
                    );
                  }}
                />
              </Suspense>
            </motion.div>
          )}

        {selectionStage === 'square-hole-runtime' && (
          <motion.div
            key="square-hole-runtime"
            initial={{ opacity: 0 }}
            animate={{ opacity: 1 }}
            exit={{ opacity: 0 }}
            className="fixed inset-0 z-[100]"
          >
            <Suspense
              fallback={<LazyPanelFallback label="正在加载方洞挑战玩法..." />}
            >
              <SquareHoleRuntimeShell
                run={squareHoleRun}
                isBusy={isSquareHoleBusy}
                error={squareHoleError}
                onBack={() => {
                  if (
                    squareHoleRun?.runId &&
                    squareHoleRun.status.toLowerCase() === 'running'
                  ) {
                    void stopSquareHoleRun(squareHoleRun.runId).catch(
                      () => undefined,
                    );
                  }
                  setSelectionStage(squareHoleRuntimeReturnStage);
                }}
                onRestart={() => {
                  if (!squareHoleRun?.runId || isSquareHoleBusy) {
                    return;
                  }

                  squareHoleFlow.setIsBusy(true);
                  setSquareHoleError(null);
                  void restartSquareHoleRun(squareHoleRun.runId)
                    .then(({ run }) => {
                      setSquareHoleRun(run);
                    })
                    .catch((error) => {
                      setSquareHoleError(
                        resolveSquareHoleErrorMessage(
                          error,
                          '重新开始方洞挑战失败。',
                        ),
                      );
                    })
                    .finally(() => {
                      squareHoleFlow.setIsBusy(false);
                    });
                }}
                onOptimisticRunChange={setSquareHoleRun}
                onDropShape={(payload) => {
                  const runId = payload.runId ?? squareHoleRun?.runId;
                  if (!runId) {
                    return Promise.reject(
                      new Error('方洞挑战运行态缺少 runId。'),
                    );
                  }
                  return dropSquareHoleShape(runId, payload);
                }}
                onTimeExpired={() => {
                  if (!squareHoleRun?.runId) {
                    return;
                  }

                  void finishSquareHoleTimeUp(squareHoleRun.runId)
                    .then(({ run }) => {
                      setSquareHoleRun(run);
                    })
                    .catch((error) => {
                      setSquareHoleError(
                        resolveSquareHoleErrorMessage(
                          error,
                          '同步方洞挑战倒计时失败。',
                        ),
                      );
                    });
                }}
              />
            </Suspense>
          </motion.div>
        )}

        {selectionStage === 'creative-agent-workspace' && (
          <motion.div
            key="creative-agent-workspace"
            initial={{ opacity: 0, y: 12 }}
            animate={{ opacity: 1, y: 0 }}
            exit={{ opacity: 0, y: -12 }}
            className="flex h-full min-h-0 flex-col"
          >
            <Suspense
              fallback={<LazyPanelFallback label="正在加载智能创作..." />}
            >
              <CreativeAgentWorkspace
                session={creativeAgentSession}
                isBusy={isCreativeAgentBusy || isPuzzleBusy}
                isStreaming={isCreativeAgentStreaming}
                error={creativeAgentError}
                eventLog={creativeAgentEvents}
                onBack={leaveCreativeAgentWorkspace}
                onSubmitMessage={(payload) => {
                  void submitCreativeAgentMessage(payload);
                }}
                onConfirmTemplate={(selection) => {
                  void confirmCreativeTemplateSelection(selection);
                }}
                onOpenTarget={() => {
                  void openCreativeAgentTarget();
                }}
              />
            </Suspense>
          </motion.div>
        )}

        {selectionStage === 'puzzle-agent-workspace' && (
          <motion.div
            key="puzzle-agent-workspace"
            initial={{ opacity: 0, y: 12 }}
            animate={{ opacity: 1, y: 0 }}
            exit={{ opacity: 0, y: -12 }}
            className="flex h-full min-h-0 flex-col"
          >
            <Suspense
              fallback={<LazyPanelFallback label="正在加载拼图创作..." />}
            >
              <PuzzleAgentWorkspace
                session={puzzleSession}
                isBusy={isPuzzleBusy || isStreamingPuzzleReply}
                error={puzzleError}
                onBack={leavePuzzleFlow}
                onSubmitMessage={(payload) => {
                  void submitPuzzleMessage(payload);
                }}
                onExecuteAction={(payload) => {
                  executePuzzleWorkspaceAction(payload);
                }}
                initialFormPayload={puzzleFormDraftPayload}
                onCreateFromForm={(payload) => {
                  void createPuzzleDraftFromForm(payload);
                }}
                onAutoSaveForm={(payload) => {
                  void savePuzzleFormDraft(payload);
                }}
              />
            </Suspense>
          </motion.div>
        )}

        {selectionStage === 'puzzle-onboarding' && (
          <motion.div
            key="puzzle-onboarding"
            initial={{ opacity: 0 }}
            animate={{ opacity: 1 }}
            exit={{ opacity: 0 }}
            className="fixed inset-0 z-[100]"
          >
            <PuzzleOnboardingView
              prompt={puzzleOnboardingPrompt}
              phase={puzzleOnboardingPhase}
              error={puzzleOnboardingError}
              onPromptChange={setPuzzleOnboardingPrompt}
              onSubmit={() => {
                void submitPuzzleOnboardingPrompt();
              }}
              onSkip={skipPuzzleOnboarding}
            />
          </motion.div>
        )}

        {selectionStage === 'puzzle-generating' && (
          <motion.div
            key="puzzle-generating"
            initial={{ opacity: 0, y: 12 }}
            animate={{ opacity: 1, y: 0 }}
            exit={{ opacity: 0, y: -12 }}
            className="flex h-full min-h-0 flex-col"
          >
            <Suspense
              fallback={<LazyPanelFallback label="正在加载拼图生成面板..." />}
            >
              <CustomWorldGenerationView
                settingText={
                  puzzleSession?.lastAssistantReply ?? '正在整理当前拼图草稿。'
                }
                anchorEntries={buildPuzzleGenerationAnchorEntries(
                  puzzleSession,
                  puzzleFormDraftPayload,
                )}
                progress={buildMiniGameDraftGenerationProgress(
                  puzzleGenerationState,
                  puzzleGenerationProgressNowMs,
                )}
                isGenerating={isPuzzleBusy}
                error={puzzleError}
                onBack={leavePuzzleFlow}
                onEditSetting={() => {
                  setSelectionStage('puzzle-agent-workspace');
                }}
                onRetry={retryPuzzleDraftGeneration}
                onInterrupt={undefined}
                backLabel="返回创作中心"
                settingActionLabel={null}
                retryLabel="重新生成草稿"
                settingTitle="当前拼图信息"
                settingDescription={null}
                progressTitle="拼图草稿生成进度"
                activeBadgeLabel="草稿生成中"
                pausedBadgeLabel="草稿生成已暂停"
                idleBadgeLabel="等待返回工作区"
              />
            </Suspense>
          </motion.div>
        )}

        {selectionStage === 'puzzle-result' &&
          puzzleSession?.draft &&
          !isPuzzleFormOnlyDraft(puzzleSession) && (
            <motion.div
              key="puzzle-result"
              initial={{ opacity: 0, y: 12 }}
              animate={{ opacity: 1, y: 0 }}
              exit={{ opacity: 0, y: -12 }}
              className="flex h-full min-h-0 flex-col"
            >
              <Suspense
                fallback={<LazyPanelFallback label="正在加载拼图结果..." />}
              >
                <PuzzleResultView
                  session={puzzleSession}
                  profileId={
                    puzzleSession.publishedProfileId ??
                    buildPuzzleResultProfileId(puzzleSession.sessionId)
                  }
                  isBusy={isPuzzleBusy}
                  error={puzzleError}
                  onBack={leavePuzzleFlow}
                  onExecuteAction={(payload) => {
                    void executePuzzleAction(payload);
                  }}
                  onStartTestRun={startPuzzleTestRunFromDraft}
                  creativeDraftEdit={
                    activeCreativeAgentSessionId
                      ? {
                          isBusy: isCreativeDraftEditBusy,
                          error: creativeDraftEditError,
                          onSubmit: submitCreativeDraftEdit,
                        }
                      : null
                  }
                />
              </Suspense>
            </motion.div>
          )}

        {selectionStage === 'visual-novel-agent-workspace' && (
          <motion.div
            key="visual-novel-agent-workspace"
            initial={{ opacity: 0, y: 12 }}
            animate={{ opacity: 1, y: 0 }}
            exit={{ opacity: 0, y: -12 }}
            className="flex h-full min-h-0 flex-col"
          >
            <Suspense
              fallback={<LazyPanelFallback label="正在加载视觉小说创作..." />}
            >
              <VisualNovelAgentWorkspace
                session={visualNovelSession}
                isBusy={isVisualNovelBusy || isVisualNovelStreamingReply}
                error={visualNovelError}
                streamingReplyText={visualNovelStreamingReplyText}
                onBack={leaveVisualNovelFlow}
                onCreateSession={(payload) => {
                  void visualNovelFlow.openWorkspace(payload);
                }}
                onSubmitMessage={(payload) => {
                  void visualNovelFlow.submitMessage(payload);
                }}
                onExecuteAction={(payload) => {
                  void visualNovelFlow.executeAction(payload);
                }}
                onOpenResult={openVisualNovelResult}
              />
            </Suspense>
          </motion.div>
        )}

        {selectionStage === 'visual-novel-result' &&
          visualNovelSession?.draft && (
            <motion.div
              key="visual-novel-result"
              initial={{ opacity: 0, y: 12 }}
              animate={{ opacity: 1, y: 0 }}
              exit={{ opacity: 0, y: -12 }}
              className="flex h-full min-h-0 flex-col"
            >
              <Suspense
                fallback={<LazyPanelFallback label="正在加载视觉小说结果..." />}
              >
                <VisualNovelResultView
                  draft={visualNovelWork?.draft ?? visualNovelSession.draft}
                  isBusy={isVisualNovelBusy}
                  error={visualNovelError}
                  onBack={() => {
                    setSelectionStage('visual-novel-agent-workspace');
                  }}
                  onSaveDraft={(draft) => {
                    void saveVisualNovelDraft(draft);
                  }}
                  onCompileWorkProfile={(draft) => {
                    void compileVisualNovelDraft(draft);
                  }}
                  onStartTestRun={(draft) => {
                    void startVisualNovelTestRunFromDraft(draft);
                  }}
                  onPublish={(draft) => {
                    void publishVisualNovelDraft(draft);
                  }}
                />
              </Suspense>
            </motion.div>
          )}

        {selectionStage === 'visual-novel-runtime' && (
          <motion.div
            key="visual-novel-runtime"
            initial={{ opacity: 0 }}
            animate={{ opacity: 1 }}
            exit={{ opacity: 0 }}
            className="fixed inset-0 z-[100]"
          >
            <Suspense
              fallback={<LazyPanelFallback label="正在加载视觉小说试玩..." />}
            >
              <VisualNovelRuntimeShell
                draft={visualNovelWork?.draft ?? visualNovelSession?.draft}
                run={visualNovelRun}
                isBusy={isVisualNovelBusy}
                error={visualNovelError}
                onBack={() => {
                  setSelectionStage(visualNovelRuntimeReturnStage);
                }}
                onSubmitAction={(payload) => {
                  void submitVisualNovelRuntimeAction(payload);
                }}
              />
            </Suspense>
          </motion.div>
        )}

        {selectionStage === 'visual-novel-gallery-detail' &&
          visualNovelWork && (
            <motion.div
              key="visual-novel-gallery-detail"
              initial={{ opacity: 0, y: 12 }}
              animate={{ opacity: 1, y: 0 }}
              exit={{ opacity: 0, y: -12 }}
              className="flex h-full min-h-0 flex-col"
            >
              <Suspense
                fallback={<LazyPanelFallback label="正在加载视觉小说详情..." />}
              >
                <VisualNovelResultView
                  draft={visualNovelWork.draft}
                  isBusy={isVisualNovelBusy}
                  error={visualNovelError}
                  onBack={() => {
                    setSelectionStage('platform');
                  }}
                  onStartTestRun={(draft) => {
                    setVisualNovelRuntimeReturnStage(
                      'visual-novel-gallery-detail',
                    );
                    void startVisualNovelTestRunFromDraft(draft);
                  }}
                />
              </Suspense>
            </motion.div>
          )}

        {selectionStage === 'puzzle-gallery-detail' && selectedPuzzleDetail && (
          <motion.div
            key="puzzle-gallery-detail"
            initial={{ opacity: 0, y: 12 }}
            animate={{ opacity: 1, y: 0 }}
            exit={{ opacity: 0, y: -12 }}
            className="flex h-full min-h-0 flex-col"
          >
            <Suspense
              fallback={<LazyPanelFallback label="正在加载拼图详情..." />}
            >
              <PuzzleGalleryDetailView
                item={selectedPuzzleDetail}
                isBusy={isPuzzleBusy}
                error={puzzleError}
                onBack={() => {
                  platformBootstrap.setPlatformTab(
                    puzzleDetailReturnTarget?.tab ?? 'home',
                  );
                  setPuzzleDetailReturnTarget(null);
                  setSelectionStage('platform');
                }}
                onEdit={
                  selectedPuzzleDetail.ownerUserId === authUi?.user?.id &&
                  Boolean(selectedPuzzleDetail.sourceSessionId?.trim())
                    ? () => {
                        runProtectedAction(() => {
                          void openPuzzleDraft(selectedPuzzleDetail);
                        });
                      }
                    : null
                }
                onStartGame={() => {
                  void startPuzzleRunFromProfile(
                    selectedPuzzleDetail.profileId,
                    'puzzle-gallery-detail',
                    selectedPuzzleDetail,
                    false,
                    null,
                    { authMode: 'isolated' },
                  );
                }}
              />
            </Suspense>
          </motion.div>
        )}

        {selectionStage === 'puzzle-runtime' && (
          <motion.div
            key="puzzle-runtime"
            initial={{ opacity: 0 }}
            animate={{ opacity: 1 }}
            exit={{ opacity: 0 }}
            className="fixed inset-0 z-[100]"
          >
            <Suspense
              fallback={<LazyPanelFallback label="正在加载拼图玩法..." />}
            >
              <PuzzleRuntimeShell
                run={puzzleRun}
                isBusy={
                  isPuzzleBusy ||
                  isPuzzleNextLevelGenerating ||
                  isPuzzleLeaderboardBusy
                }
                error={puzzleError}
                hideBackButton={Boolean(puzzleOnboardingDraft)}
                onBack={() => {
                  setSelectionStage(puzzleRuntimeReturnStage);
                }}
                onRemodelWork={
                  selectedPuzzleDetail?.publicationStatus === 'published'
                    ? remodelCurrentPuzzleRuntimeWork
                    : undefined
                }
                onSwapPieces={(payload) => {
                  void swapPuzzlePiecesInRun(payload);
                }}
                onDragPiece={(payload) => {
                  void dragPuzzlePiece(payload);
                }}
                onAdvanceNextLevel={(target) => {
                  void advancePuzzleLevel(target);
                }}
                onRestartLevel={() => {
                  void restartPuzzleCurrentLevel();
                }}
                onPauseChange={setPuzzleRuntimePaused}
                onUseProp={usePuzzleProp}
                onTimeExpired={syncPuzzleRuntimeTimeout}
              />
            </Suspense>
            {isPuzzleNextLevelGenerating ? (
              <div className="fixed inset-0 z-[120] flex items-center justify-center bg-slate-950/62 px-5 backdrop-blur-sm">
                <div className="flex max-w-[18rem] flex-col items-center gap-3 rounded-[1.5rem] border border-white/12 bg-slate-950/92 px-6 py-5 text-center text-white shadow-[0_28px_80px_rgba(0,0,0,0.35)]">
                  <Loader2 className="h-6 w-6 animate-spin text-amber-200" />
                  <div className="text-sm font-bold">正在准备下一关</div>
                  <div className="text-xs leading-5 text-white/68">
                    广场暂无可接续作品，正在生成新的候选图。
                  </div>
                </div>
              </div>
            ) : null}
            {puzzleOnboardingDraft &&
            puzzleRun?.currentLevel?.status === 'cleared' ? (
              <PuzzleOnboardingLoginOverlay
                isSaving={isPuzzleOnboardingSaving}
                error={puzzleOnboardingError}
                onLogin={requestPuzzleOnboardingLogin}
              />
            ) : null}
          </motion.div>
        )}

        {selectionStage === 'custom-world-generating' && (
          <motion.div
            key="custom-world-generating"
            initial={{ opacity: 0, y: 12 }}
            animate={{ opacity: 1, y: 0 }}
            exit={{ opacity: 0, y: -12 }}
            className="flex h-full min-h-0 flex-col"
          >
            <Suspense
              fallback={<LazyPanelFallback label="正在加载世界生成面板..." />}
            >
              <CustomWorldGenerationView
                settingText={sessionController.agentDraftSettingPreview}
                anchorEntries={sessionController.agentDraftAnchorPreviewEntries}
                progress={sessionController.agentDraftGenerationProgress}
                isGenerating={sessionController.isActiveGenerationRunning}
                error={sessionController.activeGenerationError}
                onBack={leaveAgentDraftGeneration}
                onEditSetting={leaveAgentDraftGeneration}
                onRetry={() => {
                  void sessionController.executeAgentAction({
                    action: 'draft_foundation',
                  });
                }}
                onInterrupt={undefined}
                backLabel="返回工作区"
                settingActionLabel={null}
                retryLabel="继续生成草稿"
                settingTitle="当前世界信息"
                settingDescription={null}
                progressTitle="世界草稿生成进度"
                activeBadgeLabel="草稿编译中"
                pausedBadgeLabel="草稿生成已暂停"
                idleBadgeLabel="等待返回工作区"
              />
            </Suspense>
          </motion.div>
        )}

        {selectionStage === 'custom-world-result' &&
          sessionController.generatedCustomWorldProfile && (
            <motion.div
              key="custom-world-result"
              initial={{ opacity: 0, y: 12 }}
              animate={{ opacity: 1, y: 0 }}
              exit={{ opacity: 0, y: -12 }}
              className="flex h-full min-h-0 flex-col"
            >
              <Suspense
                fallback={<LazyPanelFallback label="正在加载世界编辑器..." />}
              >
                <RpgCreationResultView
                  profile={sessionController.generatedCustomWorldProfile}
                  previewCharacters={previewCustomWorldCharacters}
                  isGenerating={false}
                  progress={0}
                  progressLabel=""
                  error={resultViewError}
                  onProfileChange={(profile) => {
                    sessionController.setGeneratedCustomWorldProfile(profile);
                  }}
                  onBack={
                    sessionController.isAgentDraftResultView
                      ? () => {
                          leaveAgentDraftResult();
                        }
                      : leaveCustomWorldResult
                  }
                  onEditSetting={undefined}
                  onRegenerate={undefined}
                  onContinueExpand={undefined}
                  onEnterWorld={() => {
                    runProtectedAction(() => {
                      void enterWorldCoordinator
                        .enterWorldFromCurrentResult()
                        .catch((error) => {
                          sessionController.setCustomWorldError(
                            resolveRpgCreationErrorMessage(
                              error,
                              '发布并进入世界失败。',
                            ),
                          );
                        });
                    });
                  }}
                  onTestWorld={
                    sessionController.isAgentDraftResultView &&
                    sessionController.agentSession?.stage !== 'published'
                      ? () => {
                          runProtectedAction(() => {
                            void enterWorldCoordinator
                              .enterWorldForTestFromCurrentResult()
                              .catch((error) => {
                                sessionController.setCustomWorldError(
                                  resolveRpgCreationErrorMessage(
                                    error,
                                    '进入作品测试失败。',
                                  ),
                                );
                              });
                          });
                        }
                      : undefined
                  }
                  onPublishWorld={
                    sessionController.isAgentDraftResultView &&
                    sessionController.agentSession?.stage !== 'published'
                      ? async () => {
                          try {
                            const publishedProfile =
                              await enterWorldCoordinator.publishCurrentResult();
                            void openRpgPublishShareModal(publishedProfile);
                          } catch (error) {
                            sessionController.setCustomWorldError(
                              resolveRpgCreationErrorMessage(
                                error,
                                '发布到广场失败。',
                              ),
                            );
                            throw error;
                          }
                        }
                      : undefined
                  }
                  onGenerateEntity={
                    sessionController.isAgentDraftResultView
                      ? async (kind) => {
                          const action =
                            kind === 'landmark'
                              ? 'generate_landmarks'
                              : 'generate_characters';
                          await autosaveCoordinator.executeAgentActionAndWait({
                            action,
                            count: 1,
                            ...(kind === 'playable'
                              ? { roleType: 'playable' as const }
                              : kind === 'story'
                                ? { roleType: 'story' as const }
                                : {}),
                          });
                          const latestView =
                            sessionController.activeAgentSessionId
                              ? await sessionController.syncAgentCreationResultView(
                                  sessionController.activeAgentSessionId,
                                )
                              : null;
                          const latestProfile =
                            rpgCreationPreviewAdapter.buildPreviewFromResultView(
                              latestView,
                            );
                          if (latestProfile) {
                            sessionController.setGeneratedCustomWorldProfile(
                              latestProfile,
                            );
                          }
                          return { profile: latestProfile };
                        }
                      : undefined
                  }
                  onDeleteEntities={
                    sessionController.isAgentDraftResultView
                      ? async (kind, ids) => {
                          if (ids.length === 0) return;
                          await autosaveCoordinator.executeAgentActionAndWait(
                            kind === 'story'
                              ? { action: 'delete_characters', roleIds: ids }
                              : { action: 'delete_landmarks', sceneIds: ids },
                          );
                          const latestView =
                            sessionController.activeAgentSessionId
                              ? await sessionController.syncAgentCreationResultView(
                                  sessionController.activeAgentSessionId,
                                )
                              : null;
                          const latestProfile =
                            rpgCreationPreviewAdapter.buildPreviewFromResultView(
                              latestView,
                            );
                          if (latestProfile) {
                            sessionController.setGeneratedCustomWorldProfile(
                              latestProfile,
                            );
                          }
                        }
                      : undefined
                  }
                  readOnly={false}
                  compactAgentResultMode={
                    sessionController.isAgentDraftResultView
                  }
                  backLabel={
                    sessionController.isAgentDraftResultView
                      ? '返回创作'
                      : undefined
                  }
                  editActionLabel="继续调整设定"
                  enterWorldActionLabel={
                    sessionController.isAgentDraftResultView &&
                    sessionController.agentSession?.stage !== 'published'
                      ? '发布并进入世界'
                      : '进入世界'
                  }
                  publishReady={
                    sessionController.isAgentDraftResultView
                      ? agentResultPublishGateView.publishReady
                      : true
                  }
                  publishBlockers={
                    sessionController.isAgentDraftResultView
                      ? agentResultPublishGateView.blockers
                      : []
                  }
                  qualityFindings={
                    sessionController.isAgentDraftResultView
                      ? agentResultPreviewQualityFindings
                      : []
                  }
                  previewSourceLabel={
                    sessionController.isAgentDraftResultView
                      ? agentResultPreviewSourceLabel
                      : null
                  }
                  autoSaveState={autosaveCoordinator.customWorldAutoSaveState}
                />
              </Suspense>
            </motion.div>
          )}
      </AnimatePresence>

      <PlatformEntryCreationTypeModal
        isOpen={showCreationTypeModal}
        isBusy={
          sessionController.isCreatingAgentSession ||
          isCreativeAgentBusy ||
          isCreativeAgentStreaming ||
          isBigFishBusy ||
          isMatch3DBusy ||
          isSquareHoleBusy ||
          isPuzzleBusy ||
          isVisualNovelBusy ||
          isVisualNovelStreamingReply
        }
        error={
          bigFishError ??
          creativeAgentError ??
          match3dError ??
          squareHoleError ??
          puzzleCreationError ??
          visualNovelError ??
          puzzleError ??
          sessionController.creationTypeError
        }
        onClose={() => {
          if (
            sessionController.isCreatingAgentSession ||
            isCreativeAgentBusy ||
            isCreativeAgentStreaming ||
            isBigFishBusy ||
            isMatch3DBusy ||
            isSquareHoleBusy ||
            isPuzzleBusy ||
            isVisualNovelBusy ||
            isVisualNovelStreamingReply
          ) {
            return;
          }
          setShowCreationTypeModal(false);
        }}
        onSelectRpg={() => {
          runProtectedAction(() => {
            void sessionController.openRpgAgentWorkspace();
          });
        }}
        onSelectBigFish={() => {
          runProtectedAction(() => {
            void openBigFishAgentWorkspace();
          });
        }}
        onSelectMatch3D={() => {
          runProtectedAction(() => {
            void openMatch3DAgentWorkspace();
          });
        }}
        onSelectSquareHole={() => {
          runProtectedAction(() => {
            void openSquareHoleAgentWorkspace();
          });
        }}
        onSelectPuzzle={() => {
          runProtectedAction(() => {
            void openPuzzleAgentWorkspace();
          });
        }}
        onSelectCreativeAgent={() => {
          runProtectedAction(() => {
            void openCreativeAgentWorkspace();
          });
        }}
        onSelectVisualNovel={() => {
          runProtectedAction(() => {
            openVisualNovelAgentWorkspace();
          });
        }}
      />
      <PublishShareModal
        open={Boolean(publishSharePayload)}
        payload={publishSharePayload}
        onClose={() => setPublishSharePayload(null)}
      />
      <UnifiedModal
        open={Boolean(pendingDeleteCreationWork)}
        title="删除作品"
        description={
          pendingDeleteCreationWork
            ? `确认删除《${pendingDeleteCreationWork.title}》吗？`
            : undefined
        }
        onClose={closeDeleteCreationWorkConfirmation}
        closeDisabled={Boolean(deletingCreationWorkId)}
        closeOnBackdrop={!deletingCreationWorkId}
        size="sm"
        overlayClassName={`platform-theme ${platformThemeClass} !items-center`}
        panelClassName="platform-remap-surface rounded-[1.75rem]"
        footer={
          <>
            <button
              type="button"
              onClick={closeDeleteCreationWorkConfirmation}
              disabled={Boolean(deletingCreationWorkId)}
              className="platform-button platform-button--ghost min-h-0 rounded-full px-4 py-2 text-sm"
            >
              取消
            </button>
            <button
              type="button"
              onClick={confirmDeleteCreationWork}
              disabled={Boolean(deletingCreationWorkId)}
              className="platform-button platform-button--danger min-h-0 rounded-full px-4 py-2 text-sm disabled:cursor-not-allowed disabled:opacity-55"
            >
              {deletingCreationWorkId ? '删除中' : '确认删除'}
            </button>
          </>
        }
      >
        <div className="text-sm leading-6 text-[var(--platform-text-base)]">
          {pendingDeleteCreationWork?.detail}
        </div>
      </UnifiedModal>
      <AnimatePresence>
        {(searchedPublicUser || publicSearchError) && (
          <motion.div
            initial={{ opacity: 0 }}
            animate={{ opacity: 1 }}
            exit={{ opacity: 0 }}
            className="fixed inset-0 z-50 flex items-center justify-center bg-black/45 p-4"
          >
            <div className="platform-surface w-full max-w-md rounded-[1.6rem] p-5">
              <div className="flex items-start justify-between gap-4">
                <div>
                  <div className="text-sm font-semibold text-[var(--platform-text-soft)]">
                    公开编号搜索
                  </div>
                  <div className="mt-1 text-xl font-black text-[var(--platform-text-strong)]">
                    {publicSearchError ? '未找到结果' : '命中用户'}
                  </div>
                </div>
                <button
                  type="button"
                  onClick={() => {
                    setSearchedPublicUser(null);
                    setPublicSearchError(null);
                  }}
                  className="platform-icon-button"
                  aria-label="关闭搜索结果"
                >
                  ×
                </button>
              </div>
              {publicSearchError ? (
                <div className="mt-4 text-sm leading-6 text-[var(--platform-text-soft)]">
                  {publicSearchError}
                </div>
              ) : searchedPublicUser ? (
                <div className="mt-4 rounded-[1.2rem] border border-[var(--platform-line-soft)] p-4">
                  <div className="text-lg font-bold text-[var(--platform-text-strong)]">
                    {searchedPublicUser.displayName}
                  </div>
                  <div className="mt-2 text-sm text-[var(--platform-text-soft)]">
                    百梦号 {searchedPublicUser.publicUserCode}
                  </div>
                </div>
              ) : null}
            </div>
          </motion.div>
        )}
      </AnimatePresence>
    </>
  );
}

export default PlatformEntryFlowShellImpl;
