blacktrigram 0.7.11 → 0.7.13

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 (73) hide show
  1. package/DATA_MODEL.md +347 -0
  2. package/lib/App.d.ts.map +1 -1
  3. package/lib/App2.js +0 -6
  4. package/lib/App2.js.map +1 -1
  5. package/lib/assets/index.css +102 -94
  6. package/lib/components/screens/combat/CombatScreen3D.d.ts +1 -1
  7. package/lib/components/screens/combat/CombatScreen3D.js +2 -2
  8. package/lib/components/screens/combat/CombatScreen3D.js.map +1 -1
  9. package/lib/components/screens/combat/components/controls/CombatButtons.d.ts.map +1 -1
  10. package/lib/components/screens/combat/components/controls/CombatButtons.js +22 -7
  11. package/lib/components/screens/combat/components/controls/CombatButtons.js.map +1 -1
  12. package/lib/components/screens/combat/components/controls/CombatControlsPanel.d.ts +2 -0
  13. package/lib/components/screens/combat/components/controls/CombatControlsPanel.d.ts.map +1 -1
  14. package/lib/components/screens/combat/components/controls/CombatControlsPanel.js +7 -4
  15. package/lib/components/screens/combat/components/controls/CombatControlsPanel.js.map +1 -1
  16. package/lib/components/screens/combat/components/controls/PauseMenu.d.ts.map +1 -1
  17. package/lib/components/screens/combat/components/controls/PauseMenu.js +15 -5
  18. package/lib/components/screens/combat/components/controls/PauseMenu.js.map +1 -1
  19. package/lib/components/screens/combat/components/feedback/RoundAnnouncementOverlayHtml.js +15 -16
  20. package/lib/components/screens/combat/components/feedback/RoundAnnouncementOverlayHtml.js.map +1 -1
  21. package/lib/components/screens/combat/components/hud/DifficultyIndicator.js +1 -2
  22. package/lib/components/screens/combat/components/hud/DifficultyIndicator.js.map +1 -1
  23. package/lib/components/screens/combat/components/hud/PlayerStateOverlayHtml.js +28 -24
  24. package/lib/components/screens/combat/components/hud/PlayerStateOverlayHtml.js.map +1 -1
  25. package/lib/components/screens/combat/components/indicators/InputBufferDisplay.js +2 -4
  26. package/lib/components/screens/combat/components/indicators/InputBufferDisplay.js.map +1 -1
  27. package/lib/components/screens/controls/ControlsScreen3D.d.ts.map +1 -1
  28. package/lib/components/screens/controls/ControlsScreen3D.js +3 -2
  29. package/lib/components/screens/controls/ControlsScreen3D.js.map +1 -1
  30. package/lib/components/screens/controls/components/InteractiveControlDemoOverlayHtml.js +28 -30
  31. package/lib/components/screens/controls/components/InteractiveControlDemoOverlayHtml.js.map +1 -1
  32. package/lib/components/screens/controls/components/VisualKeyboard3D.d.ts.map +1 -1
  33. package/lib/components/screens/controls/components/VisualKeyboard3D.js +4 -3
  34. package/lib/components/screens/controls/components/VisualKeyboard3D.js.map +1 -1
  35. package/lib/components/screens/intro/IntroScreen3D.js +1 -1
  36. package/lib/components/screens/intro/components/MenuButtonsOverlayHtml.d.ts.map +1 -1
  37. package/lib/components/screens/intro/components/MenuButtonsOverlayHtml.js +5 -2
  38. package/lib/components/screens/intro/components/MenuButtonsOverlayHtml.js.map +1 -1
  39. package/lib/components/screens/philosophy/components/TrigramSymbol3D.d.ts.map +1 -1
  40. package/lib/components/screens/training/components/FootworkDrillsOverlayHtml.js +4 -4
  41. package/lib/components/screens/training/components/FootworkDrillsOverlayHtml.js.map +1 -1
  42. package/lib/components/screens/training/components/hud/TrainingTopHUD.js +1 -1
  43. package/lib/components/screens/training/components/hud/TrainingTopHUD.js.map +1 -1
  44. package/lib/components/shared/base/ResponsiveContainer.d.ts +6 -0
  45. package/lib/components/shared/base/ResponsiveContainer.d.ts.map +1 -1
  46. package/lib/components/shared/three/effects/ActionFeedback.js +1 -2
  47. package/lib/components/shared/three/effects/ActionFeedback.js.map +1 -1
  48. package/lib/components/shared/three/effects/DamageNumbers.js +1 -2
  49. package/lib/components/shared/three/effects/DamageNumbers.js.map +1 -1
  50. package/lib/components/shared/three/ui/BodyPartHealthDisplay.js +5 -5
  51. package/lib/components/shared/three/ui/BodyPartHealthDisplay.js.map +1 -1
  52. package/lib/components/shared/three/ui/BreathingIndicator2.js +3 -2
  53. package/lib/components/shared/three/ui/BreathingIndicator2.js.map +1 -1
  54. package/lib/components/shared/three/ui/TechniqueCard.d.ts.map +1 -1
  55. package/lib/components/shared/three/ui/TechniqueCard.js +27 -30
  56. package/lib/components/shared/three/ui/TechniqueCard.js.map +1 -1
  57. package/lib/components/shared/three/ui/VitalPointOverlayControlsHtml.d.ts.map +1 -1
  58. package/lib/components/shared/three/ui/VitalPointOverlayControlsHtml.js +57 -59
  59. package/lib/components/shared/three/ui/VitalPointOverlayControlsHtml.js.map +1 -1
  60. package/lib/components/shared/ui/BaseHUDContainer.d.ts +40 -0
  61. package/lib/components/shared/ui/BaseHUDContainer.d.ts.map +1 -1
  62. package/lib/components/shared/ui/BaseHUDContainer.js +40 -0
  63. package/lib/components/shared/ui/BaseHUDContainer.js.map +1 -1
  64. package/lib/components/shared/ui/MobileHUDLayout.d.ts +13 -0
  65. package/lib/components/shared/ui/MobileHUDLayout.d.ts.map +1 -1
  66. package/lib/components/shared/ui/SplashScreen.js +10 -10
  67. package/lib/components/shared/ui/SplashScreen.js.map +1 -1
  68. package/lib/components/shared/ui/VitalPointOverlayControlsPure.d.ts.map +1 -1
  69. package/lib/components/shared/ui/VitalPointOverlayControlsPure.js +57 -62
  70. package/lib/components/shared/ui/VitalPointOverlayControlsPure.js.map +1 -1
  71. package/lib/components/shared/ui/VolumeControl.js +7 -7
  72. package/lib/components/shared/ui/VolumeControl.js.map +1 -1
  73. package/package.json +9 -9
@@ -16,6 +16,46 @@ import { jsx } from "react/jsx-runtime";
16
16
  * - Pointer events handling
17
17
  * - Backdrop blur effects
18
18
  *
19
+ * ## Z-Index Stacking Order (Combat HUD Layers)
20
+ *
21
+ * The combat screen renders multiple overlapping HUD panels. The stacking
22
+ * order is managed by the Z_INDEX constants from LayoutTypes.ts:
23
+ *
24
+ * | Layer | Z-Index | Description |
25
+ * |---------------------|---------|------------------------------------|
26
+ * | BACKGROUND | 0 | 3D scene background |
27
+ * | ARENA | 10 | Combat arena mesh |
28
+ * | PLAYERS | 20 | Character models |
29
+ * | EFFECTS | 30 | Particles and VFX |
30
+ * | HUD_BACKGROUND | 40 | HUD panel backgrounds |
31
+ * | HUD (default) | 50 | Left/Right/Top/Bottom HUD panels |
32
+ * | TECHNIQUE_BAR | 55 | Technique bar overlay |
33
+ * | HUD_OVERLAY | 60 | PlayerStateOverlay and sub-HUDs |
34
+ * | MOBILE_CONTROLS | 100 | Touch controls on mobile |
35
+ * | MODAL | 200 | Modal dialogs |
36
+ * | TOOLTIP | 300 | Tooltips and hints |
37
+ * | PAUSE_MENU | 1000 | Pause menu overlay |
38
+ * | LOADING | 2000 | Loading screens |
39
+ * | DEBUG | 9000 | Performance debug overlay |
40
+ *
41
+ * All BaseHUDContainer instances default to Z_INDEX.HUD (50). Parent
42
+ * screens can override via the `zIndex` prop when a different layer
43
+ * is needed (e.g., overlays at HUD_OVERLAY = 60).
44
+ *
45
+ * ## Viewport Breakpoints (Expected HUD Sizes)
46
+ *
47
+ * | Viewport | Width | Left/Right HUD | Top HUD | Bottom HUD |
48
+ * |---------------------|----------|----------------|---------|------------|
49
+ * | Small Phone (≤375) | ≤375px | ~120-150px | ~50px | ~90px |
50
+ * | Mobile (<768) | <768px | ~180-200px | ~60px | ~110px |
51
+ * | Tablet (768-1199) | 768-1199 | ~220-260px | ~65px | ~120px |
52
+ * | Desktop (≥1200) | ≥1200px | ~260-300px | ~70px | ~130px |
53
+ * | 4K (≥1920) | ≥1920px | ~300-400px | ~80px | ~140px |
54
+ *
55
+ * Width/height values are passed by the parent screen and scaled via
56
+ * positionScale multipliers. This table documents the expected ranges
57
+ * produced by CombatScreen3D and TrainingScreen3D layout calculations.
58
+ *
19
59
  * @module components/shared/ui
20
60
  * @korean 기본HUD컨테이너 - 공통 패턴을 가진 재사용 가능한 HUD 컨테이너
21
61
  */
@@ -1 +1 @@
1
- {"version":3,"file":"BaseHUDContainer.js","names":[],"sources":["../../../../src/components/shared/ui/BaseHUDContainer.tsx"],"sourcesContent":["/**\n * BaseHUDContainer - Reusable HUD container with common patterns\n *\n * Provides consistent container styling and positioning for all HUD components.\n * Eliminates code duplication across Training and Combat HUDs.\n *\n * Features:\n * - Responsive positioning based on HUD position (left, right, top, bottom)\n * - Korean cyberpunk theming with gradients and borders\n * - Pointer events handling\n * - Backdrop blur effects\n *\n * @module components/shared/ui\n * @korean 기본HUD컨테이너 - 공통 패턴을 가진 재사용 가능한 HUD 컨테이너\n */\n\nimport React from \"react\";\nimport { Z_INDEX } from \"../../../types/LayoutTypes\";\nimport { KOREAN_COLORS } from \"@/types/constants\";\nimport { hexToRgbaString } from \"../../../utils/colorUtils\";\n\n/**\n * HUD position type\n */\nexport type HUDPosition = \"left\" | \"right\" | \"top\" | \"bottom\";\n\n/**\n * Props for BaseHUDContainer component\n */\nexport interface BaseHUDContainerProps {\n /** Position of the HUD (left, right, top, bottom) */\n readonly position: HUDPosition;\n /** Width in pixels (used for left/right positions; top/bottom use 100% width) */\n readonly width: number;\n /** Height in pixels */\n readonly height: number;\n /** Top offset in pixels (for left/right HUDs) */\n readonly topOffset?: number;\n /** Internal padding in pixels */\n readonly padding?: number;\n /** Gap between sections in pixels */\n readonly gap?: number;\n /** Z-index for layering */\n readonly zIndex?: number;\n /** Additional CSS styles */\n readonly style?: React.CSSProperties;\n /** Child elements */\n readonly children: React.ReactNode;\n /** Test ID for testing */\n readonly dataTestId?: string;\n}\n\n/**\n * BaseHUDContainer Component\n *\n * Reusable container for HUD elements with consistent styling and positioning.\n * Handles left, right, top, and bottom positions with appropriate borders,\n * gradients, and responsive behavior.\n *\n * @example\n * ```tsx\n * <BaseHUDContainer\n * position=\"left\"\n * width={268}\n * height={940}\n * topOffset={70}\n * padding={12}\n * gap={14}\n * dataTestId=\"combat-left-hud\"\n * >\n * <PlayerHUD player={player} />\n * <SpeedIndicator speed={speed} />\n * </BaseHUDContainer>\n * ```\n */\nexport const BaseHUDContainer: React.FC<BaseHUDContainerProps> = ({\n position,\n width,\n height,\n topOffset = 0,\n padding = 10,\n gap = 12,\n zIndex = Z_INDEX.HUD,\n style = {},\n children,\n dataTestId,\n}) => {\n // Calculate position-specific styles\n // Using KOREAN_COLORS directly for stable memoization instead of useKoreanTheme\n const containerStyle = React.useMemo<React.CSSProperties>(() => {\n const baseStyle: React.CSSProperties = {\n position: \"absolute\",\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"stretch\",\n pointerEvents: \"none\",\n padding: `${padding}px`,\n boxSizing: \"border-box\",\n gap: `${gap}px`,\n zIndex,\n backdropFilter: \"blur(8px)\",\n };\n\n switch (position) {\n case \"left\":\n return {\n ...baseStyle,\n left: 0,\n top: `${topOffset}px`,\n width: `${width}px`,\n height: `${height}px`,\n justifyContent: \"flex-start\",\n borderRight: `2px solid ${hexToRgbaString(KOREAN_COLORS.PRIMARY_CYAN, 0.4)}`,\n background: `linear-gradient(90deg, ${hexToRgbaString(KOREAN_COLORS.UI_BACKGROUND_DARK, 0.85)} 0%, ${hexToRgbaString(KOREAN_COLORS.UI_BACKGROUND_DARK, 0.4)} 100%)`,\n };\n\n case \"right\":\n return {\n ...baseStyle,\n right: 0,\n top: `${topOffset}px`,\n width: `${width}px`,\n height: `${height}px`,\n justifyContent: \"flex-start\",\n borderLeft: `2px solid ${hexToRgbaString(KOREAN_COLORS.PRIMARY_CYAN, 0.4)}`,\n background: `linear-gradient(270deg, ${hexToRgbaString(KOREAN_COLORS.UI_BACKGROUND_DARK, 0.85)} 0%, ${hexToRgbaString(KOREAN_COLORS.UI_BACKGROUND_DARK, 0.4)} 100%)`,\n };\n\n case \"top\":\n return {\n ...baseStyle,\n top: 0,\n left: 0,\n width: \"100%\",\n height: `${height}px`,\n flexDirection: \"row\",\n justifyContent: \"space-between\",\n alignItems: \"center\",\n borderBottom: `2px solid ${hexToRgbaString(KOREAN_COLORS.PRIMARY_CYAN, 0.4)}`,\n background: `linear-gradient(180deg, ${hexToRgbaString(KOREAN_COLORS.UI_BACKGROUND_DARK, 0.9)} 0%, ${hexToRgbaString(KOREAN_COLORS.UI_BACKGROUND_DARK, 0.7)} 100%)`,\n };\n\n case \"bottom\":\n return {\n ...baseStyle,\n bottom: 0,\n left: 0,\n width: \"100%\",\n height: `${height}px`,\n flexDirection: \"row\",\n justifyContent: \"space-between\",\n alignItems: \"center\",\n borderTop: `2px solid ${hexToRgbaString(KOREAN_COLORS.PRIMARY_CYAN, 0.4)}`,\n background: `linear-gradient(0deg, ${hexToRgbaString(KOREAN_COLORS.UI_BACKGROUND_DARK, 0.9)} 0%, ${hexToRgbaString(KOREAN_COLORS.UI_BACKGROUND_DARK, 0.7)} 100%)`,\n };\n }\n }, [position, width, height, topOffset, padding, gap, zIndex]);\n\n return (\n <div style={{ ...containerStyle, ...style }} data-testid={dataTestId}>\n {children}\n </div>\n );\n};\n\nexport default BaseHUDContainer;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2EA,IAAa,oBAAqD,EAChE,UACA,OACA,QACA,YAAY,GACZ,UAAU,IACV,MAAM,IACN,SAAS,QAAQ,KACjB,QAAQ,EAAE,EACV,UACA,iBACI;AAwEJ,QACE,oBAAC,OAAD;EAAK,OAAO;GAAE,GAtEO,MAAM,cAAmC;IAC9D,MAAM,YAAiC;KACrC,UAAU;KACV,SAAS;KACT,eAAe;KACf,YAAY;KACZ,eAAe;KACf,SAAS,GAAG,QAAQ;KACpB,WAAW;KACX,KAAK,GAAG,IAAI;KACZ;KACA,gBAAgB;KACjB;AAED,YAAQ,UAAR;KACE,KAAK,OACH,QAAO;MACL,GAAG;MACH,MAAM;MACN,KAAK,GAAG,UAAU;MAClB,OAAO,GAAG,MAAM;MAChB,QAAQ,GAAG,OAAO;MAClB,gBAAgB;MAChB,aAAa,aAAa,gBAAgB,cAAc,cAAc,GAAI;MAC1E,YAAY,0BAA0B,gBAAgB,cAAc,oBAAoB,IAAK,CAAC,OAAO,gBAAgB,cAAc,oBAAoB,GAAI,CAAC;MAC7J;KAEH,KAAK,QACH,QAAO;MACL,GAAG;MACH,OAAO;MACP,KAAK,GAAG,UAAU;MAClB,OAAO,GAAG,MAAM;MAChB,QAAQ,GAAG,OAAO;MAClB,gBAAgB;MAChB,YAAY,aAAa,gBAAgB,cAAc,cAAc,GAAI;MACzE,YAAY,2BAA2B,gBAAgB,cAAc,oBAAoB,IAAK,CAAC,OAAO,gBAAgB,cAAc,oBAAoB,GAAI,CAAC;MAC9J;KAEH,KAAK,MACH,QAAO;MACL,GAAG;MACH,KAAK;MACL,MAAM;MACN,OAAO;MACP,QAAQ,GAAG,OAAO;MAClB,eAAe;MACf,gBAAgB;MAChB,YAAY;MACZ,cAAc,aAAa,gBAAgB,cAAc,cAAc,GAAI;MAC3E,YAAY,2BAA2B,gBAAgB,cAAc,oBAAoB,GAAI,CAAC,OAAO,gBAAgB,cAAc,oBAAoB,GAAI,CAAC;MAC7J;KAEH,KAAK,SACH,QAAO;MACL,GAAG;MACH,QAAQ;MACR,MAAM;MACN,OAAO;MACP,QAAQ,GAAG,OAAO;MAClB,eAAe;MACf,gBAAgB;MAChB,YAAY;MACZ,WAAW,aAAa,gBAAgB,cAAc,cAAc,GAAI;MACxE,YAAY,yBAAyB,gBAAgB,cAAc,oBAAoB,GAAI,CAAC,OAAO,gBAAgB,cAAc,oBAAoB,GAAI,CAAC;MAC3J;;MAEJ;IAAC;IAAU;IAAO;IAAQ;IAAW;IAAS;IAAK;IAAO,CAAC;GAG3B,GAAG;GAAO;EAAE,eAAa;EACvD;EACG,CAAA"}
1
+ {"version":3,"file":"BaseHUDContainer.js","names":[],"sources":["../../../../src/components/shared/ui/BaseHUDContainer.tsx"],"sourcesContent":["/**\n * BaseHUDContainer - Reusable HUD container with common patterns\n *\n * Provides consistent container styling and positioning for all HUD components.\n * Eliminates code duplication across Training and Combat HUDs.\n *\n * Features:\n * - Responsive positioning based on HUD position (left, right, top, bottom)\n * - Korean cyberpunk theming with gradients and borders\n * - Pointer events handling\n * - Backdrop blur effects\n *\n * ## Z-Index Stacking Order (Combat HUD Layers)\n *\n * The combat screen renders multiple overlapping HUD panels. The stacking\n * order is managed by the Z_INDEX constants from LayoutTypes.ts:\n *\n * | Layer | Z-Index | Description |\n * |---------------------|---------|------------------------------------|\n * | BACKGROUND | 0 | 3D scene background |\n * | ARENA | 10 | Combat arena mesh |\n * | PLAYERS | 20 | Character models |\n * | EFFECTS | 30 | Particles and VFX |\n * | HUD_BACKGROUND | 40 | HUD panel backgrounds |\n * | HUD (default) | 50 | Left/Right/Top/Bottom HUD panels |\n * | TECHNIQUE_BAR | 55 | Technique bar overlay |\n * | HUD_OVERLAY | 60 | PlayerStateOverlay and sub-HUDs |\n * | MOBILE_CONTROLS | 100 | Touch controls on mobile |\n * | MODAL | 200 | Modal dialogs |\n * | TOOLTIP | 300 | Tooltips and hints |\n * | PAUSE_MENU | 1000 | Pause menu overlay |\n * | LOADING | 2000 | Loading screens |\n * | DEBUG | 9000 | Performance debug overlay |\n *\n * All BaseHUDContainer instances default to Z_INDEX.HUD (50). Parent\n * screens can override via the `zIndex` prop when a different layer\n * is needed (e.g., overlays at HUD_OVERLAY = 60).\n *\n * ## Viewport Breakpoints (Expected HUD Sizes)\n *\n * | Viewport | Width | Left/Right HUD | Top HUD | Bottom HUD |\n * |---------------------|----------|----------------|---------|------------|\n * | Small Phone (≤375) | ≤375px | ~120-150px | ~50px | ~90px |\n * | Mobile (<768) | <768px | ~180-200px | ~60px | ~110px |\n * | Tablet (768-1199) | 768-1199 | ~220-260px | ~65px | ~120px |\n * | Desktop (≥1200) | ≥1200px | ~260-300px | ~70px | ~130px |\n * | 4K (≥1920) | ≥1920px | ~300-400px | ~80px | ~140px |\n *\n * Width/height values are passed by the parent screen and scaled via\n * positionScale multipliers. This table documents the expected ranges\n * produced by CombatScreen3D and TrainingScreen3D layout calculations.\n *\n * @module components/shared/ui\n * @korean 기본HUD컨테이너 - 공통 패턴을 가진 재사용 가능한 HUD 컨테이너\n */\n\nimport React from \"react\";\nimport { Z_INDEX } from \"../../../types/LayoutTypes\";\nimport { KOREAN_COLORS } from \"@/types/constants\";\nimport { hexToRgbaString } from \"../../../utils/colorUtils\";\n\n/**\n * HUD position type\n */\nexport type HUDPosition = \"left\" | \"right\" | \"top\" | \"bottom\";\n\n/**\n * Props for BaseHUDContainer component\n */\nexport interface BaseHUDContainerProps {\n /** Position of the HUD (left, right, top, bottom) */\n readonly position: HUDPosition;\n /** Width in pixels (used for left/right positions; top/bottom use 100% width) */\n readonly width: number;\n /** Height in pixels */\n readonly height: number;\n /** Top offset in pixels (for left/right HUDs) */\n readonly topOffset?: number;\n /** Internal padding in pixels */\n readonly padding?: number;\n /** Gap between sections in pixels */\n readonly gap?: number;\n /** Z-index for layering */\n readonly zIndex?: number;\n /** Additional CSS styles */\n readonly style?: React.CSSProperties;\n /** Child elements */\n readonly children: React.ReactNode;\n /** Test ID for testing */\n readonly dataTestId?: string;\n}\n\n/**\n * BaseHUDContainer Component\n *\n * Reusable container for HUD elements with consistent styling and positioning.\n * Handles left, right, top, and bottom positions with appropriate borders,\n * gradients, and responsive behavior.\n *\n * @example\n * ```tsx\n * <BaseHUDContainer\n * position=\"left\"\n * width={268}\n * height={940}\n * topOffset={70}\n * padding={12}\n * gap={14}\n * dataTestId=\"combat-left-hud\"\n * >\n * <PlayerHUD player={player} />\n * <SpeedIndicator speed={speed} />\n * </BaseHUDContainer>\n * ```\n */\nexport const BaseHUDContainer: React.FC<BaseHUDContainerProps> = ({\n position,\n width,\n height,\n topOffset = 0,\n padding = 10,\n gap = 12,\n zIndex = Z_INDEX.HUD,\n style = {},\n children,\n dataTestId,\n}) => {\n // Calculate position-specific styles\n // Using KOREAN_COLORS directly for stable memoization instead of useKoreanTheme\n //\n // Z-index stacking: defaults to Z_INDEX.HUD (50). Combat screen layers\n // are ordered as: HUD_BACKGROUND (40) < HUD (50) < TECHNIQUE_BAR (55) < HUD_OVERLAY (60).\n // Left/Right/Top/Bottom HUDs all share the same z-index (HUD = 50) and\n // rely on DOM order for overlap resolution. The PlayerStateOverlay sits\n // at HUD_OVERLAY (60) to appear above all HUD panels.\n //\n // Safe area note: left/right HUD panels are anchored to the viewport edge\n // with left: 0 / right: 0. Reducing the width prop alone will not move a\n // panel out of a landscape notch or horizontal safe-area cutout. If the\n // parent needs to avoid env(safe-area-inset-left) /\n // env(safe-area-inset-right), it should also provide a horizontal offset\n // via the existing style prop (for example, left/right or horizontal\n // padding) in addition to any width adjustments. Horizontal safe-area\n // insets affect left/right panel widths and offsets, not topOffset (which\n // is purely vertical). For portrait notch/status-bar avoidance, the parent\n // should incorporate layout.safeArea.top into the topOffset calculation.\n const containerStyle = React.useMemo<React.CSSProperties>(() => {\n const baseStyle: React.CSSProperties = {\n position: \"absolute\",\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"stretch\",\n pointerEvents: \"none\",\n padding: `${padding}px`,\n boxSizing: \"border-box\",\n gap: `${gap}px`,\n zIndex,\n backdropFilter: \"blur(8px)\",\n };\n\n switch (position) {\n case \"left\":\n return {\n ...baseStyle,\n left: 0,\n top: `${topOffset}px`,\n width: `${width}px`,\n height: `${height}px`,\n justifyContent: \"flex-start\",\n borderRight: `2px solid ${hexToRgbaString(KOREAN_COLORS.PRIMARY_CYAN, 0.4)}`,\n background: `linear-gradient(90deg, ${hexToRgbaString(KOREAN_COLORS.UI_BACKGROUND_DARK, 0.85)} 0%, ${hexToRgbaString(KOREAN_COLORS.UI_BACKGROUND_DARK, 0.4)} 100%)`,\n };\n\n case \"right\":\n return {\n ...baseStyle,\n right: 0,\n top: `${topOffset}px`,\n width: `${width}px`,\n height: `${height}px`,\n justifyContent: \"flex-start\",\n borderLeft: `2px solid ${hexToRgbaString(KOREAN_COLORS.PRIMARY_CYAN, 0.4)}`,\n background: `linear-gradient(270deg, ${hexToRgbaString(KOREAN_COLORS.UI_BACKGROUND_DARK, 0.85)} 0%, ${hexToRgbaString(KOREAN_COLORS.UI_BACKGROUND_DARK, 0.4)} 100%)`,\n };\n\n case \"top\":\n return {\n ...baseStyle,\n top: 0,\n left: 0,\n width: \"100%\",\n height: `${height}px`,\n flexDirection: \"row\",\n justifyContent: \"space-between\",\n alignItems: \"center\",\n borderBottom: `2px solid ${hexToRgbaString(KOREAN_COLORS.PRIMARY_CYAN, 0.4)}`,\n background: `linear-gradient(180deg, ${hexToRgbaString(KOREAN_COLORS.UI_BACKGROUND_DARK, 0.9)} 0%, ${hexToRgbaString(KOREAN_COLORS.UI_BACKGROUND_DARK, 0.7)} 100%)`,\n };\n\n case \"bottom\":\n return {\n ...baseStyle,\n bottom: 0,\n left: 0,\n width: \"100%\",\n height: `${height}px`,\n flexDirection: \"row\",\n justifyContent: \"space-between\",\n alignItems: \"center\",\n borderTop: `2px solid ${hexToRgbaString(KOREAN_COLORS.PRIMARY_CYAN, 0.4)}`,\n background: `linear-gradient(0deg, ${hexToRgbaString(KOREAN_COLORS.UI_BACKGROUND_DARK, 0.9)} 0%, ${hexToRgbaString(KOREAN_COLORS.UI_BACKGROUND_DARK, 0.7)} 100%)`,\n };\n }\n }, [position, width, height, topOffset, padding, gap, zIndex]);\n\n return (\n <div style={{ ...containerStyle, ...style }} data-testid={dataTestId}>\n {children}\n </div>\n );\n};\n\nexport default BaseHUDContainer;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmHA,IAAa,oBAAqD,EAChE,UACA,OACA,QACA,YAAY,GACZ,UAAU,IACV,MAAM,IACN,SAAS,QAAQ,KACjB,QAAQ,EAAE,EACV,UACA,iBACI;AAyFJ,QACE,oBAAC,OAAD;EAAK,OAAO;GAAE,GAtEO,MAAM,cAAmC;IAC9D,MAAM,YAAiC;KACrC,UAAU;KACV,SAAS;KACT,eAAe;KACf,YAAY;KACZ,eAAe;KACf,SAAS,GAAG,QAAQ;KACpB,WAAW;KACX,KAAK,GAAG,IAAI;KACZ;KACA,gBAAgB;KACjB;AAED,YAAQ,UAAR;KACE,KAAK,OACH,QAAO;MACL,GAAG;MACH,MAAM;MACN,KAAK,GAAG,UAAU;MAClB,OAAO,GAAG,MAAM;MAChB,QAAQ,GAAG,OAAO;MAClB,gBAAgB;MAChB,aAAa,aAAa,gBAAgB,cAAc,cAAc,GAAI;MAC1E,YAAY,0BAA0B,gBAAgB,cAAc,oBAAoB,IAAK,CAAC,OAAO,gBAAgB,cAAc,oBAAoB,GAAI,CAAC;MAC7J;KAEH,KAAK,QACH,QAAO;MACL,GAAG;MACH,OAAO;MACP,KAAK,GAAG,UAAU;MAClB,OAAO,GAAG,MAAM;MAChB,QAAQ,GAAG,OAAO;MAClB,gBAAgB;MAChB,YAAY,aAAa,gBAAgB,cAAc,cAAc,GAAI;MACzE,YAAY,2BAA2B,gBAAgB,cAAc,oBAAoB,IAAK,CAAC,OAAO,gBAAgB,cAAc,oBAAoB,GAAI,CAAC;MAC9J;KAEH,KAAK,MACH,QAAO;MACL,GAAG;MACH,KAAK;MACL,MAAM;MACN,OAAO;MACP,QAAQ,GAAG,OAAO;MAClB,eAAe;MACf,gBAAgB;MAChB,YAAY;MACZ,cAAc,aAAa,gBAAgB,cAAc,cAAc,GAAI;MAC3E,YAAY,2BAA2B,gBAAgB,cAAc,oBAAoB,GAAI,CAAC,OAAO,gBAAgB,cAAc,oBAAoB,GAAI,CAAC;MAC7J;KAEH,KAAK,SACH,QAAO;MACL,GAAG;MACH,QAAQ;MACR,MAAM;MACN,OAAO;MACP,QAAQ,GAAG,OAAO;MAClB,eAAe;MACf,gBAAgB;MAChB,YAAY;MACZ,WAAW,aAAa,gBAAgB,cAAc,cAAc,GAAI;MACxE,YAAY,yBAAyB,gBAAgB,cAAc,oBAAoB,GAAI,CAAC,OAAO,gBAAgB,cAAc,oBAAoB,GAAI,CAAC;MAC3J;;MAEJ;IAAC;IAAU;IAAO;IAAQ;IAAW;IAAS;IAAK;IAAO,CAAC;GAG3B,GAAG;GAAO;EAAE,eAAa;EACvD;EACG,CAAA"}
@@ -52,6 +52,19 @@ export interface MobileHUDLayoutProps {
52
52
  * - Minimal screen real estate usage
53
53
  * - Safe area aware positioning
54
54
  *
55
+ * Landscape Safe Area Notes:
56
+ * - In landscape on notched devices (iPhone X+), the left and right health
57
+ * bars are currently anchored using `layout.spacing.md` from their
58
+ * respective edges.
59
+ * - The `useResponsiveLayout` hook provides safe-area values, and this
60
+ * component uses `layout.safeArea.top` for vertical offset.
61
+ * - However, the current health bar positioning does not directly apply
62
+ * `layout.safeArea.left` / `layout.safeArea.right` for horizontal notch or
63
+ * cutout avoidance in landscape mode.
64
+ * - Consumers that require landscape horizontal safe-area protection should
65
+ * add `env(safe-area-inset-left)` / `env(safe-area-inset-right)` via CSS
66
+ * or ensure the parent container applies equivalent horizontal offsets.
67
+ *
55
68
  * @example
56
69
  * ```tsx
57
70
  * <MobileHUDLayout
@@ -1 +1 @@
1
- {"version":3,"file":"MobileHUDLayout.d.ts","sourceRoot":"","sources":["../../../../src/components/shared/ui/MobileHUDLayout.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAGH,OAAO,KAAkB,MAAM,OAAO,CAAC;AACvC,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAU/C;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,qBAAqB;IACrB,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC;IAC9B,qBAAqB;IACrB,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC;IAC9B,gCAAgC;IAChC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,2BAA2B;IAC3B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,qBAAqB;IACrB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,6BAA6B;IAC7B,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,0BAA0B;IAC1B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAkMD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,EAAE,CAAC,oBAAoB,CAkG1D,CAAC"}
1
+ {"version":3,"file":"MobileHUDLayout.d.ts","sourceRoot":"","sources":["../../../../src/components/shared/ui/MobileHUDLayout.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAGH,OAAO,KAAkB,MAAM,OAAO,CAAC;AACvC,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAU/C;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,qBAAqB;IACrB,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC;IAC9B,qBAAqB;IACrB,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC;IAC9B,gCAAgC;IAChC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,2BAA2B;IAC3B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,qBAAqB;IACrB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,6BAA6B;IAC7B,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,0BAA0B;IAC1B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAkMD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,EAAE,CAAC,oBAAoB,CAkG1D,CAAC"}
@@ -1,6 +1,6 @@
1
1
  import { KOREAN_COLORS } from "../../../types/constants/colors.js";
2
2
  import { FONT_FAMILY } from "../../../types/constants/typography.js";
3
- import { toHex } from "../../../utils/colorUtils.js";
3
+ import { hexColorToCSS, toHex } from "../../../utils/colorUtils.js";
4
4
  import { shouldUseMobileControls } from "../../../utils/deviceDetection.js";
5
5
  import { useCallback, useMemo, useState } from "react";
6
6
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -39,8 +39,8 @@ var SplashScreen = ({ onStart, width, height }) => {
39
39
  flexDirection: "column",
40
40
  justifyContent: "center",
41
41
  alignItems: "center",
42
- background: "linear-gradient(180deg, #0a0f12 0%, #1a1a2e 100%)",
43
- color: "#fff",
42
+ background: `linear-gradient(180deg, ${hexColorToCSS(KOREAN_COLORS.UI_BACKGROUND_DARK)} 0%, ${hexColorToCSS(KOREAN_COLORS.ARENA_BACKGROUND)} 100%)`,
43
+ color: hexColorToCSS(KOREAN_COLORS.TEXT_PRIMARY),
44
44
  fontFamily: FONT_FAMILY.CYBER,
45
45
  position: "relative",
46
46
  overflow: "hidden"
@@ -115,7 +115,7 @@ var SplashScreen = ({ onStart, width, height }) => {
115
115
  /* @__PURE__ */ jsx("p", {
116
116
  style: {
117
117
  fontSize: `${layoutCalculation.bodyFontSize}px`,
118
- color: "#aaa",
118
+ color: hexColorToCSS(KOREAN_COLORS.TEXT_TERTIARY),
119
119
  marginTop: "20px",
120
120
  letterSpacing: "1px"
121
121
  },
@@ -134,8 +134,8 @@ var SplashScreen = ({ onStart, width, height }) => {
134
134
  fontSize: `${layoutCalculation.buttonFontSize}px`,
135
135
  fontFamily: FONT_FAMILY.CYBER,
136
136
  fontWeight: 700,
137
- color: isLoading ? "#666" : "#000",
138
- background: isLoading ? "#333" : `linear-gradient(135deg, #${HEX_COLORS.PRIMARY_CYAN} 0%, #${HEX_COLORS.ACCENT_GOLD} 100%)`,
137
+ color: isLoading ? hexColorToCSS(KOREAN_COLORS.UI_DISABLED_TEXT) : hexColorToCSS(KOREAN_COLORS.BLACK),
138
+ background: isLoading ? hexColorToCSS(KOREAN_COLORS.UI_BACKGROUND_LIGHT) : `linear-gradient(135deg, #${HEX_COLORS.PRIMARY_CYAN} 0%, #${HEX_COLORS.ACCENT_GOLD} 100%)`,
139
139
  border: "none",
140
140
  borderRadius: "8px",
141
141
  cursor: isLoading ? "not-allowed" : "pointer",
@@ -167,7 +167,7 @@ var SplashScreen = ({ onStart, width, height }) => {
167
167
  style: {
168
168
  marginTop: "40px",
169
169
  textAlign: "center",
170
- color: "#888",
170
+ color: hexColorToCSS(KOREAN_COLORS.UI_GRAY),
171
171
  fontSize: `${layoutCalculation.instructionsFontSize}px`,
172
172
  maxWidth: "600px",
173
173
  padding: "0 20px",
@@ -183,16 +183,16 @@ var SplashScreen = ({ onStart, width, height }) => {
183
183
  }),
184
184
  /* @__PURE__ */ jsxs("div", {
185
185
  role: "contentinfo",
186
- "aria-label": `Application version 0.7.11`,
186
+ "aria-label": `Application version 0.7.13`,
187
187
  style: {
188
188
  position: "absolute",
189
189
  bottom: "20px",
190
190
  right: "20px",
191
- color: "#555",
191
+ color: hexColorToCSS(KOREAN_COLORS.UI_STEEL_GRAY),
192
192
  fontSize: "10px",
193
193
  zIndex: 1
194
194
  },
195
- children: ["v", "0.7.11"]
195
+ children: ["v", "0.7.13"]
196
196
  })
197
197
  ]
198
198
  });
@@ -1 +1 @@
1
- {"version":3,"file":"SplashScreen.js","names":[],"sources":["../../../../src/components/shared/ui/SplashScreen.tsx"],"sourcesContent":["import React, { useCallback, useMemo, useState } from \"react\";\nimport { FONT_FAMILY, KOREAN_COLORS } from \"@/types/constants\";\nimport { toHex } from \"../../../utils/colorUtils\";\nimport { shouldUseMobileControls } from \"../../../utils/deviceDetection\";\n\n// Declare global APP_VERSION constant (injected by build process)\ndeclare const APP_VERSION: string | undefined;\n\n// Constants\n// Small delay to show loading state for visual feedback.\n// 100ms is sufficient for users to perceive the state change\n// without feeling sluggish. This value can be tuned for UX.\nconst LOADING_DELAY_MS = 100;\n\n// Pre-compute hex colors from Korean color constants\nconst HEX_COLORS = {\n PRIMARY_CYAN: toHex(KOREAN_COLORS.PRIMARY_CYAN),\n ACCENT_GOLD: toHex(KOREAN_COLORS.ACCENT_GOLD),\n} as const;\n\nexport interface SplashScreenProps {\n readonly onStart: () => void;\n readonly width: number;\n readonly height: number;\n}\n\n/**\n * Splash screen that requires user interaction before starting the game.\n * This is necessary to initialize AudioContext which requires a user gesture.\n */\nexport const SplashScreen: React.FC<SplashScreenProps> = ({\n onStart,\n width,\n height,\n}) => {\n const [isLoading, setIsLoading] = useState(false);\n\n // Use proper device detection (user-agent priority for high-res phones)\n // This ensures mobile layout is used even on 4K Android devices\n // User-agent doesn't change during session, so no dependencies needed\n const isMobile = useMemo(() => shouldUseMobileControls(), []);\n\n // Memoize responsive layout values\n const layoutCalculation = useMemo(\n () => ({\n titleFontSize: isMobile ? 36 : 64,\n subtitleFontSize: isMobile ? 16 : 24,\n bodyFontSize: isMobile ? 12 : 14,\n instructionsFontSize: isMobile ? 11 : 12,\n buttonPadding: isMobile ? \"16px 48px\" : \"20px 60px\",\n buttonFontSize: isMobile ? 16 : 20,\n }),\n [isMobile],\n );\n\n const handleStart = useCallback(() => {\n setIsLoading(true);\n // Small delay to show loading state\n setTimeout(() => {\n onStart();\n }, LOADING_DELAY_MS);\n }, [onStart]);\n\n return (\n <div\n style={{\n width,\n height,\n display: \"flex\",\n flexDirection: \"column\",\n justifyContent: \"center\",\n alignItems: \"center\",\n background: \"linear-gradient(180deg, #0a0f12 0%, #1a1a2e 100%)\",\n color: \"#fff\",\n fontFamily: FONT_FAMILY.CYBER,\n position: \"relative\",\n overflow: \"hidden\",\n }}\n data-testid=\"splash-screen\"\n >\n {/* Animated background grid - decorative only */}\n <div\n role=\"presentation\"\n aria-hidden=\"true\"\n style={{\n position: \"absolute\",\n inset: 0,\n background: `\n repeating-linear-gradient(\n 0deg,\n transparent,\n transparent 50px,\n #${HEX_COLORS.PRIMARY_CYAN}08 50px,\n #${HEX_COLORS.PRIMARY_CYAN}08 51px\n ),\n repeating-linear-gradient(\n 90deg,\n transparent,\n transparent 50px,\n #${HEX_COLORS.PRIMARY_CYAN}08 50px,\n #${HEX_COLORS.PRIMARY_CYAN}08 51px\n )\n `,\n opacity: 0.3,\n }}\n />\n\n {/* Screen reader live region for loading state */}\n <div\n aria-live=\"polite\"\n aria-atomic=\"true\"\n style={{\n position: \"absolute\",\n left: \"-10000px\",\n width: \"1px\",\n height: \"1px\",\n overflow: \"hidden\",\n }}\n >\n {isLoading ? \"Initializing audio and loading game\" : \"Ready to start\"}\n </div>\n\n {/* Logo/Title */}\n <div\n style={{\n marginBottom: \"60px\",\n textAlign: \"center\",\n zIndex: 1,\n }}\n >\n <h1\n style={{\n fontSize: `${layoutCalculation.titleFontSize}px`,\n fontWeight: 900,\n color: `#${HEX_COLORS.PRIMARY_CYAN}`,\n textShadow: `0 0 20px #${HEX_COLORS.PRIMARY_CYAN}80`,\n marginBottom: \"20px\",\n letterSpacing: \"4px\",\n }}\n >\n 흑괘\n </h1>\n <h2\n style={{\n fontSize: `${layoutCalculation.subtitleFontSize}px`,\n fontWeight: 400,\n color: `#${HEX_COLORS.ACCENT_GOLD}`,\n letterSpacing: \"2px\",\n marginTop: 0,\n }}\n >\n BLACK TRIGRAM\n </h2>\n <p\n style={{\n fontSize: `${layoutCalculation.bodyFontSize}px`,\n color: \"#aaa\",\n marginTop: \"20px\",\n letterSpacing: \"1px\",\n }}\n >\n Korean Martial Arts Dojang\n </p>\n </div>\n\n {/* Start Button */}\n <button\n onClick={handleStart}\n disabled={isLoading}\n aria-label={\n isLoading\n ? \"Starting game and initializing audio\"\n : \"Start game and initialize audio\"\n }\n aria-busy={isLoading}\n aria-describedby=\"splash-instructions\"\n style={{\n padding: layoutCalculation.buttonPadding,\n fontSize: `${layoutCalculation.buttonFontSize}px`,\n fontFamily: FONT_FAMILY.CYBER,\n fontWeight: 700,\n color: isLoading ? \"#666\" : \"#000\",\n background: isLoading\n ? \"#333\"\n : `linear-gradient(135deg, #${HEX_COLORS.PRIMARY_CYAN} 0%, #${HEX_COLORS.ACCENT_GOLD} 100%)`,\n border: \"none\",\n borderRadius: \"8px\",\n cursor: isLoading ? \"not-allowed\" : \"pointer\",\n textTransform: \"uppercase\",\n letterSpacing: \"2px\",\n transition: \"all 0.3s ease\",\n boxShadow: isLoading\n ? \"none\"\n : `0 4px 20px #${HEX_COLORS.PRIMARY_CYAN}40`,\n position: \"relative\",\n zIndex: 1,\n opacity: isLoading ? 0.6 : 1,\n }}\n onMouseEnter={(e) => {\n if (!isLoading) {\n e.currentTarget.style.transform = \"scale(1.05)\";\n e.currentTarget.style.boxShadow = `0 6px 30px #${HEX_COLORS.PRIMARY_CYAN}60`;\n }\n }}\n onMouseLeave={(e) => {\n if (!isLoading) {\n e.currentTarget.style.transform = \"scale(1)\";\n e.currentTarget.style.boxShadow = `0 4px 20px #${HEX_COLORS.PRIMARY_CYAN}40`;\n }\n }}\n data-testid=\"splash-start-button\"\n >\n {isLoading ? \"시작 중... Starting...\" : \"시작 | Start\"}\n </button>\n\n {/* Instructions */}\n <div\n id=\"splash-instructions\"\n style={{\n marginTop: \"40px\",\n textAlign: \"center\",\n color: \"#888\",\n fontSize: `${layoutCalculation.instructionsFontSize}px`,\n maxWidth: \"600px\",\n padding: \"0 20px\",\n zIndex: 1,\n }}\n >\n <p style={{ margin: \"8px 0\" }}>\n Audio initialization requires user interaction\n </p>\n <p style={{ margin: \"8px 0\" }}>\n Click the button above to enable sound and start the game\n </p>\n </div>\n\n {/* Version info */}\n <div\n role=\"contentinfo\"\n aria-label={`Application version ${typeof APP_VERSION !== \"undefined\" ? APP_VERSION : \"0.5.3\"}`}\n style={{\n position: \"absolute\",\n bottom: \"20px\",\n right: \"20px\",\n color: \"#555\",\n fontSize: \"10px\",\n zIndex: 1,\n }}\n >\n v{typeof APP_VERSION !== \"undefined\" ? APP_VERSION : \"0.5.3\"}\n </div>\n </div>\n );\n};\n\nexport default SplashScreen;\n"],"mappings":"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"}
1
+ {"version":3,"file":"SplashScreen.js","names":[],"sources":["../../../../src/components/shared/ui/SplashScreen.tsx"],"sourcesContent":["import React, { useCallback, useMemo, useState } from \"react\";\nimport { FONT_FAMILY, KOREAN_COLORS } from \"@/types/constants\";\nimport { hexColorToCSS, toHex } from \"../../../utils/colorUtils\";\nimport { shouldUseMobileControls } from \"../../../utils/deviceDetection\";\n\n// Declare global APP_VERSION constant (injected by build process)\ndeclare const APP_VERSION: string | undefined;\n\n// Constants\n// Small delay to show loading state for visual feedback.\n// 100ms is sufficient for users to perceive the state change\n// without feeling sluggish. This value can be tuned for UX.\nconst LOADING_DELAY_MS = 100;\n\n// Pre-compute hex colors from Korean color constants\nconst HEX_COLORS = {\n PRIMARY_CYAN: toHex(KOREAN_COLORS.PRIMARY_CYAN),\n ACCENT_GOLD: toHex(KOREAN_COLORS.ACCENT_GOLD),\n} as const;\n\nexport interface SplashScreenProps {\n readonly onStart: () => void;\n readonly width: number;\n readonly height: number;\n}\n\n/**\n * Splash screen that requires user interaction before starting the game.\n * This is necessary to initialize AudioContext which requires a user gesture.\n */\nexport const SplashScreen: React.FC<SplashScreenProps> = ({\n onStart,\n width,\n height,\n}) => {\n const [isLoading, setIsLoading] = useState(false);\n\n // Use proper device detection (user-agent priority for high-res phones)\n // This ensures mobile layout is used even on 4K Android devices\n // User-agent doesn't change during session, so no dependencies needed\n const isMobile = useMemo(() => shouldUseMobileControls(), []);\n\n // Memoize responsive layout values\n const layoutCalculation = useMemo(\n () => ({\n titleFontSize: isMobile ? 36 : 64,\n subtitleFontSize: isMobile ? 16 : 24,\n bodyFontSize: isMobile ? 12 : 14,\n instructionsFontSize: isMobile ? 11 : 12,\n buttonPadding: isMobile ? \"16px 48px\" : \"20px 60px\",\n buttonFontSize: isMobile ? 16 : 20,\n }),\n [isMobile],\n );\n\n const handleStart = useCallback(() => {\n setIsLoading(true);\n // Small delay to show loading state\n setTimeout(() => {\n onStart();\n }, LOADING_DELAY_MS);\n }, [onStart]);\n\n return (\n <div\n style={{\n width,\n height,\n display: \"flex\",\n flexDirection: \"column\",\n justifyContent: \"center\",\n alignItems: \"center\",\n background: `linear-gradient(180deg, ${hexColorToCSS(KOREAN_COLORS.UI_BACKGROUND_DARK)} 0%, ${hexColorToCSS(KOREAN_COLORS.ARENA_BACKGROUND)} 100%)`,\n color: hexColorToCSS(KOREAN_COLORS.TEXT_PRIMARY),\n fontFamily: FONT_FAMILY.CYBER,\n position: \"relative\",\n overflow: \"hidden\",\n }}\n data-testid=\"splash-screen\"\n >\n {/* Animated background grid - decorative only */}\n <div\n role=\"presentation\"\n aria-hidden=\"true\"\n style={{\n position: \"absolute\",\n inset: 0,\n background: `\n repeating-linear-gradient(\n 0deg,\n transparent,\n transparent 50px,\n #${HEX_COLORS.PRIMARY_CYAN}08 50px,\n #${HEX_COLORS.PRIMARY_CYAN}08 51px\n ),\n repeating-linear-gradient(\n 90deg,\n transparent,\n transparent 50px,\n #${HEX_COLORS.PRIMARY_CYAN}08 50px,\n #${HEX_COLORS.PRIMARY_CYAN}08 51px\n )\n `,\n opacity: 0.3,\n }}\n />\n\n {/* Screen reader live region for loading state */}\n <div\n aria-live=\"polite\"\n aria-atomic=\"true\"\n style={{\n position: \"absolute\",\n left: \"-10000px\",\n width: \"1px\",\n height: \"1px\",\n overflow: \"hidden\",\n }}\n >\n {isLoading ? \"Initializing audio and loading game\" : \"Ready to start\"}\n </div>\n\n {/* Logo/Title */}\n <div\n style={{\n marginBottom: \"60px\",\n textAlign: \"center\",\n zIndex: 1,\n }}\n >\n <h1\n style={{\n fontSize: `${layoutCalculation.titleFontSize}px`,\n fontWeight: 900,\n color: `#${HEX_COLORS.PRIMARY_CYAN}`,\n textShadow: `0 0 20px #${HEX_COLORS.PRIMARY_CYAN}80`,\n marginBottom: \"20px\",\n letterSpacing: \"4px\",\n }}\n >\n 흑괘\n </h1>\n <h2\n style={{\n fontSize: `${layoutCalculation.subtitleFontSize}px`,\n fontWeight: 400,\n color: `#${HEX_COLORS.ACCENT_GOLD}`,\n letterSpacing: \"2px\",\n marginTop: 0,\n }}\n >\n BLACK TRIGRAM\n </h2>\n <p\n style={{\n fontSize: `${layoutCalculation.bodyFontSize}px`,\n color: hexColorToCSS(KOREAN_COLORS.TEXT_TERTIARY),\n marginTop: \"20px\",\n letterSpacing: \"1px\",\n }}\n >\n Korean Martial Arts Dojang\n </p>\n </div>\n\n {/* Start Button */}\n <button\n onClick={handleStart}\n disabled={isLoading}\n aria-label={\n isLoading\n ? \"Starting game and initializing audio\"\n : \"Start game and initialize audio\"\n }\n aria-busy={isLoading}\n aria-describedby=\"splash-instructions\"\n style={{\n padding: layoutCalculation.buttonPadding,\n fontSize: `${layoutCalculation.buttonFontSize}px`,\n fontFamily: FONT_FAMILY.CYBER,\n fontWeight: 700,\n color: isLoading ? hexColorToCSS(KOREAN_COLORS.UI_DISABLED_TEXT) : hexColorToCSS(KOREAN_COLORS.BLACK),\n background: isLoading\n ? hexColorToCSS(KOREAN_COLORS.UI_BACKGROUND_LIGHT)\n : `linear-gradient(135deg, #${HEX_COLORS.PRIMARY_CYAN} 0%, #${HEX_COLORS.ACCENT_GOLD} 100%)`,\n border: \"none\",\n borderRadius: \"8px\",\n cursor: isLoading ? \"not-allowed\" : \"pointer\",\n textTransform: \"uppercase\",\n letterSpacing: \"2px\",\n transition: \"all 0.3s ease\",\n boxShadow: isLoading\n ? \"none\"\n : `0 4px 20px #${HEX_COLORS.PRIMARY_CYAN}40`,\n position: \"relative\",\n zIndex: 1,\n opacity: isLoading ? 0.6 : 1,\n }}\n onMouseEnter={(e) => {\n if (!isLoading) {\n e.currentTarget.style.transform = \"scale(1.05)\";\n e.currentTarget.style.boxShadow = `0 6px 30px #${HEX_COLORS.PRIMARY_CYAN}60`;\n }\n }}\n onMouseLeave={(e) => {\n if (!isLoading) {\n e.currentTarget.style.transform = \"scale(1)\";\n e.currentTarget.style.boxShadow = `0 4px 20px #${HEX_COLORS.PRIMARY_CYAN}40`;\n }\n }}\n data-testid=\"splash-start-button\"\n >\n {isLoading ? \"시작 중... Starting...\" : \"시작 | Start\"}\n </button>\n\n {/* Instructions */}\n <div\n id=\"splash-instructions\"\n style={{\n marginTop: \"40px\",\n textAlign: \"center\",\n color: hexColorToCSS(KOREAN_COLORS.UI_GRAY),\n fontSize: `${layoutCalculation.instructionsFontSize}px`,\n maxWidth: \"600px\",\n padding: \"0 20px\",\n zIndex: 1,\n }}\n >\n <p style={{ margin: \"8px 0\" }}>\n Audio initialization requires user interaction\n </p>\n <p style={{ margin: \"8px 0\" }}>\n Click the button above to enable sound and start the game\n </p>\n </div>\n\n {/* Version info */}\n <div\n role=\"contentinfo\"\n aria-label={`Application version ${typeof APP_VERSION !== \"undefined\" ? APP_VERSION : \"0.5.3\"}`}\n style={{\n position: \"absolute\",\n bottom: \"20px\",\n right: \"20px\",\n color: hexColorToCSS(KOREAN_COLORS.UI_STEEL_GRAY),\n fontSize: \"10px\",\n zIndex: 1,\n }}\n >\n v{typeof APP_VERSION !== \"undefined\" ? APP_VERSION : \"0.5.3\"}\n </div>\n </div>\n );\n};\n\nexport default SplashScreen;\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"VitalPointOverlayControlsPure.d.ts","sourceRoot":"","sources":["../../../../src/components/shared/ui/VitalPointOverlayControlsPure.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAyC,MAAM,OAAO,CAAC;AAK9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAG3D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sCAAsC,CAAC;AAE7E,YAAY,EAAE,gBAAgB,EAAE,MAAM,sCAAsC,CAAC;AAE7E;;GAEG;AACH,MAAM,WAAW,kCAAkC;IACjD,2CAA2C;IAC3C,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,uCAAuC;IACvC,QAAQ,CAAC,eAAe,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IACrD,+BAA+B;IAC/B,QAAQ,CAAC,eAAe,EAAE,kBAAkB,EAAE,CAAC;IAC/C,4CAA4C;IAC5C,QAAQ,CAAC,uBAAuB,EAAE,CAAC,OAAO,EAAE,kBAAkB,EAAE,KAAK,IAAI,CAAC;IAC1E,4BAA4B;IAC5B,QAAQ,CAAC,YAAY,EAAE,gBAAgB,CAAC;IACxC,0CAA0C;IAC1C,QAAQ,CAAC,oBAAoB,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAClE,2BAA2B;IAC3B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,yCAAyC;IACzC,QAAQ,CAAC,mBAAmB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACvD,+BAA+B;IAC/B,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,6CAA6C;IAC7C,QAAQ,CAAC,kBAAkB,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACrD,qCAAqC;IACrC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,4CAA4C;IAC5C,QAAQ,CAAC,gBAAgB,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAC;IACvD,8BAA8B;IAC9B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,kCAAkC;IAClC,QAAQ,CAAC,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,+BAA+B;IAC/B,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B;;;;;OAKG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE;QACxB,+DAA+D;QAC/D,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,gEAAgE;QAChE,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,iEAAiE;QACjE,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,kEAAkE;QAClE,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,CAAC;CACH;AA6BD;;;GAGG;AACH,eAAO,MAAM,6BAA6B,EAAE,KAAK,CAAC,EAAE,CAClD,kCAAkC,CA6oBnC,CAAC;AAEF,eAAe,6BAA6B,CAAC"}
1
+ {"version":3,"file":"VitalPointOverlayControlsPure.d.ts","sourceRoot":"","sources":["../../../../src/components/shared/ui/VitalPointOverlayControlsPure.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAyC,MAAM,OAAO,CAAC;AAK9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAI3D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sCAAsC,CAAC;AAE7E,YAAY,EAAE,gBAAgB,EAAE,MAAM,sCAAsC,CAAC;AAE7E;;GAEG;AACH,MAAM,WAAW,kCAAkC;IACjD,2CAA2C;IAC3C,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,uCAAuC;IACvC,QAAQ,CAAC,eAAe,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IACrD,+BAA+B;IAC/B,QAAQ,CAAC,eAAe,EAAE,kBAAkB,EAAE,CAAC;IAC/C,4CAA4C;IAC5C,QAAQ,CAAC,uBAAuB,EAAE,CAAC,OAAO,EAAE,kBAAkB,EAAE,KAAK,IAAI,CAAC;IAC1E,4BAA4B;IAC5B,QAAQ,CAAC,YAAY,EAAE,gBAAgB,CAAC;IACxC,0CAA0C;IAC1C,QAAQ,CAAC,oBAAoB,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAClE,2BAA2B;IAC3B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,yCAAyC;IACzC,QAAQ,CAAC,mBAAmB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACvD,+BAA+B;IAC/B,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,6CAA6C;IAC7C,QAAQ,CAAC,kBAAkB,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACrD,qCAAqC;IACrC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,4CAA4C;IAC5C,QAAQ,CAAC,gBAAgB,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAC;IACvD,8BAA8B;IAC9B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,kCAAkC;IAClC,QAAQ,CAAC,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,+BAA+B;IAC/B,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B;;;;;OAKG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE;QACxB,+DAA+D;QAC/D,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,gEAAgE;QAChE,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,iEAAiE;QACjE,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,kEAAkE;QAClE,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,CAAC;CACH;AAqBD;;;GAGG;AACH,eAAO,MAAM,6BAA6B,EAAE,KAAK,CAAC,EAAE,CAClD,kCAAkC,CA6oBnC,CAAC;AAEF,eAAe,6BAA6B,CAAC"}
@@ -3,6 +3,7 @@ import { KOREAN_COLORS } from "../../../types/constants/colors.js";
3
3
  import { FONT_FAMILY } from "../../../types/constants/typography.js";
4
4
  import { Z_INDEX } from "../../../types/LayoutTypes.js";
5
5
  import KOREAN_VITAL_POINTS, { getVitalPointsStats } from "../../../systems/vitalpoint/KoreanVitalPoints.js";
6
+ import { hexColorToCSS, hexToRgbaString } from "../../../utils/colorUtils.js";
6
7
  import { useCallback, useMemo, useState } from "react";
7
8
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
8
9
  //#region src/components/shared/ui/VitalPointOverlayControlsPure.tsx
@@ -24,22 +25,16 @@ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
24
25
  * @module components/shared/ui/VitalPointOverlayControlsPure
25
26
  */
26
27
  /**
27
- * Convert numeric color to CSS hex string
28
- */
29
- var colorToHex = (color) => {
30
- return `#${color.toString(16).padStart(6, "0")}`;
31
- };
32
- /**
33
28
  * Get color for severity level
34
29
  */
35
30
  var getSeverityColor = (severity) => {
36
31
  switch (severity) {
37
- case VitalPointSeverity.LETHAL: return "#ff0000";
38
- case VitalPointSeverity.CRITICAL: return "#ff6600";
39
- case VitalPointSeverity.MAJOR: return "#ffaa00";
40
- case VitalPointSeverity.MODERATE: return "#ffd700";
41
- case VitalPointSeverity.MINOR: return "#00ff88";
42
- default: return "#00ffff";
32
+ case VitalPointSeverity.LETHAL: return hexColorToCSS(KOREAN_COLORS.NEGATIVE_RED);
33
+ case VitalPointSeverity.CRITICAL: return hexColorToCSS(KOREAN_COLORS.HEALTH_LOW);
34
+ case VitalPointSeverity.MAJOR: return hexColorToCSS(KOREAN_COLORS.ACCENT_GOLD);
35
+ case VitalPointSeverity.MODERATE: return hexColorToCSS(KOREAN_COLORS.TRIGRAM_GEON_PRIMARY);
36
+ case VitalPointSeverity.MINOR: return hexColorToCSS(KOREAN_COLORS.POSITIVE_GREEN);
37
+ default: return hexColorToCSS(KOREAN_COLORS.NEON_CYAN);
43
38
  }
44
39
  };
45
40
  /**
@@ -125,13 +120,13 @@ var VitalPointOverlayControlsPure = ({ visible, onVisibleChange, severityFilters
125
120
  right: finalPosition.right,
126
121
  bottom: finalPosition.bottom,
127
122
  width: panelWidth,
128
- background: `${colorToHex(KOREAN_COLORS.UI_BACKGROUND_DARK)}f0`,
129
- border: `2px solid ${colorToHex(KOREAN_COLORS.PRIMARY_CYAN)}`,
123
+ background: `${hexColorToCSS(KOREAN_COLORS.UI_BACKGROUND_DARK)}f0`,
124
+ border: `2px solid ${hexColorToCSS(KOREAN_COLORS.PRIMARY_CYAN)}`,
130
125
  borderRadius: "12px",
131
126
  padding: isMobile ? "12px" : "16px",
132
127
  fontFamily: FONT_FAMILY.KOREAN,
133
- color: "#ffffff",
134
- boxShadow: `0 0 30px ${colorToHex(KOREAN_COLORS.PRIMARY_CYAN)}40, inset 0 0 20px ${colorToHex(KOREAN_COLORS.UI_BACKGROUND_DARK)}80`,
128
+ color: hexColorToCSS(KOREAN_COLORS.TEXT_PRIMARY),
129
+ boxShadow: `0 0 30px ${hexColorToCSS(KOREAN_COLORS.PRIMARY_CYAN)}40, inset 0 0 20px ${hexColorToCSS(KOREAN_COLORS.UI_BACKGROUND_DARK)}80`,
135
130
  transition: "all 0.3s ease",
136
131
  pointerEvents: "all",
137
132
  zIndex: Z_INDEX.MODAL
@@ -145,8 +140,8 @@ var VitalPointOverlayControlsPure = ({ visible, onVisibleChange, severityFilters
145
140
  alignItems: "center",
146
141
  marginBottom: "12px",
147
142
  paddingBottom: "12px",
148
- borderBottom: `1px solid ${colorToHex(KOREAN_COLORS.PRIMARY_CYAN)}40`,
149
- background: `linear-gradient(90deg, ${colorToHex(KOREAN_COLORS.UI_BACKGROUND_DARK)}00 0%, ${colorToHex(KOREAN_COLORS.PRIMARY_CYAN)}10 50%, ${colorToHex(KOREAN_COLORS.UI_BACKGROUND_DARK)}00 100%)`
143
+ borderBottom: `1px solid ${hexColorToCSS(KOREAN_COLORS.PRIMARY_CYAN)}40`,
144
+ background: `linear-gradient(90deg, ${hexColorToCSS(KOREAN_COLORS.UI_BACKGROUND_DARK)}00 0%, ${hexColorToCSS(KOREAN_COLORS.PRIMARY_CYAN)}10 50%, ${hexColorToCSS(KOREAN_COLORS.UI_BACKGROUND_DARK)}00 100%)`
150
145
  },
151
146
  children: [/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("div", {
152
147
  style: {
@@ -157,7 +152,7 @@ var VitalPointOverlayControlsPure = ({ visible, onVisibleChange, severityFilters
157
152
  }), /* @__PURE__ */ jsxs("div", {
158
153
  style: {
159
154
  fontSize: smallFontSize,
160
- color: colorToHex(KOREAN_COLORS.TEXT_SECONDARY),
155
+ color: hexColorToCSS(KOREAN_COLORS.TEXT_SECONDARY),
161
156
  marginTop: "2px"
162
157
  },
163
158
  children: [
@@ -169,23 +164,23 @@ var VitalPointOverlayControlsPure = ({ visible, onVisibleChange, severityFilters
169
164
  })] }), /* @__PURE__ */ jsx("button", {
170
165
  onClick: () => setExpanded(!expanded),
171
166
  style: {
172
- background: `linear-gradient(135deg, ${colorToHex(KOREAN_COLORS.UI_BACKGROUND_MEDIUM)} 0%, ${colorToHex(KOREAN_COLORS.UI_BACKGROUND_DARK)} 100%)`,
173
- border: `2px solid ${colorToHex(KOREAN_COLORS.PRIMARY_CYAN)}`,
167
+ background: `linear-gradient(135deg, ${hexColorToCSS(KOREAN_COLORS.UI_BACKGROUND_MEDIUM)} 0%, ${hexColorToCSS(KOREAN_COLORS.UI_BACKGROUND_DARK)} 100%)`,
168
+ border: `2px solid ${hexColorToCSS(KOREAN_COLORS.PRIMARY_CYAN)}`,
174
169
  borderRadius: "6px",
175
170
  padding: "8px 14px",
176
- color: "#ffffff",
171
+ color: hexColorToCSS(KOREAN_COLORS.TEXT_PRIMARY),
177
172
  fontSize,
178
173
  cursor: "pointer",
179
174
  transition: "all 0.2s ease",
180
- boxShadow: `0 2px 8px ${colorToHex(KOREAN_COLORS.PRIMARY_CYAN)}30`
175
+ boxShadow: `0 2px 8px ${hexColorToCSS(KOREAN_COLORS.PRIMARY_CYAN)}30`
181
176
  },
182
177
  onMouseEnter: (e) => {
183
178
  e.currentTarget.style.transform = "scale(1.05)";
184
- e.currentTarget.style.boxShadow = `0 4px 12px ${colorToHex(KOREAN_COLORS.PRIMARY_CYAN)}50`;
179
+ e.currentTarget.style.boxShadow = `0 4px 12px ${hexColorToCSS(KOREAN_COLORS.PRIMARY_CYAN)}50`;
185
180
  },
186
181
  onMouseLeave: (e) => {
187
182
  e.currentTarget.style.transform = "scale(1)";
188
- e.currentTarget.style.boxShadow = `0 2px 8px ${colorToHex(KOREAN_COLORS.PRIMARY_CYAN)}30`;
183
+ e.currentTarget.style.boxShadow = `0 2px 8px ${hexColorToCSS(KOREAN_COLORS.PRIMARY_CYAN)}30`;
189
184
  },
190
185
  "data-testid": "toggle-expand-button",
191
186
  children: expanded ? "▼" : "▶"
@@ -198,25 +193,25 @@ var VitalPointOverlayControlsPure = ({ visible, onVisibleChange, severityFilters
198
193
  style: {
199
194
  width: "100%",
200
195
  height: buttonHeight,
201
- background: visible ? `linear-gradient(135deg, ${colorToHex(KOREAN_COLORS.ACCENT_GOLD)} 0%, ${colorToHex(KOREAN_COLORS.SECONDARY_YELLOW)} 100%)` : `linear-gradient(135deg, ${colorToHex(KOREAN_COLORS.UI_BACKGROUND_MEDIUM)} 0%, ${colorToHex(KOREAN_COLORS.UI_BACKGROUND_DARK)} 100%)`,
202
- border: `2px solid ${visible ? colorToHex(KOREAN_COLORS.ACCENT_GOLD) : colorToHex(KOREAN_COLORS.PRIMARY_CYAN)}`,
196
+ background: visible ? `linear-gradient(135deg, ${hexColorToCSS(KOREAN_COLORS.ACCENT_GOLD)} 0%, ${hexColorToCSS(KOREAN_COLORS.SECONDARY_YELLOW)} 100%)` : `linear-gradient(135deg, ${hexColorToCSS(KOREAN_COLORS.UI_BACKGROUND_MEDIUM)} 0%, ${hexColorToCSS(KOREAN_COLORS.UI_BACKGROUND_DARK)} 100%)`,
197
+ border: `2px solid ${visible ? hexColorToCSS(KOREAN_COLORS.ACCENT_GOLD) : hexColorToCSS(KOREAN_COLORS.PRIMARY_CYAN)}`,
203
198
  borderRadius: "8px",
204
- color: visible ? "#1a1a1a" : "#ffffff",
199
+ color: visible ? hexColorToCSS(KOREAN_COLORS.KOREAN_BLACK) : hexColorToCSS(KOREAN_COLORS.TEXT_PRIMARY),
205
200
  fontSize: isMobile ? 13 : 15,
206
201
  fontWeight: "bold",
207
202
  cursor: "pointer",
208
203
  transition: "all 0.3s ease",
209
- boxShadow: visible ? `0 4px 16px ${colorToHex(KOREAN_COLORS.ACCENT_GOLD)}60, inset 0 2px 4px rgba(255,255,255,0.2)` : `0 2px 8px ${colorToHex(KOREAN_COLORS.PRIMARY_CYAN)}30`,
204
+ boxShadow: visible ? `0 4px 16px ${hexColorToCSS(KOREAN_COLORS.ACCENT_GOLD)}60, inset 0 2px 4px ${hexToRgbaString(KOREAN_COLORS.TEXT_PRIMARY, .2)}` : `0 2px 8px ${hexColorToCSS(KOREAN_COLORS.PRIMARY_CYAN)}30`,
210
205
  textTransform: "uppercase",
211
206
  letterSpacing: "0.5px"
212
207
  },
213
208
  onMouseEnter: (e) => {
214
209
  e.currentTarget.style.transform = "translateY(-2px)";
215
- e.currentTarget.style.boxShadow = visible ? `0 6px 20px ${colorToHex(KOREAN_COLORS.ACCENT_GOLD)}80` : `0 4px 12px ${colorToHex(KOREAN_COLORS.PRIMARY_CYAN)}50`;
210
+ e.currentTarget.style.boxShadow = visible ? `0 6px 20px ${hexColorToCSS(KOREAN_COLORS.ACCENT_GOLD)}80` : `0 4px 12px ${hexColorToCSS(KOREAN_COLORS.PRIMARY_CYAN)}50`;
216
211
  },
217
212
  onMouseLeave: (e) => {
218
213
  e.currentTarget.style.transform = "translateY(0)";
219
- e.currentTarget.style.boxShadow = visible ? `0 4px 16px ${colorToHex(KOREAN_COLORS.ACCENT_GOLD)}60` : `0 2px 8px ${colorToHex(KOREAN_COLORS.PRIMARY_CYAN)}30`;
214
+ e.currentTarget.style.boxShadow = visible ? `0 4px 16px ${hexColorToCSS(KOREAN_COLORS.ACCENT_GOLD)}60` : `0 2px 8px ${hexColorToCSS(KOREAN_COLORS.PRIMARY_CYAN)}30`;
220
215
  },
221
216
  "data-testid": "toggle-visibility-button",
222
217
  children: visible ? "✓ 활성화 | Enabled" : "비활성화 | Disabled"
@@ -229,7 +224,7 @@ var VitalPointOverlayControlsPure = ({ visible, onVisibleChange, severityFilters
229
224
  style: {
230
225
  fontSize,
231
226
  marginBottom: "8px",
232
- color: colorToHex(KOREAN_COLORS.ACCENT_CYAN),
227
+ color: hexColorToCSS(KOREAN_COLORS.ACCENT_CYAN),
233
228
  fontWeight: "600",
234
229
  textTransform: "uppercase",
235
230
  letterSpacing: "1px"
@@ -247,11 +242,11 @@ var VitalPointOverlayControlsPure = ({ visible, onVisibleChange, severityFilters
247
242
  return /* @__PURE__ */ jsx("button", {
248
243
  onClick: () => toggleSeverityFilter(severity),
249
244
  style: {
250
- background: isActive ? `linear-gradient(135deg, ${severityColor} 0%, ${severityColor}cc 100%)` : `${colorToHex(KOREAN_COLORS.UI_BACKGROUND_MEDIUM)}`,
245
+ background: isActive ? `linear-gradient(135deg, ${severityColor} 0%, ${severityColor}cc 100%)` : `${hexColorToCSS(KOREAN_COLORS.UI_BACKGROUND_MEDIUM)}`,
251
246
  border: `2px solid ${severityColor}`,
252
247
  borderRadius: "6px",
253
248
  padding: "6px 12px",
254
- color: "#ffffff",
249
+ color: hexColorToCSS(KOREAN_COLORS.TEXT_PRIMARY),
255
250
  fontSize: smallFontSize,
256
251
  cursor: "pointer",
257
252
  opacity: isActive ? 1 : .6,
@@ -279,7 +274,7 @@ var VitalPointOverlayControlsPure = ({ visible, onVisibleChange, severityFilters
279
274
  style: {
280
275
  fontSize,
281
276
  marginBottom: "8px",
282
- color: colorToHex(KOREAN_COLORS.ACCENT_CYAN),
277
+ color: hexColorToCSS(KOREAN_COLORS.ACCENT_CYAN),
283
278
  fontWeight: "600",
284
279
  textTransform: "uppercase",
285
280
  letterSpacing: "1px"
@@ -296,17 +291,17 @@ var VitalPointOverlayControlsPure = ({ visible, onVisibleChange, severityFilters
296
291
  return /* @__PURE__ */ jsxs("button", {
297
292
  onClick: () => onRegionFilterChange(option.value),
298
293
  style: {
299
- background: isActive ? `linear-gradient(135deg, ${colorToHex(KOREAN_COLORS.PRIMARY_CYAN)} 0%, ${colorToHex(KOREAN_COLORS.ACCENT_BLUE)} 100%)` : `${colorToHex(KOREAN_COLORS.UI_BACKGROUND_MEDIUM)}`,
300
- border: `2px solid ${colorToHex(KOREAN_COLORS.PRIMARY_CYAN)}`,
294
+ background: isActive ? `linear-gradient(135deg, ${hexColorToCSS(KOREAN_COLORS.PRIMARY_CYAN)} 0%, ${hexColorToCSS(KOREAN_COLORS.ACCENT_BLUE)} 100%)` : `${hexColorToCSS(KOREAN_COLORS.UI_BACKGROUND_MEDIUM)}`,
295
+ border: `2px solid ${hexColorToCSS(KOREAN_COLORS.PRIMARY_CYAN)}`,
301
296
  borderRadius: "6px",
302
297
  padding: "6px 12px",
303
- color: "#ffffff",
298
+ color: hexColorToCSS(KOREAN_COLORS.TEXT_PRIMARY),
304
299
  fontSize: smallFontSize,
305
300
  cursor: "pointer",
306
301
  opacity: isActive ? 1 : .6,
307
302
  transition: "all 0.2s ease",
308
303
  fontWeight: isActive ? "bold" : "normal",
309
- boxShadow: isActive ? `0 2px 8px ${colorToHex(KOREAN_COLORS.PRIMARY_CYAN)}50` : "none"
304
+ boxShadow: isActive ? `0 2px 8px ${hexColorToCSS(KOREAN_COLORS.PRIMARY_CYAN)}50` : "none"
310
305
  },
311
306
  onMouseEnter: (e) => {
312
307
  e.currentTarget.style.opacity = "1";
@@ -332,7 +327,7 @@ var VitalPointOverlayControlsPure = ({ visible, onVisibleChange, severityFilters
332
327
  style: {
333
328
  fontSize,
334
329
  marginBottom: "8px",
335
- color: colorToHex(KOREAN_COLORS.ACCENT_CYAN),
330
+ color: hexColorToCSS(KOREAN_COLORS.ACCENT_CYAN),
336
331
  fontWeight: "600",
337
332
  textTransform: "uppercase",
338
333
  letterSpacing: "1px"
@@ -348,22 +343,22 @@ var VitalPointOverlayControlsPure = ({ visible, onVisibleChange, severityFilters
348
343
  style: {
349
344
  width: "100%",
350
345
  height: buttonHeight,
351
- background: `${colorToHex(KOREAN_COLORS.UI_BACKGROUND_MEDIUM)}`,
352
- border: `2px solid ${colorToHex(KOREAN_COLORS.PRIMARY_CYAN)}40`,
346
+ background: `${hexColorToCSS(KOREAN_COLORS.UI_BACKGROUND_MEDIUM)}`,
347
+ border: `2px solid ${hexColorToCSS(KOREAN_COLORS.PRIMARY_CYAN)}40`,
353
348
  borderRadius: "8px",
354
349
  padding: "0 40px 0 14px",
355
- color: "#ffffff",
350
+ color: hexColorToCSS(KOREAN_COLORS.TEXT_PRIMARY),
356
351
  fontSize,
357
352
  fontFamily: FONT_FAMILY.KOREAN,
358
353
  transition: "all 0.2s ease",
359
354
  outline: "none"
360
355
  },
361
356
  onFocus: (e) => {
362
- e.currentTarget.style.borderColor = colorToHex(KOREAN_COLORS.PRIMARY_CYAN);
363
- e.currentTarget.style.boxShadow = `0 0 12px ${colorToHex(KOREAN_COLORS.PRIMARY_CYAN)}40`;
357
+ e.currentTarget.style.borderColor = hexColorToCSS(KOREAN_COLORS.PRIMARY_CYAN);
358
+ e.currentTarget.style.boxShadow = `0 0 12px ${hexColorToCSS(KOREAN_COLORS.PRIMARY_CYAN)}40`;
364
359
  },
365
360
  onBlur: (e) => {
366
- e.currentTarget.style.borderColor = `${colorToHex(KOREAN_COLORS.PRIMARY_CYAN)}40`;
361
+ e.currentTarget.style.borderColor = `${hexColorToCSS(KOREAN_COLORS.PRIMARY_CYAN)}40`;
367
362
  e.currentTarget.style.boxShadow = "none";
368
363
  },
369
364
  "data-testid": "search-input"
@@ -376,7 +371,7 @@ var VitalPointOverlayControlsPure = ({ visible, onVisibleChange, severityFilters
376
371
  transform: "translateY(-50%)",
377
372
  background: "transparent",
378
373
  border: "none",
379
- color: colorToHex(KOREAN_COLORS.TEXT_SECONDARY),
374
+ color: hexColorToCSS(KOREAN_COLORS.TEXT_SECONDARY),
380
375
  cursor: "pointer",
381
376
  fontSize: "16px",
382
377
  padding: "4px",
@@ -387,11 +382,11 @@ var VitalPointOverlayControlsPure = ({ visible, onVisibleChange, severityFilters
387
382
  transition: "all 0.2s ease"
388
383
  },
389
384
  onMouseEnter: (e) => {
390
- e.currentTarget.style.color = colorToHex(KOREAN_COLORS.ACCENT_RED);
391
- e.currentTarget.style.background = `${colorToHex(KOREAN_COLORS.UI_BACKGROUND_MEDIUM)}`;
385
+ e.currentTarget.style.color = hexColorToCSS(KOREAN_COLORS.ACCENT_RED);
386
+ e.currentTarget.style.background = `${hexColorToCSS(KOREAN_COLORS.UI_BACKGROUND_MEDIUM)}`;
392
387
  },
393
388
  onMouseLeave: (e) => {
394
- e.currentTarget.style.color = colorToHex(KOREAN_COLORS.TEXT_SECONDARY);
389
+ e.currentTarget.style.color = hexColorToCSS(KOREAN_COLORS.TEXT_SECONDARY);
395
390
  e.currentTarget.style.background = "transparent";
396
391
  },
397
392
  "data-testid": "search-clear-button",
@@ -406,7 +401,7 @@ var VitalPointOverlayControlsPure = ({ visible, onVisibleChange, severityFilters
406
401
  style: {
407
402
  fontSize,
408
403
  marginBottom: "8px",
409
- color: colorToHex(KOREAN_COLORS.ACCENT_CYAN),
404
+ color: hexColorToCSS(KOREAN_COLORS.ACCENT_CYAN),
410
405
  fontWeight: "600",
411
406
  textTransform: "uppercase",
412
407
  letterSpacing: "1px"
@@ -432,7 +427,7 @@ var VitalPointOverlayControlsPure = ({ visible, onVisibleChange, severityFilters
432
427
  style: {
433
428
  width: "16px",
434
429
  height: "16px",
435
- accentColor: colorToHex(KOREAN_COLORS.PRIMARY_CYAN)
430
+ accentColor: hexColorToCSS(KOREAN_COLORS.PRIMARY_CYAN)
436
431
  },
437
432
  "data-testid": "show-labels-checkbox"
438
433
  }), /* @__PURE__ */ jsx("span", {
@@ -453,7 +448,7 @@ var VitalPointOverlayControlsPure = ({ visible, onVisibleChange, severityFilters
453
448
  style: {
454
449
  width: "16px",
455
450
  height: "16px",
456
- accentColor: colorToHex(KOREAN_COLORS.PRIMARY_CYAN)
451
+ accentColor: hexColorToCSS(KOREAN_COLORS.PRIMARY_CYAN)
457
452
  },
458
453
  "data-testid": "animated-checkbox"
459
454
  }), /* @__PURE__ */ jsx("span", {
@@ -474,21 +469,21 @@ var VitalPointOverlayControlsPure = ({ visible, onVisibleChange, severityFilters
474
469
  style: {
475
470
  width: "100%",
476
471
  height: buttonHeight - 4,
477
- background: `${colorToHex(KOREAN_COLORS.UI_BACKGROUND_MEDIUM)}`,
478
- border: `2px solid ${colorToHex(KOREAN_COLORS.ACCENT_ORANGE)}`,
472
+ background: `${hexColorToCSS(KOREAN_COLORS.UI_BACKGROUND_MEDIUM)}`,
473
+ border: `2px solid ${hexColorToCSS(KOREAN_COLORS.ACCENT_ORANGE)}`,
479
474
  borderRadius: "6px",
480
- color: colorToHex(KOREAN_COLORS.ACCENT_ORANGE),
475
+ color: hexColorToCSS(KOREAN_COLORS.ACCENT_ORANGE),
481
476
  fontSize: smallFontSize,
482
477
  cursor: "pointer",
483
478
  transition: "all 0.2s ease",
484
479
  fontWeight: "bold"
485
480
  },
486
481
  onMouseEnter: (e) => {
487
- e.currentTarget.style.background = `${colorToHex(KOREAN_COLORS.ACCENT_ORANGE)}20`;
482
+ e.currentTarget.style.background = `${hexColorToCSS(KOREAN_COLORS.ACCENT_ORANGE)}20`;
488
483
  e.currentTarget.style.transform = "translateY(-1px)";
489
484
  },
490
485
  onMouseLeave: (e) => {
491
- e.currentTarget.style.background = `${colorToHex(KOREAN_COLORS.UI_BACKGROUND_MEDIUM)}`;
486
+ e.currentTarget.style.background = `${hexColorToCSS(KOREAN_COLORS.UI_BACKGROUND_MEDIUM)}`;
492
487
  e.currentTarget.style.transform = "translateY(0)";
493
488
  },
494
489
  "data-testid": "reset-filters-button",
@@ -499,7 +494,7 @@ var VitalPointOverlayControlsPure = ({ visible, onVisibleChange, severityFilters
499
494
  style: {
500
495
  fontSize,
501
496
  marginBottom: "6px",
502
- color: colorToHex(KOREAN_COLORS.ACCENT_CYAN)
497
+ color: hexColorToCSS(KOREAN_COLORS.ACCENT_CYAN)
503
498
  },
504
499
  children: [
505
500
  "크기 | Scale: ",
@@ -515,7 +510,7 @@ var VitalPointOverlayControlsPure = ({ visible, onVisibleChange, severityFilters
515
510
  onChange: (e) => onScaleChange(parseFloat(e.target.value)),
516
511
  style: {
517
512
  width: "100%",
518
- accentColor: colorToHex(KOREAN_COLORS.PRIMARY_CYAN)
513
+ accentColor: hexColorToCSS(KOREAN_COLORS.PRIMARY_CYAN)
519
514
  },
520
515
  "data-testid": "scale-slider"
521
516
  })] }),
@@ -523,9 +518,9 @@ var VitalPointOverlayControlsPure = ({ visible, onVisibleChange, severityFilters
523
518
  style: {
524
519
  marginTop: "12px",
525
520
  paddingTop: "12px",
526
- borderTop: `1px solid ${colorToHex(KOREAN_COLORS.PRIMARY_CYAN)}40`,
521
+ borderTop: `1px solid ${hexColorToCSS(KOREAN_COLORS.PRIMARY_CYAN)}40`,
527
522
  fontSize: smallFontSize,
528
- color: colorToHex(KOREAN_COLORS.TEXT_SECONDARY)
523
+ color: hexColorToCSS(KOREAN_COLORS.TEXT_SECONDARY)
529
524
  },
530
525
  children: [/* @__PURE__ */ jsxs("div", { children: [
531
526
  "머리: ",