import { ArrowLeft, ArrowRight, Clock, Loader2, Settings, Sparkles, Trophy, X, } from 'lucide-react'; import { useCallback, useEffect, useId, useMemo, useRef, useState, } from 'react'; import { createPortal } from 'react-dom'; import puzzleLevelLogo from '../../../media/logo.png'; import type { DragPuzzlePieceRequest, PuzzleBoardSnapshot, PuzzleCellPosition, PuzzleMergedGroupState, PuzzleRecommendedNextWork, PuzzleRunSnapshot, PuzzleRuntimeLevelSnapshot, PuzzleRuntimePropKind, SwapPuzzlePiecesRequest, } from '../../../packages/shared/src/contracts/puzzleRuntimeSession'; import { useResolvedAssetReadUrl } from '../../hooks/useResolvedAssetReadUrl'; import { createRuntimeDragInputController, createRuntimeInputPointFromClient, readRuntimeInputElementBounds, resolveRuntimeInputGridCell, type RuntimeDragInputSession, type RuntimeInputPoint, } from '../../services/input-devices'; import { resolvePuzzleUiBackgroundSource } from '../../services/puzzle-runtime/puzzleUiBackgroundSource'; import { buildPuzzleUiSpriteBackgroundStyle, buildPuzzleUiSpriteHitZoneStyle, loadPuzzleUiSpritesheetLayout, type PuzzleUiSpriteKind, type PuzzleUiSpritesheetLayout, } from '../../services/puzzle-runtime/puzzleUiSpritesheetParser'; import { DEFAULT_RUNTIME_LEVEL_AUDIO_CONFIG, playRuntimeClickSound, playRuntimeCountdownSound, playRuntimeLevelClearSound, resolveRuntimeCountdownSecondBucket, } from '../../services/runtimeAudioFeedback'; import { useAuthUi } from '../auth/AuthUiContext'; import { RuntimeResourcePendingMarker } from '../common/RuntimeResourcePendingMarker'; import { ResolvedAssetImage } from '../ResolvedAssetImage'; import { buildMergedGroupOutlinePath, buildRoundedGridCellClipPath, resolveDraggedMergedGroupLayer, resolveDraggedPieceCellLayer, resolveDraggedPieceLayer, sanitizeSvgId, } from './puzzleRuntimeShape'; type PuzzleRuntimeShellProps = { run: PuzzleRunSnapshot | null; isBusy?: boolean; error?: string | null; hideBackButton?: boolean; hideExitControls?: boolean; embedded?: boolean; onBack: () => void; onRemodelWork?: (profileId: string) => void | Promise; onSwapPieces: (payload: SwapPuzzlePiecesRequest) => void; onDragPiece: (payload: DragPuzzlePieceRequest) => void; onAdvanceNextLevel: (target?: PuzzleNextLevelTarget) => void; onRestartLevel?: () => void | Promise; onPauseChange?: (paused: boolean) => void | Promise; onUseProp?: ( propKind: PuzzleRuntimePropKind, ) => Promise; onTimeExpired?: () => void | Promise; }; export type PuzzleNextLevelTarget = { profileId?: string; levelId?: string | null; }; type PuzzleBoardPieceViewModel = { pieceId: string; row: number; col: number; correctRow: number; correctCol: number; mergedGroupId: string | null; }; type PuzzleMergedGroupViewModel = { groupId: string; pieceIds: string[]; anchorPieceId: string; minRow: number; minCol: number; rowSpan: number; colSpan: number; pieces: Array< PuzzleBoardPieceViewModel & { localRow: number; localCol: number; } >; }; function boardCellKey(position: PuzzleCellPosition) { return `${position.row}:${position.col}`; } function buildBoardCells(board: PuzzleBoardSnapshot) { return Array.from({ length: board.rows * board.cols }, (_, index) => ({ row: Math.floor(index / board.cols), col: index % board.cols, })); } function PuzzleUiSprite({ src, kind, layout, className = '', withHitZone = false, }: { src: string | null; kind: PuzzleUiSpriteKind; layout: PuzzleUiSpritesheetLayout | null; className?: string; withHitZone?: boolean; }) { if (!src) { return null; } return (