blacktrigram 0.7.48 → 0.7.49

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 (38) hide show
  1. package/lib/components/screens/combat/CombatScreen3D.d.ts.map +1 -1
  2. package/lib/components/screens/combat/CombatScreen3D.js +7 -14
  3. package/lib/components/screens/combat/CombatScreen3D.js.map +1 -1
  4. package/lib/components/screens/combat/components/hud/CombatBottomHUD.d.ts.map +1 -1
  5. package/lib/components/screens/combat/components/hud/CombatBottomHUD.js +2 -2
  6. package/lib/components/screens/combat/components/hud/CombatBottomHUD.js.map +1 -1
  7. package/lib/components/screens/combat/components/hud/CombatPortraitStatusStrip.js +1 -1
  8. package/lib/components/screens/combat/components/hud/CombatTopHUD.d.ts.map +1 -1
  9. package/lib/components/screens/combat/components/hud/CombatTopHUD.js +2 -1
  10. package/lib/components/screens/combat/components/hud/CombatTopHUD.js.map +1 -1
  11. package/lib/components/screens/combat/hooks/useCombatLayout.d.ts.map +1 -1
  12. package/lib/components/screens/combat/hooks/useCombatLayout.js +11 -5
  13. package/lib/components/screens/combat/hooks/useCombatLayout.js.map +1 -1
  14. package/lib/components/screens/controls/ControlsScreen3D.js +1 -1
  15. package/lib/components/screens/intro/IntroScreen3D.js +1 -1
  16. package/lib/components/screens/philosophy/PhilosophyScreen3D.js +1 -1
  17. package/lib/components/screens/training/TrainingScreen3D.d.ts.map +1 -1
  18. package/lib/components/screens/training/TrainingScreen3D.js +2 -11
  19. package/lib/components/screens/training/TrainingScreen3D.js.map +1 -1
  20. package/lib/components/screens/training/components/hud/TrainingBottomHUD.d.ts.map +1 -1
  21. package/lib/components/screens/training/components/hud/TrainingBottomHUD.js +2 -2
  22. package/lib/components/screens/training/components/hud/TrainingBottomHUD.js.map +1 -1
  23. package/lib/components/screens/training/hooks/useTrainingLayout.d.ts.map +1 -1
  24. package/lib/components/screens/training/hooks/useTrainingLayout.js +11 -5
  25. package/lib/components/screens/training/hooks/useTrainingLayout.js.map +1 -1
  26. package/lib/components/shared/ui/SplashScreen.js +2 -2
  27. package/lib/hooks/useHUDLayout.d.ts.map +1 -1
  28. package/lib/hooks/useHUDLayout.js +3 -2
  29. package/lib/hooks/useHUDLayout.js.map +1 -1
  30. package/lib/types/constants/layout.d.ts +21 -0
  31. package/lib/types/constants/layout.d.ts.map +1 -1
  32. package/lib/types/constants/layout.js +22 -1
  33. package/lib/types/constants/layout.js.map +1 -1
  34. package/lib/utils/responsiveLayoutHelpers.d.ts +7 -0
  35. package/lib/utils/responsiveLayoutHelpers.d.ts.map +1 -1
  36. package/lib/utils/responsiveLayoutHelpers.js +16 -2
  37. package/lib/utils/responsiveLayoutHelpers.js.map +1 -1
  38. package/package.json +5 -5
@@ -1 +1 @@
1
- {"version":3,"file":"layout.d.ts","sourceRoot":"","sources":["../../../src/types/constants/layout.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;;;;;GAQG;AACH,eAAO,MAAM,uBAAuB;IAClC,0FAA0F;;IAG1F,gFAAgF;;;;;IAMhF,+DAA+D;;CAEvD,CAAC;AAEX;;;;;;;GAOG;AACH,eAAO,MAAM,yBAAyB;;;;;;IAMpC,wFAAwF;;CAEhF,CAAC;AAEX;;;;;;GAMG;AACH,eAAO,MAAM,yBAAyB;IACpC,oEAAoE;;IAGpE,oEAAoE;;IAGpE,uEAAuE;;IAGvE,yEAAyE;;CAEjE,CAAC;AAEX;;;;;;;;;GASG;AACH,eAAO,MAAM,gCAAgC,MAAM,CAAC;AAEpD;;;;;GAKG;AACH,eAAO,MAAM,+BAA+B,OAAO,CAAC;AAEpD;;;GAGG;AACH,eAAO,MAAM,oBAAoB;IAC/B,kDAAkD;;;;;IAMlD,2CAA2C;;CAEnC,CAAC;AAEX;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,OAAO,GAAG,MAAM,CAI/D;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,uBAAuB,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,MAAM,CA0BvE;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,OAAO,EAAE,WAAW,UAAQ,GAAG,MAAM,CAQ/E;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,OAAO,GAAG,MAAM,CAE5D;AAED;;GAEG;AACH,MAAM,MAAM,oBAAoB,GAAG,MAAM,CAAC;AAC1C,MAAM,MAAM,iBAAiB,GAAG,MAAM,CAAC"}
1
+ {"version":3,"file":"layout.d.ts","sourceRoot":"","sources":["../../../src/types/constants/layout.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;;;;;GAQG;AACH,eAAO,MAAM,uBAAuB;IAClC,0FAA0F;;IAG1F,gFAAgF;;;;;IAMhF,+DAA+D;;CAEvD,CAAC;AAEX;;;;;;;GAOG;AACH,eAAO,MAAM,yBAAyB;;;;;;IAMpC,wFAAwF;;CAEhF,CAAC;AAEX;;;;;;GAMG;AACH,eAAO,MAAM,yBAAyB;IACpC,oEAAoE;;IAGpE,oEAAoE;;IAGpE,uEAAuE;;IAGvE,yEAAyE;;CAEjE,CAAC;AAEX;;;;;;;;;GASG;AACH,eAAO,MAAM,gCAAgC,MAAM,CAAC;AAEpD;;;;;GAKG;AACH,eAAO,MAAM,+BAA+B,OAAO,CAAC;AAEpD;;;;;GAKG;AACH,eAAO,MAAM,6BAA6B,OAAO,CAAC;AAElD;;;;;GAKG;AACH,eAAO,MAAM,gCAAgC,MAAM,CAAC;AAEpD;;;;;GAKG;AACH,eAAO,MAAM,kCAAkC,OAAO,CAAC;AAEvD;;;GAGG;AACH,eAAO,MAAM,oBAAoB;IAC/B,kDAAkD;;;;;IAMlD,2CAA2C;;CAEnC,CAAC;AAEX;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,OAAO,GAAG,MAAM,CAI/D;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,uBAAuB,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,MAAM,CA0BvE;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,OAAO,EAAE,WAAW,UAAQ,GAAG,MAAM,CAQ/E;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,OAAO,GAAG,MAAM,CAE5D;AAED;;GAEG;AACH,MAAM,MAAM,oBAAoB,GAAG,MAAM,CAAC;AAC1C,MAAM,MAAM,iBAAiB,GAAG,MAAM,CAAC"}
@@ -79,6 +79,27 @@ var TECHNIQUE_BAR_MIN_READABLE_SCALE = .7;
79
79
  */
80
80
  var TRAINING_TOP_HUD_HEIGHT_PERCENT = .06;
81
81
  /**
82
+ * Combat top HUD height ratio.
83
+ *
84
+ * Shared by CombatTopHUD, side HUD offsets, and arena layout reservations so
85
+ * the 3D combat area starts immediately below the visible top bar.
86
+ */
87
+ var COMBAT_TOP_HUD_HEIGHT_PERCENT = .06;
88
+ /**
89
+ * Combat bottom HUD height ratio.
90
+ *
91
+ * Shared by CombatBottomHUD, side HUD offsets, and arena layout reservations so
92
+ * technique controls fit without over-reserving vertical arena space.
93
+ */
94
+ var COMBAT_BOTTOM_HUD_HEIGHT_PERCENT = .1;
95
+ /**
96
+ * Training bottom HUD height ratio.
97
+ *
98
+ * Shared by TrainingBottomHUD, side HUD offsets, and training area layout
99
+ * reservations so the dojang content is framed consistently.
100
+ */
101
+ var TRAINING_BOTTOM_HUD_HEIGHT_PERCENT = .11;
102
+ /**
82
103
  * Get mobile controls bottom position.
83
104
  *
84
105
  * Returns a viewport-responsive band for comfortable D-Pad / ActionButton
@@ -98,6 +119,6 @@ function getMobileControlsBottom(viewportHeight) {
98
119
  return Math.round(Math.max(MOBILE_CONTROLS_PLACEMENT.TALL_MIN, Math.min(MOBILE_CONTROLS_PLACEMENT.TALL_MAX, viewportHeight * MOBILE_CONTROLS_PLACEMENT.TALL_VIEWPORT_RATIO)));
99
120
  }
100
121
  //#endregion
101
- export { HUD_SIDE_CONTROL_RESERVES, TECHNIQUE_BAR_MIN_READABLE_SCALE, TRAINING_TOP_HUD_HEIGHT_PERCENT, getMobileControlsBottom };
122
+ export { COMBAT_BOTTOM_HUD_HEIGHT_PERCENT, COMBAT_TOP_HUD_HEIGHT_PERCENT, HUD_SIDE_CONTROL_RESERVES, TECHNIQUE_BAR_MIN_READABLE_SCALE, TRAINING_BOTTOM_HUD_HEIGHT_PERCENT, TRAINING_TOP_HUD_HEIGHT_PERCENT, getMobileControlsBottom };
102
123
 
103
124
  //# sourceMappingURL=layout.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"layout.js","names":[],"sources":["../../../src/types/constants/layout.ts"],"sourcesContent":["/**\n * Layout positioning constants for combat and training screens\n * Centralized to prevent magic numbers and ensure consistency\n * \n * @category UI Constants\n * @korean 레이아웃위치상수\n */\n\n/**\n * Bottom positioning for UI elements (in pixels)\n * Values designed to prevent overlap and prioritize gameplay visibility:\n * - Mobile controls at 200px for ergonomic touch access\n * - TechniqueBar lowered to 20px (mobile) / 30px (desktop) to minimize arena obstruction\n * - Back button moved to top-right corner (off-center) for accessibility\n * \n * PRIORITY: Combat arena visibility > TechniqueBar > Mobile controls > Back button\n */\nexport const LAYOUT_BOTTOM_POSITIONS = {\n /** Mobile controls (VirtualDPad, ActionButtons) - must stay at 200px for accessibility */\n MOBILE_CONTROLS: 200,\n \n /** TechniqueBar container - lowered to bottom to prioritize arena visibility */\n TECHNIQUE_BAR: {\n MOBILE: 20, // Near bottom of screen to minimize arena obstruction\n DESKTOP: 30, // Slightly higher on desktop for better visibility\n },\n \n /** TechniqueBar container height (for overlap calculations) */\n TECHNIQUE_BAR_HEIGHT: 180,\n} as const;\n\n/**\n * Mobile touch controls placement ratios:\n * - 24% on short landscape viewports, clamped to 96–120 px, keeps controls\n * below the arena center while preserving thumb reach.\n * - 17% on tall portrait viewports, clamped to 128–200 px, lowers controls\n * toward the nav/home area without colliding with the bottom HUD or browser\n * safe area.\n */\nexport const MOBILE_CONTROLS_PLACEMENT = {\n SHORT_VIEWPORT_RATIO: 0.24,\n TALL_VIEWPORT_RATIO: 0.17,\n SHORT_MIN: 96,\n SHORT_MAX: 120,\n TALL_MIN: 128,\n /** 200 px upper bound intentionally matches LAYOUT_BOTTOM_POSITIONS.MOBILE_CONTROLS. */\n TALL_MAX: LAYOUT_BOTTOM_POSITIONS.MOBILE_CONTROLS,\n} as const;\n\n/**\n * Shared horizontal reservations for HUD side controls.\n *\n * These values keep centered technique cards readable while preserving space\n * for absolutely positioned controls such as the compact volume control and\n * mobile archetype selector.\n */\nexport const HUD_SIDE_CONTROL_RESERVES = {\n /** Compact volume control width (162px) plus interaction margin. */\n VOLUME_CONTROL: 180,\n\n /** Mobile archetype selector reserve in the training bottom HUD. */\n ARCHETYPE_SELECTOR: 180,\n\n /** Embedded mobile technique bar side reserve for compact controls. */\n TECHNIQUE_BAR_MOBILE: 96,\n\n /** Embedded desktop technique bar side reserve for side HUD controls. */\n TECHNIQUE_BAR_DESKTOP: 190,\n} as const;\n\n/**\n * Minimum visual scale before technique cards become unreadable.\n *\n * The 70% threshold preserves readable Korean/English labels and keyboard\n * hints on compact HUDs. Below this value, embedded bars switch to horizontal\n * scrolling instead of shrinking so touch targets and text remain usable.\n *\n * Used by TechniqueBar when calculating whether embedded cards should scale\n * down or remain full size with horizontal scroll.\n */\nexport const TECHNIQUE_BAR_MIN_READABLE_SCALE = 0.7;\n\n/**\n * Training top HUD height ratio.\n *\n * Must match the training context top offset used by useHUDLayout so the\n * top HUD and side HUDs align without overlap across responsive breakpoints.\n */\nexport const TRAINING_TOP_HUD_HEIGHT_PERCENT = 0.06;\n\n/**\n * Top positioning for UI elements (in pixels)\n * Used for elements positioned from the top of the screen\n */\nexport const LAYOUT_TOP_POSITIONS = {\n /** Back to Menu button positioned at top-right */\n BACK_BUTTON: {\n MOBILE: 10, // 10px from top on mobile\n DESKTOP: 20, // 20px from top on desktop\n },\n \n /** Safe area offset for notched devices */\n SAFE_AREA_TOP: 44, // iPhone notch height\n} as const;\n\n/**\n * Helper function to get technique bar bottom position\n * NOTE: positionScale NOT applied to prevent layout bugs on 4K displays\n * \n * @param isMobile - Whether device is mobile (<768px)\n * @returns Bottom position in pixels\n */\nexport function getTechniqueBarBottom(isMobile: boolean): number {\n return isMobile \n ? LAYOUT_BOTTOM_POSITIONS.TECHNIQUE_BAR.MOBILE\n : LAYOUT_BOTTOM_POSITIONS.TECHNIQUE_BAR.DESKTOP;\n}\n\n/**\n * Get mobile controls bottom position.\n *\n * Returns a viewport-responsive band for comfortable D-Pad / ActionButton\n * reach without pushing controls into the arena center. Short landscape\n * viewports use a 96–120 px clamped band, while tall numeric viewports use a\n * 128–200 px clamped band.\n *\n * @param viewportHeight - Optional current viewport height in pixels.\n * When omitted or NaN, returns the 200 px default.\n * Numeric heights >= 500 use the tall viewport ratio\n * clamped to 128–200 px.\n * @returns Bottom position in pixels\n */\nexport function getMobileControlsBottom(viewportHeight?: number): number {\n if (typeof viewportHeight !== \"number\" || Number.isNaN(viewportHeight)) {\n return LAYOUT_BOTTOM_POSITIONS.MOBILE_CONTROLS;\n }\n\n if (viewportHeight < 500) {\n return Math.round(\n Math.max(\n MOBILE_CONTROLS_PLACEMENT.SHORT_MIN,\n Math.min(\n MOBILE_CONTROLS_PLACEMENT.SHORT_MAX,\n viewportHeight * MOBILE_CONTROLS_PLACEMENT.SHORT_VIEWPORT_RATIO,\n ),\n ),\n );\n }\n\n return Math.round(\n Math.max(\n MOBILE_CONTROLS_PLACEMENT.TALL_MIN,\n Math.min(\n MOBILE_CONTROLS_PLACEMENT.TALL_MAX,\n viewportHeight * MOBILE_CONTROLS_PLACEMENT.TALL_VIEWPORT_RATIO,\n ),\n ),\n );\n}\n\n/**\n * Get back button top position for top-right corner placement\n * Includes safe area offset for notched devices\n * \n * @param isMobile - Whether device is mobile (<768px)\n * @param hasSafeArea - Whether device has notch/safe area (default: false)\n * @returns Top position in pixels\n */\nexport function getBackButtonTop(isMobile: boolean, hasSafeArea = false): number {\n const baseTop = isMobile \n ? LAYOUT_TOP_POSITIONS.BACK_BUTTON.MOBILE\n : LAYOUT_TOP_POSITIONS.BACK_BUTTON.DESKTOP;\n \n return hasSafeArea \n ? baseTop + LAYOUT_TOP_POSITIONS.SAFE_AREA_TOP\n : baseTop;\n}\n\n/**\n * Get back button right position for top-right corner placement\n * \n * @param isMobile - Whether device is mobile (<768px)\n * @returns Right position in pixels\n */\nexport function getBackButtonRight(isMobile: boolean): number {\n return isMobile ? 10 : 20;\n}\n\n/**\n * Type for layout position values\n */\nexport type LayoutBottomPosition = number;\nexport type LayoutTopPosition = number;\n"],"mappings":";;;;;;;;;;;;;;;;;AAiBA,IAAa,0BAA0B;;CAErC,iBAAiB;;CAGjB,eAAe;EACb,QAAQ;EACR,SAAS;CACX;;CAGA,sBAAsB;AACxB;;;;;;;;;AAUA,IAAa,4BAA4B;CACvC,sBAAsB;CACtB,qBAAqB;CACrB,WAAW;CACX,WAAW;CACX,UAAU;;CAEV,UAAU,wBAAwB;AACpC;;;;;;;;AASA,IAAa,4BAA4B;;CAEvC,gBAAgB;;CAGhB,oBAAoB;;CAGpB,sBAAsB;;CAGtB,uBAAuB;AACzB;;;;;;;;;;;AAYA,IAAa,mCAAmC;;;;;;;AAQhD,IAAa,kCAAkC;;;;;;;;;;;;;;;AA4C/C,SAAgB,wBAAwB,gBAAiC;CACvE,IAAI,OAAO,mBAAmB,YAAY,OAAO,MAAM,cAAc,GACnE,OAAO,wBAAwB;CAGjC,IAAI,iBAAiB,KACnB,OAAO,KAAK,MACV,KAAK,IACH,0BAA0B,WAC1B,KAAK,IACH,0BAA0B,WAC1B,iBAAiB,0BAA0B,oBAC7C,CACF,CACF;CAGF,OAAO,KAAK,MACV,KAAK,IACH,0BAA0B,UAC1B,KAAK,IACH,0BAA0B,UAC1B,iBAAiB,0BAA0B,mBAC7C,CACF,CACF;AACF"}
1
+ {"version":3,"file":"layout.js","names":[],"sources":["../../../src/types/constants/layout.ts"],"sourcesContent":["/**\n * Layout positioning constants for combat and training screens\n * Centralized to prevent magic numbers and ensure consistency\n * \n * @category UI Constants\n * @korean 레이아웃위치상수\n */\n\n/**\n * Bottom positioning for UI elements (in pixels)\n * Values designed to prevent overlap and prioritize gameplay visibility:\n * - Mobile controls at 200px for ergonomic touch access\n * - TechniqueBar lowered to 20px (mobile) / 30px (desktop) to minimize arena obstruction\n * - Back button moved to top-right corner (off-center) for accessibility\n * \n * PRIORITY: Combat arena visibility > TechniqueBar > Mobile controls > Back button\n */\nexport const LAYOUT_BOTTOM_POSITIONS = {\n /** Mobile controls (VirtualDPad, ActionButtons) - must stay at 200px for accessibility */\n MOBILE_CONTROLS: 200,\n \n /** TechniqueBar container - lowered to bottom to prioritize arena visibility */\n TECHNIQUE_BAR: {\n MOBILE: 20, // Near bottom of screen to minimize arena obstruction\n DESKTOP: 30, // Slightly higher on desktop for better visibility\n },\n \n /** TechniqueBar container height (for overlap calculations) */\n TECHNIQUE_BAR_HEIGHT: 180,\n} as const;\n\n/**\n * Mobile touch controls placement ratios:\n * - 24% on short landscape viewports, clamped to 96–120 px, keeps controls\n * below the arena center while preserving thumb reach.\n * - 17% on tall portrait viewports, clamped to 128–200 px, lowers controls\n * toward the nav/home area without colliding with the bottom HUD or browser\n * safe area.\n */\nexport const MOBILE_CONTROLS_PLACEMENT = {\n SHORT_VIEWPORT_RATIO: 0.24,\n TALL_VIEWPORT_RATIO: 0.17,\n SHORT_MIN: 96,\n SHORT_MAX: 120,\n TALL_MIN: 128,\n /** 200 px upper bound intentionally matches LAYOUT_BOTTOM_POSITIONS.MOBILE_CONTROLS. */\n TALL_MAX: LAYOUT_BOTTOM_POSITIONS.MOBILE_CONTROLS,\n} as const;\n\n/**\n * Shared horizontal reservations for HUD side controls.\n *\n * These values keep centered technique cards readable while preserving space\n * for absolutely positioned controls such as the compact volume control and\n * mobile archetype selector.\n */\nexport const HUD_SIDE_CONTROL_RESERVES = {\n /** Compact volume control width (162px) plus interaction margin. */\n VOLUME_CONTROL: 180,\n\n /** Mobile archetype selector reserve in the training bottom HUD. */\n ARCHETYPE_SELECTOR: 180,\n\n /** Embedded mobile technique bar side reserve for compact controls. */\n TECHNIQUE_BAR_MOBILE: 96,\n\n /** Embedded desktop technique bar side reserve for side HUD controls. */\n TECHNIQUE_BAR_DESKTOP: 190,\n} as const;\n\n/**\n * Minimum visual scale before technique cards become unreadable.\n *\n * The 70% threshold preserves readable Korean/English labels and keyboard\n * hints on compact HUDs. Below this value, embedded bars switch to horizontal\n * scrolling instead of shrinking so touch targets and text remain usable.\n *\n * Used by TechniqueBar when calculating whether embedded cards should scale\n * down or remain full size with horizontal scroll.\n */\nexport const TECHNIQUE_BAR_MIN_READABLE_SCALE = 0.7;\n\n/**\n * Training top HUD height ratio.\n *\n * Must match the training context top offset used by useHUDLayout so the\n * top HUD and side HUDs align without overlap across responsive breakpoints.\n */\nexport const TRAINING_TOP_HUD_HEIGHT_PERCENT = 0.06;\n\n/**\n * Combat top HUD height ratio.\n *\n * Shared by CombatTopHUD, side HUD offsets, and arena layout reservations so\n * the 3D combat area starts immediately below the visible top bar.\n */\nexport const COMBAT_TOP_HUD_HEIGHT_PERCENT = 0.06;\n\n/**\n * Combat bottom HUD height ratio.\n *\n * Shared by CombatBottomHUD, side HUD offsets, and arena layout reservations so\n * technique controls fit without over-reserving vertical arena space.\n */\nexport const COMBAT_BOTTOM_HUD_HEIGHT_PERCENT = 0.1;\n\n/**\n * Training bottom HUD height ratio.\n *\n * Shared by TrainingBottomHUD, side HUD offsets, and training area layout\n * reservations so the dojang content is framed consistently.\n */\nexport const TRAINING_BOTTOM_HUD_HEIGHT_PERCENT = 0.11;\n\n/**\n * Top positioning for UI elements (in pixels)\n * Used for elements positioned from the top of the screen\n */\nexport const LAYOUT_TOP_POSITIONS = {\n /** Back to Menu button positioned at top-right */\n BACK_BUTTON: {\n MOBILE: 10, // 10px from top on mobile\n DESKTOP: 20, // 20px from top on desktop\n },\n \n /** Safe area offset for notched devices */\n SAFE_AREA_TOP: 44, // iPhone notch height\n} as const;\n\n/**\n * Helper function to get technique bar bottom position\n * NOTE: positionScale NOT applied to prevent layout bugs on 4K displays\n * \n * @param isMobile - Whether device is mobile (<768px)\n * @returns Bottom position in pixels\n */\nexport function getTechniqueBarBottom(isMobile: boolean): number {\n return isMobile \n ? LAYOUT_BOTTOM_POSITIONS.TECHNIQUE_BAR.MOBILE\n : LAYOUT_BOTTOM_POSITIONS.TECHNIQUE_BAR.DESKTOP;\n}\n\n/**\n * Get mobile controls bottom position.\n *\n * Returns a viewport-responsive band for comfortable D-Pad / ActionButton\n * reach without pushing controls into the arena center. Short landscape\n * viewports use a 96–120 px clamped band, while tall numeric viewports use a\n * 128–200 px clamped band.\n *\n * @param viewportHeight - Optional current viewport height in pixels.\n * When omitted or NaN, returns the 200 px default.\n * Numeric heights >= 500 use the tall viewport ratio\n * clamped to 128–200 px.\n * @returns Bottom position in pixels\n */\nexport function getMobileControlsBottom(viewportHeight?: number): number {\n if (typeof viewportHeight !== \"number\" || Number.isNaN(viewportHeight)) {\n return LAYOUT_BOTTOM_POSITIONS.MOBILE_CONTROLS;\n }\n\n if (viewportHeight < 500) {\n return Math.round(\n Math.max(\n MOBILE_CONTROLS_PLACEMENT.SHORT_MIN,\n Math.min(\n MOBILE_CONTROLS_PLACEMENT.SHORT_MAX,\n viewportHeight * MOBILE_CONTROLS_PLACEMENT.SHORT_VIEWPORT_RATIO,\n ),\n ),\n );\n }\n\n return Math.round(\n Math.max(\n MOBILE_CONTROLS_PLACEMENT.TALL_MIN,\n Math.min(\n MOBILE_CONTROLS_PLACEMENT.TALL_MAX,\n viewportHeight * MOBILE_CONTROLS_PLACEMENT.TALL_VIEWPORT_RATIO,\n ),\n ),\n );\n}\n\n/**\n * Get back button top position for top-right corner placement\n * Includes safe area offset for notched devices\n * \n * @param isMobile - Whether device is mobile (<768px)\n * @param hasSafeArea - Whether device has notch/safe area (default: false)\n * @returns Top position in pixels\n */\nexport function getBackButtonTop(isMobile: boolean, hasSafeArea = false): number {\n const baseTop = isMobile \n ? LAYOUT_TOP_POSITIONS.BACK_BUTTON.MOBILE\n : LAYOUT_TOP_POSITIONS.BACK_BUTTON.DESKTOP;\n \n return hasSafeArea \n ? baseTop + LAYOUT_TOP_POSITIONS.SAFE_AREA_TOP\n : baseTop;\n}\n\n/**\n * Get back button right position for top-right corner placement\n * \n * @param isMobile - Whether device is mobile (<768px)\n * @returns Right position in pixels\n */\nexport function getBackButtonRight(isMobile: boolean): number {\n return isMobile ? 10 : 20;\n}\n\n/**\n * Type for layout position values\n */\nexport type LayoutBottomPosition = number;\nexport type LayoutTopPosition = number;\n"],"mappings":";;;;;;;;;;;;;;;;;AAiBA,IAAa,0BAA0B;;CAErC,iBAAiB;;CAGjB,eAAe;EACb,QAAQ;EACR,SAAS;CACX;;CAGA,sBAAsB;AACxB;;;;;;;;;AAUA,IAAa,4BAA4B;CACvC,sBAAsB;CACtB,qBAAqB;CACrB,WAAW;CACX,WAAW;CACX,UAAU;;CAEV,UAAU,wBAAwB;AACpC;;;;;;;;AASA,IAAa,4BAA4B;;CAEvC,gBAAgB;;CAGhB,oBAAoB;;CAGpB,sBAAsB;;CAGtB,uBAAuB;AACzB;;;;;;;;;;;AAYA,IAAa,mCAAmC;;;;;;;AAQhD,IAAa,kCAAkC;;;;;;;AAQ/C,IAAa,gCAAgC;;;;;;;AAQ7C,IAAa,mCAAmC;;;;;;;AAQhD,IAAa,qCAAqC;;;;;;;;;;;;;;;AA4ClD,SAAgB,wBAAwB,gBAAiC;CACvE,IAAI,OAAO,mBAAmB,YAAY,OAAO,MAAM,cAAc,GACnE,OAAO,wBAAwB;CAGjC,IAAI,iBAAiB,KACnB,OAAO,KAAK,MACV,KAAK,IACH,0BAA0B,WAC1B,KAAK,IACH,0BAA0B,WAC1B,iBAAiB,0BAA0B,oBAC7C,CACF,CACF;CAGF,OAAO,KAAK,MACV,KAAK,IACH,0BAA0B,UAC1B,KAAK,IACH,0BAA0B,UAC1B,iBAAiB,0BAA0B,mBAC7C,CACF,CACF;AACF"}
@@ -18,6 +18,13 @@ import type { ScreenSize } from '../systems/ResponsiveScaling';
18
18
  *
19
19
  */
20
20
  export declare function getDesktopArenaWidthBudget(width: number): number;
21
+ /**
22
+ * Shared HUD position scale for combat and training screens.
23
+ *
24
+ * Keeps top/bottom HUDs, side HUD offsets, and arena reservations synchronized
25
+ * on large and 4K displays.
26
+ */
27
+ export declare function getHUDPositionScale(screenSize: ScreenSize, isMobile: boolean): number;
21
28
  /**
22
29
  * Calculate responsive padding value
23
30
  *
@@ -1 +1 @@
1
- {"version":3,"file":"responsiveLayoutHelpers.d.ts","sourceRoot":"","sources":["../../src/utils/responsiveLayoutHelpers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAa/D;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEhE;AAiDD;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,UAAU,GAAG,MAAM,CAEnE;AAED;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,UAAU,EAAE,UAAU,GAAG,MAAM,CAExE;AAED;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,UAAU,EAAE,UAAU,GAAG,MAAM,CAExE;AAED;;;;;;GAMG;AACH,wBAAgB,2BAA2B,CAAC,UAAU,EAAE,UAAU,GAAG,MAAM,CAE1E;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,UAAU,EAAE,UAAU,GAAG,MAAM,CAEtE;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM;;;;;;EAU/C;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,OAAO;;;;;;;EA8DzE"}
1
+ {"version":3,"file":"responsiveLayoutHelpers.d.ts","sourceRoot":"","sources":["../../src/utils/responsiveLayoutHelpers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAa/D;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEhE;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,UAAU,EACtB,QAAQ,EAAE,OAAO,GAChB,MAAM,CAgBR;AAiDD;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,UAAU,GAAG,MAAM,CAEnE;AAED;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,UAAU,EAAE,UAAU,GAAG,MAAM,CAExE;AAED;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,UAAU,EAAE,UAAU,GAAG,MAAM,CAExE;AAED;;;;;;GAMG;AACH,wBAAgB,2BAA2B,CAAC,UAAU,EAAE,UAAU,GAAG,MAAM,CAE1E;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,UAAU,EAAE,UAAU,GAAG,MAAM,CAEtE;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM;;;;;;EAU/C;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,OAAO;;;;;;;EA8DzE"}
@@ -12,7 +12,7 @@ import { getScreenSize } from "../systems/ResponsiveScaling.js";
12
12
  * @korean 반응형레이아웃도우미
13
13
  */
14
14
  /** Desktop arena width as a proportion of viewport width. */
15
- var DESKTOP_ARENA_WIDTH_RATIO = .8;
15
+ var DESKTOP_ARENA_WIDTH_RATIO = .68;
16
16
  /**
17
17
  * Maximum desktop arena width in CSS pixels.
18
18
  *
@@ -31,6 +31,20 @@ function getDesktopArenaWidthBudget(width) {
31
31
  return Math.min(width * DESKTOP_ARENA_WIDTH_RATIO, DESKTOP_ARENA_MAX_WIDTH_PX);
32
32
  }
33
33
  /**
34
+ * Shared HUD position scale for combat and training screens.
35
+ *
36
+ * Keeps top/bottom HUDs, side HUD offsets, and arena reservations synchronized
37
+ * on large and 4K displays.
38
+ */
39
+ function getHUDPositionScale(screenSize, isMobile) {
40
+ if (isMobile) return 1;
41
+ switch (screenSize) {
42
+ case "large": return 1.25;
43
+ case "xlarge": return 1.5;
44
+ default: return 1;
45
+ }
46
+ }
47
+ /**
34
48
  * Base layout values for different screen sizes
35
49
  * These serve as reference values that scale proportionally
36
50
  */
@@ -203,6 +217,6 @@ function getCombatLayoutConstants(width, isMobile) {
203
217
  };
204
218
  }
205
219
  //#endregion
206
- export { getCombatLayoutConstants, getDesktopArenaWidthBudget, getLayoutConstants };
220
+ export { getCombatLayoutConstants, getDesktopArenaWidthBudget, getHUDPositionScale, getLayoutConstants };
207
221
 
208
222
  //# sourceMappingURL=responsiveLayoutHelpers.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"responsiveLayoutHelpers.js","names":[],"sources":["../../src/utils/responsiveLayoutHelpers.ts"],"sourcesContent":["/**\n * Responsive Layout Helpers\n * \n * Centralized utilities for calculating responsive layout constants\n * across different screen components. Uses the centralized ResponsiveScaling\n * system for consistent scaling patterns.\n * \n * @module utils/responsiveLayoutHelpers\n * @category Layout\n * @korean 반응형레이아웃도우미\n */\n\nimport { getScreenSize } from '../systems/ResponsiveScaling';\nimport type { ScreenSize } from '../systems/ResponsiveScaling';\n\n/** Desktop arena width as a proportion of viewport width. */\nconst DESKTOP_ARENA_WIDTH_RATIO = 0.8;\n\n/**\n * Maximum desktop arena width in CSS pixels.\n *\n * Caps ultra-wide/8K displays to protect WebGL fill-rate while preserving a\n * large, readable 4K desktop arena.\n */\nconst DESKTOP_ARENA_MAX_WIDTH_PX = 2560;\n\n/**\n * Calculate maximum desktop arena width for combat/training screens.\n *\n * @param width - Viewport width in CSS pixels\n * @returns Width budget for the 4:3 desktop arena\n *\n */\nexport function getDesktopArenaWidthBudget(width: number): number {\n return Math.min(width * DESKTOP_ARENA_WIDTH_RATIO, DESKTOP_ARENA_MAX_WIDTH_PX);\n}\n\n/**\n * Base layout values for different screen sizes\n * These serve as reference values that scale proportionally\n */\nconst BASE_LAYOUT_VALUES = {\n // Base padding values (desktop reference)\n padding: {\n mobile: 20,\n tablet: 25,\n desktop: 30,\n large: 32,\n xlarge: 35,\n },\n // Base header height values\n headerHeight: {\n mobile: 90,\n tablet: 100,\n desktop: 110,\n large: 115,\n xlarge: 120,\n },\n // Base footer height values\n footerHeight: {\n mobile: 75,\n tablet: 85,\n desktop: 90,\n large: 95,\n xlarge: 100,\n },\n // Base section spacing values\n sectionSpacing: {\n mobile: 15,\n tablet: 18,\n desktop: 20,\n large: 22,\n xlarge: 25,\n },\n // Base button area values\n buttonArea: {\n mobile: 75,\n tablet: 85,\n desktop: 95,\n large: 102,\n xlarge: 110,\n },\n} as const;\n\n/**\n * Calculate responsive padding value\n * \n * @param screenSize - Current screen size category\n * @returns Calculated padding in pixels\n * \n */\nexport function getResponsivePadding(screenSize: ScreenSize): number {\n return BASE_LAYOUT_VALUES.padding[screenSize];\n}\n\n/**\n * Calculate responsive header height value\n * \n * @param screenSize - Current screen size category\n * @returns Calculated header height in pixels\n * \n */\nexport function getResponsiveHeaderHeight(screenSize: ScreenSize): number {\n return BASE_LAYOUT_VALUES.headerHeight[screenSize];\n}\n\n/**\n * Calculate responsive footer height value\n * \n * @param screenSize - Current screen size category\n * @returns Calculated footer height in pixels\n * \n */\nexport function getResponsiveFooterHeight(screenSize: ScreenSize): number {\n return BASE_LAYOUT_VALUES.footerHeight[screenSize];\n}\n\n/**\n * Calculate responsive section spacing value\n * \n * @param screenSize - Current screen size category\n * @returns Calculated section spacing in pixels\n * \n */\nexport function getResponsiveSectionSpacing(screenSize: ScreenSize): number {\n return BASE_LAYOUT_VALUES.sectionSpacing[screenSize];\n}\n\n/**\n * Calculate responsive button area value\n * \n * @param screenSize - Current screen size category\n * @returns Calculated button area in pixels\n * \n */\nexport function getResponsiveButtonArea(screenSize: ScreenSize): number {\n return BASE_LAYOUT_VALUES.buttonArea[screenSize];\n}\n\n/**\n * Get all layout constants for a given screen size\n * Convenient helper that returns all layout values at once\n * \n * @param width - Screen width in pixels\n * @returns Object with all layout constant values\n * \n */\nexport function getLayoutConstants(width: number) {\n const screenSize = getScreenSize(width);\n \n return {\n padding: getResponsivePadding(screenSize),\n headerHeight: getResponsiveHeaderHeight(screenSize),\n footerHeight: getResponsiveFooterHeight(screenSize),\n sectionSpacing: getResponsiveSectionSpacing(screenSize),\n buttonArea: getResponsiveButtonArea(screenSize),\n };\n}\n\n/**\n * Get combat-specific layout constants for a given screen size\n * \n * Optimized for narrow devices (<450px), with extra-small device support\n * explicitly tuned for ultra-small screens (<380px) like iPhone SE, old\n * Android phones, and budget smartphones.\n * \n * Now properly handles high-resolution mobile devices (2K+, Super HD) by\n * checking isMobile flag to ensure they get mobile-optimized layout values\n * regardless of pixel width.\n * \n * @param width - Screen width in pixels\n * @param isMobile - Optional: Whether device is mobile (from user-agent detection)\n * @returns Object with combat layout constant values\n * \n */\nexport function getCombatLayoutConstants(width: number, isMobile?: boolean) {\n // For mobile devices, force 'mobile' screen size regardless of pixel width\n // This ensures high-res mobile devices (2K+) get mobile-optimized layouts\n const screenSize = isMobile ? 'mobile' : getScreenSize(width);\n \n // Extra-small detection for low-end mobile devices (<380px)\n const isExtraSmall = isMobile && width < 380;\n \n // Combat screen uses different base values for compact HUD\n const hudHeightMap = {\n mobile: isExtraSmall ? 85 : 95,\n tablet: 100,\n desktop: 130,\n large: 135,\n xlarge: 140,\n };\n \n // Note: Tablet optimizations - controlsHeight and footerHeight are intentionally\n // smaller on tablets than mobile for better landscape orientation ergonomics.\n // Mobile (portrait) needs taller controls for thumb reach, while tablets\n // (often landscape) can use more compact controls with better screen utilization.\n const controlsHeightMap = {\n mobile: isExtraSmall ? 150 : 160, // Taller for portrait thumb reach\n tablet: 140, // Optimized for landscape - more compact\n desktop: 170,\n large: 175,\n xlarge: 180,\n };\n \n const footerHeightMap = {\n mobile: 34, // Adequate for portrait orientation\n tablet: 30, // Optimized for landscape - more compact\n desktop: 35,\n large: 37,\n xlarge: 40,\n };\n \n const healthBarHeightMap = {\n mobile: 48,\n tablet: 50,\n desktop: 65,\n large: 67,\n xlarge: 70,\n };\n \n // Touch target heights - WCAG AA compliance (minimum 44px)\n const buttonHeightMap = {\n mobile: isExtraSmall ? 48 : 55, // Minimum 48px for extra-small\n tablet: 55,\n desktop: 60,\n large: 60,\n xlarge: 60,\n };\n \n return {\n padding: isExtraSmall ? 8 : 10, // Reduced padding for extra-small\n hudHeight: hudHeightMap[screenSize],\n controlsHeight: controlsHeightMap[screenSize],\n footerHeight: footerHeightMap[screenSize],\n healthBarHeight: healthBarHeightMap[screenSize],\n buttonHeight: buttonHeightMap[screenSize],\n };\n}\n"],"mappings":";;;;;;;;;;;;;;AAgBA,IAAM,4BAA4B;;;;;;;AAQlC,IAAM,6BAA6B;;;;;;;;AASnC,SAAgB,2BAA2B,OAAuB;CAChE,OAAO,KAAK,IAAI,QAAQ,2BAA2B,0BAA0B;AAC/E;;;;;AAMA,IAAM,qBAAqB;CAEzB,SAAS;EACP,QAAQ;EACR,QAAQ;EACR,SAAS;EACT,OAAO;EACP,QAAQ;CACV;CAEA,cAAc;EACZ,QAAQ;EACR,QAAQ;EACR,SAAS;EACT,OAAO;EACP,QAAQ;CACV;CAEA,cAAc;EACZ,QAAQ;EACR,QAAQ;EACR,SAAS;EACT,OAAO;EACP,QAAQ;CACV;CAEA,gBAAgB;EACd,QAAQ;EACR,QAAQ;EACR,SAAS;EACT,OAAO;EACP,QAAQ;CACV;CAEA,YAAY;EACV,QAAQ;EACR,QAAQ;EACR,SAAS;EACT,OAAO;EACP,QAAQ;CACV;AACF;;;;;;;;AASA,SAAgB,qBAAqB,YAAgC;CACnE,OAAO,mBAAmB,QAAQ;AACpC;;;;;;;;AASA,SAAgB,0BAA0B,YAAgC;CACxE,OAAO,mBAAmB,aAAa;AACzC;;;;;;;;AASA,SAAgB,0BAA0B,YAAgC;CACxE,OAAO,mBAAmB,aAAa;AACzC;;;;;;;;AASA,SAAgB,4BAA4B,YAAgC;CAC1E,OAAO,mBAAmB,eAAe;AAC3C;;;;;;;;AASA,SAAgB,wBAAwB,YAAgC;CACtE,OAAO,mBAAmB,WAAW;AACvC;;;;;;;;;AAUA,SAAgB,mBAAmB,OAAe;CAChD,MAAM,aAAa,cAAc,KAAK;CAEtC,OAAO;EACL,SAAS,qBAAqB,UAAU;EACxC,cAAc,0BAA0B,UAAU;EAClD,cAAc,0BAA0B,UAAU;EAClD,gBAAgB,4BAA4B,UAAU;EACtD,YAAY,wBAAwB,UAAU;CAChD;AACF;;;;;;;;;;;;;;;;;AAkBA,SAAgB,yBAAyB,OAAe,UAAoB;CAG1E,MAAM,aAAa,WAAW,WAAW,cAAc,KAAK;CAG5D,MAAM,eAAe,YAAY,QAAQ;CAGzC,MAAM,eAAe;EACnB,QAAQ,eAAe,KAAK;EAC5B,QAAQ;EACR,SAAS;EACT,OAAO;EACP,QAAQ;CACV;CAMA,MAAM,oBAAoB;EACxB,QAAQ,eAAe,MAAM;EAC7B,QAAQ;EACR,SAAS;EACT,OAAO;EACP,QAAQ;CACV;CAEA,MAAM,kBAAkB;EACtB,QAAQ;EACR,QAAQ;EACR,SAAS;EACT,OAAO;EACP,QAAQ;CACV;CAEA,MAAM,qBAAqB;EACzB,QAAQ;EACR,QAAQ;EACR,SAAS;EACT,OAAO;EACP,QAAQ;CACV;CAGA,MAAM,kBAAkB;EACtB,QAAQ,eAAe,KAAK;EAC5B,QAAQ;EACR,SAAS;EACT,OAAO;EACP,QAAQ;CACV;CAEA,OAAO;EACL,SAAS,eAAe,IAAI;EAC5B,WAAW,aAAa;EACxB,gBAAgB,kBAAkB;EAClC,cAAc,gBAAgB;EAC9B,iBAAiB,mBAAmB;EACpC,cAAc,gBAAgB;CAChC;AACF"}
1
+ {"version":3,"file":"responsiveLayoutHelpers.js","names":[],"sources":["../../src/utils/responsiveLayoutHelpers.ts"],"sourcesContent":["/**\n * Responsive Layout Helpers\n * \n * Centralized utilities for calculating responsive layout constants\n * across different screen components. Uses the centralized ResponsiveScaling\n * system for consistent scaling patterns.\n * \n * @module utils/responsiveLayoutHelpers\n * @category Layout\n * @korean 반응형레이아웃도우미\n */\n\nimport { getScreenSize } from '../systems/ResponsiveScaling';\nimport type { ScreenSize } from '../systems/ResponsiveScaling';\n\n/** Desktop arena width as a proportion of viewport width. */\nconst DESKTOP_ARENA_WIDTH_RATIO = 0.68;\n\n/**\n * Maximum desktop arena width in CSS pixels.\n *\n * Caps ultra-wide/8K displays to protect WebGL fill-rate while preserving a\n * large, readable 4K desktop arena.\n */\nconst DESKTOP_ARENA_MAX_WIDTH_PX = 2560;\n\n/**\n * Calculate maximum desktop arena width for combat/training screens.\n *\n * @param width - Viewport width in CSS pixels\n * @returns Width budget for the 4:3 desktop arena\n *\n */\nexport function getDesktopArenaWidthBudget(width: number): number {\n return Math.min(width * DESKTOP_ARENA_WIDTH_RATIO, DESKTOP_ARENA_MAX_WIDTH_PX);\n}\n\n/**\n * Shared HUD position scale for combat and training screens.\n *\n * Keeps top/bottom HUDs, side HUD offsets, and arena reservations synchronized\n * on large and 4K displays.\n */\nexport function getHUDPositionScale(\n screenSize: ScreenSize,\n isMobile: boolean,\n): number {\n if (isMobile) {\n return 1.0;\n }\n\n switch (screenSize) {\n case \"large\":\n return 1.25;\n case \"xlarge\":\n return 1.5;\n case \"mobile\":\n case \"tablet\":\n case \"desktop\":\n default:\n return 1.0;\n }\n}\n\n/**\n * Base layout values for different screen sizes\n * These serve as reference values that scale proportionally\n */\nconst BASE_LAYOUT_VALUES = {\n // Base padding values (desktop reference)\n padding: {\n mobile: 20,\n tablet: 25,\n desktop: 30,\n large: 32,\n xlarge: 35,\n },\n // Base header height values\n headerHeight: {\n mobile: 90,\n tablet: 100,\n desktop: 110,\n large: 115,\n xlarge: 120,\n },\n // Base footer height values\n footerHeight: {\n mobile: 75,\n tablet: 85,\n desktop: 90,\n large: 95,\n xlarge: 100,\n },\n // Base section spacing values\n sectionSpacing: {\n mobile: 15,\n tablet: 18,\n desktop: 20,\n large: 22,\n xlarge: 25,\n },\n // Base button area values\n buttonArea: {\n mobile: 75,\n tablet: 85,\n desktop: 95,\n large: 102,\n xlarge: 110,\n },\n} as const;\n\n/**\n * Calculate responsive padding value\n * \n * @param screenSize - Current screen size category\n * @returns Calculated padding in pixels\n * \n */\nexport function getResponsivePadding(screenSize: ScreenSize): number {\n return BASE_LAYOUT_VALUES.padding[screenSize];\n}\n\n/**\n * Calculate responsive header height value\n * \n * @param screenSize - Current screen size category\n * @returns Calculated header height in pixels\n * \n */\nexport function getResponsiveHeaderHeight(screenSize: ScreenSize): number {\n return BASE_LAYOUT_VALUES.headerHeight[screenSize];\n}\n\n/**\n * Calculate responsive footer height value\n * \n * @param screenSize - Current screen size category\n * @returns Calculated footer height in pixels\n * \n */\nexport function getResponsiveFooterHeight(screenSize: ScreenSize): number {\n return BASE_LAYOUT_VALUES.footerHeight[screenSize];\n}\n\n/**\n * Calculate responsive section spacing value\n * \n * @param screenSize - Current screen size category\n * @returns Calculated section spacing in pixels\n * \n */\nexport function getResponsiveSectionSpacing(screenSize: ScreenSize): number {\n return BASE_LAYOUT_VALUES.sectionSpacing[screenSize];\n}\n\n/**\n * Calculate responsive button area value\n * \n * @param screenSize - Current screen size category\n * @returns Calculated button area in pixels\n * \n */\nexport function getResponsiveButtonArea(screenSize: ScreenSize): number {\n return BASE_LAYOUT_VALUES.buttonArea[screenSize];\n}\n\n/**\n * Get all layout constants for a given screen size\n * Convenient helper that returns all layout values at once\n * \n * @param width - Screen width in pixels\n * @returns Object with all layout constant values\n * \n */\nexport function getLayoutConstants(width: number) {\n const screenSize = getScreenSize(width);\n \n return {\n padding: getResponsivePadding(screenSize),\n headerHeight: getResponsiveHeaderHeight(screenSize),\n footerHeight: getResponsiveFooterHeight(screenSize),\n sectionSpacing: getResponsiveSectionSpacing(screenSize),\n buttonArea: getResponsiveButtonArea(screenSize),\n };\n}\n\n/**\n * Get combat-specific layout constants for a given screen size\n * \n * Optimized for narrow devices (<450px), with extra-small device support\n * explicitly tuned for ultra-small screens (<380px) like iPhone SE, old\n * Android phones, and budget smartphones.\n * \n * Now properly handles high-resolution mobile devices (2K+, Super HD) by\n * checking isMobile flag to ensure they get mobile-optimized layout values\n * regardless of pixel width.\n * \n * @param width - Screen width in pixels\n * @param isMobile - Optional: Whether device is mobile (from user-agent detection)\n * @returns Object with combat layout constant values\n * \n */\nexport function getCombatLayoutConstants(width: number, isMobile?: boolean) {\n // For mobile devices, force 'mobile' screen size regardless of pixel width\n // This ensures high-res mobile devices (2K+) get mobile-optimized layouts\n const screenSize = isMobile ? 'mobile' : getScreenSize(width);\n \n // Extra-small detection for low-end mobile devices (<380px)\n const isExtraSmall = isMobile && width < 380;\n \n // Combat screen uses different base values for compact HUD\n const hudHeightMap = {\n mobile: isExtraSmall ? 85 : 95,\n tablet: 100,\n desktop: 130,\n large: 135,\n xlarge: 140,\n };\n \n // Note: Tablet optimizations - controlsHeight and footerHeight are intentionally\n // smaller on tablets than mobile for better landscape orientation ergonomics.\n // Mobile (portrait) needs taller controls for thumb reach, while tablets\n // (often landscape) can use more compact controls with better screen utilization.\n const controlsHeightMap = {\n mobile: isExtraSmall ? 150 : 160, // Taller for portrait thumb reach\n tablet: 140, // Optimized for landscape - more compact\n desktop: 170,\n large: 175,\n xlarge: 180,\n };\n \n const footerHeightMap = {\n mobile: 34, // Adequate for portrait orientation\n tablet: 30, // Optimized for landscape - more compact\n desktop: 35,\n large: 37,\n xlarge: 40,\n };\n \n const healthBarHeightMap = {\n mobile: 48,\n tablet: 50,\n desktop: 65,\n large: 67,\n xlarge: 70,\n };\n \n // Touch target heights - WCAG AA compliance (minimum 44px)\n const buttonHeightMap = {\n mobile: isExtraSmall ? 48 : 55, // Minimum 48px for extra-small\n tablet: 55,\n desktop: 60,\n large: 60,\n xlarge: 60,\n };\n \n return {\n padding: isExtraSmall ? 8 : 10, // Reduced padding for extra-small\n hudHeight: hudHeightMap[screenSize],\n controlsHeight: controlsHeightMap[screenSize],\n footerHeight: footerHeightMap[screenSize],\n healthBarHeight: healthBarHeightMap[screenSize],\n buttonHeight: buttonHeightMap[screenSize],\n };\n}\n"],"mappings":";;;;;;;;;;;;;;AAgBA,IAAM,4BAA4B;;;;;;;AAQlC,IAAM,6BAA6B;;;;;;;;AASnC,SAAgB,2BAA2B,OAAuB;CAChE,OAAO,KAAK,IAAI,QAAQ,2BAA2B,0BAA0B;AAC/E;;;;;;;AAQA,SAAgB,oBACd,YACA,UACQ;CACR,IAAI,UACF,OAAO;CAGT,QAAQ,YAAR;EACE,KAAK,SACH,OAAO;EACT,KAAK,UACH,OAAO;EAIT,SACE,OAAO;CACX;AACF;;;;;AAMA,IAAM,qBAAqB;CAEzB,SAAS;EACP,QAAQ;EACR,QAAQ;EACR,SAAS;EACT,OAAO;EACP,QAAQ;CACV;CAEA,cAAc;EACZ,QAAQ;EACR,QAAQ;EACR,SAAS;EACT,OAAO;EACP,QAAQ;CACV;CAEA,cAAc;EACZ,QAAQ;EACR,QAAQ;EACR,SAAS;EACT,OAAO;EACP,QAAQ;CACV;CAEA,gBAAgB;EACd,QAAQ;EACR,QAAQ;EACR,SAAS;EACT,OAAO;EACP,QAAQ;CACV;CAEA,YAAY;EACV,QAAQ;EACR,QAAQ;EACR,SAAS;EACT,OAAO;EACP,QAAQ;CACV;AACF;;;;;;;;AASA,SAAgB,qBAAqB,YAAgC;CACnE,OAAO,mBAAmB,QAAQ;AACpC;;;;;;;;AASA,SAAgB,0BAA0B,YAAgC;CACxE,OAAO,mBAAmB,aAAa;AACzC;;;;;;;;AASA,SAAgB,0BAA0B,YAAgC;CACxE,OAAO,mBAAmB,aAAa;AACzC;;;;;;;;AASA,SAAgB,4BAA4B,YAAgC;CAC1E,OAAO,mBAAmB,eAAe;AAC3C;;;;;;;;AASA,SAAgB,wBAAwB,YAAgC;CACtE,OAAO,mBAAmB,WAAW;AACvC;;;;;;;;;AAUA,SAAgB,mBAAmB,OAAe;CAChD,MAAM,aAAa,cAAc,KAAK;CAEtC,OAAO;EACL,SAAS,qBAAqB,UAAU;EACxC,cAAc,0BAA0B,UAAU;EAClD,cAAc,0BAA0B,UAAU;EAClD,gBAAgB,4BAA4B,UAAU;EACtD,YAAY,wBAAwB,UAAU;CAChD;AACF;;;;;;;;;;;;;;;;;AAkBA,SAAgB,yBAAyB,OAAe,UAAoB;CAG1E,MAAM,aAAa,WAAW,WAAW,cAAc,KAAK;CAG5D,MAAM,eAAe,YAAY,QAAQ;CAGzC,MAAM,eAAe;EACnB,QAAQ,eAAe,KAAK;EAC5B,QAAQ;EACR,SAAS;EACT,OAAO;EACP,QAAQ;CACV;CAMA,MAAM,oBAAoB;EACxB,QAAQ,eAAe,MAAM;EAC7B,QAAQ;EACR,SAAS;EACT,OAAO;EACP,QAAQ;CACV;CAEA,MAAM,kBAAkB;EACtB,QAAQ;EACR,QAAQ;EACR,SAAS;EACT,OAAO;EACP,QAAQ;CACV;CAEA,MAAM,qBAAqB;EACzB,QAAQ;EACR,QAAQ;EACR,SAAS;EACT,OAAO;EACP,QAAQ;CACV;CAGA,MAAM,kBAAkB;EACtB,QAAQ,eAAe,KAAK;EAC5B,QAAQ;EACR,SAAS;EACT,OAAO;EACP,QAAQ;CACV;CAEA,OAAO;EACL,SAAS,eAAe,IAAI;EAC5B,WAAW,aAAa;EACxB,gBAAgB,kBAAkB;EAClC,cAAc,gBAAgB;EAC9B,iBAAiB,mBAAmB;EACpC,cAAc,gBAAgB;CAChC;AACF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blacktrigram",
3
- "version": "0.7.48",
3
+ "version": "0.7.49",
4
4
  "description": "Black Trigram (흑괘) - Korean Martial Arts Combat Simulator. Reusable game systems, combat mechanics, animation framework, and Korean martial arts data built with React, Three.js, and TypeScript.",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",
@@ -187,7 +187,7 @@
187
187
  "three": "0.184.0"
188
188
  },
189
189
  "devDependencies": {
190
- "@aws-sdk/client-bedrock-runtime": "3.1046.0",
190
+ "@aws-sdk/client-bedrock-runtime": "3.1047.0",
191
191
  "@eslint/js": "10.0.1",
192
192
  "@react-three/drei": "10.7.7",
193
193
  "@react-three/fiber": "9.6.1",
@@ -196,11 +196,11 @@
196
196
  "@testing-library/jest-dom": "6.9.1",
197
197
  "@testing-library/react": "16.3.2",
198
198
  "@testing-library/user-event": "14.6.1",
199
- "@types/node": "25.7.0",
199
+ "@types/node": "25.8.0",
200
200
  "@types/react": "19.2.14",
201
201
  "@types/react-dom": "19.2.3",
202
202
  "@types/three": "0.184.1",
203
- "@vitejs/plugin-react": "6.0.1",
203
+ "@vitejs/plugin-react": "6.0.2",
204
204
  "@vitest/coverage-v8": "4.1.6",
205
205
  "@vitest/ui": "4.1.6",
206
206
  "cypress": "15.15.0",
@@ -215,7 +215,7 @@
215
215
  "globals": "17.6.0",
216
216
  "jest-axe": "10.0.0",
217
217
  "jsdom": "29.1.1",
218
- "knip": "6.13.1",
218
+ "knip": "6.14.0",
219
219
  "license-compliance": "3.0.1",
220
220
  "mocha-junit-reporter": "2.2.1",
221
221
  "mochawesome": "7.1.4",