blacktrigram 0.7.39 → 0.7.41

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (354) hide show
  1. package/lib/App2.js.map +1 -1
  2. package/lib/audio/AudioAssetLoader.js.map +1 -1
  3. package/lib/audio/AudioAssetRegistry.js.map +1 -1
  4. package/lib/audio/AudioCache.js.map +1 -1
  5. package/lib/audio/AudioManager.js.map +1 -1
  6. package/lib/audio/AudioMonitor.js.map +1 -1
  7. package/lib/audio/AudioPool.js.map +1 -1
  8. package/lib/audio/AudioProvider.js.map +1 -1
  9. package/lib/audio/AudioUtils.js.map +1 -1
  10. package/lib/audio/BoneImpactAudioMap.js.map +1 -1
  11. package/lib/audio/VariantSelector.js.map +1 -1
  12. package/lib/audio/types.js.map +1 -1
  13. package/lib/components/screens/combat/CombatScreen3D.js.map +1 -1
  14. package/lib/components/screens/combat/components/controls/CombatButtons.js.map +1 -1
  15. package/lib/components/screens/combat/components/controls/CombatControlsPanel.js.map +1 -1
  16. package/lib/components/screens/combat/components/controls/ControlsGuide.js.map +1 -1
  17. package/lib/components/screens/combat/components/controls/KeyboardHints.js.map +1 -1
  18. package/lib/components/screens/combat/components/controls/PauseMenu.js.map +1 -1
  19. package/lib/components/screens/combat/components/controls/PauseMenuButton.js.map +1 -1
  20. package/lib/components/screens/combat/components/controls/QuickSettings.js.map +1 -1
  21. package/lib/components/screens/combat/components/effects/BloodDecals3D.js.map +1 -1
  22. package/lib/components/screens/combat/components/effects/BloodLossOverlayHtml.js.map +1 -1
  23. package/lib/components/screens/combat/components/effects/BloodParticles3D.js.map +1 -1
  24. package/lib/components/screens/combat/components/effects/BloodViscosity3D.js.map +1 -1
  25. package/lib/components/screens/combat/components/effects/CombatParticleEffects3D.js.map +1 -1
  26. package/lib/components/screens/combat/components/effects/ConsciousnessBlur.js.map +1 -1
  27. package/lib/components/screens/combat/components/effects/InternalDamage3D.js.map +1 -1
  28. package/lib/components/screens/combat/components/effects/PainVignette.js.map +1 -1
  29. package/lib/components/screens/combat/components/effects/ParticleAudio3D.js.map +1 -1
  30. package/lib/components/screens/combat/components/effects/TraumaOverlay3D.js.map +1 -1
  31. package/lib/components/screens/combat/components/feedback/MatchCountdown.js.map +1 -1
  32. package/lib/components/screens/combat/components/feedback/RoundAnnouncementOverlayHtml.js.map +1 -1
  33. package/lib/components/screens/combat/components/feedback/RoundDisplayStatus.js.map +1 -1
  34. package/lib/components/screens/combat/components/feedback/RoundStartAnnouncementOverlayHtml.js.map +1 -1
  35. package/lib/components/screens/combat/components/hud/CombatBottomHUD.js.map +1 -1
  36. package/lib/components/screens/combat/components/hud/CombatLeftHUD.js.map +1 -1
  37. package/lib/components/screens/combat/components/hud/CombatPortraitStatusStrip.js.map +1 -1
  38. package/lib/components/screens/combat/components/hud/CombatRightHUD.js.map +1 -1
  39. package/lib/components/screens/combat/components/hud/CombatTopHUD.js.map +1 -1
  40. package/lib/components/screens/combat/components/hud/DifficultyIndicator.js.map +1 -1
  41. package/lib/components/screens/combat/components/hud/FPSMonitor.js.map +1 -1
  42. package/lib/components/screens/combat/components/hud/MobileControlsWrapper.js.map +1 -1
  43. package/lib/components/screens/combat/components/hud/PlayerStateOverlayHtml.js.map +1 -1
  44. package/lib/components/screens/combat/components/indicators/BalanceIndicator.js.map +1 -1
  45. package/lib/components/screens/combat/components/indicators/InputBufferDisplay.js.map +1 -1
  46. package/lib/components/screens/combat/components/indicators/StaminaWarning.js.map +1 -1
  47. package/lib/components/screens/combat/components/indicators/TechniqueNameDisplay.js.map +1 -1
  48. package/lib/components/screens/combat/helpers/AnimationUpdater.js.map +1 -1
  49. package/lib/components/screens/combat/helpers/combatHelpers.js.map +1 -1
  50. package/lib/components/screens/combat/hooks/useAICombat.js.map +1 -1
  51. package/lib/components/screens/combat/hooks/useCombatActions.js.map +1 -1
  52. package/lib/components/screens/combat/hooks/useCombatAttackMovement.js.map +1 -1
  53. package/lib/components/screens/combat/hooks/useCombatAudio.js.map +1 -1
  54. package/lib/components/screens/combat/hooks/useCombatLayout.js.map +1 -1
  55. package/lib/components/screens/combat/hooks/useCombatState.js.map +1 -1
  56. package/lib/components/screens/controls/ControlsScreen3D.js.map +1 -1
  57. package/lib/components/screens/controls/components/ControlBindingsOverlayHtml.js.map +1 -1
  58. package/lib/components/screens/controls/components/ControlCategoryTabsOverlayHtml.js.map +1 -1
  59. package/lib/components/screens/controls/components/GamepadVisualization3D.js.map +1 -1
  60. package/lib/components/screens/controls/components/InteractiveControlDemoOverlayHtml.js.map +1 -1
  61. package/lib/components/screens/controls/components/Key3D.js.map +1 -1
  62. package/lib/components/screens/controls/components/VisualKeyboard3D.js.map +1 -1
  63. package/lib/components/screens/controls/constants/ControlsConstants.js.map +1 -1
  64. package/lib/components/screens/controls/hooks/useControlsState.js.map +1 -1
  65. package/lib/components/screens/endscreen/EndScreen3D.js.map +1 -1
  66. package/lib/components/screens/endscreen/components/DefeatAnimation3D.js.map +1 -1
  67. package/lib/components/screens/endscreen/components/MatchStatisticsDisplayOverlayHtml.js.map +1 -1
  68. package/lib/components/screens/endscreen/components/NavigationButtonsOverlayHtml.js.map +1 -1
  69. package/lib/components/screens/endscreen/components/PerformanceBreakdownOverlayHtml.js.map +1 -1
  70. package/lib/components/screens/endscreen/components/PerformanceRatingOverlayHtml.js.map +1 -1
  71. package/lib/components/screens/endscreen/components/VictoryAnimation3D.js.map +1 -1
  72. package/lib/components/screens/endscreen/components/WinnerDisplayOverlayHtml.js.map +1 -1
  73. package/lib/components/screens/intro/IntroScreen3D.js +1 -1
  74. package/lib/components/screens/intro/IntroScreen3D.js.map +1 -1
  75. package/lib/components/screens/intro/components/AbilityListOverlayHtml.js.map +1 -1
  76. package/lib/components/screens/intro/components/ArchetypeCardGridOverlayHtml.js.map +1 -1
  77. package/lib/components/screens/intro/components/ArchetypeCardOverlayHtml.js.map +1 -1
  78. package/lib/components/screens/intro/components/ArchetypeDisplayOverlayHtml.js.map +1 -1
  79. package/lib/components/screens/intro/components/EnhancedArchetypeDisplayOverlayHtml.js.map +1 -1
  80. package/lib/components/screens/intro/components/MenuButtonsOverlayHtml.js.map +1 -1
  81. package/lib/components/screens/intro/components/MenuSectionOverlayHtml.js.map +1 -1
  82. package/lib/components/screens/intro/components/StatBarOverlayHtml.js.map +1 -1
  83. package/lib/components/screens/philosophy/PhilosophyScreen3D.js.map +1 -1
  84. package/lib/components/screens/training/TrainingScreen3D.js.map +1 -1
  85. package/lib/components/screens/training/components/AnatomyControlsOverlayHtml.js.map +1 -1
  86. package/lib/components/screens/training/components/AnatomyOverlay3D.js.map +1 -1
  87. package/lib/components/screens/training/components/FootPlacementMarkers3D.js.map +1 -1
  88. package/lib/components/screens/training/components/FootworkDrillsOverlayHtml.js.map +1 -1
  89. package/lib/components/screens/training/components/HitFeedbackEffect3D.js.map +1 -1
  90. package/lib/components/screens/training/components/TrainingButtonsOverlayHtml.js.map +1 -1
  91. package/lib/components/screens/training/components/TrainingControlsOverlayHtml.js.map +1 -1
  92. package/lib/components/screens/training/components/TrainingDummy3D.js.map +1 -1
  93. package/lib/components/screens/training/components/TrainingFeedbackOverlayHtml.js.map +1 -1
  94. package/lib/components/screens/training/components/TrainingModeSelectorOverlayHtml.js.map +1 -1
  95. package/lib/components/screens/training/components/TrainingStatsOverlayHtml.js.map +1 -1
  96. package/lib/components/screens/training/components/VitalPointMarker3D.js.map +1 -1
  97. package/lib/components/screens/training/components/VitalPointTrainingOverlayHtml.js.map +1 -1
  98. package/lib/components/screens/training/components/hud/TrainingBottomHUD.js.map +1 -1
  99. package/lib/components/screens/training/components/hud/TrainingLeftHUD.js.map +1 -1
  100. package/lib/components/screens/training/components/hud/TrainingRightHUD.js.map +1 -1
  101. package/lib/components/screens/training/components/hud/TrainingTopHUD.js.map +1 -1
  102. package/lib/components/screens/training/hooks/useAttackMovement.js.map +1 -1
  103. package/lib/components/screens/training/hooks/useTrainingActions.js.map +1 -1
  104. package/lib/components/screens/training/hooks/useTrainingLayout.js.map +1 -1
  105. package/lib/components/screens/training/hooks/useTrainingState.js.map +1 -1
  106. package/lib/components/shared/base/BaseButton.js.map +1 -1
  107. package/lib/components/shared/base/BaseButtonOverlayHtml.js.map +1 -1
  108. package/lib/components/shared/base/BasePanel.js.map +1 -1
  109. package/lib/components/shared/base/BaseText.js.map +1 -1
  110. package/lib/components/shared/base/useKoreanTheme.js.map +1 -1
  111. package/lib/components/shared/debug/PerformanceDebugOverlayHtml.js.map +1 -1
  112. package/lib/components/shared/mobile/ActionButtons.js.map +1 -1
  113. package/lib/components/shared/mobile/GestureRecognizerPure.js.map +1 -1
  114. package/lib/components/shared/mobile/HapticController.js.map +1 -1
  115. package/lib/components/shared/mobile/MobileControlsPure.js.map +1 -1
  116. package/lib/components/shared/mobile/StanceWheelPure.js.map +1 -1
  117. package/lib/components/shared/mobile/TouchOptimizer.js.map +1 -1
  118. package/lib/components/shared/mobile/VirtualDPad.js.map +1 -1
  119. package/lib/components/shared/three/anatomy/BodySurface.js.map +1 -1
  120. package/lib/components/shared/three/anatomy/BoneAttachedMuscles.js.map +1 -1
  121. package/lib/components/shared/three/anatomy/BoneClothing.js.map +1 -1
  122. package/lib/components/shared/three/anatomy/BoneRenderer.js.map +1 -1
  123. package/lib/components/shared/three/anatomy/Face3D.js.map +1 -1
  124. package/lib/components/shared/three/anatomy/Foot3D.js.map +1 -1
  125. package/lib/components/shared/three/anatomy/Hand3D.js.map +1 -1
  126. package/lib/components/shared/three/effects/ActionFeedback.js.map +1 -1
  127. package/lib/components/shared/three/effects/DamageNumbers.js.map +1 -1
  128. package/lib/components/shared/three/effects/HitEffects3D.js.map +1 -1
  129. package/lib/components/shared/three/effects/PlayerStateIndicators.js.map +1 -1
  130. package/lib/components/shared/three/effects/StanceSymbol3D.js.map +1 -1
  131. package/lib/components/shared/three/effects/StanceTransitionEffect.js.map +1 -1
  132. package/lib/components/shared/three/effects/VitalPointMarkers3D.js.map +1 -1
  133. package/lib/components/shared/three/indicators/ElementalColorSystem.js.map +1 -1
  134. package/lib/components/shared/three/indicators/GuardIndicator.js.map +1 -1
  135. package/lib/components/shared/three/indicators/HapticFeedback.js.map +1 -1
  136. package/lib/components/shared/three/indicators/StanceChangeIndicator.js.map +1 -1
  137. package/lib/components/shared/three/models/Player3DWithTransitions.js.map +1 -1
  138. package/lib/components/shared/three/models/SkeletalPlayer3D.js.map +1 -1
  139. package/lib/components/shared/three/optimization/AdaptiveQuality.js.map +1 -1
  140. package/lib/components/shared/three/scene/AtmosphericParticles3D.js.map +1 -1
  141. package/lib/components/shared/three/scene/BackgroundScene3D.js.map +1 -1
  142. package/lib/components/shared/three/scene/CombatArena3D.js.map +1 -1
  143. package/lib/components/shared/three/scene/KoreanSignage3D.js.map +1 -1
  144. package/lib/components/shared/three/ui/ArchetypeCard.js.map +1 -1
  145. package/lib/components/shared/three/ui/BodyPartHealthDisplay.js.map +1 -1
  146. package/lib/components/shared/three/ui/BreathingIndicator2.js.map +1 -1
  147. package/lib/components/shared/three/ui/CombatReadinessBar.js.map +1 -1
  148. package/lib/components/shared/three/ui/ComboCounter.js.map +1 -1
  149. package/lib/components/shared/three/ui/HealthBar.js.map +1 -1
  150. package/lib/components/shared/three/ui/KoreanButton.js.map +1 -1
  151. package/lib/components/shared/three/ui/KoreanPanel.js.map +1 -1
  152. package/lib/components/shared/three/ui/KoreanText.js.map +1 -1
  153. package/lib/components/shared/three/ui/MenuList.js.map +1 -1
  154. package/lib/components/shared/three/ui/PlayerHUD.js.map +1 -1
  155. package/lib/components/shared/three/ui/ProgressBar.js.map +1 -1
  156. package/lib/components/shared/three/ui/SpeedIndicatorHUD.js.map +1 -1
  157. package/lib/components/shared/three/ui/StaminaBar.js.map +1 -1
  158. package/lib/components/shared/three/ui/TechniqueBar.js.map +1 -1
  159. package/lib/components/shared/three/ui/TechniqueCard.js.map +1 -1
  160. package/lib/components/shared/three/ui/VitalPointOverlayControlsHtml.js.map +1 -1
  161. package/lib/components/shared/ui/BackButton.js.map +1 -1
  162. package/lib/components/shared/ui/BaseHUDContainer.js.map +1 -1
  163. package/lib/components/shared/ui/CombatTimer.js.map +1 -1
  164. package/lib/components/shared/ui/ErrorModal.js.map +1 -1
  165. package/lib/components/shared/ui/LoadingState.js.map +1 -1
  166. package/lib/components/shared/ui/SplashScreen.js +2 -2
  167. package/lib/components/shared/ui/SplashScreen.js.map +1 -1
  168. package/lib/components/shared/ui/VitalPointOverlayControlsPure.js.map +1 -1
  169. package/lib/components/shared/ui/VolumeControl.js.map +1 -1
  170. package/lib/components/shared/ui/shared/ConfirmDialog.js.map +1 -1
  171. package/lib/components/ui/combat/BalanceIndicatorOverlayHtml.js.map +1 -1
  172. package/lib/constants/bodyDimensions.js.map +1 -1
  173. package/lib/constants/bodyRenderingConstants.js.map +1 -1
  174. package/lib/data/archetypeClothing.js.map +1 -1
  175. package/lib/data/archetypePhysicalAttributes.js.map +1 -1
  176. package/lib/data/techniqueMappings.js.map +1 -1
  177. package/lib/data/techniques.js.map +1 -1
  178. package/lib/hooks/useActionFeedback.js.map +1 -1
  179. package/lib/hooks/useBalanceAnimations.js.map +1 -1
  180. package/lib/hooks/useCombatTimer.js.map +1 -1
  181. package/lib/hooks/useDebounce.js.map +1 -1
  182. package/lib/hooks/useHUDLayout.js.map +1 -1
  183. package/lib/hooks/useHandPoseTransitions.js.map +1 -1
  184. package/lib/hooks/useKeyboardControls.js.map +1 -1
  185. package/lib/hooks/useMatchCountdown.js.map +1 -1
  186. package/lib/hooks/useMuscleActivation.js.map +1 -1
  187. package/lib/hooks/usePauseMenu.js.map +1 -1
  188. package/lib/hooks/usePlayerAnimation.js.map +1 -1
  189. package/lib/hooks/useResponsiveLayout.js.map +1 -1
  190. package/lib/hooks/useRoundTransition.js.map +1 -1
  191. package/lib/hooks/useSkeletalAnimation.js.map +1 -1
  192. package/lib/hooks/useTechniqueSelection.js.map +1 -1
  193. package/lib/hooks/useThrottle.js.map +1 -1
  194. package/lib/hooks/useTouchControls.js.map +1 -1
  195. package/lib/hooks/useWebGLContextLossHandler.js.map +1 -1
  196. package/lib/hooks/useWindowSize.js.map +1 -1
  197. package/lib/systems/CombatSystem.js.map +1 -1
  198. package/lib/systems/EffectCalculator.js.map +1 -1
  199. package/lib/systems/LayoutSystem.js.map +1 -1
  200. package/lib/systems/PlayerEffectManager.js.map +1 -1
  201. package/lib/systems/ResponsiveScaling.js.map +1 -1
  202. package/lib/systems/TrigramSystem.js.map +1 -1
  203. package/lib/systems/VitalPointSystem.js.map +1 -1
  204. package/lib/systems/ai/AIPersonality.js.map +1 -1
  205. package/lib/systems/ai/AdaptiveDifficulty.js.map +1 -1
  206. package/lib/systems/ai/ArchetypeEnforcer.js.map +1 -1
  207. package/lib/systems/ai/ComboSystem.js.map +1 -1
  208. package/lib/systems/ai/DecisionTree.js.map +1 -1
  209. package/lib/systems/ai/TrainingAI.js.map +1 -1
  210. package/lib/systems/ai/types.js.map +1 -1
  211. package/lib/systems/animation/builders/AnimationBuilder.js.map +1 -1
  212. package/lib/systems/animation/builders/HandPoseApplicator.js.map +1 -1
  213. package/lib/systems/animation/builders/HandPoses.js.map +1 -1
  214. package/lib/systems/animation/builders/KeyframeConfig.js.map +1 -1
  215. package/lib/systems/animation/builders/KeyframeInterpolation.js +3 -90
  216. package/lib/systems/animation/builders/KeyframeInterpolation.js.map +1 -1
  217. package/lib/systems/animation/builders/KickPhaseApplicator.js.map +1 -1
  218. package/lib/systems/animation/builders/KoreanGuardPositions.js.map +1 -1
  219. package/lib/systems/animation/builders/MartialArtsAnimationBuilder.js.map +1 -1
  220. package/lib/systems/animation/builders/MartialArtsConstants.js.map +1 -1
  221. package/lib/systems/animation/builders/MartialPoseApplicator.js.map +1 -1
  222. package/lib/systems/animation/builders/PunchPhaseApplicator.js.map +1 -1
  223. package/lib/systems/animation/builders/SkeletonRig.js.map +1 -1
  224. package/lib/systems/animation/builders/TrigramGuardApplicator.js.map +1 -1
  225. package/lib/systems/animation/catalogs/DefensiveAnimations.js.map +1 -1
  226. package/lib/systems/animation/catalogs/FootworkSkeletalAnimations.js.map +1 -1
  227. package/lib/systems/animation/catalogs/RecoveryAnimations.js.map +1 -1
  228. package/lib/systems/animation/catalogs/StanceAnimations.js.map +1 -1
  229. package/lib/systems/animation/catalogs/StanceAttackAnimations.js.map +1 -1
  230. package/lib/systems/animation/catalogs/StanceGuardPoses.js.map +1 -1
  231. package/lib/systems/animation/catalogs/StanceIdleAnimations.js.map +1 -1
  232. package/lib/systems/animation/catalogs/StanceLocomotionAnimations.js.map +1 -1
  233. package/lib/systems/animation/catalogs/StepSkeletalAnimations.js.map +1 -1
  234. package/lib/systems/animation/core/AnimationHitTiming.js.map +1 -1
  235. package/lib/systems/animation/core/AnimationOptimizations.js.map +1 -1
  236. package/lib/systems/animation/core/AnimationPriority.js.map +1 -1
  237. package/lib/systems/animation/core/AnimationRegistry.js.map +1 -1
  238. package/lib/systems/animation/core/AnimationStateMachine.js.map +1 -1
  239. package/lib/systems/animation/core/AnimationTransitions.js.map +1 -1
  240. package/lib/systems/animation/core/LateralityTransform.js.map +1 -1
  241. package/lib/systems/animation/core/RecoveryPhaseEnhancer.js.map +1 -1
  242. package/lib/systems/animation/core/TechniqueAnimationMapper.js.map +1 -1
  243. package/lib/systems/animation/core/TechniqueAnimationMapping.js.map +1 -1
  244. package/lib/systems/animation/core/types.js.map +1 -1
  245. package/lib/systems/animation/systems/AdvancedJointMovements.js.map +1 -1
  246. package/lib/systems/animation/systems/BodyFacingSystem.js.map +1 -1
  247. package/lib/systems/animation/systems/FacialExpressions.js.map +1 -1
  248. package/lib/systems/animation/systems/FallAnimations.js.map +1 -1
  249. package/lib/systems/animation/systems/MuscleActivation.js.map +1 -1
  250. package/lib/systems/bodypart/BodyPartDamageIntegration.js.map +1 -1
  251. package/lib/systems/bodypart/BodyPartHealthSystem.js.map +1 -1
  252. package/lib/systems/bodypart/BodyPartPositionMapping.js.map +1 -1
  253. package/lib/systems/bodypart/CombatInjuryIntegration.js.map +1 -1
  254. package/lib/systems/bodypart/InjuryIntegration.js.map +1 -1
  255. package/lib/systems/bodypart/InjuryTracker.js.map +1 -1
  256. package/lib/systems/bodypart/MovementPenaltySystem.js.map +1 -1
  257. package/lib/systems/bodypart/PlayerInjuryTrackingManager.js.map +1 -1
  258. package/lib/systems/bodypart/types.js.map +1 -1
  259. package/lib/systems/breathing/BreathingDisruptionSystem.js.map +1 -1
  260. package/lib/systems/breathing/feedback.js.map +1 -1
  261. package/lib/systems/breathing/integration.js.map +1 -1
  262. package/lib/systems/combat/BalanceSystem.js.map +1 -1
  263. package/lib/systems/combat/BreakingStatusEffects.js.map +1 -1
  264. package/lib/systems/combat/CombatStateSystem.js.map +1 -1
  265. package/lib/systems/combat/ConsciousnessSystem.js.map +1 -1
  266. package/lib/systems/combat/FallIntegration.js.map +1 -1
  267. package/lib/systems/combat/GrappleSystem.js.map +1 -1
  268. package/lib/systems/combat/LimbExposureSystem.js.map +1 -1
  269. package/lib/systems/combat/PainResponseSystem.js.map +1 -1
  270. package/lib/systems/combat/TrainingCombatSystem.js.map +1 -1
  271. package/lib/systems/combat/painConsciousnessUtils.js.map +1 -1
  272. package/lib/systems/combat/typeGuards.js.map +1 -1
  273. package/lib/systems/effects.js.map +1 -1
  274. package/lib/systems/game.js.map +1 -1
  275. package/lib/systems/movement/InjuryMovementModifier.js.map +1 -1
  276. package/lib/systems/movement/helpers/AccelerationUpdater.js.map +1 -1
  277. package/lib/systems/movement/helpers/accelerationUtils.js.map +1 -1
  278. package/lib/systems/movement/integration.js.map +1 -1
  279. package/lib/systems/physics/AttackMovementPhysics.js.map +1 -1
  280. package/lib/systems/physics/CollisionDetection.js.map +1 -1
  281. package/lib/systems/physics/CoordinateMapper.js.map +1 -1
  282. package/lib/systems/physics/KnockbackPhysics.js.map +1 -1
  283. package/lib/systems/physics/MovementPhysics.js.map +1 -1
  284. package/lib/systems/physics/PhysicalReachCalculator.js.map +1 -1
  285. package/lib/systems/physics/SpeedModifierSystem.js.map +1 -1
  286. package/lib/systems/trigram/KoreanCulture.js.map +1 -1
  287. package/lib/systems/trigram/KoreanTechniques.js.map +1 -1
  288. package/lib/systems/trigram/StanceManager.js.map +1 -1
  289. package/lib/systems/trigram/TransitionCalculator.js.map +1 -1
  290. package/lib/systems/trigram/TrigramCalculator.js.map +1 -1
  291. package/lib/systems/trigram/techniques/DarkOpsTechniques.js.map +1 -1
  292. package/lib/systems/trigram/techniques/GamTechniques.js.map +1 -1
  293. package/lib/systems/trigram/techniques/GanTechniques.js.map +1 -1
  294. package/lib/systems/trigram/techniques/GonTechniques.js.map +1 -1
  295. package/lib/systems/trigram/techniques/SonTechniques.js.map +1 -1
  296. package/lib/systems/trigram/techniques/TechniqueConfig.js.map +1 -1
  297. package/lib/systems/trigram/techniques/index.js.map +1 -1
  298. package/lib/systems/trigram/types/GonTechniqueExtensions.js.map +1 -1
  299. package/lib/systems/trigram/types.js.map +1 -1
  300. package/lib/systems/types.js.map +1 -1
  301. package/lib/systems/vitalpoint/DamageCalculator.js.map +1 -1
  302. package/lib/systems/vitalpoint/HitDetection.js.map +1 -1
  303. package/lib/systems/vitalpoint/KoreanAnatomy.js.map +1 -1
  304. package/lib/systems/vitalpoint/KoreanVitalPoints.js.map +1 -1
  305. package/lib/systems/vitalpoint/MeridianVitalPointMapping.js.map +1 -1
  306. package/lib/types/AccessibilityTypes.js.map +1 -1
  307. package/lib/types/PhysicsTypes.js.map +1 -1
  308. package/lib/types/common.js.map +1 -1
  309. package/lib/types/constants/colors.js.map +1 -1
  310. package/lib/types/constants/designSystem.js.map +1 -1
  311. package/lib/types/constants/layout.js.map +1 -1
  312. package/lib/types/constants/performance.js.map +1 -1
  313. package/lib/types/constants/typography.js.map +1 -1
  314. package/lib/types/facial.js.map +1 -1
  315. package/lib/types/hand-animation.js.map +1 -1
  316. package/lib/types/injury.js.map +1 -1
  317. package/lib/types/physics.js.map +1 -1
  318. package/lib/types/skeletal.js.map +1 -1
  319. package/lib/types/techniqueId.js.map +1 -1
  320. package/lib/utils/accessibility.js.map +1 -1
  321. package/lib/utils/arenaWorldDimensions.js.map +1 -1
  322. package/lib/utils/assetConfig.js.map +1 -1
  323. package/lib/utils/characterScaling.js.map +1 -1
  324. package/lib/utils/colorHelpers.js.map +1 -1
  325. package/lib/utils/colorUtils.js.map +1 -1
  326. package/lib/utils/combatReadiness.js.map +1 -1
  327. package/lib/utils/controlMapping.js.map +1 -1
  328. package/lib/utils/deviceDetection.js.map +1 -1
  329. package/lib/utils/effectUtils.js.map +1 -1
  330. package/lib/utils/fabricTextures.js.map +1 -1
  331. package/lib/utils/hapticFeedback.js.map +1 -1
  332. package/lib/utils/haptics.js.map +1 -1
  333. package/lib/utils/htmlOverlayHelpers.js.map +1 -1
  334. package/lib/utils/inputSystem.js.map +1 -1
  335. package/lib/utils/koreanThemeHelpers.js.map +1 -1
  336. package/lib/utils/math.js.map +1 -1
  337. package/lib/utils/mobileLayoutHelpers.js.map +1 -1
  338. package/lib/utils/mobileUIUtils.js.map +1 -1
  339. package/lib/utils/performance/PerformanceMonitor.js.map +1 -1
  340. package/lib/utils/performance/PerformanceOverlay3D.js.map +1 -1
  341. package/lib/utils/performance/usePerformanceMonitor.js.map +1 -1
  342. package/lib/utils/performanceOptimization.js.map +1 -1
  343. package/lib/utils/player3DHelpers.js.map +1 -1
  344. package/lib/utils/playerUtils.js.map +1 -1
  345. package/lib/utils/responsiveLayout.js.map +1 -1
  346. package/lib/utils/responsiveLayoutHelpers.js.map +1 -1
  347. package/lib/utils/responsiveOrientationConstants.js.map +1 -1
  348. package/lib/utils/safeAreaUtils.js.map +1 -1
  349. package/lib/utils/sharedPhysicsConfig.js.map +1 -1
  350. package/lib/utils/skeletonScaling.js.map +1 -1
  351. package/lib/utils/stanceHelpers.js.map +1 -1
  352. package/lib/utils/threeObjectPool.js.map +1 -1
  353. package/lib/utils/visualEffects.js.map +1 -1
  354. package/package.json +8 -8
@@ -1 +1 @@
1
- {"version":3,"file":"ArchetypeCardOverlayHtml.js","names":[],"sources":["../../../../../src/components/screens/intro/components/ArchetypeCardOverlayHtml.tsx"],"sourcesContent":["import React, { useCallback, useMemo } from \"react\";\nimport { PlayerArchetype } from \"../../../../types/common\";\nimport { FALLBACK_ARCHETYPE_IMAGE, FONT_FAMILY, KOREAN_COLORS } from \"../../../../types/constants\";\nimport { hexToRgbaString, hexColorToCSS } from \"../../../../utils/colorUtils\";\nimport { BaseButtonOverlayHtml } from \"../../../shared/base/BaseButtonOverlayHtml\";\nimport { AbilityList } from \"./AbilityListOverlayHtml\";\nimport { StatBar } from \"./StatBarOverlayHtml\";\n\nexport interface ArchetypeCardData {\n readonly archetype: PlayerArchetype;\n readonly id: string;\n readonly korean: string;\n readonly english: string;\n readonly description: string;\n readonly color: number;\n readonly textureKey: string;\n readonly stats: {\n readonly attackPower: number;\n readonly defense: number;\n readonly speed: number;\n readonly technique: number;\n };\n readonly philosophy: {\n readonly korean: string;\n readonly english: string;\n };\n readonly specialAbilities?: readonly string[];\n}\n\nexport interface ArchetypeCardProps {\n readonly data: ArchetypeCardData;\n readonly isSelected: boolean;\n readonly onSelect: () => void;\n readonly onConfirm?: () => void;\n readonly isMobile?: boolean;\n readonly width?: number;\n readonly showSelectButton?: boolean;\n}\n\n/**\n * ArchetypeCard component - Displays detailed archetype preview card\n * Shows stats, abilities, philosophy, and selection interface\n */\nexport const ArchetypeCard: React.FC<ArchetypeCardProps> = React.memo(\n ({\n data,\n isSelected,\n onSelect,\n onConfirm,\n isMobile = false,\n width = 380,\n showSelectButton = true,\n }) => {\n const {\n archetype,\n korean,\n english,\n color,\n textureKey,\n stats,\n philosophy,\n specialAbilities = [],\n } = data;\n\n // Calculate responsive dimensions\n const cardWidth = isMobile ? Math.min(280, width) : width;\n const imageSize = isMobile ? 120 : 160;\n const padding = isMobile ? 16 : 24;\n const gap = isMobile ? 10 : 16;\n\n // Memoize color calculations\n const colors = useMemo(\n () => ({\n background: hexToRgbaString(KOREAN_COLORS.UI_BACKGROUND_DARK, 0.95),\n border: hexToRgbaString(\n isSelected ? KOREAN_COLORS.ACCENT_GOLD : color,\n isSelected ? 1 : 0.7\n ),\n cardColor: hexColorToCSS(color),\n titleColor: isSelected\n ? hexColorToCSS(KOREAN_COLORS.ACCENT_GOLD)\n : hexColorToCSS(color),\n philosophyColor: hexColorToCSS(KOREAN_COLORS.TEXT_SECONDARY),\n buttonBackground: hexToRgbaString(KOREAN_COLORS.ACCENT_GOLD, 0.9),\n buttonText: hexColorToCSS(KOREAN_COLORS.UI_BACKGROUND_DARK),\n imageGlow: hexToRgbaString(color, 0.3),\n }),\n [color, isSelected]\n );\n\n // Handle card click\n const handleCardClick = useCallback(() => {\n if (!isSelected) {\n onSelect();\n }\n }, [isSelected, onSelect]);\n\n // Handle confirm button click\n const handleConfirmClick = useCallback(() => {\n onConfirm?.();\n }, [onConfirm]);\n\n // Get archetype image path\n const imagePath = `/assets/visual/archetypes/${textureKey}.png`;\n\n // Transform stats to StatBar format\n const statBars = useMemo(\n () => [\n {\n label: \"공격 | Attack\",\n value: stats.attackPower,\n },\n {\n label: \"방어 | Defense\",\n value: stats.defense,\n },\n {\n label: \"속도 | Speed\",\n value: stats.speed,\n },\n {\n label: \"기술 | Technique\",\n value: stats.technique,\n },\n ],\n [stats]\n );\n\n return (\n <div\n onClick={handleCardClick}\n onKeyDown={(e) => {\n if ((e.key === \"Enter\" || e.key === \" \") && !isSelected) {\n e.preventDefault();\n onSelect();\n }\n }}\n tabIndex={0}\n role=\"button\"\n aria-label={`${korean} ${english} archetype card`}\n aria-pressed={isSelected}\n style={{\n width: `${cardWidth}px`,\n padding: `${padding}px`,\n backgroundColor: colors.background,\n border: `${isSelected ? \"3px\" : \"2px\"} solid ${colors.border}`,\n borderRadius: \"12px\",\n cursor: isSelected ? \"default\" : \"pointer\",\n transition: \"all 0.3s ease\",\n transform: isSelected ? \"scale(1.05)\" : \"scale(1)\",\n boxShadow: isSelected\n ? `0 0 20px ${colors.imageGlow}, 0 4px 8px rgba(0, 0, 0, 0.3)`\n : \"0 2px 4px rgba(0, 0, 0, 0.2)\",\n display: \"flex\",\n flexDirection: \"column\",\n gap: `${gap}px`,\n position: \"relative\",\n overflow: \"hidden\",\n }}\n data-testid={`archetype-card-${archetype}`}\n >\n {/* Selected indicator badge */}\n {isSelected && (\n <div\n style={{\n position: \"absolute\",\n top: 10,\n right: 10,\n padding: \"4px 8px\",\n background: colors.buttonBackground,\n color: colors.buttonText,\n fontSize: isMobile ? \"10px\" : \"12px\",\n fontWeight: \"bold\",\n fontFamily: FONT_FAMILY.KOREAN,\n borderRadius: \"4px\",\n zIndex: 10,\n }}\n data-testid=\"selected-badge\"\n >\n 선택됨 | Selected\n </div>\n )}\n\n {/* Character Image */}\n <div\n style={{\n width: \"100%\",\n display: \"flex\",\n justifyContent: \"center\",\n alignItems: \"center\",\n marginBottom: `${gap / 2}px`,\n }}\n >\n <div\n style={{\n width: `${imageSize}px`,\n height: `${imageSize}px`,\n background: `radial-gradient(circle, ${colors.imageGlow}, transparent)`,\n borderRadius: \"8px\",\n border: `2px solid ${colors.cardColor}`,\n display: \"flex\",\n justifyContent: \"center\",\n alignItems: \"center\",\n overflow: \"hidden\",\n }}\n data-testid=\"archetype-image-container\"\n >\n <img\n src={imagePath}\n alt={`${korean} - ${english}`}\n style={{\n width: `${imageSize - 10}px`,\n height: `${imageSize - 10}px`,\n objectFit: \"contain\",\n }}\n data-testid=\"archetype-image\"\n onError={(e) => {\n const target = e.currentTarget as HTMLImageElement;\n if (!target.src.endsWith(FALLBACK_ARCHETYPE_IMAGE)) {\n target.src = FALLBACK_ARCHETYPE_IMAGE;\n }\n }}\n />\n </div>\n </div>\n\n {/* Archetype Name */}\n <h2\n style={{\n fontSize: isMobile ? \"20px\" : \"28px\",\n color: colors.titleColor,\n fontFamily: FONT_FAMILY.KOREAN,\n fontWeight: \"bold\",\n margin: 0,\n textAlign: \"center\",\n textShadow: `0 0 10px ${colors.imageGlow}`,\n }}\n data-testid=\"archetype-name\"\n >\n {korean}\n <br />\n <span\n style={{\n fontSize: isMobile ? \"16px\" : \"22px\",\n }}\n >\n {english}\n </span>\n </h2>\n\n {/* Philosophy/Description */}\n <p\n style={{\n fontSize: isMobile ? \"12px\" : \"14px\",\n color: colors.philosophyColor,\n fontFamily: FONT_FAMILY.KOREAN,\n margin: 0,\n lineHeight: \"1.5\",\n textAlign: \"center\",\n fontStyle: \"italic\",\n }}\n data-testid=\"archetype-philosophy\"\n >\n {philosophy.korean}\n <br />\n {philosophy.english}\n </p>\n\n {/* Stats Visualization */}\n <div\n style={{\n width: \"100%\",\n display: \"flex\",\n flexDirection: \"column\",\n gap: \"8px\",\n marginTop: `${gap / 2}px`,\n }}\n data-testid=\"archetype-stats\"\n >\n <div\n style={{\n fontSize: isMobile ? \"14px\" : \"16px\",\n fontWeight: \"bold\",\n fontFamily: FONT_FAMILY.KOREAN,\n color: colors.cardColor,\n }}\n >\n 전투 능력치 | Combat Stats\n </div>\n\n {statBars.map((stat) => (\n <StatBar\n key={stat.label}\n label={stat.label}\n value={stat.value}\n max={100}\n color={color}\n height={isMobile ? 10 : 12}\n showValue={true}\n isMobile={isMobile}\n />\n ))}\n </div>\n\n {/* Special Abilities */}\n {specialAbilities.length > 0 && (\n <div style={{ marginTop: `${gap / 2}px` }}>\n <AbilityList\n abilities={specialAbilities}\n maxAbilities={3}\n color={color}\n isMobile={isMobile}\n />\n </div>\n )}\n\n {/* Select Button */}\n {isSelected && showSelectButton && onConfirm && (\n // Note: Wrapper div with stopPropagation prevents card click when clicking button\n // Future enhancement: Consider adding stopPropagation prop to BaseButtonOverlayHtml\n // to eliminate this wrapper and make the component more self-contained\n <div \n style={{ marginTop: `${gap}px` }}\n onClick={(e) => e.stopPropagation()}\n >\n <BaseButtonOverlayHtml\n korean=\"선택\"\n english=\"Select\"\n onClick={handleConfirmClick}\n variant=\"primary\"\n size={isMobile ? \"sm\" : \"md\"}\n fullWidth={true}\n testId={`select-button-${archetype}`}\n isMobile={isMobile}\n ariaLabel={`Select ${korean} ${english} archetype`}\n />\n </div>\n )}\n </div>\n );\n }\n);\n\nArchetypeCard.displayName = \"ArchetypeCard\";\n\nexport default ArchetypeCard;\n"],"mappings":"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"}
1
+ {"version":3,"file":"ArchetypeCardOverlayHtml.js","names":[],"sources":["../../../../../src/components/screens/intro/components/ArchetypeCardOverlayHtml.tsx"],"sourcesContent":["import React, { useCallback, useMemo } from \"react\";\nimport { PlayerArchetype } from \"../../../../types/common\";\nimport { FALLBACK_ARCHETYPE_IMAGE, FONT_FAMILY, KOREAN_COLORS } from \"../../../../types/constants\";\nimport { hexToRgbaString, hexColorToCSS } from \"../../../../utils/colorUtils\";\nimport { BaseButtonOverlayHtml } from \"../../../shared/base/BaseButtonOverlayHtml\";\nimport { AbilityList } from \"./AbilityListOverlayHtml\";\nimport { StatBar } from \"./StatBarOverlayHtml\";\n\nexport interface ArchetypeCardData {\n readonly archetype: PlayerArchetype;\n readonly id: string;\n readonly korean: string;\n readonly english: string;\n readonly description: string;\n readonly color: number;\n readonly textureKey: string;\n readonly stats: {\n readonly attackPower: number;\n readonly defense: number;\n readonly speed: number;\n readonly technique: number;\n };\n readonly philosophy: {\n readonly korean: string;\n readonly english: string;\n };\n readonly specialAbilities?: readonly string[];\n}\n\nexport interface ArchetypeCardProps {\n readonly data: ArchetypeCardData;\n readonly isSelected: boolean;\n readonly onSelect: () => void;\n readonly onConfirm?: () => void;\n readonly isMobile?: boolean;\n readonly width?: number;\n readonly showSelectButton?: boolean;\n}\n\n/**\n * ArchetypeCard component - Displays detailed archetype preview card\n * Shows stats, abilities, philosophy, and selection interface\n */\nexport const ArchetypeCard: React.FC<ArchetypeCardProps> = React.memo(\n ({\n data,\n isSelected,\n onSelect,\n onConfirm,\n isMobile = false,\n width = 380,\n showSelectButton = true,\n }) => {\n const {\n archetype,\n korean,\n english,\n color,\n textureKey,\n stats,\n philosophy,\n specialAbilities = [],\n } = data;\n\n // Calculate responsive dimensions\n const cardWidth = isMobile ? Math.min(280, width) : width;\n const imageSize = isMobile ? 120 : 160;\n const padding = isMobile ? 16 : 24;\n const gap = isMobile ? 10 : 16;\n\n // Memoize color calculations\n const colors = useMemo(\n () => ({\n background: hexToRgbaString(KOREAN_COLORS.UI_BACKGROUND_DARK, 0.95),\n border: hexToRgbaString(\n isSelected ? KOREAN_COLORS.ACCENT_GOLD : color,\n isSelected ? 1 : 0.7\n ),\n cardColor: hexColorToCSS(color),\n titleColor: isSelected\n ? hexColorToCSS(KOREAN_COLORS.ACCENT_GOLD)\n : hexColorToCSS(color),\n philosophyColor: hexColorToCSS(KOREAN_COLORS.TEXT_SECONDARY),\n buttonBackground: hexToRgbaString(KOREAN_COLORS.ACCENT_GOLD, 0.9),\n buttonText: hexColorToCSS(KOREAN_COLORS.UI_BACKGROUND_DARK),\n imageGlow: hexToRgbaString(color, 0.3),\n }),\n [color, isSelected]\n );\n\n // Handle card click\n const handleCardClick = useCallback(() => {\n if (!isSelected) {\n onSelect();\n }\n }, [isSelected, onSelect]);\n\n // Handle confirm button click\n const handleConfirmClick = useCallback(() => {\n onConfirm?.();\n }, [onConfirm]);\n\n // Get archetype image path\n const imagePath = `/assets/visual/archetypes/${textureKey}.png`;\n\n // Transform stats to StatBar format\n const statBars = useMemo(\n () => [\n {\n label: \"공격 | Attack\",\n value: stats.attackPower,\n },\n {\n label: \"방어 | Defense\",\n value: stats.defense,\n },\n {\n label: \"속도 | Speed\",\n value: stats.speed,\n },\n {\n label: \"기술 | Technique\",\n value: stats.technique,\n },\n ],\n [stats]\n );\n\n return (\n <div\n onClick={handleCardClick}\n onKeyDown={(e) => {\n if ((e.key === \"Enter\" || e.key === \" \") && !isSelected) {\n e.preventDefault();\n onSelect();\n }\n }}\n tabIndex={0}\n role=\"button\"\n aria-label={`${korean} ${english} archetype card`}\n aria-pressed={isSelected}\n style={{\n width: `${cardWidth}px`,\n padding: `${padding}px`,\n backgroundColor: colors.background,\n border: `${isSelected ? \"3px\" : \"2px\"} solid ${colors.border}`,\n borderRadius: \"12px\",\n cursor: isSelected ? \"default\" : \"pointer\",\n transition: \"all 0.3s ease\",\n transform: isSelected ? \"scale(1.05)\" : \"scale(1)\",\n boxShadow: isSelected\n ? `0 0 20px ${colors.imageGlow}, 0 4px 8px rgba(0, 0, 0, 0.3)`\n : \"0 2px 4px rgba(0, 0, 0, 0.2)\",\n display: \"flex\",\n flexDirection: \"column\",\n gap: `${gap}px`,\n position: \"relative\",\n overflow: \"hidden\",\n }}\n data-testid={`archetype-card-${archetype}`}\n >\n {/* Selected indicator badge */}\n {isSelected && (\n <div\n style={{\n position: \"absolute\",\n top: 10,\n right: 10,\n padding: \"4px 8px\",\n background: colors.buttonBackground,\n color: colors.buttonText,\n fontSize: isMobile ? \"10px\" : \"12px\",\n fontWeight: \"bold\",\n fontFamily: FONT_FAMILY.KOREAN,\n borderRadius: \"4px\",\n zIndex: 10,\n }}\n data-testid=\"selected-badge\"\n >\n 선택됨 | Selected\n </div>\n )}\n\n {/* Character Image */}\n <div\n style={{\n width: \"100%\",\n display: \"flex\",\n justifyContent: \"center\",\n alignItems: \"center\",\n marginBottom: `${gap / 2}px`,\n }}\n >\n <div\n style={{\n width: `${imageSize}px`,\n height: `${imageSize}px`,\n background: `radial-gradient(circle, ${colors.imageGlow}, transparent)`,\n borderRadius: \"8px\",\n border: `2px solid ${colors.cardColor}`,\n display: \"flex\",\n justifyContent: \"center\",\n alignItems: \"center\",\n overflow: \"hidden\",\n }}\n data-testid=\"archetype-image-container\"\n >\n <img\n src={imagePath}\n alt={`${korean} - ${english}`}\n style={{\n width: `${imageSize - 10}px`,\n height: `${imageSize - 10}px`,\n objectFit: \"contain\",\n }}\n data-testid=\"archetype-image\"\n onError={(e) => {\n const target = e.currentTarget as HTMLImageElement;\n if (!target.src.endsWith(FALLBACK_ARCHETYPE_IMAGE)) {\n target.src = FALLBACK_ARCHETYPE_IMAGE;\n }\n }}\n />\n </div>\n </div>\n\n {/* Archetype Name */}\n <h2\n style={{\n fontSize: isMobile ? \"20px\" : \"28px\",\n color: colors.titleColor,\n fontFamily: FONT_FAMILY.KOREAN,\n fontWeight: \"bold\",\n margin: 0,\n textAlign: \"center\",\n textShadow: `0 0 10px ${colors.imageGlow}`,\n }}\n data-testid=\"archetype-name\"\n >\n {korean}\n <br />\n <span\n style={{\n fontSize: isMobile ? \"16px\" : \"22px\",\n }}\n >\n {english}\n </span>\n </h2>\n\n {/* Philosophy/Description */}\n <p\n style={{\n fontSize: isMobile ? \"12px\" : \"14px\",\n color: colors.philosophyColor,\n fontFamily: FONT_FAMILY.KOREAN,\n margin: 0,\n lineHeight: \"1.5\",\n textAlign: \"center\",\n fontStyle: \"italic\",\n }}\n data-testid=\"archetype-philosophy\"\n >\n {philosophy.korean}\n <br />\n {philosophy.english}\n </p>\n\n {/* Stats Visualization */}\n <div\n style={{\n width: \"100%\",\n display: \"flex\",\n flexDirection: \"column\",\n gap: \"8px\",\n marginTop: `${gap / 2}px`,\n }}\n data-testid=\"archetype-stats\"\n >\n <div\n style={{\n fontSize: isMobile ? \"14px\" : \"16px\",\n fontWeight: \"bold\",\n fontFamily: FONT_FAMILY.KOREAN,\n color: colors.cardColor,\n }}\n >\n 전투 능력치 | Combat Stats\n </div>\n\n {statBars.map((stat) => (\n <StatBar\n key={stat.label}\n label={stat.label}\n value={stat.value}\n max={100}\n color={color}\n height={isMobile ? 10 : 12}\n showValue={true}\n isMobile={isMobile}\n />\n ))}\n </div>\n\n {/* Special Abilities */}\n {specialAbilities.length > 0 && (\n <div style={{ marginTop: `${gap / 2}px` }}>\n <AbilityList\n abilities={specialAbilities}\n maxAbilities={3}\n color={color}\n isMobile={isMobile}\n />\n </div>\n )}\n\n {/* Select Button */}\n {isSelected && showSelectButton && onConfirm && (\n // Note: Wrapper div with stopPropagation prevents card click when clicking button\n // Future enhancement: Consider adding stopPropagation prop to BaseButtonOverlayHtml\n // to eliminate this wrapper and make the component more self-contained\n <div \n style={{ marginTop: `${gap}px` }}\n onClick={(e) => e.stopPropagation()}\n >\n <BaseButtonOverlayHtml\n korean=\"선택\"\n english=\"Select\"\n onClick={handleConfirmClick}\n variant=\"primary\"\n size={isMobile ? \"sm\" : \"md\"}\n fullWidth={true}\n testId={`select-button-${archetype}`}\n isMobile={isMobile}\n ariaLabel={`Select ${korean} ${english} archetype`}\n />\n </div>\n )}\n </div>\n );\n }\n);\n\nArchetypeCard.displayName = \"ArchetypeCard\";\n\nexport default ArchetypeCard;\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"ArchetypeDisplayOverlayHtml.js","names":[],"sources":["../../../../../src/components/screens/intro/components/ArchetypeDisplayOverlayHtml.tsx"],"sourcesContent":["import React, { useCallback, useMemo } from \"react\";\nimport {\n FALLBACK_ARCHETYPE_IMAGE,\n FONT_FAMILY,\n KOREAN_COLORS,\n} from \"../../../../types/constants\";\nimport { hexToRgbaString } from \"../../../../utils/colorUtils\";\nimport { getEnhancedKoreanOverlayStyles } from \"../../../../utils/koreanThemeHelpers\";\nimport {\n getKoreanFontOptimization,\n getNeonTextShadow,\n getSmoothTransition,\n} from \"../../../../utils/visualEffects\";\nimport { StatBar } from \"./StatBarOverlayHtml\";\n\n// Enhanced shape matching PLAYER_ARCHETYPES_DATA entries\nexport interface ArchetypeDataShape {\n readonly id: string;\n readonly korean: string;\n readonly english: string;\n readonly description: string;\n readonly color: number;\n readonly textureKey: string;\n readonly stats: {\n readonly attackPower: number;\n readonly defense: number;\n readonly speed: number;\n readonly technique: number;\n };\n readonly philosophy: {\n readonly korean: string;\n readonly english: string;\n };\n readonly specialAbilities?: readonly string[]; // Optional special abilities\n}\n\nexport interface ArchetypeDisplayOverlayHtmlProps {\n readonly archetypes: readonly ArchetypeDataShape[];\n readonly selectedIndex: number;\n readonly onArchetypeChange: (index: number) => void;\n readonly onPlaySFX: (sound: string) => void;\n readonly width?: number;\n readonly height?: number;\n readonly isMobile?: boolean;\n}\n\n/**\n * HTML-based ArchetypeDisplay component for Three.js integration\n */\nexport const ArchetypeDisplayOverlayHtml: React.FC<ArchetypeDisplayOverlayHtmlProps> =\n React.memo(\n ({\n archetypes,\n selectedIndex,\n onArchetypeChange,\n onPlaySFX,\n width = 800,\n height = 300,\n isMobile: _isMobile = false, // Kept for interface compatibility, layout uses width\n }) => {\n // Note: _isMobile intentionally unused - layout sizing uses width-based checks\n void _isMobile;\n const selectedArchetype = archetypes[selectedIndex];\n\n // Responsive sizing with large desktop support - use width for layout\n const isSmallScreen = width < 768;\n const isLargeContainer = width >= 1100;\n const archImageWidth = isSmallScreen ? 140 : isLargeContainer ? 120 : 180;\n const archImageHeight = isSmallScreen\n ? 200\n : isLargeContainer\n ? 170\n : 260;\n const containerPadding = isSmallScreen ? 20 : isLargeContainer ? 12 : 20;\n const contentGap = isSmallScreen ? 10 : isLargeContainer ? 8 : 16;\n const infoGap = isSmallScreen ? 8 : isLargeContainer ? 6 : 12;\n const titleFontSize = isSmallScreen ? 14 : isLargeContainer ? 14 : 18;\n const philosophyFontSize = isSmallScreen\n ? 10\n : isLargeContainer\n ? 10\n : 12;\n const statBarHeight = isSmallScreen ? 10 : isLargeContainer ? 10 : 12;\n\n const handlePrevious = useCallback(() => {\n const newIndex =\n selectedIndex === 0 ? archetypes.length - 1 : selectedIndex - 1;\n onArchetypeChange(newIndex);\n onPlaySFX(\"menu_hover\");\n }, [selectedIndex, archetypes.length, onArchetypeChange, onPlaySFX]);\n\n const handleNext = useCallback(() => {\n const newIndex = (selectedIndex + 1) % archetypes.length;\n onArchetypeChange(newIndex);\n onPlaySFX(\"menu_hover\");\n }, [selectedIndex, archetypes.length, onArchetypeChange, onPlaySFX]);\n\n // Convert real stats to 0-1 scale for visualization\n const combatStats = useMemo(() => {\n const maxStatValue = 100;\n return [\n {\n korean: \"공격\",\n english: \"Attack\",\n value: selectedArchetype.stats.attackPower / maxStatValue,\n rawValue: selectedArchetype.stats.attackPower,\n },\n {\n korean: \"방어\",\n english: \"Defense\",\n value: selectedArchetype.stats.defense / maxStatValue,\n rawValue: selectedArchetype.stats.defense,\n },\n {\n korean: \"속도\",\n english: \"Speed\",\n value: selectedArchetype.stats.speed / maxStatValue,\n rawValue: selectedArchetype.stats.speed,\n },\n {\n korean: \"기술\",\n english: \"Technique\",\n value: selectedArchetype.stats.technique / maxStatValue,\n rawValue: selectedArchetype.stats.technique,\n },\n ];\n }, [selectedArchetype.stats]);\n\n // Memoize RGBA color calculations and enhanced styles\n const colors = useMemo(\n () => ({\n archetypeColor: `#${selectedArchetype.color\n .toString(16)\n .padStart(6, \"0\")}`,\n titleGold: `#${KOREAN_COLORS.ACCENT_GOLD.toString(16).padStart(\n 6,\n \"0\",\n )}`,\n statsBackground: hexToRgbaString(\n KOREAN_COLORS.UI_BACKGROUND_MEDIUM,\n 0.9,\n ),\n statsBorder: hexToRgbaString(KOREAN_COLORS.PRIMARY_CYAN, 0.5),\n statBarBackground: hexToRgbaString(\n KOREAN_COLORS.UI_BACKGROUND_MEDIUM,\n 1,\n ),\n statBarFill: hexToRgbaString(selectedArchetype.color, 0.9),\n }),\n [selectedArchetype.color],\n );\n\n // Enhanced overlay styles\n const containerStyle = useMemo(\n () => ({\n ...getEnhancedKoreanOverlayStyles({\n opacity: 0.95,\n glowIntensity: \"medium\",\n includeGradient: false,\n includeBackdropBlur: false,\n depthLayers: 3,\n }),\n width: `${width}px`,\n height: `${height}px`,\n display: \"flex\",\n flexDirection: \"row\" as const,\n alignItems: \"flex-start\",\n justifyContent: \"flex-start\",\n gap: `${contentGap}px`,\n border: `2px solid ${colors.archetypeColor}`,\n padding: `${containerPadding}px`,\n position: \"relative\" as const,\n overflow: \"hidden\" as const,\n }),\n [width, height, contentGap, containerPadding, colors.archetypeColor],\n );\n\n // Get archetype image path\n const archetypeImagePath = useMemo(() => {\n return `/assets/visual/archetypes/${selectedArchetype.textureKey}.png`;\n }, [selectedArchetype.textureKey]);\n\n return (\n <div style={containerStyle} data-testid=\"archetype-display-container\">\n {/* Left Side - Character Image and Navigation */}\n <div\n style={{\n width: `${archImageWidth + 40}px`,\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n justifyContent: \"center\",\n gap: `${infoGap}px`,\n flexShrink: 0,\n }}\n data-testid=\"archetype-image-section\"\n >\n {/* Character Image */}\n <div\n style={{\n width: `${archImageWidth + 20}px`,\n height: `${archImageHeight + 20}px`,\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n position: \"relative\",\n background: `radial-gradient(circle, ${colors.archetypeColor}26, transparent)`,\n borderRadius: \"4px\",\n border: `2px solid ${colors.archetypeColor}`,\n }}\n data-testid=\"archetype-image-container\"\n >\n <img\n src={archetypeImagePath}\n alt={`${selectedArchetype.korean} - ${selectedArchetype.english}`}\n style={{\n width: `${archImageWidth}px`,\n height: `${archImageHeight}px`,\n objectFit: \"contain\",\n cursor: \"pointer\",\n }}\n onClick={handleNext}\n onKeyDown={(e) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n handleNext();\n }\n }}\n tabIndex={0}\n role=\"button\"\n aria-label={`${selectedArchetype.korean} ${selectedArchetype.english} - Click or press Enter to cycle to next archetype`}\n data-testid=\"archetype-image\"\n onError={(e) => {\n // Fallback if image doesn't load: use Black Trigram logo, prevent infinite loop\n const target = e.currentTarget as HTMLImageElement;\n if (!target.src.endsWith(FALLBACK_ARCHETYPE_IMAGE)) {\n target.src = FALLBACK_ARCHETYPE_IMAGE;\n target.alt = `${selectedArchetype.korean} (image unavailable)`;\n }\n }}\n />\n </div>\n\n {/* Navigation Buttons */}\n <div\n style={{\n display: \"flex\",\n flexDirection: \"row\",\n gap: \"8px\",\n width: \"100%\",\n }}\n data-testid=\"archetype-navigation\"\n >\n <button\n onClick={handlePrevious}\n aria-label=\"Previous archetype\"\n style={{\n flex: 1,\n height: \"30px\",\n fontSize: \"14px\",\n fontWeight: \"bold\",\n color: `#${KOREAN_COLORS.TEXT_PRIMARY.toString(16).padStart(\n 6,\n \"0\",\n )}`,\n background: colors.statsBackground,\n border: `1px solid ${hexToRgbaString(\n KOREAN_COLORS.ACCENT_GOLD,\n 0.7,\n )}`,\n borderRadius: \"4px\",\n cursor: \"pointer\",\n }}\n onFocus={(e) => {\n e.currentTarget.style.outline = `3px solid ${hexToRgbaString(KOREAN_COLORS.ACCENT_GOLD)}`;\n e.currentTarget.style.outlineOffset = \"2px\";\n }}\n onBlur={(e) => {\n e.currentTarget.style.outline = \"none\";\n }}\n data-testid=\"prev-archetype-button\"\n >\n ◀\n </button>\n <button\n onClick={handleNext}\n aria-label=\"Next archetype\"\n style={{\n flex: 1,\n height: \"30px\",\n fontSize: \"14px\",\n fontWeight: \"bold\",\n color: `#${KOREAN_COLORS.TEXT_PRIMARY.toString(16).padStart(\n 6,\n \"0\",\n )}`,\n background: colors.statsBackground,\n border: `1px solid ${hexToRgbaString(\n KOREAN_COLORS.ACCENT_GOLD,\n 0.7,\n )}`,\n borderRadius: \"4px\",\n cursor: \"pointer\",\n }}\n onFocus={(e) => {\n e.currentTarget.style.outline = `3px solid ${hexToRgbaString(KOREAN_COLORS.ACCENT_GOLD)}`;\n e.currentTarget.style.outlineOffset = \"2px\";\n }}\n onBlur={(e) => {\n e.currentTarget.style.outline = \"none\";\n }}\n data-testid=\"next-archetype-button\"\n >\n ▶\n </button>\n </div>\n </div>\n\n {/* Right Side - Archetype Information */}\n <div\n style={{\n flex: 1,\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"flex-start\",\n justifyContent: \"flex-start\",\n gap: `${infoGap}px`,\n minWidth: 0,\n overflow: \"hidden\",\n }}\n data-testid=\"archetype-info\"\n >\n {/* Header with name and counter */}\n <div\n style={{\n width: \"100%\",\n display: \"flex\",\n flexDirection: \"row\",\n alignItems: \"center\",\n justifyContent: \"space-between\",\n }}\n >\n <div\n style={{\n fontSize: `${titleFontSize}px`,\n fontWeight: \"bold\",\n fontFamily: FONT_FAMILY.KOREAN,\n color: colors.archetypeColor,\n textShadow: getNeonTextShadow(\n selectedArchetype.color,\n \"strong\",\n ),\n transition: getSmoothTransition(\"all\", \"normal\"),\n }}\n data-testid=\"archetype-title\"\n >\n {selectedArchetype.korean} | {selectedArchetype.english}\n </div>\n <div\n style={{\n fontSize: \"12px\",\n fontWeight: \"bold\",\n fontFamily: FONT_FAMILY.PRIMARY,\n color: colors.archetypeColor,\n textShadow: getNeonTextShadow(\n selectedArchetype.color,\n \"subtle\",\n ),\n }}\n data-testid=\"archetype-counter\"\n >\n {selectedIndex + 1} / {archetypes.length}\n </div>\n </div>\n\n {/* Philosophy */}\n <div\n style={{\n fontSize: `${philosophyFontSize}px`,\n fontStyle: \"italic\",\n fontFamily: FONT_FAMILY.KOREAN,\n color: `#${KOREAN_COLORS.TEXT_SECONDARY.toString(16).padStart(\n 6,\n \"0\",\n )}`,\n lineHeight: \"1.4\",\n ...getKoreanFontOptimization(philosophyFontSize, \"normal\"),\n transition: getSmoothTransition(\"all\", \"normal\"),\n }}\n data-testid=\"archetype-philosophy\"\n >\n {selectedArchetype.philosophy.korean} |{\" \"}\n {selectedArchetype.philosophy.english}\n </div>\n\n {/* Combat Stats */}\n <div\n style={{\n width: \"100%\",\n display: \"flex\",\n flexDirection: \"column\",\n gap: \"8px\",\n }}\n data-testid=\"combat-stats\"\n >\n <div\n style={{\n fontSize: isSmallScreen ? \"12px\" : \"14px\",\n fontWeight: \"bold\",\n fontFamily: FONT_FAMILY.KOREAN,\n color: `#${KOREAN_COLORS.ACCENT_GOLD.toString(16).padStart(\n 6,\n \"0\",\n )}`,\n }}\n >\n 전투 능력치 | Combat Stats\n </div>\n\n {/* Individual stat bars using refactored StatBar component */}\n {combatStats.map((stat) => (\n <StatBar\n key={stat.korean}\n label={`${stat.korean} | ${stat.english}`}\n value={stat.rawValue}\n max={100}\n color={selectedArchetype.color}\n height={statBarHeight}\n showValue={true}\n isMobile={isSmallScreen}\n />\n ))}\n </div>\n </div>\n </div>\n );\n },\n );\n\nArchetypeDisplayOverlayHtml.displayName = \"ArchetypeDisplayOverlayHtml\";\n\nexport default ArchetypeDisplayOverlayHtml;\n"],"mappings":"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"}
1
+ {"version":3,"file":"ArchetypeDisplayOverlayHtml.js","names":[],"sources":["../../../../../src/components/screens/intro/components/ArchetypeDisplayOverlayHtml.tsx"],"sourcesContent":["import React, { useCallback, useMemo } from \"react\";\nimport {\n FALLBACK_ARCHETYPE_IMAGE,\n FONT_FAMILY,\n KOREAN_COLORS,\n} from \"../../../../types/constants\";\nimport { hexToRgbaString } from \"../../../../utils/colorUtils\";\nimport { getEnhancedKoreanOverlayStyles } from \"../../../../utils/koreanThemeHelpers\";\nimport {\n getKoreanFontOptimization,\n getNeonTextShadow,\n getSmoothTransition,\n} from \"../../../../utils/visualEffects\";\nimport { StatBar } from \"./StatBarOverlayHtml\";\n\n// Enhanced shape matching PLAYER_ARCHETYPES_DATA entries\nexport interface ArchetypeDataShape {\n readonly id: string;\n readonly korean: string;\n readonly english: string;\n readonly description: string;\n readonly color: number;\n readonly textureKey: string;\n readonly stats: {\n readonly attackPower: number;\n readonly defense: number;\n readonly speed: number;\n readonly technique: number;\n };\n readonly philosophy: {\n readonly korean: string;\n readonly english: string;\n };\n readonly specialAbilities?: readonly string[]; // Optional special abilities\n}\n\nexport interface ArchetypeDisplayOverlayHtmlProps {\n readonly archetypes: readonly ArchetypeDataShape[];\n readonly selectedIndex: number;\n readonly onArchetypeChange: (index: number) => void;\n readonly onPlaySFX: (sound: string) => void;\n readonly width?: number;\n readonly height?: number;\n readonly isMobile?: boolean;\n}\n\n/**\n * HTML-based ArchetypeDisplay component for Three.js integration\n */\nexport const ArchetypeDisplayOverlayHtml: React.FC<ArchetypeDisplayOverlayHtmlProps> =\n React.memo(\n ({\n archetypes,\n selectedIndex,\n onArchetypeChange,\n onPlaySFX,\n width = 800,\n height = 300,\n isMobile: _isMobile = false, // Kept for interface compatibility, layout uses width\n }) => {\n // Note: _isMobile intentionally unused - layout sizing uses width-based checks\n void _isMobile;\n const selectedArchetype = archetypes[selectedIndex];\n\n // Responsive sizing with large desktop support - use width for layout\n const isSmallScreen = width < 768;\n const isLargeContainer = width >= 1100;\n const archImageWidth = isSmallScreen ? 140 : isLargeContainer ? 120 : 180;\n const archImageHeight = isSmallScreen\n ? 200\n : isLargeContainer\n ? 170\n : 260;\n const containerPadding = isSmallScreen ? 20 : isLargeContainer ? 12 : 20;\n const contentGap = isSmallScreen ? 10 : isLargeContainer ? 8 : 16;\n const infoGap = isSmallScreen ? 8 : isLargeContainer ? 6 : 12;\n const titleFontSize = isSmallScreen ? 14 : isLargeContainer ? 14 : 18;\n const philosophyFontSize = isSmallScreen\n ? 10\n : isLargeContainer\n ? 10\n : 12;\n const statBarHeight = isSmallScreen ? 10 : isLargeContainer ? 10 : 12;\n\n const handlePrevious = useCallback(() => {\n const newIndex =\n selectedIndex === 0 ? archetypes.length - 1 : selectedIndex - 1;\n onArchetypeChange(newIndex);\n onPlaySFX(\"menu_hover\");\n }, [selectedIndex, archetypes.length, onArchetypeChange, onPlaySFX]);\n\n const handleNext = useCallback(() => {\n const newIndex = (selectedIndex + 1) % archetypes.length;\n onArchetypeChange(newIndex);\n onPlaySFX(\"menu_hover\");\n }, [selectedIndex, archetypes.length, onArchetypeChange, onPlaySFX]);\n\n // Convert real stats to 0-1 scale for visualization\n const combatStats = useMemo(() => {\n const maxStatValue = 100;\n return [\n {\n korean: \"공격\",\n english: \"Attack\",\n value: selectedArchetype.stats.attackPower / maxStatValue,\n rawValue: selectedArchetype.stats.attackPower,\n },\n {\n korean: \"방어\",\n english: \"Defense\",\n value: selectedArchetype.stats.defense / maxStatValue,\n rawValue: selectedArchetype.stats.defense,\n },\n {\n korean: \"속도\",\n english: \"Speed\",\n value: selectedArchetype.stats.speed / maxStatValue,\n rawValue: selectedArchetype.stats.speed,\n },\n {\n korean: \"기술\",\n english: \"Technique\",\n value: selectedArchetype.stats.technique / maxStatValue,\n rawValue: selectedArchetype.stats.technique,\n },\n ];\n }, [selectedArchetype.stats]);\n\n // Memoize RGBA color calculations and enhanced styles\n const colors = useMemo(\n () => ({\n archetypeColor: `#${selectedArchetype.color\n .toString(16)\n .padStart(6, \"0\")}`,\n titleGold: `#${KOREAN_COLORS.ACCENT_GOLD.toString(16).padStart(\n 6,\n \"0\",\n )}`,\n statsBackground: hexToRgbaString(\n KOREAN_COLORS.UI_BACKGROUND_MEDIUM,\n 0.9,\n ),\n statsBorder: hexToRgbaString(KOREAN_COLORS.PRIMARY_CYAN, 0.5),\n statBarBackground: hexToRgbaString(\n KOREAN_COLORS.UI_BACKGROUND_MEDIUM,\n 1,\n ),\n statBarFill: hexToRgbaString(selectedArchetype.color, 0.9),\n }),\n [selectedArchetype.color],\n );\n\n // Enhanced overlay styles\n const containerStyle = useMemo(\n () => ({\n ...getEnhancedKoreanOverlayStyles({\n opacity: 0.95,\n glowIntensity: \"medium\",\n includeGradient: false,\n includeBackdropBlur: false,\n depthLayers: 3,\n }),\n width: `${width}px`,\n height: `${height}px`,\n display: \"flex\",\n flexDirection: \"row\" as const,\n alignItems: \"flex-start\",\n justifyContent: \"flex-start\",\n gap: `${contentGap}px`,\n border: `2px solid ${colors.archetypeColor}`,\n padding: `${containerPadding}px`,\n position: \"relative\" as const,\n overflow: \"hidden\" as const,\n }),\n [width, height, contentGap, containerPadding, colors.archetypeColor],\n );\n\n // Get archetype image path\n const archetypeImagePath = useMemo(() => {\n return `/assets/visual/archetypes/${selectedArchetype.textureKey}.png`;\n }, [selectedArchetype.textureKey]);\n\n return (\n <div style={containerStyle} data-testid=\"archetype-display-container\">\n {/* Left Side - Character Image and Navigation */}\n <div\n style={{\n width: `${archImageWidth + 40}px`,\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n justifyContent: \"center\",\n gap: `${infoGap}px`,\n flexShrink: 0,\n }}\n data-testid=\"archetype-image-section\"\n >\n {/* Character Image */}\n <div\n style={{\n width: `${archImageWidth + 20}px`,\n height: `${archImageHeight + 20}px`,\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n position: \"relative\",\n background: `radial-gradient(circle, ${colors.archetypeColor}26, transparent)`,\n borderRadius: \"4px\",\n border: `2px solid ${colors.archetypeColor}`,\n }}\n data-testid=\"archetype-image-container\"\n >\n <img\n src={archetypeImagePath}\n alt={`${selectedArchetype.korean} - ${selectedArchetype.english}`}\n style={{\n width: `${archImageWidth}px`,\n height: `${archImageHeight}px`,\n objectFit: \"contain\",\n cursor: \"pointer\",\n }}\n onClick={handleNext}\n onKeyDown={(e) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n handleNext();\n }\n }}\n tabIndex={0}\n role=\"button\"\n aria-label={`${selectedArchetype.korean} ${selectedArchetype.english} - Click or press Enter to cycle to next archetype`}\n data-testid=\"archetype-image\"\n onError={(e) => {\n // Fallback if image doesn't load: use Black Trigram logo, prevent infinite loop\n const target = e.currentTarget as HTMLImageElement;\n if (!target.src.endsWith(FALLBACK_ARCHETYPE_IMAGE)) {\n target.src = FALLBACK_ARCHETYPE_IMAGE;\n target.alt = `${selectedArchetype.korean} (image unavailable)`;\n }\n }}\n />\n </div>\n\n {/* Navigation Buttons */}\n <div\n style={{\n display: \"flex\",\n flexDirection: \"row\",\n gap: \"8px\",\n width: \"100%\",\n }}\n data-testid=\"archetype-navigation\"\n >\n <button\n onClick={handlePrevious}\n aria-label=\"Previous archetype\"\n style={{\n flex: 1,\n height: \"30px\",\n fontSize: \"14px\",\n fontWeight: \"bold\",\n color: `#${KOREAN_COLORS.TEXT_PRIMARY.toString(16).padStart(\n 6,\n \"0\",\n )}`,\n background: colors.statsBackground,\n border: `1px solid ${hexToRgbaString(\n KOREAN_COLORS.ACCENT_GOLD,\n 0.7,\n )}`,\n borderRadius: \"4px\",\n cursor: \"pointer\",\n }}\n onFocus={(e) => {\n e.currentTarget.style.outline = `3px solid ${hexToRgbaString(KOREAN_COLORS.ACCENT_GOLD)}`;\n e.currentTarget.style.outlineOffset = \"2px\";\n }}\n onBlur={(e) => {\n e.currentTarget.style.outline = \"none\";\n }}\n data-testid=\"prev-archetype-button\"\n >\n ◀\n </button>\n <button\n onClick={handleNext}\n aria-label=\"Next archetype\"\n style={{\n flex: 1,\n height: \"30px\",\n fontSize: \"14px\",\n fontWeight: \"bold\",\n color: `#${KOREAN_COLORS.TEXT_PRIMARY.toString(16).padStart(\n 6,\n \"0\",\n )}`,\n background: colors.statsBackground,\n border: `1px solid ${hexToRgbaString(\n KOREAN_COLORS.ACCENT_GOLD,\n 0.7,\n )}`,\n borderRadius: \"4px\",\n cursor: \"pointer\",\n }}\n onFocus={(e) => {\n e.currentTarget.style.outline = `3px solid ${hexToRgbaString(KOREAN_COLORS.ACCENT_GOLD)}`;\n e.currentTarget.style.outlineOffset = \"2px\";\n }}\n onBlur={(e) => {\n e.currentTarget.style.outline = \"none\";\n }}\n data-testid=\"next-archetype-button\"\n >\n ▶\n </button>\n </div>\n </div>\n\n {/* Right Side - Archetype Information */}\n <div\n style={{\n flex: 1,\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"flex-start\",\n justifyContent: \"flex-start\",\n gap: `${infoGap}px`,\n minWidth: 0,\n overflow: \"hidden\",\n }}\n data-testid=\"archetype-info\"\n >\n {/* Header with name and counter */}\n <div\n style={{\n width: \"100%\",\n display: \"flex\",\n flexDirection: \"row\",\n alignItems: \"center\",\n justifyContent: \"space-between\",\n }}\n >\n <div\n style={{\n fontSize: `${titleFontSize}px`,\n fontWeight: \"bold\",\n fontFamily: FONT_FAMILY.KOREAN,\n color: colors.archetypeColor,\n textShadow: getNeonTextShadow(\n selectedArchetype.color,\n \"strong\",\n ),\n transition: getSmoothTransition(\"all\", \"normal\"),\n }}\n data-testid=\"archetype-title\"\n >\n {selectedArchetype.korean} | {selectedArchetype.english}\n </div>\n <div\n style={{\n fontSize: \"12px\",\n fontWeight: \"bold\",\n fontFamily: FONT_FAMILY.PRIMARY,\n color: colors.archetypeColor,\n textShadow: getNeonTextShadow(\n selectedArchetype.color,\n \"subtle\",\n ),\n }}\n data-testid=\"archetype-counter\"\n >\n {selectedIndex + 1} / {archetypes.length}\n </div>\n </div>\n\n {/* Philosophy */}\n <div\n style={{\n fontSize: `${philosophyFontSize}px`,\n fontStyle: \"italic\",\n fontFamily: FONT_FAMILY.KOREAN,\n color: `#${KOREAN_COLORS.TEXT_SECONDARY.toString(16).padStart(\n 6,\n \"0\",\n )}`,\n lineHeight: \"1.4\",\n ...getKoreanFontOptimization(philosophyFontSize, \"normal\"),\n transition: getSmoothTransition(\"all\", \"normal\"),\n }}\n data-testid=\"archetype-philosophy\"\n >\n {selectedArchetype.philosophy.korean} |{\" \"}\n {selectedArchetype.philosophy.english}\n </div>\n\n {/* Combat Stats */}\n <div\n style={{\n width: \"100%\",\n display: \"flex\",\n flexDirection: \"column\",\n gap: \"8px\",\n }}\n data-testid=\"combat-stats\"\n >\n <div\n style={{\n fontSize: isSmallScreen ? \"12px\" : \"14px\",\n fontWeight: \"bold\",\n fontFamily: FONT_FAMILY.KOREAN,\n color: `#${KOREAN_COLORS.ACCENT_GOLD.toString(16).padStart(\n 6,\n \"0\",\n )}`,\n }}\n >\n 전투 능력치 | Combat Stats\n </div>\n\n {/* Individual stat bars using refactored StatBar component */}\n {combatStats.map((stat) => (\n <StatBar\n key={stat.korean}\n label={`${stat.korean} | ${stat.english}`}\n value={stat.rawValue}\n max={100}\n color={selectedArchetype.color}\n height={statBarHeight}\n showValue={true}\n isMobile={isSmallScreen}\n />\n ))}\n </div>\n </div>\n </div>\n );\n },\n );\n\nArchetypeDisplayOverlayHtml.displayName = \"ArchetypeDisplayOverlayHtml\";\n\nexport default ArchetypeDisplayOverlayHtml;\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"EnhancedArchetypeDisplayOverlayHtml.js","names":[],"sources":["../../../../../src/components/screens/intro/components/EnhancedArchetypeDisplayOverlayHtml.tsx"],"sourcesContent":["import React, { useCallback, useMemo, useState } from \"react\";\nimport { PlayerArchetype } from \"../../../../types/common\";\nimport { BaseButtonOverlayHtml } from \"../../../shared/base/BaseButtonOverlayHtml\";\nimport { ArchetypeCardGrid } from \"./ArchetypeCardGridOverlayHtml\";\nimport {\n ArchetypeDataShape,\n ArchetypeDisplayOverlayHtml,\n} from \"./ArchetypeDisplayOverlayHtml\";\n\nexport interface EnhancedArchetypeDisplayProps {\n readonly archetypes: readonly ArchetypeDataShape[];\n readonly selectedIndex: number;\n readonly onArchetypeChange: (index: number) => void;\n readonly onPlaySFX: (sound: string) => void;\n readonly width?: number;\n readonly height?: number;\n readonly isMobile?: boolean; // For controls/haptics only, use width for layout sizing\n readonly allowDetailedView?: boolean; // Allow switching to card grid view\n}\n\n/**\n * EnhancedArchetypeDisplay - Provides both compact and detailed card view modes\n * Can switch between ArchetypeDisplayOverlayHtml (compact) and ArchetypeCardGrid (detailed)\n */\nexport const EnhancedArchetypeDisplay: React.FC<EnhancedArchetypeDisplayProps> =\n React.memo(\n ({\n archetypes,\n selectedIndex,\n onArchetypeChange,\n onPlaySFX,\n width = 800,\n height = 300,\n isMobile = false,\n allowDetailedView = true,\n }) => {\n const [viewMode, setViewMode] = useState<\"compact\" | \"detailed\">(\n \"compact\",\n );\n\n // Use width for layout sizing decisions\n const isSmallScreen = width < 768;\n\n // Convert archetype data to card data format\n const cardData = useMemo(() => {\n return archetypes.map((archetype) => ({\n archetype: Object.values(PlayerArchetype).find(\n (key) => key === archetype.id,\n ) as PlayerArchetype,\n id: archetype.id,\n korean: archetype.korean,\n english: archetype.english,\n description: archetype.description,\n color: archetype.color,\n textureKey: archetype.textureKey,\n stats: archetype.stats,\n philosophy: archetype.philosophy,\n specialAbilities: archetype.specialAbilities ?? [], // Use actual data or empty array\n }));\n }, [archetypes]);\n\n // Get current archetype\n const currentArchetype = useMemo(() => {\n const archetype = archetypes[selectedIndex];\n return Object.values(PlayerArchetype).find(\n (key) => key === archetype.id,\n ) as PlayerArchetype;\n }, [archetypes, selectedIndex]);\n\n // Toggle view mode\n const handleToggleView = useCallback(() => {\n setViewMode((prev) => (prev === \"compact\" ? \"detailed\" : \"compact\"));\n onPlaySFX(\"menu_hover\");\n }, [onPlaySFX]);\n\n // Handle archetype change from card grid\n const handleArchetypeChangeFromGrid = useCallback(\n (archetype: PlayerArchetype) => {\n const index = archetypes.findIndex((a) => a.id === archetype);\n if (index !== -1) {\n onArchetypeChange(index);\n }\n },\n [archetypes, onArchetypeChange],\n );\n\n // Memoize height calculation for detailed view (minimum 600px or 2x compact height)\n const detailedHeight = useMemo(\n () => Math.max(height * 2, 600),\n [height],\n );\n\n return (\n <div\n style={{\n width: `${width}px`,\n display: \"flex\",\n flexDirection: \"column\",\n gap: isSmallScreen ? \"10px\" : \"16px\",\n }}\n data-testid=\"enhanced-archetype-display\"\n >\n {/* View toggle button (only show if allowed and not small screen) */}\n {allowDetailedView && !isSmallScreen && (\n <div\n style={{\n display: \"flex\",\n justifyContent: \"flex-end\",\n width: \"100%\",\n }}\n >\n <BaseButtonOverlayHtml\n korean={viewMode === \"compact\" ? \"상세 보기\" : \"간단 보기\"}\n english={viewMode === \"compact\" ? \"Detailed View\" : \"Compact View\"}\n onClick={handleToggleView}\n variant=\"secondary\"\n size=\"sm\"\n testId=\"view-toggle-button\"\n ariaLabel={`Toggle ${viewMode === \"compact\" ? \"detailed\" : \"compact\"} view`}\n />\n </div>\n )}\n\n {/* Render appropriate view */}\n {viewMode === \"compact\" ? (\n <ArchetypeDisplayOverlayHtml\n archetypes={archetypes}\n selectedIndex={selectedIndex}\n onArchetypeChange={onArchetypeChange}\n onPlaySFX={onPlaySFX}\n width={width}\n height={height}\n isMobile={isMobile}\n />\n ) : (\n <ArchetypeCardGrid\n archetypes={cardData}\n selectedArchetype={currentArchetype}\n onArchetypeChange={handleArchetypeChangeFromGrid}\n onPlaySFX={onPlaySFX}\n width={width}\n height={detailedHeight}\n isMobile={isMobile}\n />\n )}\n </div>\n );\n },\n );\n\nEnhancedArchetypeDisplay.displayName = \"EnhancedArchetypeDisplay\";\n\nexport default EnhancedArchetypeDisplay;\n"],"mappings":";;;;;;;;;;;AAwBA,IAAa,2BACX,MAAM,MACH,EACC,YACA,eACA,mBACA,WACA,QAAQ,KACR,SAAS,KACT,WAAW,OACX,oBAAoB,WAChB;CACJ,MAAM,CAAC,UAAU,eAAe,SAC9B,UACD;CAGD,MAAM,gBAAgB,QAAQ;CAG9B,MAAM,WAAW,cAAc;AAC7B,SAAO,WAAW,KAAK,eAAe;GACpC,WAAW,OAAO,OAAO,gBAAgB,CAAC,MACvC,QAAQ,QAAQ,UAAU,GAC5B;GACD,IAAI,UAAU;GACd,QAAQ,UAAU;GAClB,SAAS,UAAU;GACnB,aAAa,UAAU;GACvB,OAAO,UAAU;GACjB,YAAY,UAAU;GACtB,OAAO,UAAU;GACjB,YAAY,UAAU;GACtB,kBAAkB,UAAU,oBAAoB,EAAE;GACnD,EAAE;IACF,CAAC,WAAW,CAAC;CAGhB,MAAM,mBAAmB,cAAc;EACrC,MAAM,YAAY,WAAW;AAC7B,SAAO,OAAO,OAAO,gBAAgB,CAAC,MACnC,QAAQ,QAAQ,UAAU,GAC5B;IACA,CAAC,YAAY,cAAc,CAAC;CAG/B,MAAM,mBAAmB,kBAAkB;AACzC,eAAa,SAAU,SAAS,YAAY,aAAa,UAAW;AACpE,YAAU,aAAa;IACtB,CAAC,UAAU,CAAC;CAGf,MAAM,gCAAgC,aACnC,cAA+B;EAC9B,MAAM,QAAQ,WAAW,WAAW,MAAM,EAAE,OAAO,UAAU;AAC7D,MAAI,UAAU,GACZ,mBAAkB,MAAM;IAG5B,CAAC,YAAY,kBAAkB,CAChC;CAGD,MAAM,iBAAiB,cACf,KAAK,IAAI,SAAS,GAAG,IAAI,EAC/B,CAAC,OAAO,CACT;AAED,QACE,qBAAC,OAAD;EACE,OAAO;GACL,OAAO,GAAG,MAAM;GAChB,SAAS;GACT,eAAe;GACf,KAAK,gBAAgB,SAAS;GAC/B;EACD,eAAY;YAPd,CAUG,qBAAqB,CAAC,iBACrB,oBAAC,OAAD;GACE,OAAO;IACL,SAAS;IACT,gBAAgB;IAChB,OAAO;IACR;aAED,oBAAC,uBAAD;IACE,QAAQ,aAAa,YAAY,UAAU;IAC3C,SAAS,aAAa,YAAY,kBAAkB;IACpD,SAAS;IACT,SAAQ;IACR,MAAK;IACL,QAAO;IACP,WAAW,UAAU,aAAa,YAAY,aAAa,UAAU;IACrE,CAAA;GACE,CAAA,EAIP,aAAa,YACZ,oBAAC,6BAAD;GACc;GACG;GACI;GACR;GACJ;GACC;GACE;GACV,CAAA,GAEF,oBAAC,mBAAD;GACE,YAAY;GACZ,mBAAmB;GACnB,mBAAmB;GACR;GACJ;GACP,QAAQ;GACE;GACV,CAAA,CAEA;;EAGX;AAEH,yBAAyB,cAAc"}
1
+ {"version":3,"file":"EnhancedArchetypeDisplayOverlayHtml.js","names":[],"sources":["../../../../../src/components/screens/intro/components/EnhancedArchetypeDisplayOverlayHtml.tsx"],"sourcesContent":["import React, { useCallback, useMemo, useState } from \"react\";\nimport { PlayerArchetype } from \"../../../../types/common\";\nimport { BaseButtonOverlayHtml } from \"../../../shared/base/BaseButtonOverlayHtml\";\nimport { ArchetypeCardGrid } from \"./ArchetypeCardGridOverlayHtml\";\nimport {\n ArchetypeDataShape,\n ArchetypeDisplayOverlayHtml,\n} from \"./ArchetypeDisplayOverlayHtml\";\n\nexport interface EnhancedArchetypeDisplayProps {\n readonly archetypes: readonly ArchetypeDataShape[];\n readonly selectedIndex: number;\n readonly onArchetypeChange: (index: number) => void;\n readonly onPlaySFX: (sound: string) => void;\n readonly width?: number;\n readonly height?: number;\n readonly isMobile?: boolean; // For controls/haptics only, use width for layout sizing\n readonly allowDetailedView?: boolean; // Allow switching to card grid view\n}\n\n/**\n * EnhancedArchetypeDisplay - Provides both compact and detailed card view modes\n * Can switch between ArchetypeDisplayOverlayHtml (compact) and ArchetypeCardGrid (detailed)\n */\nexport const EnhancedArchetypeDisplay: React.FC<EnhancedArchetypeDisplayProps> =\n React.memo(\n ({\n archetypes,\n selectedIndex,\n onArchetypeChange,\n onPlaySFX,\n width = 800,\n height = 300,\n isMobile = false,\n allowDetailedView = true,\n }) => {\n const [viewMode, setViewMode] = useState<\"compact\" | \"detailed\">(\n \"compact\",\n );\n\n // Use width for layout sizing decisions\n const isSmallScreen = width < 768;\n\n // Convert archetype data to card data format\n const cardData = useMemo(() => {\n return archetypes.map((archetype) => ({\n archetype: Object.values(PlayerArchetype).find(\n (key) => key === archetype.id,\n ) as PlayerArchetype,\n id: archetype.id,\n korean: archetype.korean,\n english: archetype.english,\n description: archetype.description,\n color: archetype.color,\n textureKey: archetype.textureKey,\n stats: archetype.stats,\n philosophy: archetype.philosophy,\n specialAbilities: archetype.specialAbilities ?? [], // Use actual data or empty array\n }));\n }, [archetypes]);\n\n // Get current archetype\n const currentArchetype = useMemo(() => {\n const archetype = archetypes[selectedIndex];\n return Object.values(PlayerArchetype).find(\n (key) => key === archetype.id,\n ) as PlayerArchetype;\n }, [archetypes, selectedIndex]);\n\n // Toggle view mode\n const handleToggleView = useCallback(() => {\n setViewMode((prev) => (prev === \"compact\" ? \"detailed\" : \"compact\"));\n onPlaySFX(\"menu_hover\");\n }, [onPlaySFX]);\n\n // Handle archetype change from card grid\n const handleArchetypeChangeFromGrid = useCallback(\n (archetype: PlayerArchetype) => {\n const index = archetypes.findIndex((a) => a.id === archetype);\n if (index !== -1) {\n onArchetypeChange(index);\n }\n },\n [archetypes, onArchetypeChange],\n );\n\n // Memoize height calculation for detailed view (minimum 600px or 2x compact height)\n const detailedHeight = useMemo(\n () => Math.max(height * 2, 600),\n [height],\n );\n\n return (\n <div\n style={{\n width: `${width}px`,\n display: \"flex\",\n flexDirection: \"column\",\n gap: isSmallScreen ? \"10px\" : \"16px\",\n }}\n data-testid=\"enhanced-archetype-display\"\n >\n {/* View toggle button (only show if allowed and not small screen) */}\n {allowDetailedView && !isSmallScreen && (\n <div\n style={{\n display: \"flex\",\n justifyContent: \"flex-end\",\n width: \"100%\",\n }}\n >\n <BaseButtonOverlayHtml\n korean={viewMode === \"compact\" ? \"상세 보기\" : \"간단 보기\"}\n english={viewMode === \"compact\" ? \"Detailed View\" : \"Compact View\"}\n onClick={handleToggleView}\n variant=\"secondary\"\n size=\"sm\"\n testId=\"view-toggle-button\"\n ariaLabel={`Toggle ${viewMode === \"compact\" ? \"detailed\" : \"compact\"} view`}\n />\n </div>\n )}\n\n {/* Render appropriate view */}\n {viewMode === \"compact\" ? (\n <ArchetypeDisplayOverlayHtml\n archetypes={archetypes}\n selectedIndex={selectedIndex}\n onArchetypeChange={onArchetypeChange}\n onPlaySFX={onPlaySFX}\n width={width}\n height={height}\n isMobile={isMobile}\n />\n ) : (\n <ArchetypeCardGrid\n archetypes={cardData}\n selectedArchetype={currentArchetype}\n onArchetypeChange={handleArchetypeChangeFromGrid}\n onPlaySFX={onPlaySFX}\n width={width}\n height={detailedHeight}\n isMobile={isMobile}\n />\n )}\n </div>\n );\n },\n );\n\nEnhancedArchetypeDisplay.displayName = \"EnhancedArchetypeDisplay\";\n\nexport default EnhancedArchetypeDisplay;\n"],"mappings":";;;;;;;;;;;AAwBA,IAAa,2BACX,MAAM,MACH,EACC,YACA,eACA,mBACA,WACA,QAAQ,KACR,SAAS,KACT,WAAW,OACX,oBAAoB,WAChB;CACJ,MAAM,CAAC,UAAU,eAAe,SAC9B,UACD;CAGD,MAAM,gBAAgB,QAAQ;CAG9B,MAAM,WAAW,cAAc;EAC7B,OAAO,WAAW,KAAK,eAAe;GACpC,WAAW,OAAO,OAAO,gBAAgB,CAAC,MACvC,QAAQ,QAAQ,UAAU,GAC5B;GACD,IAAI,UAAU;GACd,QAAQ,UAAU;GAClB,SAAS,UAAU;GACnB,aAAa,UAAU;GACvB,OAAO,UAAU;GACjB,YAAY,UAAU;GACtB,OAAO,UAAU;GACjB,YAAY,UAAU;GACtB,kBAAkB,UAAU,oBAAoB,EAAE;GACnD,EAAE;IACF,CAAC,WAAW,CAAC;CAGhB,MAAM,mBAAmB,cAAc;EACrC,MAAM,YAAY,WAAW;EAC7B,OAAO,OAAO,OAAO,gBAAgB,CAAC,MACnC,QAAQ,QAAQ,UAAU,GAC5B;IACA,CAAC,YAAY,cAAc,CAAC;CAG/B,MAAM,mBAAmB,kBAAkB;EACzC,aAAa,SAAU,SAAS,YAAY,aAAa,UAAW;EACpE,UAAU,aAAa;IACtB,CAAC,UAAU,CAAC;CAGf,MAAM,gCAAgC,aACnC,cAA+B;EAC9B,MAAM,QAAQ,WAAW,WAAW,MAAM,EAAE,OAAO,UAAU;EAC7D,IAAI,UAAU,IACZ,kBAAkB,MAAM;IAG5B,CAAC,YAAY,kBAAkB,CAChC;CAGD,MAAM,iBAAiB,cACf,KAAK,IAAI,SAAS,GAAG,IAAI,EAC/B,CAAC,OAAO,CACT;CAED,OACE,qBAAC,OAAD;EACE,OAAO;GACL,OAAO,GAAG,MAAM;GAChB,SAAS;GACT,eAAe;GACf,KAAK,gBAAgB,SAAS;GAC/B;EACD,eAAY;YAPd,CAUG,qBAAqB,CAAC,iBACrB,oBAAC,OAAD;GACE,OAAO;IACL,SAAS;IACT,gBAAgB;IAChB,OAAO;IACR;aAED,oBAAC,uBAAD;IACE,QAAQ,aAAa,YAAY,UAAU;IAC3C,SAAS,aAAa,YAAY,kBAAkB;IACpD,SAAS;IACT,SAAQ;IACR,MAAK;IACL,QAAO;IACP,WAAW,UAAU,aAAa,YAAY,aAAa,UAAU;IACrE,CAAA;GACE,CAAA,EAIP,aAAa,YACZ,oBAAC,6BAAD;GACc;GACG;GACI;GACR;GACJ;GACC;GACE;GACV,CAAA,GAEF,oBAAC,mBAAD;GACE,YAAY;GACZ,mBAAmB;GACnB,mBAAmB;GACR;GACJ;GACP,QAAQ;GACE;GACV,CAAA,CAEA;;EAGX;AAEH,yBAAyB,cAAc"}
@@ -1 +1 @@
1
- {"version":3,"file":"MenuButtonsOverlayHtml.js","names":[],"sources":["../../../../../src/components/screens/intro/components/MenuButtonsOverlayHtml.tsx"],"sourcesContent":["/**\n * MenuButtons - Reusable menu button grid for IntroScreen\n * \n * Provides 2x2 grid (desktop) or column (mobile) layout for menu navigation.\n * Extracted from MenuSectionOverlayHtml to reduce code duplication.\n * \n * @module components/screens/intro\n * @category Intro UI\n * @korean 메뉴버튼\n */\n\nimport React, { useCallback, useMemo } from \"react\";\nimport { GameMode } from \"../../../../types/common\";\nimport { FONT_FAMILY, KOREAN_COLORS } from \"../../../../types/constants\";\nimport { hexToRgbaString } from \"../../../../utils/colorUtils\";\nimport { UIHaptics } from \"../../../../utils/hapticFeedback\";\nimport {\n getButtonVisualEffectsOnly,\n} from \"../../../../utils/koreanThemeHelpers\";\nimport { getMobileKoreanFontSize } from \"../../../../utils/mobileUIUtils\";\nimport {\n getKoreanFontOptimization,\n} from \"../../../../utils/visualEffects\";\n\nexport interface MenuButtonsProps {\n /** Array of menu items to display */\n readonly menuItems: Array<{\n mode: GameMode;\n korean: string;\n english: string;\n }>;\n /** Currently selected menu item index */\n readonly selectedIndex: number;\n /** Index of currently hovered menu item (null if none) */\n readonly hoveredIndex: number | null;\n /** Callback when a menu item is selected */\n readonly onModeSelect: (mode: GameMode) => void;\n /** Callback when hover state changes */\n readonly onHoverChange: (index: number | null) => void;\n /** Callback to play sound effects */\n readonly onPlaySFX?: (sound: string) => void;\n /** Screen width for responsive sizing */\n readonly width?: number;\n /** Whether on mobile device (for haptics) */\n readonly isMobile?: boolean;\n}\n\n/**\n * MenuButtons Component\n * \n * Displays menu navigation buttons with:\n * - 2x2 grid layout on larger screens\n * - Column layout on small screens\n * - Selected/hovered state visualization\n * - Korean bilingual text\n * - Haptic feedback support\n * \n * This component delegates to inline button elements with custom styling\n * since BaseButtonOverlayHtml doesn't support the complex selection state\n * and color transitions needed for menu navigation.\n * \n * Reduces code duplication by 62 lines (MenuSectionOverlayHtml: 372 → 310)\n * \n * @example\n * ```tsx\n * <MenuButtons\n * menuItems={MENU_ITEMS}\n * selectedIndex={0}\n * hoveredIndex={null}\n * onModeSelect={(mode) => handleModeSelect(mode)}\n * onHoverChange={(idx) => setHovered(idx)}\n * width={800}\n * />\n * ```\n */\nexport const MenuButtons: React.FC<MenuButtonsProps> = ({\n menuItems,\n selectedIndex,\n hoveredIndex,\n onModeSelect,\n onHoverChange,\n onPlaySFX,\n width = 800,\n isMobile: _isMobile = false, // Prefix with _ to indicate intentionally unused\n}) => {\n // Responsive sizing based on screen width\n const isSmallScreen = width < 768;\n const useGridLayout = !isSmallScreen;\n const buttonHeight = isSmallScreen ? 44 : 40;\n const buttonFontSize = isSmallScreen\n ? getMobileKoreanFontSize(\"SMALL\", width ?? 375)\n : 13;\n const buttonGap = isSmallScreen ? 6 : 8;\n\n // Memoize button state colors\n const colors = useMemo(\n () => ({\n buttonSelectedBg: hexToRgbaString(KOREAN_COLORS.ACCENT_GOLD, 0.98),\n buttonHoveredBg: hexToRgbaString(KOREAN_COLORS.UI_BACKGROUND_LIGHT, 0.92),\n buttonDefaultBg: hexToRgbaString(\n KOREAN_COLORS.UI_BACKGROUND_MEDIUM,\n 0.92,\n ),\n buttonSelectedBorder: hexToRgbaString(\n KOREAN_COLORS.UI_BACKGROUND_DARK,\n 1.0,\n ),\n buttonHoveredBorder: hexToRgbaString(KOREAN_COLORS.ACCENT_GOLD, 0.8),\n buttonDefaultBorder: hexToRgbaString(KOREAN_COLORS.PRIMARY_CYAN, 0.7),\n textSelected: `#${KOREAN_COLORS.UI_BACKGROUND_DARK.toString(16).padStart(6, \"0\")}`,\n textHovered: `#${KOREAN_COLORS.ACCENT_GOLD.toString(16).padStart(6, \"0\")}`,\n textDefault: `#${KOREAN_COLORS.TEXT_PRIMARY.toString(16).padStart(6, \"0\")}`,\n }),\n [],\n );\n\n const handleButtonClick = useCallback(\n (mode: GameMode) => {\n UIHaptics.buttonTap();\n onModeSelect(mode);\n onPlaySFX?.(\"menu_select\");\n },\n [onModeSelect, onPlaySFX],\n );\n\n const handleButtonHover = useCallback(\n (index: number, isHovering: boolean) => {\n const newIndex = isHovering ? index : null;\n onHoverChange(newIndex);\n if (isHovering) {\n UIHaptics.menuHover();\n onPlaySFX?.(\"menu_hover\");\n }\n },\n [onHoverChange, onPlaySFX],\n );\n\n return (\n <div\n style={{\n display: \"grid\",\n gridTemplateColumns: useGridLayout ? \"1fr 1fr\" : \"1fr\",\n gap: `${buttonGap}px`,\n width: \"100%\",\n }}\n data-testid=\"main-menu-buttons\"\n >\n {menuItems.map((item, index) => {\n const isSelected = selectedIndex === index;\n const isHovered = hoveredIndex === index;\n\n // Get only visual effects (glow, transitions, transforms)\n const visualEffects = getButtonVisualEffectsOnly({\n variant: \"primary\",\n isHovered,\n isPressed: false,\n isFocused: false,\n glowIntensity: isSelected\n ? \"medium\"\n : isHovered\n ? \"medium\"\n : \"subtle\",\n hoverAnimation: \"combined\",\n });\n\n return (\n <button\n key={item.mode}\n onClick={() => handleButtonClick(item.mode)}\n onMouseEnter={() => handleButtonHover(index, true)}\n onMouseLeave={() => handleButtonHover(index, false)}\n onFocus={(e) => {\n e.currentTarget.style.outline = `3px solid ${hexToRgbaString(KOREAN_COLORS.ACCENT_GOLD)}`;\n e.currentTarget.style.outlineOffset = \"2px\";\n }}\n onBlur={(e) => {\n e.currentTarget.style.outline = \"none\";\n }}\n aria-label={`${item.korean} (${item.english})`}\n aria-selected={isSelected}\n role=\"menuitem\"\n style={{\n ...visualEffects,\n ...getKoreanFontOptimization(\n buttonFontSize,\n isSelected ? \"bold\" : \"normal\",\n ),\n fontFamily: FONT_FAMILY.KOREAN,\n width: \"100%\",\n height: `${buttonHeight}px`,\n // Menu-specific color, background, and border\n color: isSelected\n ? colors.textSelected\n : isHovered\n ? colors.textHovered\n : colors.textDefault,\n background: isSelected\n ? colors.buttonSelectedBg\n : isHovered\n ? colors.buttonHoveredBg\n : colors.buttonDefaultBg,\n border: isSelected\n ? `3px solid ${colors.buttonSelectedBorder}`\n : isHovered\n ? `2px solid ${colors.buttonHoveredBorder}`\n : `2px solid ${colors.buttonDefaultBorder}`,\n cursor: \"pointer\",\n }}\n data-testid={`menu-item-${item.mode}`}\n >\n {/* Add test ID aliases for backward compatibility */}\n {item.mode === GameMode.TRAINING && (\n <span\n data-testid=\"training-button\"\n style={{ display: \"none\" }}\n />\n )}\n {item.mode === GameMode.VERSUS && (\n <span data-testid=\"combat-button\" style={{ display: \"none\" }} />\n )}\n {item.korean} ({item.english})\n </button>\n );\n })}\n </div>\n );\n};\n\nexport default MenuButtons;\n"],"mappings":"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"}
1
+ {"version":3,"file":"MenuButtonsOverlayHtml.js","names":[],"sources":["../../../../../src/components/screens/intro/components/MenuButtonsOverlayHtml.tsx"],"sourcesContent":["/**\n * MenuButtons - Reusable menu button grid for IntroScreen\n * \n * Provides 2x2 grid (desktop) or column (mobile) layout for menu navigation.\n * Extracted from MenuSectionOverlayHtml to reduce code duplication.\n * \n * @module components/screens/intro\n * @category Intro UI\n * @korean 메뉴버튼\n */\n\nimport React, { useCallback, useMemo } from \"react\";\nimport { GameMode } from \"../../../../types/common\";\nimport { FONT_FAMILY, KOREAN_COLORS } from \"../../../../types/constants\";\nimport { hexToRgbaString } from \"../../../../utils/colorUtils\";\nimport { UIHaptics } from \"../../../../utils/hapticFeedback\";\nimport {\n getButtonVisualEffectsOnly,\n} from \"../../../../utils/koreanThemeHelpers\";\nimport { getMobileKoreanFontSize } from \"../../../../utils/mobileUIUtils\";\nimport {\n getKoreanFontOptimization,\n} from \"../../../../utils/visualEffects\";\n\nexport interface MenuButtonsProps {\n /** Array of menu items to display */\n readonly menuItems: Array<{\n mode: GameMode;\n korean: string;\n english: string;\n }>;\n /** Currently selected menu item index */\n readonly selectedIndex: number;\n /** Index of currently hovered menu item (null if none) */\n readonly hoveredIndex: number | null;\n /** Callback when a menu item is selected */\n readonly onModeSelect: (mode: GameMode) => void;\n /** Callback when hover state changes */\n readonly onHoverChange: (index: number | null) => void;\n /** Callback to play sound effects */\n readonly onPlaySFX?: (sound: string) => void;\n /** Screen width for responsive sizing */\n readonly width?: number;\n /** Whether on mobile device (for haptics) */\n readonly isMobile?: boolean;\n}\n\n/**\n * MenuButtons Component\n * \n * Displays menu navigation buttons with:\n * - 2x2 grid layout on larger screens\n * - Column layout on small screens\n * - Selected/hovered state visualization\n * - Korean bilingual text\n * - Haptic feedback support\n * \n * This component delegates to inline button elements with custom styling\n * since BaseButtonOverlayHtml doesn't support the complex selection state\n * and color transitions needed for menu navigation.\n * \n * Reduces code duplication by 62 lines (MenuSectionOverlayHtml: 372 → 310)\n * \n * @example\n * ```tsx\n * <MenuButtons\n * menuItems={MENU_ITEMS}\n * selectedIndex={0}\n * hoveredIndex={null}\n * onModeSelect={(mode) => handleModeSelect(mode)}\n * onHoverChange={(idx) => setHovered(idx)}\n * width={800}\n * />\n * ```\n */\nexport const MenuButtons: React.FC<MenuButtonsProps> = ({\n menuItems,\n selectedIndex,\n hoveredIndex,\n onModeSelect,\n onHoverChange,\n onPlaySFX,\n width = 800,\n isMobile: _isMobile = false, // Prefix with _ to indicate intentionally unused\n}) => {\n // Responsive sizing based on screen width\n const isSmallScreen = width < 768;\n const useGridLayout = !isSmallScreen;\n const buttonHeight = isSmallScreen ? 44 : 40;\n const buttonFontSize = isSmallScreen\n ? getMobileKoreanFontSize(\"SMALL\", width ?? 375)\n : 13;\n const buttonGap = isSmallScreen ? 6 : 8;\n\n // Memoize button state colors\n const colors = useMemo(\n () => ({\n buttonSelectedBg: hexToRgbaString(KOREAN_COLORS.ACCENT_GOLD, 0.98),\n buttonHoveredBg: hexToRgbaString(KOREAN_COLORS.UI_BACKGROUND_LIGHT, 0.92),\n buttonDefaultBg: hexToRgbaString(\n KOREAN_COLORS.UI_BACKGROUND_MEDIUM,\n 0.92,\n ),\n buttonSelectedBorder: hexToRgbaString(\n KOREAN_COLORS.UI_BACKGROUND_DARK,\n 1.0,\n ),\n buttonHoveredBorder: hexToRgbaString(KOREAN_COLORS.ACCENT_GOLD, 0.8),\n buttonDefaultBorder: hexToRgbaString(KOREAN_COLORS.PRIMARY_CYAN, 0.7),\n textSelected: `#${KOREAN_COLORS.UI_BACKGROUND_DARK.toString(16).padStart(6, \"0\")}`,\n textHovered: `#${KOREAN_COLORS.ACCENT_GOLD.toString(16).padStart(6, \"0\")}`,\n textDefault: `#${KOREAN_COLORS.TEXT_PRIMARY.toString(16).padStart(6, \"0\")}`,\n }),\n [],\n );\n\n const handleButtonClick = useCallback(\n (mode: GameMode) => {\n UIHaptics.buttonTap();\n onModeSelect(mode);\n onPlaySFX?.(\"menu_select\");\n },\n [onModeSelect, onPlaySFX],\n );\n\n const handleButtonHover = useCallback(\n (index: number, isHovering: boolean) => {\n const newIndex = isHovering ? index : null;\n onHoverChange(newIndex);\n if (isHovering) {\n UIHaptics.menuHover();\n onPlaySFX?.(\"menu_hover\");\n }\n },\n [onHoverChange, onPlaySFX],\n );\n\n return (\n <div\n style={{\n display: \"grid\",\n gridTemplateColumns: useGridLayout ? \"1fr 1fr\" : \"1fr\",\n gap: `${buttonGap}px`,\n width: \"100%\",\n }}\n data-testid=\"main-menu-buttons\"\n >\n {menuItems.map((item, index) => {\n const isSelected = selectedIndex === index;\n const isHovered = hoveredIndex === index;\n\n // Get only visual effects (glow, transitions, transforms)\n const visualEffects = getButtonVisualEffectsOnly({\n variant: \"primary\",\n isHovered,\n isPressed: false,\n isFocused: false,\n glowIntensity: isSelected\n ? \"medium\"\n : isHovered\n ? \"medium\"\n : \"subtle\",\n hoverAnimation: \"combined\",\n });\n\n return (\n <button\n key={item.mode}\n onClick={() => handleButtonClick(item.mode)}\n onMouseEnter={() => handleButtonHover(index, true)}\n onMouseLeave={() => handleButtonHover(index, false)}\n onFocus={(e) => {\n e.currentTarget.style.outline = `3px solid ${hexToRgbaString(KOREAN_COLORS.ACCENT_GOLD)}`;\n e.currentTarget.style.outlineOffset = \"2px\";\n }}\n onBlur={(e) => {\n e.currentTarget.style.outline = \"none\";\n }}\n aria-label={`${item.korean} (${item.english})`}\n aria-selected={isSelected}\n role=\"menuitem\"\n style={{\n ...visualEffects,\n ...getKoreanFontOptimization(\n buttonFontSize,\n isSelected ? \"bold\" : \"normal\",\n ),\n fontFamily: FONT_FAMILY.KOREAN,\n width: \"100%\",\n height: `${buttonHeight}px`,\n // Menu-specific color, background, and border\n color: isSelected\n ? colors.textSelected\n : isHovered\n ? colors.textHovered\n : colors.textDefault,\n background: isSelected\n ? colors.buttonSelectedBg\n : isHovered\n ? colors.buttonHoveredBg\n : colors.buttonDefaultBg,\n border: isSelected\n ? `3px solid ${colors.buttonSelectedBorder}`\n : isHovered\n ? `2px solid ${colors.buttonHoveredBorder}`\n : `2px solid ${colors.buttonDefaultBorder}`,\n cursor: \"pointer\",\n }}\n data-testid={`menu-item-${item.mode}`}\n >\n {/* Add test ID aliases for backward compatibility */}\n {item.mode === GameMode.TRAINING && (\n <span\n data-testid=\"training-button\"\n style={{ display: \"none\" }}\n />\n )}\n {item.mode === GameMode.VERSUS && (\n <span data-testid=\"combat-button\" style={{ display: \"none\" }} />\n )}\n {item.korean} ({item.english})\n </button>\n );\n })}\n </div>\n );\n};\n\nexport default MenuButtons;\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"MenuSectionOverlayHtml.js","names":[],"sources":["../../../../../src/components/screens/intro/components/MenuSectionOverlayHtml.tsx"],"sourcesContent":["import React, { useEffect, useMemo, useState } from \"react\";\nimport { GameMode } from \"../../../../types/common\";\nimport { FONT_FAMILY, KOREAN_COLORS } from \"../../../../types/constants\";\nimport {\n getEnhancedKoreanOverlayStyles,\n} from \"../../../../utils/koreanThemeHelpers\";\nimport { getMobileKoreanFontSize } from \"../../../../utils/mobileUIUtils\";\nimport { getSafeAreaPadding } from \"../../../../utils/safeAreaUtils\";\nimport {\n getNeonTextShadow,\n} from \"../../../../utils/visualEffects\";\nimport { MenuButtons } from \"./MenuButtonsOverlayHtml\";\n\nexport interface MenuSectionOverlayHtmlProps {\n readonly menuItems: Array<{\n mode: GameMode;\n korean: string;\n english: string;\n }>;\n readonly selectedIndex: number;\n readonly onModeSelect: (mode: GameMode) => void;\n readonly onSelectedIndexChange?: (index: number) => void;\n readonly onPlaySFX?: (sound: string) => void;\n readonly width?: number;\n readonly height?: number;\n readonly isMobile?: boolean; // For controls/haptics only, use width for layout sizing\n}\n\n/**\n * HTML-based MenuSection component for Three.js integration\n *\n * Optimized with React.memo for 60fps performance:\n * - Memoized to prevent unnecessary re-renders\n * - All callbacks use useCallback\n * - Styles pre-calculated and memoized\n */\nexport const MenuSectionOverlayHtml = React.memo<MenuSectionOverlayHtmlProps>(\n ({\n menuItems,\n selectedIndex,\n onModeSelect,\n onSelectedIndexChange,\n onPlaySFX,\n width = 800,\n height = 300,\n isMobile = false, // Default to false, parent should pass proper device detection\n }) => {\n const [hoveredItem, setHoveredItem] = useState<number | null>(null);\n const [focused, setFocused] = useState<boolean>(false);\n\n // Enhanced overlay styles with neon glow and depth\n const enhancedOverlayStyles = useMemo(\n () =>\n getEnhancedKoreanOverlayStyles({\n opacity: 0.96,\n glowIntensity: focused ? \"medium\" : \"subtle\",\n includeGradient: false,\n includeBackdropBlur: false,\n depthLayers: 2,\n }),\n [focused],\n );\n\n // Memoize title color\n const titleColor = useMemo(\n () => `#${KOREAN_COLORS.ACCENT_GOLD.toString(16).padStart(6, \"0\")}`,\n [],\n );\n\n // Keyboard navigation - stops propagation to prevent conflicts with parent\n useEffect(() => {\n const handleKeyDown = (event: KeyboardEvent) => {\n if (!onSelectedIndexChange) return;\n\n if (event.key === \"ArrowUp\") {\n event.preventDefault();\n event.stopPropagation();\n const nextIndex =\n selectedIndex === 0 ? menuItems.length - 1 : selectedIndex - 1;\n onSelectedIndexChange(nextIndex);\n onPlaySFX?.(\"menu_hover\");\n } else if (event.key === \"ArrowDown\") {\n event.preventDefault();\n event.stopPropagation();\n const nextIndex =\n selectedIndex === menuItems.length - 1 ? 0 : selectedIndex + 1;\n onSelectedIndexChange(nextIndex);\n onPlaySFX?.(\"menu_hover\");\n } else if (event.key === \" \" || event.key === \"Enter\") {\n event.preventDefault();\n event.stopPropagation();\n onPlaySFX?.(\"menu_select\");\n onModeSelect(menuItems[selectedIndex].mode);\n } else {\n const numKey = parseInt(event.key);\n if (numKey >= 1 && numKey <= menuItems.length) {\n event.stopPropagation();\n const targetIndex = numKey - 1;\n onSelectedIndexChange(targetIndex);\n onPlaySFX?.(\"menu_select\");\n onModeSelect(menuItems[targetIndex].mode);\n }\n }\n };\n window.addEventListener(\"keydown\", handleKeyDown);\n return () => window.removeEventListener(\"keydown\", handleKeyDown);\n }, [\n selectedIndex,\n menuItems,\n onSelectedIndexChange,\n onModeSelect,\n onPlaySFX,\n ]);\n\n // Focus ring for accessibility\n useEffect(() => {\n const handleFocus = () => setFocused(true);\n const handleBlur = () => setFocused(false);\n window.addEventListener(\"focusin\", handleFocus);\n window.addEventListener(\"focusout\", handleBlur);\n return () => {\n window.removeEventListener(\"focusin\", handleFocus);\n window.removeEventListener(\"focusout\", handleBlur);\n };\n }, []);\n\n // Use device detection from prop, with width-based fallback for sizing adjustments\n const isSmallScreen = width < 768; // Mobile-sized screens\n\n const containerPadding = isSmallScreen ? 16 : 12;\n const titleFontSize = isSmallScreen\n ? getMobileKoreanFontSize(\"SMALL\", width ?? 375)\n : 14;\n const sectionGap = isSmallScreen ? 8 : 6;\n\n // Safe area support for notched devices (use isMobile for actual device detection)\n const safeAreaStyles = useMemo(\n () =>\n isMobile ? getSafeAreaPadding([\"top\", \"bottom\"], containerPadding) : {},\n [isMobile, containerPadding],\n );\n\n return (\n <div\n style={{\n ...enhancedOverlayStyles,\n ...safeAreaStyles,\n width: `${width}px`,\n minHeight: `${height}px`,\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n justifyContent: \"center\",\n gap: `${sectionGap}px`,\n padding: `${containerPadding}px`,\n position: \"relative\",\n overflow: \"visible\",\n }}\n data-testid=\"main-menu-section\"\n >\n {/* Menu Title */}\n <div\n style={{\n fontSize: `${titleFontSize}px`,\n color: titleColor,\n fontWeight: \"bold\",\n fontFamily: FONT_FAMILY.KOREAN,\n textAlign: \"center\",\n textShadow: getNeonTextShadow(KOREAN_COLORS.ACCENT_GOLD, \"medium\"),\n }}\n data-testid=\"menu-title\"\n >\n 메인 메뉴 | Main Menu\n </div>\n\n {/* Menu Buttons - Extracted to MenuButtons component */}\n <MenuButtons\n menuItems={menuItems}\n selectedIndex={selectedIndex}\n hoveredIndex={hoveredItem}\n onModeSelect={onModeSelect}\n onHoverChange={setHoveredItem}\n onPlaySFX={onPlaySFX}\n width={width}\n isMobile={isMobile}\n />\n\n {/* Navigation Instructions */}\n <div\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n gap: isSmallScreen ? \"4px\" : \"6px\",\n textAlign: \"center\",\n fontSize: isSmallScreen ? \"10px\" : \"12px\",\n color: `#${KOREAN_COLORS.TEXT_SECONDARY.toString(16).padStart(\n 6,\n \"0\",\n )}`,\n fontFamily: FONT_FAMILY.KOREAN,\n marginTop: \"auto\",\n }}\n data-testid=\"navigation-hint-container\"\n >\n <div data-testid=\"menu-navigation-hint-korean\">\n 방향키/마우스로 이동 • Enter/클릭으로 선택 • 숫자키로 바로가기\n </div>\n <div\n style={{ fontSize: isSmallScreen ? \"9px\" : \"10px\" }}\n data-testid=\"menu-navigation-hint-english\"\n >\n Arrow keys/mouse to navigate • Enter/click to select • Number keys for\n shortcuts\n </div>\n </div>\n </div>\n );\n },\n (prevProps, nextProps) => {\n // Custom comparison for optimal re-render prevention\n // Including callback props prevents stale closures when parent provides\n // new functions that capture updated state.\n return (\n prevProps.selectedIndex === nextProps.selectedIndex &&\n prevProps.width === nextProps.width &&\n prevProps.height === nextProps.height &&\n prevProps.isMobile === nextProps.isMobile &&\n prevProps.menuItems.length === nextProps.menuItems.length &&\n prevProps.onModeSelect === nextProps.onModeSelect &&\n prevProps.onSelectedIndexChange === nextProps.onSelectedIndexChange &&\n prevProps.onPlaySFX === nextProps.onPlaySFX\n );\n },\n);\n\nMenuSectionOverlayHtml.displayName = \"MenuSectionOverlayHtml\";\n\nexport default MenuSectionOverlayHtml;\n"],"mappings":";;;;;;;;;;;;;;;;;;AAoCA,IAAa,yBAAyB,MAAM,MACzC,EACC,WACA,eACA,cACA,uBACA,WACA,QAAQ,KACR,SAAS,KACT,WAAW,YACP;CACN,MAAM,CAAC,aAAa,kBAAkB,SAAwB,KAAK;CACnE,MAAM,CAAC,SAAS,cAAc,SAAkB,MAAM;CAGtD,MAAM,wBAAwB,cAE1B,+BAA+B;EAC7B,SAAS;EACT,eAAe,UAAU,WAAW;EACpC,iBAAiB;EACjB,qBAAqB;EACrB,aAAa;EACd,CAAC,EACJ,CAAC,QAAQ,CACV;CAGD,MAAM,aAAa,cACX,IAAI,cAAc,YAAY,SAAS,GAAG,CAAC,SAAS,GAAG,IAAI,IACjE,EAAE,CACH;AAGD,iBAAgB;EACd,MAAM,iBAAiB,UAAyB;AAC9C,OAAI,CAAC,sBAAuB;AAE5B,OAAI,MAAM,QAAQ,WAAW;AAC3B,UAAM,gBAAgB;AACtB,UAAM,iBAAiB;AAGvB,0BADE,kBAAkB,IAAI,UAAU,SAAS,IAAI,gBAAgB,EAC/B;AAChC,gBAAY,aAAa;cAChB,MAAM,QAAQ,aAAa;AACpC,UAAM,gBAAgB;AACtB,UAAM,iBAAiB;AAGvB,0BADE,kBAAkB,UAAU,SAAS,IAAI,IAAI,gBAAgB,EAC/B;AAChC,gBAAY,aAAa;cAChB,MAAM,QAAQ,OAAO,MAAM,QAAQ,SAAS;AACrD,UAAM,gBAAgB;AACtB,UAAM,iBAAiB;AACvB,gBAAY,cAAc;AAC1B,iBAAa,UAAU,eAAe,KAAK;UACtC;IACL,MAAM,SAAS,SAAS,MAAM,IAAI;AAClC,QAAI,UAAU,KAAK,UAAU,UAAU,QAAQ;AAC7C,WAAM,iBAAiB;KACvB,MAAM,cAAc,SAAS;AAC7B,2BAAsB,YAAY;AAClC,iBAAY,cAAc;AAC1B,kBAAa,UAAU,aAAa,KAAK;;;;AAI/C,SAAO,iBAAiB,WAAW,cAAc;AACjD,eAAa,OAAO,oBAAoB,WAAW,cAAc;IAChE;EACD;EACA;EACA;EACA;EACA;EACD,CAAC;AAGF,iBAAgB;EACd,MAAM,oBAAoB,WAAW,KAAK;EAC1C,MAAM,mBAAmB,WAAW,MAAM;AAC1C,SAAO,iBAAiB,WAAW,YAAY;AAC/C,SAAO,iBAAiB,YAAY,WAAW;AAC/C,eAAa;AACX,UAAO,oBAAoB,WAAW,YAAY;AAClD,UAAO,oBAAoB,YAAY,WAAW;;IAEnD,EAAE,CAAC;CAGN,MAAM,gBAAgB,QAAQ;CAE9B,MAAM,mBAAmB,gBAAgB,KAAK;CAC9C,MAAM,gBAAgB,gBAClB,wBAAwB,SAAS,SAAS,IAAI,GAC9C;CACJ,MAAM,aAAa,gBAAgB,IAAI;CAGvC,MAAM,iBAAiB,cAEnB,WAAW,mBAAmB,CAAC,OAAO,SAAS,EAAE,iBAAiB,GAAG,EAAE,EACzE,CAAC,UAAU,iBAAiB,CAC7B;AAED,QACE,qBAAC,OAAD;EACE,OAAO;GACL,GAAG;GACH,GAAG;GACH,OAAO,GAAG,MAAM;GAChB,WAAW,GAAG,OAAO;GACrB,SAAS;GACT,eAAe;GACf,YAAY;GACZ,gBAAgB;GAChB,KAAK,GAAG,WAAW;GACnB,SAAS,GAAG,iBAAiB;GAC7B,UAAU;GACV,UAAU;GACX;EACD,eAAY;YAfd;GAkBE,oBAAC,OAAD;IACE,OAAO;KACL,UAAU,GAAG,cAAc;KAC3B,OAAO;KACP,YAAY;KACZ,YAAY,YAAY;KACxB,WAAW;KACX,YAAY,kBAAkB,cAAc,aAAa,SAAS;KACnE;IACD,eAAY;cACb;IAEK,CAAA;GAGN,oBAAC,aAAD;IACa;IACI;IACf,cAAc;IACA;IACd,eAAe;IACJ;IACJ;IACG;IACV,CAAA;GAGF,qBAAC,OAAD;IACE,OAAO;KACL,SAAS;KACT,eAAe;KACf,KAAK,gBAAgB,QAAQ;KAC7B,WAAW;KACX,UAAU,gBAAgB,SAAS;KACnC,OAAO,IAAI,cAAc,eAAe,SAAS,GAAG,CAAC,SACnD,GACA,IACD;KACD,YAAY,YAAY;KACxB,WAAW;KACZ;IACD,eAAY;cAdd,CAgBE,oBAAC,OAAD;KAAK,eAAY;eAA8B;KAEzC,CAAA,EACN,oBAAC,OAAD;KACE,OAAO,EAAE,UAAU,gBAAgB,QAAQ,QAAQ;KACnD,eAAY;eACb;KAGK,CAAA,CACF;;GACF;;IAGP,WAAW,cAAc;AAIxB,QACE,UAAU,kBAAkB,UAAU,iBACtC,UAAU,UAAU,UAAU,SAC9B,UAAU,WAAW,UAAU,UAC/B,UAAU,aAAa,UAAU,YACjC,UAAU,UAAU,WAAW,UAAU,UAAU,UACnD,UAAU,iBAAiB,UAAU,gBACrC,UAAU,0BAA0B,UAAU,yBAC9C,UAAU,cAAc,UAAU;EAGvC;AAED,uBAAuB,cAAc"}
1
+ {"version":3,"file":"MenuSectionOverlayHtml.js","names":[],"sources":["../../../../../src/components/screens/intro/components/MenuSectionOverlayHtml.tsx"],"sourcesContent":["import React, { useEffect, useMemo, useState } from \"react\";\nimport { GameMode } from \"../../../../types/common\";\nimport { FONT_FAMILY, KOREAN_COLORS } from \"../../../../types/constants\";\nimport {\n getEnhancedKoreanOverlayStyles,\n} from \"../../../../utils/koreanThemeHelpers\";\nimport { getMobileKoreanFontSize } from \"../../../../utils/mobileUIUtils\";\nimport { getSafeAreaPadding } from \"../../../../utils/safeAreaUtils\";\nimport {\n getNeonTextShadow,\n} from \"../../../../utils/visualEffects\";\nimport { MenuButtons } from \"./MenuButtonsOverlayHtml\";\n\nexport interface MenuSectionOverlayHtmlProps {\n readonly menuItems: Array<{\n mode: GameMode;\n korean: string;\n english: string;\n }>;\n readonly selectedIndex: number;\n readonly onModeSelect: (mode: GameMode) => void;\n readonly onSelectedIndexChange?: (index: number) => void;\n readonly onPlaySFX?: (sound: string) => void;\n readonly width?: number;\n readonly height?: number;\n readonly isMobile?: boolean; // For controls/haptics only, use width for layout sizing\n}\n\n/**\n * HTML-based MenuSection component for Three.js integration\n *\n * Optimized with React.memo for 60fps performance:\n * - Memoized to prevent unnecessary re-renders\n * - All callbacks use useCallback\n * - Styles pre-calculated and memoized\n */\nexport const MenuSectionOverlayHtml = React.memo<MenuSectionOverlayHtmlProps>(\n ({\n menuItems,\n selectedIndex,\n onModeSelect,\n onSelectedIndexChange,\n onPlaySFX,\n width = 800,\n height = 300,\n isMobile = false, // Default to false, parent should pass proper device detection\n }) => {\n const [hoveredItem, setHoveredItem] = useState<number | null>(null);\n const [focused, setFocused] = useState<boolean>(false);\n\n // Enhanced overlay styles with neon glow and depth\n const enhancedOverlayStyles = useMemo(\n () =>\n getEnhancedKoreanOverlayStyles({\n opacity: 0.96,\n glowIntensity: focused ? \"medium\" : \"subtle\",\n includeGradient: false,\n includeBackdropBlur: false,\n depthLayers: 2,\n }),\n [focused],\n );\n\n // Memoize title color\n const titleColor = useMemo(\n () => `#${KOREAN_COLORS.ACCENT_GOLD.toString(16).padStart(6, \"0\")}`,\n [],\n );\n\n // Keyboard navigation - stops propagation to prevent conflicts with parent\n useEffect(() => {\n const handleKeyDown = (event: KeyboardEvent) => {\n if (!onSelectedIndexChange) return;\n\n if (event.key === \"ArrowUp\") {\n event.preventDefault();\n event.stopPropagation();\n const nextIndex =\n selectedIndex === 0 ? menuItems.length - 1 : selectedIndex - 1;\n onSelectedIndexChange(nextIndex);\n onPlaySFX?.(\"menu_hover\");\n } else if (event.key === \"ArrowDown\") {\n event.preventDefault();\n event.stopPropagation();\n const nextIndex =\n selectedIndex === menuItems.length - 1 ? 0 : selectedIndex + 1;\n onSelectedIndexChange(nextIndex);\n onPlaySFX?.(\"menu_hover\");\n } else if (event.key === \" \" || event.key === \"Enter\") {\n event.preventDefault();\n event.stopPropagation();\n onPlaySFX?.(\"menu_select\");\n onModeSelect(menuItems[selectedIndex].mode);\n } else {\n const numKey = parseInt(event.key);\n if (numKey >= 1 && numKey <= menuItems.length) {\n event.stopPropagation();\n const targetIndex = numKey - 1;\n onSelectedIndexChange(targetIndex);\n onPlaySFX?.(\"menu_select\");\n onModeSelect(menuItems[targetIndex].mode);\n }\n }\n };\n window.addEventListener(\"keydown\", handleKeyDown);\n return () => window.removeEventListener(\"keydown\", handleKeyDown);\n }, [\n selectedIndex,\n menuItems,\n onSelectedIndexChange,\n onModeSelect,\n onPlaySFX,\n ]);\n\n // Focus ring for accessibility\n useEffect(() => {\n const handleFocus = () => setFocused(true);\n const handleBlur = () => setFocused(false);\n window.addEventListener(\"focusin\", handleFocus);\n window.addEventListener(\"focusout\", handleBlur);\n return () => {\n window.removeEventListener(\"focusin\", handleFocus);\n window.removeEventListener(\"focusout\", handleBlur);\n };\n }, []);\n\n // Use device detection from prop, with width-based fallback for sizing adjustments\n const isSmallScreen = width < 768; // Mobile-sized screens\n\n const containerPadding = isSmallScreen ? 16 : 12;\n const titleFontSize = isSmallScreen\n ? getMobileKoreanFontSize(\"SMALL\", width ?? 375)\n : 14;\n const sectionGap = isSmallScreen ? 8 : 6;\n\n // Safe area support for notched devices (use isMobile for actual device detection)\n const safeAreaStyles = useMemo(\n () =>\n isMobile ? getSafeAreaPadding([\"top\", \"bottom\"], containerPadding) : {},\n [isMobile, containerPadding],\n );\n\n return (\n <div\n style={{\n ...enhancedOverlayStyles,\n ...safeAreaStyles,\n width: `${width}px`,\n minHeight: `${height}px`,\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n justifyContent: \"center\",\n gap: `${sectionGap}px`,\n padding: `${containerPadding}px`,\n position: \"relative\",\n overflow: \"visible\",\n }}\n data-testid=\"main-menu-section\"\n >\n {/* Menu Title */}\n <div\n style={{\n fontSize: `${titleFontSize}px`,\n color: titleColor,\n fontWeight: \"bold\",\n fontFamily: FONT_FAMILY.KOREAN,\n textAlign: \"center\",\n textShadow: getNeonTextShadow(KOREAN_COLORS.ACCENT_GOLD, \"medium\"),\n }}\n data-testid=\"menu-title\"\n >\n 메인 메뉴 | Main Menu\n </div>\n\n {/* Menu Buttons - Extracted to MenuButtons component */}\n <MenuButtons\n menuItems={menuItems}\n selectedIndex={selectedIndex}\n hoveredIndex={hoveredItem}\n onModeSelect={onModeSelect}\n onHoverChange={setHoveredItem}\n onPlaySFX={onPlaySFX}\n width={width}\n isMobile={isMobile}\n />\n\n {/* Navigation Instructions */}\n <div\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n gap: isSmallScreen ? \"4px\" : \"6px\",\n textAlign: \"center\",\n fontSize: isSmallScreen ? \"10px\" : \"12px\",\n color: `#${KOREAN_COLORS.TEXT_SECONDARY.toString(16).padStart(\n 6,\n \"0\",\n )}`,\n fontFamily: FONT_FAMILY.KOREAN,\n marginTop: \"auto\",\n }}\n data-testid=\"navigation-hint-container\"\n >\n <div data-testid=\"menu-navigation-hint-korean\">\n 방향키/마우스로 이동 • Enter/클릭으로 선택 • 숫자키로 바로가기\n </div>\n <div\n style={{ fontSize: isSmallScreen ? \"9px\" : \"10px\" }}\n data-testid=\"menu-navigation-hint-english\"\n >\n Arrow keys/mouse to navigate • Enter/click to select • Number keys for\n shortcuts\n </div>\n </div>\n </div>\n );\n },\n (prevProps, nextProps) => {\n // Custom comparison for optimal re-render prevention\n // Including callback props prevents stale closures when parent provides\n // new functions that capture updated state.\n return (\n prevProps.selectedIndex === nextProps.selectedIndex &&\n prevProps.width === nextProps.width &&\n prevProps.height === nextProps.height &&\n prevProps.isMobile === nextProps.isMobile &&\n prevProps.menuItems.length === nextProps.menuItems.length &&\n prevProps.onModeSelect === nextProps.onModeSelect &&\n prevProps.onSelectedIndexChange === nextProps.onSelectedIndexChange &&\n prevProps.onPlaySFX === nextProps.onPlaySFX\n );\n },\n);\n\nMenuSectionOverlayHtml.displayName = \"MenuSectionOverlayHtml\";\n\nexport default MenuSectionOverlayHtml;\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"StatBarOverlayHtml.js","names":[],"sources":["../../../../../src/components/screens/intro/components/StatBarOverlayHtml.tsx"],"sourcesContent":["/**\n * StatBar - Enhanced stat visualization component with Korean theming\n * \n * Refactored to use useKoreanTheme hook for consistent styling\n * Displays horizontal bar chart for combat statistics\n * \n * Performance optimized with React.memo and useMemo\n * \n * @module components/screens/intro\n * @category Intro UI\n * @korean 능력치바\n */\n\nimport React, { useMemo } from \"react\";\nimport { useKoreanTheme } from \"../../../shared/base/useKoreanTheme\";\nimport { KOREAN_COLORS } from \"../../../../types/constants\";\nimport { hexToRgbaString, hexColorToCSS } from \"../../../../utils/colorUtils\";\n\nexport interface StatBarProps {\n readonly label: string; // Format: \"Korean | English\"\n readonly value: number; // 0-100 range\n readonly max?: number; // Maximum value for scaling\n readonly color?: number; // Hex color for bar\n readonly height?: number; // Bar height in pixels\n readonly showValue?: boolean; // Show numeric value\n readonly isMobile?: boolean;\n}\n\n/**\n * StatBar component - Displays a horizontal bar chart for stats\n * \n * Refactored to use useKoreanTheme for consistent Korean theming:\n * - Uses Korean typography configuration\n * - Applies Korean color palette\n * - Responsive sizing based on device type\n * - Memoized for optimal performance\n * \n * Used in archetype cards to visualize combat statistics\n * \n * @example\n * ```tsx\n * <StatBar\n * label=\"공격 | Attack\"\n * value={85}\n * max={100}\n * color={KOREAN_COLORS.PRIMARY_CYAN}\n * isMobile={false}\n * />\n * ```\n */\nexport const StatBar: React.FC<StatBarProps> = React.memo(\n ({\n label,\n value,\n max = 100,\n color = KOREAN_COLORS.PRIMARY_CYAN,\n height = 12,\n showValue = true,\n isMobile = false,\n }) => {\n // Use Korean theme hook for consistent styling\n const { koreanTypography, colors: themeColors, calculateResponsiveSize } = useKoreanTheme({\n size: \"small\",\n isMobile,\n });\n\n // Calculate percentage for bar width\n const percentage = useMemo(\n () => Math.min(100, Math.max(0, (value / max) * 100)),\n [value, max]\n );\n\n // Memoize color calculations with Korean theme\n const statBarColors = useMemo(\n () => ({\n barBackground: hexToRgbaString(themeColors.UI_BACKGROUND_MEDIUM, 1),\n barFill: hexToRgbaString(color, 0.9),\n barBorder: hexToRgbaString(color, 0.7),\n labelColor: hexColorToCSS(themeColors.TEXT_SECONDARY),\n valueColor: hexColorToCSS(color),\n }),\n [color, themeColors]\n );\n\n // Responsive sizing using Korean theme utilities\n const fontSize = calculateResponsiveSize(isMobile ? 9 : 11);\n const labelWidth = calculateResponsiveSize(isMobile ? 70 : 80);\n const valueWidth = calculateResponsiveSize(isMobile ? 25 : 30);\n\n return (\n <div\n style={{\n width: \"100%\",\n display: \"flex\",\n flexDirection: \"row\",\n alignItems: \"center\",\n gap: `${calculateResponsiveSize(12)}px`,\n }}\n data-testid={`stat-bar-${label.split(\"|\")[0].trim()}`}\n >\n {/* Stat label with Korean typography */}\n <div\n style={{\n width: `${labelWidth}px`,\n fontSize: `${fontSize}px`,\n fontFamily: koreanTypography.fontFamily,\n color: statBarColors.labelColor,\n flexShrink: 0,\n whiteSpace: \"nowrap\",\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n // Apply Korean typography optimization\n lineHeight: koreanTypography.lineHeight,\n letterSpacing: koreanTypography.letterSpacing,\n wordBreak: koreanTypography.wordBreak,\n }}\n data-testid=\"stat-label\"\n >\n {label}\n </div>\n\n {/* Stat bar container */}\n <div\n style={{\n flex: 1,\n height: `${height}px`,\n background: statBarColors.barBackground,\n borderRadius: \"2px\",\n position: \"relative\",\n border: `1px solid ${statBarColors.barBorder}`,\n overflow: \"hidden\",\n }}\n data-testid=\"stat-bar-container\"\n >\n {/* Stat bar fill with smooth transition */}\n <div\n style={{\n width: `${percentage}%`,\n height: \"100%\",\n background: statBarColors.barFill,\n borderRadius: \"2px\",\n transition: \"width 0.3s ease\",\n boxShadow: `0 0 8px ${hexToRgbaString(color, 0.5)}`,\n }}\n data-testid=\"stat-bar-fill\"\n />\n </div>\n\n {/* Stat value */}\n {showValue && (\n <div\n style={{\n width: `${valueWidth}px`,\n fontSize: `${fontSize}px`,\n fontWeight: \"bold\",\n fontFamily: koreanTypography.fontFamily,\n color: statBarColors.valueColor,\n textAlign: \"right\",\n flexShrink: 0,\n }}\n data-testid=\"stat-value\"\n >\n {Math.round(value)}\n </div>\n )}\n </div>\n );\n }\n);\n\nStatBar.displayName = \"StatBar\";\n\nexport default StatBar;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkDA,IAAa,UAAkC,MAAM,MAClD,EACC,OACA,OACA,MAAM,KACN,QAAQ,cAAc,cACtB,SAAS,IACT,YAAY,MACZ,WAAW,YACP;CAEJ,MAAM,EAAE,kBAAkB,QAAQ,aAAa,4BAA4B,eAAe;EACxF,MAAM;EACN;EACD,CAAC;CAGF,MAAM,aAAa,cACX,KAAK,IAAI,KAAK,KAAK,IAAI,GAAI,QAAQ,MAAO,IAAI,CAAC,EACrD,CAAC,OAAO,IAAI,CACb;CAGD,MAAM,gBAAgB,eACb;EACL,eAAe,gBAAgB,YAAY,sBAAsB,EAAE;EACnE,SAAS,gBAAgB,OAAO,GAAI;EACpC,WAAW,gBAAgB,OAAO,GAAI;EACtC,YAAY,cAAc,YAAY,eAAe;EACrD,YAAY,cAAc,MAAM;EACjC,GACD,CAAC,OAAO,YAAY,CACrB;CAGD,MAAM,WAAW,wBAAwB,WAAW,IAAI,GAAG;CAC3D,MAAM,aAAa,wBAAwB,WAAW,KAAK,GAAG;CAC9D,MAAM,aAAa,wBAAwB,WAAW,KAAK,GAAG;AAE9D,QACE,qBAAC,OAAD;EACE,OAAO;GACL,OAAO;GACP,SAAS;GACT,eAAe;GACf,YAAY;GACZ,KAAK,GAAG,wBAAwB,GAAG,CAAC;GACrC;EACD,eAAa,YAAY,MAAM,MAAM,IAAI,CAAC,GAAG,MAAM;YARrD;GAWE,oBAAC,OAAD;IACE,OAAO;KACL,OAAO,GAAG,WAAW;KACrB,UAAU,GAAG,SAAS;KACtB,YAAY,iBAAiB;KAC7B,OAAO,cAAc;KACrB,YAAY;KACZ,YAAY;KACZ,UAAU;KACV,cAAc;KAEd,YAAY,iBAAiB;KAC7B,eAAe,iBAAiB;KAChC,WAAW,iBAAiB;KAC7B;IACD,eAAY;cAEX;IACG,CAAA;GAGN,oBAAC,OAAD;IACE,OAAO;KACL,MAAM;KACN,QAAQ,GAAG,OAAO;KAClB,YAAY,cAAc;KAC1B,cAAc;KACd,UAAU;KACV,QAAQ,aAAa,cAAc;KACnC,UAAU;KACX;IACD,eAAY;cAGZ,oBAAC,OAAD;KACE,OAAO;MACL,OAAO,GAAG,WAAW;MACrB,QAAQ;MACR,YAAY,cAAc;MAC1B,cAAc;MACd,YAAY;MACZ,WAAW,WAAW,gBAAgB,OAAO,GAAI;MAClD;KACD,eAAY;KACZ,CAAA;IACE,CAAA;GAGL,aACC,oBAAC,OAAD;IACE,OAAO;KACL,OAAO,GAAG,WAAW;KACrB,UAAU,GAAG,SAAS;KACtB,YAAY;KACZ,YAAY,iBAAiB;KAC7B,OAAO,cAAc;KACrB,WAAW;KACX,YAAY;KACb;IACD,eAAY;cAEX,KAAK,MAAM,MAAM;IACd,CAAA;GAEJ;;EAGX;AAED,QAAQ,cAAc"}
1
+ {"version":3,"file":"StatBarOverlayHtml.js","names":[],"sources":["../../../../../src/components/screens/intro/components/StatBarOverlayHtml.tsx"],"sourcesContent":["/**\n * StatBar - Enhanced stat visualization component with Korean theming\n * \n * Refactored to use useKoreanTheme hook for consistent styling\n * Displays horizontal bar chart for combat statistics\n * \n * Performance optimized with React.memo and useMemo\n * \n * @module components/screens/intro\n * @category Intro UI\n * @korean 능력치바\n */\n\nimport React, { useMemo } from \"react\";\nimport { useKoreanTheme } from \"../../../shared/base/useKoreanTheme\";\nimport { KOREAN_COLORS } from \"../../../../types/constants\";\nimport { hexToRgbaString, hexColorToCSS } from \"../../../../utils/colorUtils\";\n\nexport interface StatBarProps {\n readonly label: string; // Format: \"Korean | English\"\n readonly value: number; // 0-100 range\n readonly max?: number; // Maximum value for scaling\n readonly color?: number; // Hex color for bar\n readonly height?: number; // Bar height in pixels\n readonly showValue?: boolean; // Show numeric value\n readonly isMobile?: boolean;\n}\n\n/**\n * StatBar component - Displays a horizontal bar chart for stats\n * \n * Refactored to use useKoreanTheme for consistent Korean theming:\n * - Uses Korean typography configuration\n * - Applies Korean color palette\n * - Responsive sizing based on device type\n * - Memoized for optimal performance\n * \n * Used in archetype cards to visualize combat statistics\n * \n * @example\n * ```tsx\n * <StatBar\n * label=\"공격 | Attack\"\n * value={85}\n * max={100}\n * color={KOREAN_COLORS.PRIMARY_CYAN}\n * isMobile={false}\n * />\n * ```\n */\nexport const StatBar: React.FC<StatBarProps> = React.memo(\n ({\n label,\n value,\n max = 100,\n color = KOREAN_COLORS.PRIMARY_CYAN,\n height = 12,\n showValue = true,\n isMobile = false,\n }) => {\n // Use Korean theme hook for consistent styling\n const { koreanTypography, colors: themeColors, calculateResponsiveSize } = useKoreanTheme({\n size: \"small\",\n isMobile,\n });\n\n // Calculate percentage for bar width\n const percentage = useMemo(\n () => Math.min(100, Math.max(0, (value / max) * 100)),\n [value, max]\n );\n\n // Memoize color calculations with Korean theme\n const statBarColors = useMemo(\n () => ({\n barBackground: hexToRgbaString(themeColors.UI_BACKGROUND_MEDIUM, 1),\n barFill: hexToRgbaString(color, 0.9),\n barBorder: hexToRgbaString(color, 0.7),\n labelColor: hexColorToCSS(themeColors.TEXT_SECONDARY),\n valueColor: hexColorToCSS(color),\n }),\n [color, themeColors]\n );\n\n // Responsive sizing using Korean theme utilities\n const fontSize = calculateResponsiveSize(isMobile ? 9 : 11);\n const labelWidth = calculateResponsiveSize(isMobile ? 70 : 80);\n const valueWidth = calculateResponsiveSize(isMobile ? 25 : 30);\n\n return (\n <div\n style={{\n width: \"100%\",\n display: \"flex\",\n flexDirection: \"row\",\n alignItems: \"center\",\n gap: `${calculateResponsiveSize(12)}px`,\n }}\n data-testid={`stat-bar-${label.split(\"|\")[0].trim()}`}\n >\n {/* Stat label with Korean typography */}\n <div\n style={{\n width: `${labelWidth}px`,\n fontSize: `${fontSize}px`,\n fontFamily: koreanTypography.fontFamily,\n color: statBarColors.labelColor,\n flexShrink: 0,\n whiteSpace: \"nowrap\",\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n // Apply Korean typography optimization\n lineHeight: koreanTypography.lineHeight,\n letterSpacing: koreanTypography.letterSpacing,\n wordBreak: koreanTypography.wordBreak,\n }}\n data-testid=\"stat-label\"\n >\n {label}\n </div>\n\n {/* Stat bar container */}\n <div\n style={{\n flex: 1,\n height: `${height}px`,\n background: statBarColors.barBackground,\n borderRadius: \"2px\",\n position: \"relative\",\n border: `1px solid ${statBarColors.barBorder}`,\n overflow: \"hidden\",\n }}\n data-testid=\"stat-bar-container\"\n >\n {/* Stat bar fill with smooth transition */}\n <div\n style={{\n width: `${percentage}%`,\n height: \"100%\",\n background: statBarColors.barFill,\n borderRadius: \"2px\",\n transition: \"width 0.3s ease\",\n boxShadow: `0 0 8px ${hexToRgbaString(color, 0.5)}`,\n }}\n data-testid=\"stat-bar-fill\"\n />\n </div>\n\n {/* Stat value */}\n {showValue && (\n <div\n style={{\n width: `${valueWidth}px`,\n fontSize: `${fontSize}px`,\n fontWeight: \"bold\",\n fontFamily: koreanTypography.fontFamily,\n color: statBarColors.valueColor,\n textAlign: \"right\",\n flexShrink: 0,\n }}\n data-testid=\"stat-value\"\n >\n {Math.round(value)}\n </div>\n )}\n </div>\n );\n }\n);\n\nStatBar.displayName = \"StatBar\";\n\nexport default StatBar;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkDA,IAAa,UAAkC,MAAM,MAClD,EACC,OACA,OACA,MAAM,KACN,QAAQ,cAAc,cACtB,SAAS,IACT,YAAY,MACZ,WAAW,YACP;CAEJ,MAAM,EAAE,kBAAkB,QAAQ,aAAa,4BAA4B,eAAe;EACxF,MAAM;EACN;EACD,CAAC;CAGF,MAAM,aAAa,cACX,KAAK,IAAI,KAAK,KAAK,IAAI,GAAI,QAAQ,MAAO,IAAI,CAAC,EACrD,CAAC,OAAO,IAAI,CACb;CAGD,MAAM,gBAAgB,eACb;EACL,eAAe,gBAAgB,YAAY,sBAAsB,EAAE;EACnE,SAAS,gBAAgB,OAAO,GAAI;EACpC,WAAW,gBAAgB,OAAO,GAAI;EACtC,YAAY,cAAc,YAAY,eAAe;EACrD,YAAY,cAAc,MAAM;EACjC,GACD,CAAC,OAAO,YAAY,CACrB;CAGD,MAAM,WAAW,wBAAwB,WAAW,IAAI,GAAG;CAC3D,MAAM,aAAa,wBAAwB,WAAW,KAAK,GAAG;CAC9D,MAAM,aAAa,wBAAwB,WAAW,KAAK,GAAG;CAE9D,OACE,qBAAC,OAAD;EACE,OAAO;GACL,OAAO;GACP,SAAS;GACT,eAAe;GACf,YAAY;GACZ,KAAK,GAAG,wBAAwB,GAAG,CAAC;GACrC;EACD,eAAa,YAAY,MAAM,MAAM,IAAI,CAAC,GAAG,MAAM;YARrD;GAWE,oBAAC,OAAD;IACE,OAAO;KACL,OAAO,GAAG,WAAW;KACrB,UAAU,GAAG,SAAS;KACtB,YAAY,iBAAiB;KAC7B,OAAO,cAAc;KACrB,YAAY;KACZ,YAAY;KACZ,UAAU;KACV,cAAc;KAEd,YAAY,iBAAiB;KAC7B,eAAe,iBAAiB;KAChC,WAAW,iBAAiB;KAC7B;IACD,eAAY;cAEX;IACG,CAAA;GAGN,oBAAC,OAAD;IACE,OAAO;KACL,MAAM;KACN,QAAQ,GAAG,OAAO;KAClB,YAAY,cAAc;KAC1B,cAAc;KACd,UAAU;KACV,QAAQ,aAAa,cAAc;KACnC,UAAU;KACX;IACD,eAAY;cAGZ,oBAAC,OAAD;KACE,OAAO;MACL,OAAO,GAAG,WAAW;MACrB,QAAQ;MACR,YAAY,cAAc;MAC1B,cAAc;MACd,YAAY;MACZ,WAAW,WAAW,gBAAgB,OAAO,GAAI;MAClD;KACD,eAAY;KACZ,CAAA;IACE,CAAA;GAGL,aACC,oBAAC,OAAD;IACE,OAAO;KACL,OAAO,GAAG,WAAW;KACrB,UAAU,GAAG,SAAS;KACtB,YAAY;KACZ,YAAY,iBAAiB;KAC7B,OAAO,cAAc;KACrB,WAAW;KACX,YAAY;KACb;IACD,eAAY;cAEX,KAAK,MAAM,MAAM;IACd,CAAA;GAEJ;;EAGX;AAED,QAAQ,cAAc"}
@@ -1 +1 @@
1
- {"version":3,"file":"PhilosophyScreen3D.js","names":[],"sources":["../../../../src/components/screens/philosophy/PhilosophyScreen3D.tsx"],"sourcesContent":["// UI renders outside Canvas in absolute-positioned div - no Html needed\nimport { Canvas } from \"@react-three/fiber\";\nimport React, { useCallback, useEffect, useMemo } from \"react\";\nimport { useAudio } from \"../../../audio/AudioProvider\";\nimport { useWebGLContextLossHandler } from \"../../../hooks/useWebGLContextLossHandler\";\nimport { useWindowSize } from \"../../../hooks/useWindowSize\";\nimport { PLAYER_ARCHETYPES_DATA } from \"../../../systems\";\nimport { KoreanCulture } from \"../../../systems/trigram/KoreanCulture\";\nimport { TRIGRAM_DATA } from \"../../../systems/trigram/types\";\nimport { TrigramStance } from \"../../../types\";\nimport { Z_INDEX } from \"../../../types/LayoutTypes\";\nimport { hexToRgbaString } from \"../../../utils/colorUtils\";\nimport { shouldUseMobileControls } from \"../../../utils/deviceDetection\";\nimport { getLayoutConstants } from \"../../../utils/responsiveLayoutHelpers\";\nimport { useKoreanTheme } from \"../../shared/base/useKoreanTheme\";\nimport { BackgroundScene3D } from \"../../shared/three\";\nimport { BackButton, LinkButton } from \"../../shared/ui/BackButton\";\nimport { VolumeControl } from \"../../shared/ui/VolumeControl\";\n\nexport interface PhilosophyScreen3DProps {\n readonly onReturnToMenu: () => void;\n readonly width?: number;\n readonly height?: number;\n}\n\n/**\n * Three.js-based PhilosophyScreen Component\n */\nexport const PhilosophyScreen3D: React.FC<PhilosophyScreen3DProps> = ({\n onReturnToMenu,\n width: propWidth,\n height: propHeight,\n}) => {\n // UI now renders outside Canvas - no mount state needed\n\n // Handle WebGL context loss and restoration (for 3D background only)\n useWebGLContextLossHandler({\n onContextLost: () => {\n console.warn(\"⚠️ WebGL context lost in PhilosophyScreen\");\n },\n onContextRestored: () => {\n console.log(\"✓ WebGL context restored in PhilosophyScreen\");\n },\n autoRestore: true,\n });\n\n const audio = useAudio();\n const { width, height } = useWindowSize();\n\n // Use prop dimensions if provided, otherwise use window size\n const screenWidth = propWidth ?? width;\n const screenHeight = propHeight ?? height;\n\n // Responsive layout calculations with large desktop support\n // Use device detection instead of width-only breakpoint to correctly identify high-res mobile devices\n const isMobile = shouldUseMobileControls();\n // Only use width for tablet/desktop distinction when NOT mobile\n const isTablet = useMemo(\n () => !isMobile && screenWidth >= 768 && screenWidth < 1024,\n [isMobile, screenWidth],\n );\n const isLargeDesktop = useMemo(\n () => !isMobile && screenWidth >= 1920,\n [isMobile, screenWidth],\n ); // 4K/2K displays\n\n // Use centralized responsive layout helper for consistent scaling\n const layoutConstants = useMemo(\n () => getLayoutConstants(screenWidth),\n [screenWidth],\n );\n\n // Use Korean theme hook for consistent theming\n const theme = useKoreanTheme({\n variant: \"primary\",\n size: \"md\",\n isMobile,\n });\n\n // Memoize scrollbar style to prevent re-creating style tag on every render\n const scrollbarStyle = useMemo(\n () => ({\n __html: `\n .philosophy-scrollbar::-webkit-scrollbar {\n width: 12px !important;\n display: block !important;\n }\n .philosophy-scrollbar::-webkit-scrollbar-track {\n background: ${hexToRgbaString(theme.colors.UI_BACKGROUND_DARK, 0.8)};\n border-radius: 6px;\n }\n .philosophy-scrollbar::-webkit-scrollbar-thumb {\n background: ${hexToRgbaString(theme.colors.ACCENT_GOLD, 1)};\n border-radius: 6px;\n border: 2px solid ${hexToRgbaString(theme.colors.UI_BACKGROUND_DARK, 0.8)};\n }\n .philosophy-scrollbar::-webkit-scrollbar-thumb:hover {\n background: ${hexToRgbaString(theme.colors.PRIMARY_CYAN, 1)};\n }\n `,\n }),\n [theme],\n );\n\n // Memoize colors from theme for performance\n const colors = useMemo(\n () => ({\n background: hexToRgbaString(theme.colors.UI_BACKGROUND_DARK, 0.95),\n headerBg: hexToRgbaString(theme.colors.UI_BACKGROUND_DARK, 0.9),\n sectionBg: hexToRgbaString(theme.colors.UI_BACKGROUND_LIGHT, 0.8),\n borderGold: hexToRgbaString(theme.colors.ACCENT_GOLD, 0.8),\n borderCyan: hexToRgbaString(theme.colors.PRIMARY_CYAN, 0.6),\n borderMagenta: hexToRgbaString(theme.colors.SECONDARY_MAGENTA, 0.6),\n borderRed: hexToRgbaString(theme.colors.KOREAN_RED, 0.6),\n textPrimary: `#${theme.colors.TEXT_PRIMARY.toString(16).padStart(6, \"0\")}`,\n textSecondary: `#${theme.colors.TEXT_SECONDARY.toString(16).padStart(6, \"0\")}`,\n textTertiary: `#${theme.colors.TEXT_TERTIARY.toString(16).padStart(6, \"0\")}`,\n accentGold: `#${theme.colors.ACCENT_GOLD.toString(16).padStart(6, \"0\")}`,\n accentCyan: `#${theme.colors.PRIMARY_CYAN.toString(16).padStart(6, \"0\")}`,\n }),\n [theme],\n );\n\n // Audio lifecycle management for philosophy screen\n useEffect(() => {\n const startMusic = async () => {\n await audio.fadeIn(\"underground_theme\", 2000);\n };\n void startMusic().catch((err) =>\n console.warn(\"Failed to start philosophy music:\", err),\n );\n\n return () => {\n void audio\n .fadeOut(2000)\n .then(() => audio.stopMusic())\n .catch((err) => console.warn(\"Failed to stop philosophy music:\", err));\n };\n }, [audio]);\n\n // Enhanced keyboard handling for screen-level navigation\n useEffect(() => {\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key === \"Escape\" || event.key.toLowerCase() === \"m\") {\n event.preventDefault();\n audio.playSFX(\"menu_back\");\n onReturnToMenu();\n }\n };\n\n window.addEventListener(\"keydown\", handleKeyDown, { passive: false });\n return () => window.removeEventListener(\"keydown\", handleKeyDown);\n }, [onReturnToMenu, audio]);\n\n // Handle back button click\n const handleBackClick = useCallback(() => {\n audio.playSFX(\"menu_back\");\n onReturnToMenu();\n }, [audio, onReturnToMenu]);\n\n // Handle ISMS link click\n const handleISMSClick = useCallback(() => {\n audio.playSFX(\"menu_select\");\n window.open(\"https://github.com/Hack23/ISMS-PUBLIC\", \"_blank\");\n }, [audio]);\n\n // Get philosophy data\n const martialValues = useMemo(\n () => Object.entries(KoreanCulture.MARTIAL_VALUES),\n [],\n );\n\n const trigramPhilosophies = useMemo(\n () =>\n Object.entries(TRIGRAM_DATA).map(([stance, data]) => ({\n stance: stance as TrigramStance,\n ...data,\n })),\n [],\n );\n\n const archetypes = useMemo(() => Object.entries(PLAYER_ARCHETYPES_DATA), []);\n\n // Grid layout calculations\n const valuesPerRow = isMobile ? 3 : isTablet ? 4 : isLargeDesktop ? 8 : 6;\n const trigramsPerRow = isMobile ? 2 : isTablet ? 3 : isLargeDesktop ? 5 : 4;\n\n return (\n <div\n style={{\n width: screenWidth,\n height: screenHeight,\n position: \"relative\",\n overflow: \"hidden\",\n }}\n data-testid=\"philosophy-screen\"\n >\n {/* Volume Control - outside Canvas to maintain AudioProvider context */}\n <VolumeControl position=\"top-right\" compact={isMobile} />\n\n {/* Three.js Canvas for 3D background */}\n <Canvas\n style={{\n position: \"absolute\",\n top: 0,\n left: 0,\n width: \"100%\",\n height: \"100%\",\n zIndex: Z_INDEX.ARENA,\n }}\n gl={{\n antialias: true,\n alpha: false,\n powerPreference: \"high-performance\",\n }}\n dpr={[1, 2]}\n camera={{ position: [0, 5, 10], fov: 75 }}\n onCreated={({ gl }) => {\n gl.setClearColor(theme.colors.UI_BACKGROUND_DARK, 1);\n }}\n >\n {/* 3D Background Scene */}\n <BackgroundScene3D theme=\"philosophy\" />\n </Canvas>\n\n {/* UI Overlay (positioned absolutely over Canvas) - matches CombatScreen pattern */}\n <div\n style={{\n position: \"absolute\",\n top: 0,\n left: 0,\n width: \"100%\",\n height: \"100%\",\n pointerEvents: \"none\",\n zIndex: Z_INDEX.HUD,\n }}\n data-testid=\"philosophy-hud-overlay\"\n >\n <div\n style={{\n width: \"100%\",\n height: \"100%\",\n display: \"flex\",\n flexDirection: \"column\",\n color: colors.textPrimary,\n fontFamily: theme.koreanTypography.fontFamily,\n lineHeight: theme.koreanTypography.lineHeight,\n pointerEvents: \"auto\",\n }}\n >\n {/* Header */}\n <div\n style={{\n height: `${layoutConstants.headerHeight}px`,\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n justifyContent: \"center\",\n background: colors.headerBg,\n borderBottom: `3px solid ${colors.borderGold}`,\n padding: `${layoutConstants.padding}px`,\n position: \"relative\",\n }}\n data-testid=\"philosophy-header\"\n >\n <h1\n style={{\n fontSize: isMobile ? \"28px\" : \"36px\",\n fontWeight: \"bold\",\n color: colors.accentGold,\n margin: 0,\n textShadow: `0 0 15px ${hexToRgbaString(\n theme.colors.ACCENT_GOLD,\n 0.6,\n )}`,\n }}\n >\n 흑괘 무도 철학\n </h1>\n <p\n style={{\n fontSize: isMobile ? \"14px\" : \"18px\",\n color: colors.textSecondary,\n margin: \"8px 0 0 0\",\n }}\n >\n Black Trigram Martial Philosophy\n </p>\n\n {/* Decorative line */}\n <div\n style={{\n width: \"80%\",\n height: \"2px\",\n background: `linear-gradient(90deg, transparent, ${colors.borderGold}, transparent)`,\n marginTop: \"10px\",\n }}\n />\n </div>\n\n {/* WebKit Scrollbar Styling - Using !important to override global hide */}\n <style dangerouslySetInnerHTML={scrollbarStyle} />\n\n {/* Content Area - Scrollable */}\n <div\n className=\"philosophy-scrollbar\"\n style={{\n flex: 1,\n overflowY: \"auto\",\n overflowX: \"hidden\",\n padding: `${layoutConstants.padding}px`,\n scrollbarWidth: \"thin\",\n scrollbarColor: `${colors.accentGold} ${colors.sectionBg}`,\n }}\n data-testid=\"philosophy-content\"\n >\n {/* Martial Values Section */}\n <div\n style={{\n marginBottom: `${layoutConstants.sectionSpacing}px`,\n background: colors.sectionBg,\n borderRadius: \"10px\",\n border: `2px solid ${colors.borderRed}`,\n padding: \"20px\",\n }}\n data-testid=\"martial-values\"\n >\n <h2\n style={{\n fontSize: isMobile ? \"18px\" : \"22px\",\n fontWeight: \"bold\",\n color: colors.accentGold,\n margin: \"0 0 20px 0\",\n }}\n >\n 무도 가치관 (Martial Values)\n </h2>\n\n {/* Values Grid */}\n <div\n style={{\n display: \"grid\",\n gridTemplateColumns: `repeat(${valuesPerRow}, 1fr)`,\n gap: \"10px\",\n }}\n >\n {martialValues.map(([key, value]) => (\n <div\n key={key}\n style={{\n background: hexToRgbaString(\n theme.colors.UI_BACKGROUND_DARK,\n 0.7,\n ),\n borderRadius: \"6px\",\n border: `1px solid ${colors.borderGold}`,\n padding: \"12px\",\n textAlign: \"center\",\n }}\n data-testid={`martial-value-${key}`}\n >\n <div\n style={{\n fontSize: isMobile ? \"16px\" : \"18px\",\n fontWeight: \"bold\",\n color: colors.textPrimary,\n marginBottom: \"4px\",\n }}\n >\n {value.korean}\n </div>\n <div\n style={{\n fontSize: isMobile ? \"11px\" : \"13px\",\n color: colors.textSecondary,\n }}\n >\n {value.english}\n </div>\n </div>\n ))}\n </div>\n </div>\n\n {/* Trigram Philosophy Section */}\n <div\n style={{\n marginBottom: `${layoutConstants.sectionSpacing}px`,\n background: colors.sectionBg,\n borderRadius: \"10px\",\n border: `2px solid ${colors.borderCyan}`,\n padding: \"20px\",\n }}\n data-testid=\"trigram-philosophy\"\n >\n <h2\n style={{\n fontSize: isMobile ? \"18px\" : \"22px\",\n fontWeight: \"bold\",\n color: colors.accentGold,\n margin: \"0 0 20px 0\",\n }}\n >\n 팔괘 철학 (Eight Trigrams Philosophy)\n </h2>\n\n {/* Trigrams Grid */}\n <div\n style={{\n display: \"grid\",\n gridTemplateColumns: `repeat(${trigramsPerRow}, 1fr)`,\n gap: \"15px\",\n }}\n >\n {trigramPhilosophies.map((trigram) => (\n <div\n key={trigram.stance}\n style={{\n background: hexToRgbaString(trigram.theme.primary, 0.25),\n borderRadius: \"8px\",\n border: `2px solid #${trigram.theme.primary\n .toString(16)\n .padStart(6, \"0\")}`,\n padding: \"15px\",\n }}\n data-testid={`trigram-${trigram.stance}`}\n >\n {/* Trigram Symbol */}\n <div\n style={{\n fontSize: isMobile ? \"32px\" : \"40px\",\n color: `#${trigram.theme.primary\n .toString(16)\n .padStart(6, \"0\")}`,\n textAlign: \"center\",\n marginBottom: \"10px\",\n }}\n >\n {trigram.symbol}\n </div>\n\n {/* Name with Chinese character */}\n <div\n style={{\n fontSize: isMobile ? \"12px\" : \"14px\",\n fontWeight: \"bold\",\n color: colors.textPrimary,\n textAlign: \"center\",\n marginBottom: \"4px\",\n }}\n >\n {trigram.name.korean} ({trigram.name.english})\n </div>\n\n {/* Chinese character and attribute */}\n <div\n style={{\n fontSize: isMobile ? \"11px\" : \"13px\",\n color: colors.accentGold,\n textAlign: \"center\",\n marginBottom: \"8px\",\n fontWeight: \"bold\",\n }}\n >\n {trigram.chinese} - {trigram.attribute.chinese},{\" \"}\n {trigram.attribute.korean}\n </div>\n\n {/* Core meaning */}\n <div\n style={{\n fontSize: isMobile ? \"10px\" : \"11px\",\n color: colors.accentCyan,\n textAlign: \"center\",\n marginBottom: \"8px\",\n }}\n >\n {trigram.meaning.korean} | {trigram.meaning.english}\n </div>\n\n {/* Philosophy */}\n <div\n style={{\n fontSize: isMobile ? \"10px\" : \"11px\",\n color: colors.textSecondary,\n textAlign: \"center\",\n marginBottom: \"6px\",\n }}\n >\n {trigram.philosophy.korean}\n </div>\n\n {/* Combat description */}\n <div\n style={{\n fontSize: isMobile ? \"9px\" : \"10px\",\n color: colors.textTertiary,\n textAlign: \"center\",\n fontStyle: \"italic\",\n }}\n >\n {trigram.combat.english}\n </div>\n </div>\n ))}\n </div>\n </div>\n\n {/* Archetype Philosophy Section */}\n <div\n style={{\n marginBottom: `${layoutConstants.sectionSpacing}px`,\n background: colors.sectionBg,\n borderRadius: \"10px\",\n border: `2px solid ${colors.borderMagenta}`,\n padding: \"20px\",\n }}\n data-testid=\"archetype-philosophy\"\n >\n <h2\n style={{\n fontSize: isMobile ? \"18px\" : \"22px\",\n fontWeight: \"bold\",\n color: colors.accentGold,\n margin: \"0 0 20px 0\",\n }}\n >\n 무사 유형 철학 (Warrior Archetype Philosophy)\n </h2>\n\n {/* Archetypes List */}\n <div\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n gap: \"12px\",\n }}\n >\n {archetypes.map(([archetype, data]) => (\n <div\n key={archetype}\n style={{\n background: hexToRgbaString(data.colors.primary, 0.2),\n borderRadius: \"6px\",\n border: `1px solid #${data.colors.primary\n .toString(16)\n .padStart(6, \"0\")}`,\n padding: \"15px\",\n }}\n data-testid={`archetype-${archetype}`}\n >\n {/* Name */}\n <div\n style={{\n fontSize: isMobile ? \"16px\" : \"18px\",\n fontWeight: \"bold\",\n color: `#${data.colors.primary\n .toString(16)\n .padStart(6, \"0\")}`,\n marginBottom: \"8px\",\n }}\n >\n {data.name.korean} ({data.name.english})\n </div>\n\n {/* Description */}\n <div\n style={{\n display: \"grid\",\n gridTemplateColumns: isMobile ? \"1fr\" : \"1fr 1fr\",\n gap: \"10px\",\n }}\n >\n <div\n style={{\n fontSize: isMobile ? \"11px\" : \"13px\",\n color: colors.textPrimary,\n }}\n >\n {data.description.korean}\n </div>\n <div\n style={{\n fontSize: isMobile ? \"10px\" : \"12px\",\n color: colors.textSecondary,\n }}\n >\n {data.description.english}\n </div>\n </div>\n\n {/* Specialist tag */}\n <div\n style={{\n marginTop: \"8px\",\n fontSize: isMobile ? \"9px\" : \"11px\",\n color: colors.textTertiary,\n }}\n >\n 전통 무예 전문가\n </div>\n </div>\n ))}\n </div>\n </div>\n </div>\n\n {/* Footer */}\n <div\n style={{\n minHeight: `${layoutConstants.footerHeight}px`,\n background: colors.headerBg,\n borderTop: `3px solid ${colors.borderGold}`,\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n justifyContent: \"center\",\n padding: `${layoutConstants.padding}px`,\n gap: \"15px\",\n }}\n data-testid=\"philosophy-footer\"\n >\n {/* Motivation Quote */}\n <div\n style={{\n background: hexToRgbaString(theme.colors.ACCENT_GOLD, 0.15),\n borderRadius: \"8px\",\n border: `1px solid ${colors.borderGold}`,\n padding: \"15px\",\n textAlign: \"center\",\n maxWidth: \"90%\",\n }}\n >\n <div\n style={{\n fontSize: isMobile ? \"12px\" : \"14px\",\n color: colors.accentGold,\n fontStyle: \"italic\",\n marginBottom: \"4px\",\n }}\n >\n 무술은 단순한 격투가 아닌, 자신을 수양하고 상대를 존중하는\n 도(道)입니다\n </div>\n <div\n style={{\n fontSize: isMobile ? \"10px\" : \"12px\",\n color: colors.textSecondary,\n fontStyle: \"italic\",\n }}\n >\n Martial arts is not mere combat, but the Way (道) of\n self-cultivation and respect for others\n </div>\n </div>\n\n {/* ISMS Link */}\n <LinkButton\n onClick={handleISMSClick}\n korean=\"공개 보안 정책\"\n english=\"View Security Policies\"\n icon=\"🔐\"\n isMobile={isMobile}\n testId=\"isms-public-link\"\n />\n\n {/* Action Row */}\n <div\n style={{\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"space-between\",\n width: \"100%\",\n gap: \"15px\",\n }}\n >\n {/* Back Button */}\n <BackButton\n onClick={handleBackClick}\n korean=\"돌아가기\"\n english=\"Return\"\n isMobile={isMobile}\n testId=\"philosophy-back-button\"\n />\n\n {/* Keyboard Hint */}\n <div\n style={{\n background: hexToRgbaString(\n theme.colors.UI_BACKGROUND_MEDIUM,\n 0.9,\n ),\n borderRadius: \"6px\",\n padding: \"8px 12px\",\n fontSize: isMobile ? \"11px\" : \"12px\",\n fontWeight: \"bold\",\n color: `#${theme.colors.SECONDARY_MAGENTA.toString(\n 16,\n ).padStart(6, \"0\")}`,\n border: `1px solid ${colors.borderGold}`,\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n gap: \"2px\",\n }}\n data-testid=\"keyboard-shortcuts\"\n >\n <span>ESC</span>\n <span>M</span>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n );\n};\n\nexport default PhilosophyScreen3D;\n"],"mappings":"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"}
1
+ {"version":3,"file":"PhilosophyScreen3D.js","names":[],"sources":["../../../../src/components/screens/philosophy/PhilosophyScreen3D.tsx"],"sourcesContent":["// UI renders outside Canvas in absolute-positioned div - no Html needed\nimport { Canvas } from \"@react-three/fiber\";\nimport React, { useCallback, useEffect, useMemo } from \"react\";\nimport { useAudio } from \"../../../audio/AudioProvider\";\nimport { useWebGLContextLossHandler } from \"../../../hooks/useWebGLContextLossHandler\";\nimport { useWindowSize } from \"../../../hooks/useWindowSize\";\nimport { PLAYER_ARCHETYPES_DATA } from \"../../../systems\";\nimport { KoreanCulture } from \"../../../systems/trigram/KoreanCulture\";\nimport { TRIGRAM_DATA } from \"../../../systems/trigram/types\";\nimport { TrigramStance } from \"../../../types\";\nimport { Z_INDEX } from \"../../../types/LayoutTypes\";\nimport { hexToRgbaString } from \"../../../utils/colorUtils\";\nimport { shouldUseMobileControls } from \"../../../utils/deviceDetection\";\nimport { getLayoutConstants } from \"../../../utils/responsiveLayoutHelpers\";\nimport { useKoreanTheme } from \"../../shared/base/useKoreanTheme\";\nimport { BackgroundScene3D } from \"../../shared/three\";\nimport { BackButton, LinkButton } from \"../../shared/ui/BackButton\";\nimport { VolumeControl } from \"../../shared/ui/VolumeControl\";\n\nexport interface PhilosophyScreen3DProps {\n readonly onReturnToMenu: () => void;\n readonly width?: number;\n readonly height?: number;\n}\n\n/**\n * Three.js-based PhilosophyScreen Component\n */\nexport const PhilosophyScreen3D: React.FC<PhilosophyScreen3DProps> = ({\n onReturnToMenu,\n width: propWidth,\n height: propHeight,\n}) => {\n // UI now renders outside Canvas - no mount state needed\n\n // Handle WebGL context loss and restoration (for 3D background only)\n useWebGLContextLossHandler({\n onContextLost: () => {\n console.warn(\"⚠️ WebGL context lost in PhilosophyScreen\");\n },\n onContextRestored: () => {\n console.log(\"✓ WebGL context restored in PhilosophyScreen\");\n },\n autoRestore: true,\n });\n\n const audio = useAudio();\n const { width, height } = useWindowSize();\n\n // Use prop dimensions if provided, otherwise use window size\n const screenWidth = propWidth ?? width;\n const screenHeight = propHeight ?? height;\n\n // Responsive layout calculations with large desktop support\n // Use device detection instead of width-only breakpoint to correctly identify high-res mobile devices\n const isMobile = shouldUseMobileControls();\n // Only use width for tablet/desktop distinction when NOT mobile\n const isTablet = useMemo(\n () => !isMobile && screenWidth >= 768 && screenWidth < 1024,\n [isMobile, screenWidth],\n );\n const isLargeDesktop = useMemo(\n () => !isMobile && screenWidth >= 1920,\n [isMobile, screenWidth],\n ); // 4K/2K displays\n\n // Use centralized responsive layout helper for consistent scaling\n const layoutConstants = useMemo(\n () => getLayoutConstants(screenWidth),\n [screenWidth],\n );\n\n // Use Korean theme hook for consistent theming\n const theme = useKoreanTheme({\n variant: \"primary\",\n size: \"md\",\n isMobile,\n });\n\n // Memoize scrollbar style to prevent re-creating style tag on every render\n const scrollbarStyle = useMemo(\n () => ({\n __html: `\n .philosophy-scrollbar::-webkit-scrollbar {\n width: 12px !important;\n display: block !important;\n }\n .philosophy-scrollbar::-webkit-scrollbar-track {\n background: ${hexToRgbaString(theme.colors.UI_BACKGROUND_DARK, 0.8)};\n border-radius: 6px;\n }\n .philosophy-scrollbar::-webkit-scrollbar-thumb {\n background: ${hexToRgbaString(theme.colors.ACCENT_GOLD, 1)};\n border-radius: 6px;\n border: 2px solid ${hexToRgbaString(theme.colors.UI_BACKGROUND_DARK, 0.8)};\n }\n .philosophy-scrollbar::-webkit-scrollbar-thumb:hover {\n background: ${hexToRgbaString(theme.colors.PRIMARY_CYAN, 1)};\n }\n `,\n }),\n [theme],\n );\n\n // Memoize colors from theme for performance\n const colors = useMemo(\n () => ({\n background: hexToRgbaString(theme.colors.UI_BACKGROUND_DARK, 0.95),\n headerBg: hexToRgbaString(theme.colors.UI_BACKGROUND_DARK, 0.9),\n sectionBg: hexToRgbaString(theme.colors.UI_BACKGROUND_LIGHT, 0.8),\n borderGold: hexToRgbaString(theme.colors.ACCENT_GOLD, 0.8),\n borderCyan: hexToRgbaString(theme.colors.PRIMARY_CYAN, 0.6),\n borderMagenta: hexToRgbaString(theme.colors.SECONDARY_MAGENTA, 0.6),\n borderRed: hexToRgbaString(theme.colors.KOREAN_RED, 0.6),\n textPrimary: `#${theme.colors.TEXT_PRIMARY.toString(16).padStart(6, \"0\")}`,\n textSecondary: `#${theme.colors.TEXT_SECONDARY.toString(16).padStart(6, \"0\")}`,\n textTertiary: `#${theme.colors.TEXT_TERTIARY.toString(16).padStart(6, \"0\")}`,\n accentGold: `#${theme.colors.ACCENT_GOLD.toString(16).padStart(6, \"0\")}`,\n accentCyan: `#${theme.colors.PRIMARY_CYAN.toString(16).padStart(6, \"0\")}`,\n }),\n [theme],\n );\n\n // Audio lifecycle management for philosophy screen\n useEffect(() => {\n const startMusic = async () => {\n await audio.fadeIn(\"underground_theme\", 2000);\n };\n void startMusic().catch((err) =>\n console.warn(\"Failed to start philosophy music:\", err),\n );\n\n return () => {\n void audio\n .fadeOut(2000)\n .then(() => audio.stopMusic())\n .catch((err) => console.warn(\"Failed to stop philosophy music:\", err));\n };\n }, [audio]);\n\n // Enhanced keyboard handling for screen-level navigation\n useEffect(() => {\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key === \"Escape\" || event.key.toLowerCase() === \"m\") {\n event.preventDefault();\n audio.playSFX(\"menu_back\");\n onReturnToMenu();\n }\n };\n\n window.addEventListener(\"keydown\", handleKeyDown, { passive: false });\n return () => window.removeEventListener(\"keydown\", handleKeyDown);\n }, [onReturnToMenu, audio]);\n\n // Handle back button click\n const handleBackClick = useCallback(() => {\n audio.playSFX(\"menu_back\");\n onReturnToMenu();\n }, [audio, onReturnToMenu]);\n\n // Handle ISMS link click\n const handleISMSClick = useCallback(() => {\n audio.playSFX(\"menu_select\");\n window.open(\"https://github.com/Hack23/ISMS-PUBLIC\", \"_blank\");\n }, [audio]);\n\n // Get philosophy data\n const martialValues = useMemo(\n () => Object.entries(KoreanCulture.MARTIAL_VALUES),\n [],\n );\n\n const trigramPhilosophies = useMemo(\n () =>\n Object.entries(TRIGRAM_DATA).map(([stance, data]) => ({\n stance: stance as TrigramStance,\n ...data,\n })),\n [],\n );\n\n const archetypes = useMemo(() => Object.entries(PLAYER_ARCHETYPES_DATA), []);\n\n // Grid layout calculations\n const valuesPerRow = isMobile ? 3 : isTablet ? 4 : isLargeDesktop ? 8 : 6;\n const trigramsPerRow = isMobile ? 2 : isTablet ? 3 : isLargeDesktop ? 5 : 4;\n\n return (\n <div\n style={{\n width: screenWidth,\n height: screenHeight,\n position: \"relative\",\n overflow: \"hidden\",\n }}\n data-testid=\"philosophy-screen\"\n >\n {/* Volume Control - outside Canvas to maintain AudioProvider context */}\n <VolumeControl position=\"top-right\" compact={isMobile} />\n\n {/* Three.js Canvas for 3D background */}\n <Canvas\n style={{\n position: \"absolute\",\n top: 0,\n left: 0,\n width: \"100%\",\n height: \"100%\",\n zIndex: Z_INDEX.ARENA,\n }}\n gl={{\n antialias: true,\n alpha: false,\n powerPreference: \"high-performance\",\n }}\n dpr={[1, 2]}\n camera={{ position: [0, 5, 10], fov: 75 }}\n onCreated={({ gl }) => {\n gl.setClearColor(theme.colors.UI_BACKGROUND_DARK, 1);\n }}\n >\n {/* 3D Background Scene */}\n <BackgroundScene3D theme=\"philosophy\" />\n </Canvas>\n\n {/* UI Overlay (positioned absolutely over Canvas) - matches CombatScreen pattern */}\n <div\n style={{\n position: \"absolute\",\n top: 0,\n left: 0,\n width: \"100%\",\n height: \"100%\",\n pointerEvents: \"none\",\n zIndex: Z_INDEX.HUD,\n }}\n data-testid=\"philosophy-hud-overlay\"\n >\n <div\n style={{\n width: \"100%\",\n height: \"100%\",\n display: \"flex\",\n flexDirection: \"column\",\n color: colors.textPrimary,\n fontFamily: theme.koreanTypography.fontFamily,\n lineHeight: theme.koreanTypography.lineHeight,\n pointerEvents: \"auto\",\n }}\n >\n {/* Header */}\n <div\n style={{\n height: `${layoutConstants.headerHeight}px`,\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n justifyContent: \"center\",\n background: colors.headerBg,\n borderBottom: `3px solid ${colors.borderGold}`,\n padding: `${layoutConstants.padding}px`,\n position: \"relative\",\n }}\n data-testid=\"philosophy-header\"\n >\n <h1\n style={{\n fontSize: isMobile ? \"28px\" : \"36px\",\n fontWeight: \"bold\",\n color: colors.accentGold,\n margin: 0,\n textShadow: `0 0 15px ${hexToRgbaString(\n theme.colors.ACCENT_GOLD,\n 0.6,\n )}`,\n }}\n >\n 흑괘 무도 철학\n </h1>\n <p\n style={{\n fontSize: isMobile ? \"14px\" : \"18px\",\n color: colors.textSecondary,\n margin: \"8px 0 0 0\",\n }}\n >\n Black Trigram Martial Philosophy\n </p>\n\n {/* Decorative line */}\n <div\n style={{\n width: \"80%\",\n height: \"2px\",\n background: `linear-gradient(90deg, transparent, ${colors.borderGold}, transparent)`,\n marginTop: \"10px\",\n }}\n />\n </div>\n\n {/* WebKit Scrollbar Styling - Using !important to override global hide */}\n <style dangerouslySetInnerHTML={scrollbarStyle} />\n\n {/* Content Area - Scrollable */}\n <div\n className=\"philosophy-scrollbar\"\n style={{\n flex: 1,\n overflowY: \"auto\",\n overflowX: \"hidden\",\n padding: `${layoutConstants.padding}px`,\n scrollbarWidth: \"thin\",\n scrollbarColor: `${colors.accentGold} ${colors.sectionBg}`,\n }}\n data-testid=\"philosophy-content\"\n >\n {/* Martial Values Section */}\n <div\n style={{\n marginBottom: `${layoutConstants.sectionSpacing}px`,\n background: colors.sectionBg,\n borderRadius: \"10px\",\n border: `2px solid ${colors.borderRed}`,\n padding: \"20px\",\n }}\n data-testid=\"martial-values\"\n >\n <h2\n style={{\n fontSize: isMobile ? \"18px\" : \"22px\",\n fontWeight: \"bold\",\n color: colors.accentGold,\n margin: \"0 0 20px 0\",\n }}\n >\n 무도 가치관 (Martial Values)\n </h2>\n\n {/* Values Grid */}\n <div\n style={{\n display: \"grid\",\n gridTemplateColumns: `repeat(${valuesPerRow}, 1fr)`,\n gap: \"10px\",\n }}\n >\n {martialValues.map(([key, value]) => (\n <div\n key={key}\n style={{\n background: hexToRgbaString(\n theme.colors.UI_BACKGROUND_DARK,\n 0.7,\n ),\n borderRadius: \"6px\",\n border: `1px solid ${colors.borderGold}`,\n padding: \"12px\",\n textAlign: \"center\",\n }}\n data-testid={`martial-value-${key}`}\n >\n <div\n style={{\n fontSize: isMobile ? \"16px\" : \"18px\",\n fontWeight: \"bold\",\n color: colors.textPrimary,\n marginBottom: \"4px\",\n }}\n >\n {value.korean}\n </div>\n <div\n style={{\n fontSize: isMobile ? \"11px\" : \"13px\",\n color: colors.textSecondary,\n }}\n >\n {value.english}\n </div>\n </div>\n ))}\n </div>\n </div>\n\n {/* Trigram Philosophy Section */}\n <div\n style={{\n marginBottom: `${layoutConstants.sectionSpacing}px`,\n background: colors.sectionBg,\n borderRadius: \"10px\",\n border: `2px solid ${colors.borderCyan}`,\n padding: \"20px\",\n }}\n data-testid=\"trigram-philosophy\"\n >\n <h2\n style={{\n fontSize: isMobile ? \"18px\" : \"22px\",\n fontWeight: \"bold\",\n color: colors.accentGold,\n margin: \"0 0 20px 0\",\n }}\n >\n 팔괘 철학 (Eight Trigrams Philosophy)\n </h2>\n\n {/* Trigrams Grid */}\n <div\n style={{\n display: \"grid\",\n gridTemplateColumns: `repeat(${trigramsPerRow}, 1fr)`,\n gap: \"15px\",\n }}\n >\n {trigramPhilosophies.map((trigram) => (\n <div\n key={trigram.stance}\n style={{\n background: hexToRgbaString(trigram.theme.primary, 0.25),\n borderRadius: \"8px\",\n border: `2px solid #${trigram.theme.primary\n .toString(16)\n .padStart(6, \"0\")}`,\n padding: \"15px\",\n }}\n data-testid={`trigram-${trigram.stance}`}\n >\n {/* Trigram Symbol */}\n <div\n style={{\n fontSize: isMobile ? \"32px\" : \"40px\",\n color: `#${trigram.theme.primary\n .toString(16)\n .padStart(6, \"0\")}`,\n textAlign: \"center\",\n marginBottom: \"10px\",\n }}\n >\n {trigram.symbol}\n </div>\n\n {/* Name with Chinese character */}\n <div\n style={{\n fontSize: isMobile ? \"12px\" : \"14px\",\n fontWeight: \"bold\",\n color: colors.textPrimary,\n textAlign: \"center\",\n marginBottom: \"4px\",\n }}\n >\n {trigram.name.korean} ({trigram.name.english})\n </div>\n\n {/* Chinese character and attribute */}\n <div\n style={{\n fontSize: isMobile ? \"11px\" : \"13px\",\n color: colors.accentGold,\n textAlign: \"center\",\n marginBottom: \"8px\",\n fontWeight: \"bold\",\n }}\n >\n {trigram.chinese} - {trigram.attribute.chinese},{\" \"}\n {trigram.attribute.korean}\n </div>\n\n {/* Core meaning */}\n <div\n style={{\n fontSize: isMobile ? \"10px\" : \"11px\",\n color: colors.accentCyan,\n textAlign: \"center\",\n marginBottom: \"8px\",\n }}\n >\n {trigram.meaning.korean} | {trigram.meaning.english}\n </div>\n\n {/* Philosophy */}\n <div\n style={{\n fontSize: isMobile ? \"10px\" : \"11px\",\n color: colors.textSecondary,\n textAlign: \"center\",\n marginBottom: \"6px\",\n }}\n >\n {trigram.philosophy.korean}\n </div>\n\n {/* Combat description */}\n <div\n style={{\n fontSize: isMobile ? \"9px\" : \"10px\",\n color: colors.textTertiary,\n textAlign: \"center\",\n fontStyle: \"italic\",\n }}\n >\n {trigram.combat.english}\n </div>\n </div>\n ))}\n </div>\n </div>\n\n {/* Archetype Philosophy Section */}\n <div\n style={{\n marginBottom: `${layoutConstants.sectionSpacing}px`,\n background: colors.sectionBg,\n borderRadius: \"10px\",\n border: `2px solid ${colors.borderMagenta}`,\n padding: \"20px\",\n }}\n data-testid=\"archetype-philosophy\"\n >\n <h2\n style={{\n fontSize: isMobile ? \"18px\" : \"22px\",\n fontWeight: \"bold\",\n color: colors.accentGold,\n margin: \"0 0 20px 0\",\n }}\n >\n 무사 유형 철학 (Warrior Archetype Philosophy)\n </h2>\n\n {/* Archetypes List */}\n <div\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n gap: \"12px\",\n }}\n >\n {archetypes.map(([archetype, data]) => (\n <div\n key={archetype}\n style={{\n background: hexToRgbaString(data.colors.primary, 0.2),\n borderRadius: \"6px\",\n border: `1px solid #${data.colors.primary\n .toString(16)\n .padStart(6, \"0\")}`,\n padding: \"15px\",\n }}\n data-testid={`archetype-${archetype}`}\n >\n {/* Name */}\n <div\n style={{\n fontSize: isMobile ? \"16px\" : \"18px\",\n fontWeight: \"bold\",\n color: `#${data.colors.primary\n .toString(16)\n .padStart(6, \"0\")}`,\n marginBottom: \"8px\",\n }}\n >\n {data.name.korean} ({data.name.english})\n </div>\n\n {/* Description */}\n <div\n style={{\n display: \"grid\",\n gridTemplateColumns: isMobile ? \"1fr\" : \"1fr 1fr\",\n gap: \"10px\",\n }}\n >\n <div\n style={{\n fontSize: isMobile ? \"11px\" : \"13px\",\n color: colors.textPrimary,\n }}\n >\n {data.description.korean}\n </div>\n <div\n style={{\n fontSize: isMobile ? \"10px\" : \"12px\",\n color: colors.textSecondary,\n }}\n >\n {data.description.english}\n </div>\n </div>\n\n {/* Specialist tag */}\n <div\n style={{\n marginTop: \"8px\",\n fontSize: isMobile ? \"9px\" : \"11px\",\n color: colors.textTertiary,\n }}\n >\n 전통 무예 전문가\n </div>\n </div>\n ))}\n </div>\n </div>\n </div>\n\n {/* Footer */}\n <div\n style={{\n minHeight: `${layoutConstants.footerHeight}px`,\n background: colors.headerBg,\n borderTop: `3px solid ${colors.borderGold}`,\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n justifyContent: \"center\",\n padding: `${layoutConstants.padding}px`,\n gap: \"15px\",\n }}\n data-testid=\"philosophy-footer\"\n >\n {/* Motivation Quote */}\n <div\n style={{\n background: hexToRgbaString(theme.colors.ACCENT_GOLD, 0.15),\n borderRadius: \"8px\",\n border: `1px solid ${colors.borderGold}`,\n padding: \"15px\",\n textAlign: \"center\",\n maxWidth: \"90%\",\n }}\n >\n <div\n style={{\n fontSize: isMobile ? \"12px\" : \"14px\",\n color: colors.accentGold,\n fontStyle: \"italic\",\n marginBottom: \"4px\",\n }}\n >\n 무술은 단순한 격투가 아닌, 자신을 수양하고 상대를 존중하는\n 도(道)입니다\n </div>\n <div\n style={{\n fontSize: isMobile ? \"10px\" : \"12px\",\n color: colors.textSecondary,\n fontStyle: \"italic\",\n }}\n >\n Martial arts is not mere combat, but the Way (道) of\n self-cultivation and respect for others\n </div>\n </div>\n\n {/* ISMS Link */}\n <LinkButton\n onClick={handleISMSClick}\n korean=\"공개 보안 정책\"\n english=\"View Security Policies\"\n icon=\"🔐\"\n isMobile={isMobile}\n testId=\"isms-public-link\"\n />\n\n {/* Action Row */}\n <div\n style={{\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"space-between\",\n width: \"100%\",\n gap: \"15px\",\n }}\n >\n {/* Back Button */}\n <BackButton\n onClick={handleBackClick}\n korean=\"돌아가기\"\n english=\"Return\"\n isMobile={isMobile}\n testId=\"philosophy-back-button\"\n />\n\n {/* Keyboard Hint */}\n <div\n style={{\n background: hexToRgbaString(\n theme.colors.UI_BACKGROUND_MEDIUM,\n 0.9,\n ),\n borderRadius: \"6px\",\n padding: \"8px 12px\",\n fontSize: isMobile ? \"11px\" : \"12px\",\n fontWeight: \"bold\",\n color: `#${theme.colors.SECONDARY_MAGENTA.toString(\n 16,\n ).padStart(6, \"0\")}`,\n border: `1px solid ${colors.borderGold}`,\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n gap: \"2px\",\n }}\n data-testid=\"keyboard-shortcuts\"\n >\n <span>ESC</span>\n <span>M</span>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n );\n};\n\nexport default PhilosophyScreen3D;\n"],"mappings":"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"}