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
@@ -2,6 +2,7 @@ import { VitalPointSeverity } from "../../../../types/common.js";
2
2
  import { KOREAN_COLORS } from "../../../../types/constants/colors.js";
3
3
  import { FONT_FAMILY } from "../../../../types/constants/typography.js";
4
4
  import KOREAN_VITAL_POINTS, { getVitalPointsStats } from "../../../../systems/vitalpoint/KoreanVitalPoints.js";
5
+ import { hexColorToCSS, hexToRgbaString } from "../../../../utils/colorUtils.js";
5
6
  import { useCallback, useMemo, useState } from "react";
6
7
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
7
8
  import { Html } from "@react-three/drei";
@@ -23,20 +24,17 @@ import { Html } from "@react-three/drei";
23
24
  /**
24
25
  * Convert numeric color to CSS hex string
25
26
  */
26
- var colorToHex = (color) => {
27
- return `#${color.toString(16).padStart(6, "0")}`;
28
- };
29
27
  /**
30
28
  * Get color for severity level
31
29
  */
32
30
  var getSeverityColor = (severity) => {
33
31
  switch (severity) {
34
- case VitalPointSeverity.LETHAL: return "#ff0000";
35
- case VitalPointSeverity.CRITICAL: return "#ff6600";
36
- case VitalPointSeverity.MAJOR: return "#ffaa00";
37
- case VitalPointSeverity.MODERATE: return "#ffd700";
38
- case VitalPointSeverity.MINOR: return "#00ff88";
39
- 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);
40
38
  }
41
39
  };
42
40
  /**
@@ -125,13 +123,13 @@ var VitalPointOverlayControlsHtml = ({ visible, onVisibleChange, severityFilters
125
123
  right: finalPosition.right,
126
124
  bottom: finalPosition.bottom,
127
125
  width: panelWidth,
128
- background: `${colorToHex(KOREAN_COLORS.UI_BACKGROUND_DARK)}f0`,
129
- border: `2px solid ${colorToHex(KOREAN_COLORS.PRIMARY_CYAN)}`,
126
+ background: `${hexColorToCSS(KOREAN_COLORS.UI_BACKGROUND_DARK)}f0`,
127
+ border: `2px solid ${hexColorToCSS(KOREAN_COLORS.PRIMARY_CYAN)}`,
130
128
  borderRadius: "12px",
131
129
  padding: isMobile ? "12px" : "16px",
132
130
  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`,
131
+ color: hexColorToCSS(KOREAN_COLORS.TEXT_PRIMARY),
132
+ boxShadow: `0 0 30px ${hexColorToCSS(KOREAN_COLORS.PRIMARY_CYAN)}40, inset 0 0 20px ${hexColorToCSS(KOREAN_COLORS.UI_BACKGROUND_DARK)}80`,
135
133
  transition: "all 0.3s ease",
136
134
  pointerEvents: "all",
137
135
  zIndex: 200
@@ -145,8 +143,8 @@ var VitalPointOverlayControlsHtml = ({ visible, onVisibleChange, severityFilters
145
143
  alignItems: "center",
146
144
  marginBottom: "12px",
147
145
  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%)`
146
+ borderBottom: `1px solid ${hexColorToCSS(KOREAN_COLORS.PRIMARY_CYAN)}40`,
147
+ 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
148
  },
151
149
  children: [/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("div", {
152
150
  style: {
@@ -157,7 +155,7 @@ var VitalPointOverlayControlsHtml = ({ visible, onVisibleChange, severityFilters
157
155
  }), /* @__PURE__ */ jsxs("div", {
158
156
  style: {
159
157
  fontSize: smallFontSize,
160
- color: colorToHex(KOREAN_COLORS.TEXT_SECONDARY),
158
+ color: hexColorToCSS(KOREAN_COLORS.TEXT_SECONDARY),
161
159
  marginTop: "2px"
162
160
  },
163
161
  children: [
@@ -169,23 +167,23 @@ var VitalPointOverlayControlsHtml = ({ visible, onVisibleChange, severityFilters
169
167
  })] }), /* @__PURE__ */ jsx("button", {
170
168
  onClick: () => setExpanded(!expanded),
171
169
  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)}`,
170
+ background: `linear-gradient(135deg, ${hexColorToCSS(KOREAN_COLORS.UI_BACKGROUND_MEDIUM)} 0%, ${hexColorToCSS(KOREAN_COLORS.UI_BACKGROUND_DARK)} 100%)`,
171
+ border: `2px solid ${hexColorToCSS(KOREAN_COLORS.PRIMARY_CYAN)}`,
174
172
  borderRadius: "6px",
175
173
  padding: "8px 14px",
176
- color: "#ffffff",
174
+ color: hexColorToCSS(KOREAN_COLORS.TEXT_PRIMARY),
177
175
  fontSize,
178
176
  cursor: "pointer",
179
177
  transition: "all 0.2s ease",
180
- boxShadow: `0 2px 8px ${colorToHex(KOREAN_COLORS.PRIMARY_CYAN)}30`
178
+ boxShadow: `0 2px 8px ${hexColorToCSS(KOREAN_COLORS.PRIMARY_CYAN)}30`
181
179
  },
182
180
  onMouseEnter: (e) => {
183
181
  e.currentTarget.style.transform = "scale(1.05)";
184
- e.currentTarget.style.boxShadow = `0 4px 12px ${colorToHex(KOREAN_COLORS.PRIMARY_CYAN)}50`;
182
+ e.currentTarget.style.boxShadow = `0 4px 12px ${hexColorToCSS(KOREAN_COLORS.PRIMARY_CYAN)}50`;
185
183
  },
186
184
  onMouseLeave: (e) => {
187
185
  e.currentTarget.style.transform = "scale(1)";
188
- e.currentTarget.style.boxShadow = `0 2px 8px ${colorToHex(KOREAN_COLORS.PRIMARY_CYAN)}30`;
186
+ e.currentTarget.style.boxShadow = `0 2px 8px ${hexColorToCSS(KOREAN_COLORS.PRIMARY_CYAN)}30`;
189
187
  },
190
188
  "data-testid": "toggle-expand-button",
191
189
  children: expanded ? "▼" : "▶"
@@ -198,25 +196,25 @@ var VitalPointOverlayControlsHtml = ({ visible, onVisibleChange, severityFilters
198
196
  style: {
199
197
  width: "100%",
200
198
  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)}`,
199
+ 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%)`,
200
+ border: `2px solid ${visible ? hexColorToCSS(KOREAN_COLORS.ACCENT_GOLD) : hexColorToCSS(KOREAN_COLORS.PRIMARY_CYAN)}`,
203
201
  borderRadius: "8px",
204
- color: visible ? "#1a1a1a" : "#ffffff",
202
+ color: visible ? hexColorToCSS(KOREAN_COLORS.KOREAN_BLACK) : hexColorToCSS(KOREAN_COLORS.TEXT_PRIMARY),
205
203
  fontSize: isMobile ? 13 : 15,
206
204
  fontWeight: "bold",
207
205
  cursor: "pointer",
208
206
  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`,
207
+ 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
208
  textTransform: "uppercase",
211
209
  letterSpacing: "0.5px"
212
210
  },
213
211
  onMouseEnter: (e) => {
214
212
  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`;
213
+ e.currentTarget.style.boxShadow = visible ? `0 6px 20px ${hexColorToCSS(KOREAN_COLORS.ACCENT_GOLD)}80` : `0 4px 12px ${hexColorToCSS(KOREAN_COLORS.PRIMARY_CYAN)}50`;
216
214
  },
217
215
  onMouseLeave: (e) => {
218
216
  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`;
217
+ e.currentTarget.style.boxShadow = visible ? `0 4px 16px ${hexColorToCSS(KOREAN_COLORS.ACCENT_GOLD)}60` : `0 2px 8px ${hexColorToCSS(KOREAN_COLORS.PRIMARY_CYAN)}30`;
220
218
  },
221
219
  "data-testid": "toggle-visibility-button",
222
220
  children: visible ? "✓ 활성화 | Enabled" : "비활성화 | Disabled"
@@ -229,7 +227,7 @@ var VitalPointOverlayControlsHtml = ({ visible, onVisibleChange, severityFilters
229
227
  style: {
230
228
  fontSize,
231
229
  marginBottom: "8px",
232
- color: colorToHex(KOREAN_COLORS.ACCENT_CYAN),
230
+ color: hexColorToCSS(KOREAN_COLORS.ACCENT_CYAN),
233
231
  fontWeight: "600",
234
232
  textTransform: "uppercase",
235
233
  letterSpacing: "1px"
@@ -247,11 +245,11 @@ var VitalPointOverlayControlsHtml = ({ visible, onVisibleChange, severityFilters
247
245
  return /* @__PURE__ */ jsx("button", {
248
246
  onClick: () => toggleSeverityFilter(severity),
249
247
  style: {
250
- background: isActive ? `linear-gradient(135deg, ${severityColor} 0%, ${severityColor}cc 100%)` : `${colorToHex(KOREAN_COLORS.UI_BACKGROUND_MEDIUM)}`,
248
+ background: isActive ? `linear-gradient(135deg, ${severityColor} 0%, ${severityColor}cc 100%)` : `${hexColorToCSS(KOREAN_COLORS.UI_BACKGROUND_MEDIUM)}`,
251
249
  border: `2px solid ${severityColor}`,
252
250
  borderRadius: "6px",
253
251
  padding: "6px 12px",
254
- color: "#ffffff",
252
+ color: hexColorToCSS(KOREAN_COLORS.TEXT_PRIMARY),
255
253
  fontSize: smallFontSize,
256
254
  cursor: "pointer",
257
255
  opacity: isActive ? 1 : .6,
@@ -279,7 +277,7 @@ var VitalPointOverlayControlsHtml = ({ visible, onVisibleChange, severityFilters
279
277
  style: {
280
278
  fontSize,
281
279
  marginBottom: "8px",
282
- color: colorToHex(KOREAN_COLORS.ACCENT_CYAN),
280
+ color: hexColorToCSS(KOREAN_COLORS.ACCENT_CYAN),
283
281
  fontWeight: "600",
284
282
  textTransform: "uppercase",
285
283
  letterSpacing: "1px"
@@ -296,17 +294,17 @@ var VitalPointOverlayControlsHtml = ({ visible, onVisibleChange, severityFilters
296
294
  return /* @__PURE__ */ jsxs("button", {
297
295
  onClick: () => onRegionFilterChange(option.value),
298
296
  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)}`,
297
+ background: isActive ? `linear-gradient(135deg, ${hexColorToCSS(KOREAN_COLORS.PRIMARY_CYAN)} 0%, ${hexColorToCSS(KOREAN_COLORS.ACCENT_BLUE)} 100%)` : `${hexColorToCSS(KOREAN_COLORS.UI_BACKGROUND_MEDIUM)}`,
298
+ border: `2px solid ${hexColorToCSS(KOREAN_COLORS.PRIMARY_CYAN)}`,
301
299
  borderRadius: "6px",
302
300
  padding: "6px 12px",
303
- color: "#ffffff",
301
+ color: hexColorToCSS(KOREAN_COLORS.TEXT_PRIMARY),
304
302
  fontSize: smallFontSize,
305
303
  cursor: "pointer",
306
304
  opacity: isActive ? 1 : .6,
307
305
  transition: "all 0.2s ease",
308
306
  fontWeight: isActive ? "bold" : "normal",
309
- boxShadow: isActive ? `0 2px 8px ${colorToHex(KOREAN_COLORS.PRIMARY_CYAN)}50` : "none"
307
+ boxShadow: isActive ? `0 2px 8px ${hexColorToCSS(KOREAN_COLORS.PRIMARY_CYAN)}50` : "none"
310
308
  },
311
309
  onMouseEnter: (e) => {
312
310
  e.currentTarget.style.opacity = "1";
@@ -332,7 +330,7 @@ var VitalPointOverlayControlsHtml = ({ visible, onVisibleChange, severityFilters
332
330
  style: {
333
331
  fontSize,
334
332
  marginBottom: "8px",
335
- color: colorToHex(KOREAN_COLORS.ACCENT_CYAN),
333
+ color: hexColorToCSS(KOREAN_COLORS.ACCENT_CYAN),
336
334
  fontWeight: "600",
337
335
  textTransform: "uppercase",
338
336
  letterSpacing: "1px"
@@ -348,22 +346,22 @@ var VitalPointOverlayControlsHtml = ({ visible, onVisibleChange, severityFilters
348
346
  style: {
349
347
  width: "100%",
350
348
  height: buttonHeight,
351
- background: `${colorToHex(KOREAN_COLORS.UI_BACKGROUND_MEDIUM)}`,
352
- border: `2px solid ${colorToHex(KOREAN_COLORS.PRIMARY_CYAN)}40`,
349
+ background: `${hexColorToCSS(KOREAN_COLORS.UI_BACKGROUND_MEDIUM)}`,
350
+ border: `2px solid ${hexColorToCSS(KOREAN_COLORS.PRIMARY_CYAN)}40`,
353
351
  borderRadius: "8px",
354
352
  padding: "0 40px 0 14px",
355
- color: "#ffffff",
353
+ color: hexColorToCSS(KOREAN_COLORS.TEXT_PRIMARY),
356
354
  fontSize,
357
355
  fontFamily: FONT_FAMILY.KOREAN,
358
356
  transition: "all 0.2s ease",
359
357
  outline: "none"
360
358
  },
361
359
  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`;
360
+ e.currentTarget.style.borderColor = hexColorToCSS(KOREAN_COLORS.PRIMARY_CYAN);
361
+ e.currentTarget.style.boxShadow = `0 0 12px ${hexColorToCSS(KOREAN_COLORS.PRIMARY_CYAN)}40`;
364
362
  },
365
363
  onBlur: (e) => {
366
- e.currentTarget.style.borderColor = `${colorToHex(KOREAN_COLORS.PRIMARY_CYAN)}40`;
364
+ e.currentTarget.style.borderColor = `${hexColorToCSS(KOREAN_COLORS.PRIMARY_CYAN)}40`;
367
365
  e.currentTarget.style.boxShadow = "none";
368
366
  },
369
367
  "data-testid": "search-input"
@@ -376,7 +374,7 @@ var VitalPointOverlayControlsHtml = ({ visible, onVisibleChange, severityFilters
376
374
  transform: "translateY(-50%)",
377
375
  background: "transparent",
378
376
  border: "none",
379
- color: colorToHex(KOREAN_COLORS.TEXT_SECONDARY),
377
+ color: hexColorToCSS(KOREAN_COLORS.TEXT_SECONDARY),
380
378
  cursor: "pointer",
381
379
  fontSize: "16px",
382
380
  padding: "4px",
@@ -387,11 +385,11 @@ var VitalPointOverlayControlsHtml = ({ visible, onVisibleChange, severityFilters
387
385
  transition: "all 0.2s ease"
388
386
  },
389
387
  onMouseEnter: (e) => {
390
- e.currentTarget.style.color = colorToHex(KOREAN_COLORS.ACCENT_RED);
391
- e.currentTarget.style.background = `${colorToHex(KOREAN_COLORS.UI_BACKGROUND_MEDIUM)}`;
388
+ e.currentTarget.style.color = hexColorToCSS(KOREAN_COLORS.ACCENT_RED);
389
+ e.currentTarget.style.background = `${hexColorToCSS(KOREAN_COLORS.UI_BACKGROUND_MEDIUM)}`;
392
390
  },
393
391
  onMouseLeave: (e) => {
394
- e.currentTarget.style.color = colorToHex(KOREAN_COLORS.TEXT_SECONDARY);
392
+ e.currentTarget.style.color = hexColorToCSS(KOREAN_COLORS.TEXT_SECONDARY);
395
393
  e.currentTarget.style.background = "transparent";
396
394
  },
397
395
  "data-testid": "search-clear-button",
@@ -406,7 +404,7 @@ var VitalPointOverlayControlsHtml = ({ visible, onVisibleChange, severityFilters
406
404
  style: {
407
405
  fontSize,
408
406
  marginBottom: "8px",
409
- color: colorToHex(KOREAN_COLORS.ACCENT_CYAN),
407
+ color: hexColorToCSS(KOREAN_COLORS.ACCENT_CYAN),
410
408
  fontWeight: "600",
411
409
  textTransform: "uppercase",
412
410
  letterSpacing: "1px"
@@ -432,7 +430,7 @@ var VitalPointOverlayControlsHtml = ({ visible, onVisibleChange, severityFilters
432
430
  style: {
433
431
  width: "16px",
434
432
  height: "16px",
435
- accentColor: colorToHex(KOREAN_COLORS.PRIMARY_CYAN)
433
+ accentColor: hexColorToCSS(KOREAN_COLORS.PRIMARY_CYAN)
436
434
  },
437
435
  "data-testid": "show-labels-checkbox"
438
436
  }), /* @__PURE__ */ jsx("span", {
@@ -453,7 +451,7 @@ var VitalPointOverlayControlsHtml = ({ visible, onVisibleChange, severityFilters
453
451
  style: {
454
452
  width: "16px",
455
453
  height: "16px",
456
- accentColor: colorToHex(KOREAN_COLORS.PRIMARY_CYAN)
454
+ accentColor: hexColorToCSS(KOREAN_COLORS.PRIMARY_CYAN)
457
455
  },
458
456
  "data-testid": "animated-checkbox"
459
457
  }), /* @__PURE__ */ jsx("span", {
@@ -474,21 +472,21 @@ var VitalPointOverlayControlsHtml = ({ visible, onVisibleChange, severityFilters
474
472
  style: {
475
473
  width: "100%",
476
474
  height: buttonHeight - 4,
477
- background: `${colorToHex(KOREAN_COLORS.UI_BACKGROUND_MEDIUM)}`,
478
- border: `2px solid ${colorToHex(KOREAN_COLORS.ACCENT_ORANGE)}`,
475
+ background: `${hexColorToCSS(KOREAN_COLORS.UI_BACKGROUND_MEDIUM)}`,
476
+ border: `2px solid ${hexColorToCSS(KOREAN_COLORS.ACCENT_ORANGE)}`,
479
477
  borderRadius: "6px",
480
- color: colorToHex(KOREAN_COLORS.ACCENT_ORANGE),
478
+ color: hexColorToCSS(KOREAN_COLORS.ACCENT_ORANGE),
481
479
  fontSize: smallFontSize,
482
480
  cursor: "pointer",
483
481
  transition: "all 0.2s ease",
484
482
  fontWeight: "bold"
485
483
  },
486
484
  onMouseEnter: (e) => {
487
- e.currentTarget.style.background = `${colorToHex(KOREAN_COLORS.ACCENT_ORANGE)}20`;
485
+ e.currentTarget.style.background = `${hexColorToCSS(KOREAN_COLORS.ACCENT_ORANGE)}20`;
488
486
  e.currentTarget.style.transform = "translateY(-1px)";
489
487
  },
490
488
  onMouseLeave: (e) => {
491
- e.currentTarget.style.background = `${colorToHex(KOREAN_COLORS.UI_BACKGROUND_MEDIUM)}`;
489
+ e.currentTarget.style.background = `${hexColorToCSS(KOREAN_COLORS.UI_BACKGROUND_MEDIUM)}`;
492
490
  e.currentTarget.style.transform = "translateY(0)";
493
491
  },
494
492
  "data-testid": "reset-filters-button",
@@ -499,7 +497,7 @@ var VitalPointOverlayControlsHtml = ({ visible, onVisibleChange, severityFilters
499
497
  style: {
500
498
  fontSize,
501
499
  marginBottom: "6px",
502
- color: colorToHex(KOREAN_COLORS.ACCENT_CYAN)
500
+ color: hexColorToCSS(KOREAN_COLORS.ACCENT_CYAN)
503
501
  },
504
502
  children: [
505
503
  "크기 | Scale: ",
@@ -515,7 +513,7 @@ var VitalPointOverlayControlsHtml = ({ visible, onVisibleChange, severityFilters
515
513
  onChange: (e) => onScaleChange(parseFloat(e.target.value)),
516
514
  style: {
517
515
  width: "100%",
518
- accentColor: colorToHex(KOREAN_COLORS.PRIMARY_CYAN)
516
+ accentColor: hexColorToCSS(KOREAN_COLORS.PRIMARY_CYAN)
519
517
  },
520
518
  "data-testid": "scale-slider"
521
519
  })] }),
@@ -523,9 +521,9 @@ var VitalPointOverlayControlsHtml = ({ visible, onVisibleChange, severityFilters
523
521
  style: {
524
522
  marginTop: "12px",
525
523
  paddingTop: "12px",
526
- borderTop: `1px solid ${colorToHex(KOREAN_COLORS.PRIMARY_CYAN)}44`,
524
+ borderTop: `1px solid ${hexColorToCSS(KOREAN_COLORS.PRIMARY_CYAN)}44`,
527
525
  fontSize: smallFontSize,
528
- color: colorToHex(KOREAN_COLORS.TEXT_SECONDARY)
526
+ color: hexColorToCSS(KOREAN_COLORS.TEXT_SECONDARY)
529
527
  },
530
528
  children: [/* @__PURE__ */ jsxs("div", { children: [
531
529
  "머리: ",
@@ -1 +1 @@
1
- {"version":3,"file":"VitalPointOverlayControlsHtml.js","names":[],"sources":["../../../../../src/components/shared/three/ui/VitalPointOverlayControlsHtml.tsx"],"sourcesContent":["/**\n * VitalPointOverlayControlsHtml - UI controls for vital point visualization\n *\n * Provides comprehensive controls for the 70-point vital point overlay system:\n * - Toggle overlay visibility\n * - Filter by severity level\n * - Filter by body region\n * - Search vital points\n * - Adjust marker scale\n * - Toggle labels\n * - Toggle animations\n *\n * @module components/shared/three/ui/VitalPointOverlayControlsHtml\n */\n\nimport { Html } from \"@react-three/drei\";\nimport React, { useCallback, useMemo, useState } from \"react\";\nimport {\n KOREAN_VITAL_POINTS,\n getVitalPointsStats,\n} from \"../../../../systems/vitalpoint/KoreanVitalPoints\";\nimport { VitalPointSeverity } from \"../../../../types/common\";\nimport { FONT_FAMILY, KOREAN_COLORS } from \"../../../../types/constants\";\nimport type { BodyRegionFilter } from \"../effects/VitalPointMarkers3D\";\n\nexport type { BodyRegionFilter } from \"../effects/VitalPointMarkers3D\";\n\n/**\n * Props for VitalPointOverlayControlsHtml component\n */\nexport interface VitalPointOverlayControlsProps {\n /** Whether overlay is currently visible */\n readonly visible: boolean;\n /** Callback when visibility changes */\n readonly onVisibleChange: (visible: boolean) => void;\n /** Current severity filters */\n readonly severityFilters: VitalPointSeverity[];\n /** Callback when severity filters change */\n readonly onSeverityFiltersChange: (filters: VitalPointSeverity[]) => void;\n /** Current region filter */\n readonly regionFilter: BodyRegionFilter;\n /** Callback when region filter changes */\n readonly onRegionFilterChange: (filter: BodyRegionFilter) => void;\n /** Current search query */\n readonly searchQuery?: string;\n /** Callback when search query changes */\n readonly onSearchQueryChange?: (query: string) => void;\n /** Whether labels are shown */\n readonly showLabels: boolean;\n /** Callback when label visibility changes */\n readonly onShowLabelsChange: (show: boolean) => void;\n /** Whether animations are enabled */\n readonly animated: boolean;\n /** Callback when animation state changes */\n readonly onAnimatedChange: (animated: boolean) => void;\n /** Marker scale multiplier */\n readonly scale: number;\n /** Callback when scale changes */\n readonly onScaleChange: (scale: number) => void;\n /** Whether on mobile device */\n readonly isMobile?: boolean;\n /**\n * Screen position for the control panel.\n *\n * All values must be valid CSS position values, such as `\"20px\"`, `\"10%\"`, `\"1rem\"`, or `\"auto\"`.\n * These are applied directly to the `style` of the Html overlay container.\n */\n readonly screenPosition?: {\n /** CSS `top` position (e.g., \"20px\", \"10%\", \"1rem\", \"auto\") */\n top?: string;\n /** CSS `left` position (e.g., \"20px\", \"10%\", \"1rem\", \"auto\") */\n left?: string;\n /** CSS `right` position (e.g., \"20px\", \"10%\", \"1rem\", \"auto\") */\n right?: string;\n /** CSS `bottom` position (e.g., \"20px\", \"10%\", \"1rem\", \"auto\") */\n bottom?: string;\n };\n}\n\n/**\n * Convert numeric color to CSS hex string\n */\nconst colorToHex = (color: number): string => {\n return `#${color.toString(16).padStart(6, \"0\")}`;\n};\n\n/**\n * Get color for severity level\n */\nconst getSeverityColor = (severity: VitalPointSeverity): string => {\n switch (severity) {\n case VitalPointSeverity.LETHAL:\n return \"#ff0000\"; // Red\n case VitalPointSeverity.CRITICAL:\n return \"#ff6600\"; // Orange\n case VitalPointSeverity.MAJOR:\n return \"#ffaa00\"; // Gold\n case VitalPointSeverity.MODERATE:\n return \"#ffd700\"; // Yellow\n case VitalPointSeverity.MINOR:\n return \"#00ff88\"; // Green\n default:\n return \"#00ffff\"; // Cyan\n }\n};\n\n/**\n * VitalPointOverlayControlsHtml Component\n * Provides comprehensive UI for vital point visualization control\n */\nexport const VitalPointOverlayControlsHtml: React.FC<\n VitalPointOverlayControlsProps\n> = ({\n visible,\n onVisibleChange,\n severityFilters,\n onSeverityFiltersChange,\n regionFilter,\n onRegionFilterChange,\n searchQuery: externalSearchQuery,\n onSearchQueryChange,\n showLabels,\n onShowLabelsChange,\n animated,\n onAnimatedChange,\n scale,\n onScaleChange,\n isMobile = false,\n screenPosition,\n}) => {\n const [expanded, setExpanded] = useState(false);\n\n // Use internal state if no external control provided\n const [internalSearchQuery, setInternalSearchQuery] = useState(\"\");\n const searchQuery = externalSearchQuery ?? internalSearchQuery;\n const setSearchQuery = onSearchQueryChange ?? setInternalSearchQuery;\n\n // Get system statistics\n const stats = useMemo(() => getVitalPointsStats(), []);\n\n // Default screen position - left side, below player 1 status (accounting for stance indicator)\n const defaultPosition: {\n top?: string;\n left?: string;\n right?: string;\n bottom?: string;\n } = useMemo(\n () => ({\n top: isMobile ? \"180px\" : \"220px\",\n left: isMobile ? \"10px\" : \"20px\",\n }),\n [isMobile]\n );\n\n const finalPosition = screenPosition ?? defaultPosition;\n\n // Get filtered count\n const filteredCount = useMemo(() => {\n let points = [...KOREAN_VITAL_POINTS];\n\n // Filter by severity\n if (severityFilters.length > 0) {\n points = points.filter((vp) => severityFilters.includes(vp.severity));\n }\n\n // Filter by region\n if (regionFilter !== \"all\") {\n if (regionFilter === \"arms\") {\n // Match both left and right arm vital points\n points = points.filter(\n (vp) =>\n vp.id.startsWith(\"arm_left_\") || vp.id.startsWith(\"arm_right_\")\n );\n } else if (regionFilter === \"legs\") {\n // Match both left and right leg vital points\n points = points.filter(\n (vp) =>\n vp.id.startsWith(\"leg_left_\") || vp.id.startsWith(\"leg_right_\")\n );\n } else {\n // Simple prefix match for head_ or torso_\n const prefix = `${regionFilter}_`;\n points = points.filter((vp) => vp.id.startsWith(prefix));\n }\n }\n\n // Filter by search query\n if (searchQuery) {\n const query = searchQuery.toLowerCase();\n points = points.filter(\n (vp) =>\n vp.names.korean.toLowerCase().includes(query) ||\n vp.names.english.toLowerCase().includes(query) ||\n vp.names.romanized.toLowerCase().includes(query) ||\n vp.id.toLowerCase().includes(query)\n );\n }\n\n return points.length;\n }, [severityFilters, regionFilter, searchQuery]);\n\n // Toggle severity filter\n const toggleSeverityFilter = useCallback(\n (severity: VitalPointSeverity) => {\n const newFilters = severityFilters.includes(severity)\n ? severityFilters.filter((s) => s !== severity)\n : [...severityFilters, severity];\n onSeverityFiltersChange(newFilters);\n },\n [severityFilters, onSeverityFiltersChange]\n );\n\n // Severity options\n const severityOptions: VitalPointSeverity[] = [\n VitalPointSeverity.LETHAL,\n VitalPointSeverity.CRITICAL,\n VitalPointSeverity.MAJOR,\n VitalPointSeverity.MODERATE,\n VitalPointSeverity.MINOR,\n ];\n\n // Region options\n const regionOptions: {\n value: BodyRegionFilter;\n label: string;\n korean: string;\n }[] = [\n { value: \"all\", label: \"All Regions\", korean: \"전체\" },\n { value: \"head\", label: \"Head\", korean: \"머리\" },\n { value: \"torso\", label: \"Torso\", korean: \"몸통\" },\n { value: \"arms\", label: \"Arms\", korean: \"팔\" },\n { value: \"legs\", label: \"Legs\", korean: \"다리\" },\n ];\n\n // Panel styles\n const panelWidth = isMobile ? 280 : 350;\n const buttonHeight = isMobile ? 32 : 36;\n const fontSize = isMobile ? 11 : 13;\n const smallFontSize = isMobile ? 9 : 10;\n\n return (\n <Html fullscreen style={{ pointerEvents: \"none\" }}>\n <div\n style={{\n position: \"absolute\",\n top: finalPosition.top,\n left: finalPosition.left,\n right: finalPosition.right,\n bottom: finalPosition.bottom,\n width: panelWidth,\n background: `${colorToHex(KOREAN_COLORS.UI_BACKGROUND_DARK)}f0`,\n border: `2px solid ${colorToHex(KOREAN_COLORS.PRIMARY_CYAN)}`,\n borderRadius: \"12px\",\n padding: isMobile ? \"12px\" : \"16px\",\n fontFamily: FONT_FAMILY.KOREAN,\n color: \"#ffffff\",\n boxShadow: `0 0 30px ${colorToHex(\n KOREAN_COLORS.PRIMARY_CYAN\n )}40, inset 0 0 20px ${colorToHex(\n KOREAN_COLORS.UI_BACKGROUND_DARK\n )}80`,\n transition: \"all 0.3s ease\",\n pointerEvents: \"all\",\n zIndex: 200,\n }}\n data-testid=\"vital-point-overlay-controls\"\n >\n {/* Header with toggle */}\n <div\n style={{\n display: \"flex\",\n justifyContent: \"space-between\",\n alignItems: \"center\",\n marginBottom: \"12px\",\n paddingBottom: \"12px\",\n borderBottom: `1px solid ${colorToHex(\n KOREAN_COLORS.PRIMARY_CYAN\n )}40`,\n background: `linear-gradient(90deg, ${colorToHex(\n KOREAN_COLORS.UI_BACKGROUND_DARK\n )}00 0%, ${colorToHex(\n KOREAN_COLORS.PRIMARY_CYAN\n )}10 50%, ${colorToHex(KOREAN_COLORS.UI_BACKGROUND_DARK)}00 100%)`,\n }}\n >\n <div>\n <div style={{ fontSize: isMobile ? 14 : 16, fontWeight: \"bold\" }}>\n 급소 오버레이 | Vital Points\n </div>\n <div\n style={{\n fontSize: smallFontSize,\n color: colorToHex(KOREAN_COLORS.TEXT_SECONDARY),\n marginTop: \"2px\",\n }}\n >\n {filteredCount} / {stats.total} 표시 | Showing\n </div>\n </div>\n <button\n onClick={() => setExpanded(!expanded)}\n style={{\n background: `linear-gradient(135deg, ${colorToHex(\n KOREAN_COLORS.UI_BACKGROUND_MEDIUM\n )} 0%, ${colorToHex(KOREAN_COLORS.UI_BACKGROUND_DARK)} 100%)`,\n border: `2px solid ${colorToHex(KOREAN_COLORS.PRIMARY_CYAN)}`,\n borderRadius: \"6px\",\n padding: \"8px 14px\",\n color: \"#ffffff\",\n fontSize,\n cursor: \"pointer\",\n transition: \"all 0.2s ease\",\n boxShadow: `0 2px 8px ${colorToHex(\n KOREAN_COLORS.PRIMARY_CYAN\n )}30`,\n }}\n onMouseEnter={(e) => {\n e.currentTarget.style.transform = \"scale(1.05)\";\n e.currentTarget.style.boxShadow = `0 4px 12px ${colorToHex(\n KOREAN_COLORS.PRIMARY_CYAN\n )}50`;\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.transform = \"scale(1)\";\n e.currentTarget.style.boxShadow = `0 2px 8px ${colorToHex(\n KOREAN_COLORS.PRIMARY_CYAN\n )}30`;\n }}\n data-testid=\"toggle-expand-button\"\n >\n {expanded ? \"▼\" : \"▶\"}\n </button>\n </div>\n\n {/* Main toggle */}\n <div style={{ marginBottom: \"14px\" }}>\n <button\n onClick={() => onVisibleChange(!visible)}\n style={{\n width: \"100%\",\n height: buttonHeight,\n background: visible\n ? `linear-gradient(135deg, ${colorToHex(\n KOREAN_COLORS.ACCENT_GOLD\n )} 0%, ${colorToHex(KOREAN_COLORS.SECONDARY_YELLOW)} 100%)`\n : `linear-gradient(135deg, ${colorToHex(\n KOREAN_COLORS.UI_BACKGROUND_MEDIUM\n )} 0%, ${colorToHex(KOREAN_COLORS.UI_BACKGROUND_DARK)} 100%)`,\n border: `2px solid ${\n visible\n ? colorToHex(KOREAN_COLORS.ACCENT_GOLD)\n : colorToHex(KOREAN_COLORS.PRIMARY_CYAN)\n }`,\n borderRadius: \"8px\",\n color: visible ? \"#1a1a1a\" : \"#ffffff\",\n fontSize: isMobile ? 13 : 15,\n fontWeight: \"bold\",\n cursor: \"pointer\",\n transition: \"all 0.3s ease\",\n boxShadow: visible\n ? `0 4px 16px ${colorToHex(\n KOREAN_COLORS.ACCENT_GOLD\n )}60, inset 0 2px 4px rgba(255,255,255,0.2)`\n : `0 2px 8px ${colorToHex(KOREAN_COLORS.PRIMARY_CYAN)}30`,\n textTransform: \"uppercase\",\n letterSpacing: \"0.5px\",\n }}\n onMouseEnter={(e) => {\n e.currentTarget.style.transform = \"translateY(-2px)\";\n e.currentTarget.style.boxShadow = visible\n ? `0 6px 20px ${colorToHex(KOREAN_COLORS.ACCENT_GOLD)}80`\n : `0 4px 12px ${colorToHex(KOREAN_COLORS.PRIMARY_CYAN)}50`;\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.transform = \"translateY(0)\";\n e.currentTarget.style.boxShadow = visible\n ? `0 4px 16px ${colorToHex(KOREAN_COLORS.ACCENT_GOLD)}60`\n : `0 2px 8px ${colorToHex(KOREAN_COLORS.PRIMARY_CYAN)}30`;\n }}\n data-testid=\"toggle-visibility-button\"\n >\n {visible ? \"✓ 활성화 | Enabled\" : \"비활성화 | Disabled\"}\n </button>\n </div>\n\n {/* Expanded controls */}\n {expanded && visible && (\n <>\n {/* Severity filters */}\n <div style={{ marginBottom: \"14px\" }}>\n <div\n style={{\n fontSize,\n marginBottom: \"8px\",\n color: colorToHex(KOREAN_COLORS.ACCENT_CYAN),\n fontWeight: \"600\",\n textTransform: \"uppercase\",\n letterSpacing: \"1px\",\n }}\n >\n 심각도 필터 | Severity Filter\n </div>\n <div\n style={{\n display: \"flex\",\n flexWrap: \"wrap\",\n gap: \"8px\",\n }}\n >\n {severityOptions.map((severity) => {\n const isActive = severityFilters.includes(severity);\n const severityColor = getSeverityColor(severity);\n return (\n <button\n key={severity}\n onClick={() => toggleSeverityFilter(severity)}\n style={{\n background: isActive\n ? `linear-gradient(135deg, ${severityColor} 0%, ${severityColor}cc 100%)`\n : `${colorToHex(KOREAN_COLORS.UI_BACKGROUND_MEDIUM)}`,\n border: `2px solid ${severityColor}`,\n borderRadius: \"6px\",\n padding: \"6px 12px\",\n color: \"#ffffff\",\n fontSize: smallFontSize,\n cursor: \"pointer\",\n opacity: isActive ? 1 : 0.6,\n transition: \"all 0.2s ease\",\n fontWeight: isActive ? \"bold\" : \"normal\",\n boxShadow: isActive\n ? `0 2px 8px ${severityColor}50`\n : \"none\",\n }}\n onMouseEnter={(e) => {\n e.currentTarget.style.opacity = \"1\";\n e.currentTarget.style.transform = \"scale(1.05)\";\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.opacity = isActive ? \"1\" : \"0.6\";\n e.currentTarget.style.transform = \"scale(1)\";\n }}\n data-testid={`severity-filter-${severity}`}\n >\n {severity}\n </button>\n );\n })}\n </div>\n </div>\n\n {/* Region filter */}\n <div style={{ marginBottom: \"14px\" }}>\n <div\n style={{\n fontSize,\n marginBottom: \"8px\",\n color: colorToHex(KOREAN_COLORS.ACCENT_CYAN),\n fontWeight: \"600\",\n textTransform: \"uppercase\",\n letterSpacing: \"1px\",\n }}\n >\n 부위 필터 | Region Filter\n </div>\n <div\n style={{\n display: \"flex\",\n flexWrap: \"wrap\",\n gap: \"8px\",\n }}\n >\n {regionOptions.map((option) => {\n const isActive = regionFilter === option.value;\n return (\n <button\n key={option.value}\n onClick={() => onRegionFilterChange(option.value)}\n style={{\n background: isActive\n ? `linear-gradient(135deg, ${colorToHex(\n KOREAN_COLORS.PRIMARY_CYAN\n )} 0%, ${colorToHex(\n KOREAN_COLORS.ACCENT_BLUE\n )} 100%)`\n : `${colorToHex(KOREAN_COLORS.UI_BACKGROUND_MEDIUM)}`,\n border: `2px solid ${colorToHex(\n KOREAN_COLORS.PRIMARY_CYAN\n )}`,\n borderRadius: \"6px\",\n padding: \"6px 12px\",\n color: \"#ffffff\",\n fontSize: smallFontSize,\n cursor: \"pointer\",\n opacity: isActive ? 1 : 0.6,\n transition: \"all 0.2s ease\",\n fontWeight: isActive ? \"bold\" : \"normal\",\n boxShadow: isActive\n ? `0 2px 8px ${colorToHex(\n KOREAN_COLORS.PRIMARY_CYAN\n )}50`\n : \"none\",\n }}\n onMouseEnter={(e) => {\n e.currentTarget.style.opacity = \"1\";\n e.currentTarget.style.transform = \"scale(1.05)\";\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.opacity = isActive ? \"1\" : \"0.6\";\n e.currentTarget.style.transform = \"scale(1)\";\n }}\n data-testid={`region-filter-${option.value}`}\n >\n {option.korean} | {option.label}\n </button>\n );\n })}\n </div>\n </div>\n\n {/* Search box with clear button */}\n <div style={{ marginBottom: \"14px\" }}>\n <div\n style={{\n fontSize,\n marginBottom: \"8px\",\n color: colorToHex(KOREAN_COLORS.ACCENT_CYAN),\n fontWeight: \"600\",\n textTransform: \"uppercase\",\n letterSpacing: \"1px\",\n }}\n >\n 검색 | Search\n </div>\n <div style={{ position: \"relative\" }}>\n <input\n type=\"text\"\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n placeholder=\"급소 이름... | Point name...\"\n style={{\n width: \"100%\",\n height: buttonHeight,\n background: `${colorToHex(\n KOREAN_COLORS.UI_BACKGROUND_MEDIUM\n )}`,\n border: `2px solid ${colorToHex(\n KOREAN_COLORS.PRIMARY_CYAN\n )}40`,\n borderRadius: \"8px\",\n padding: \"0 40px 0 14px\", // Add right padding for clear button\n color: \"#ffffff\",\n fontSize,\n fontFamily: FONT_FAMILY.KOREAN,\n transition: \"all 0.2s ease\",\n outline: \"none\",\n }}\n onFocus={(e) => {\n e.currentTarget.style.borderColor = colorToHex(\n KOREAN_COLORS.PRIMARY_CYAN\n );\n e.currentTarget.style.boxShadow = `0 0 12px ${colorToHex(\n KOREAN_COLORS.PRIMARY_CYAN\n )}40`;\n }}\n onBlur={(e) => {\n e.currentTarget.style.borderColor = `${colorToHex(\n KOREAN_COLORS.PRIMARY_CYAN\n )}40`;\n e.currentTarget.style.boxShadow = \"none\";\n }}\n data-testid=\"search-input\"\n />\n {/* Clear button */}\n {searchQuery && (\n <button\n onClick={() => setSearchQuery(\"\")}\n style={{\n position: \"absolute\",\n right: \"8px\",\n top: \"50%\",\n transform: \"translateY(-50%)\",\n background: \"transparent\",\n border: \"none\",\n color: colorToHex(KOREAN_COLORS.TEXT_SECONDARY),\n cursor: \"pointer\",\n fontSize: \"16px\",\n padding: \"4px\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n borderRadius: \"4px\",\n transition: \"all 0.2s ease\",\n }}\n onMouseEnter={(e) => {\n e.currentTarget.style.color = colorToHex(\n KOREAN_COLORS.ACCENT_RED\n );\n e.currentTarget.style.background = `${colorToHex(\n KOREAN_COLORS.UI_BACKGROUND_MEDIUM\n )}`;\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.color = colorToHex(\n KOREAN_COLORS.TEXT_SECONDARY\n );\n e.currentTarget.style.background = \"transparent\";\n }}\n data-testid=\"search-clear-button\"\n title=\"Clear search\"\n >\n ✕\n </button>\n )}\n </div>\n </div>\n\n {/* Display options */}\n <div style={{ marginBottom: \"14px\" }}>\n <div\n style={{\n fontSize,\n marginBottom: \"8px\",\n color: colorToHex(KOREAN_COLORS.ACCENT_CYAN),\n fontWeight: \"600\",\n textTransform: \"uppercase\",\n letterSpacing: \"1px\",\n }}\n >\n 표시 옵션 | Display Options\n </div>\n <div\n style={{ display: \"flex\", flexDirection: \"column\", gap: \"8px\" }}\n >\n <label\n style={{\n display: \"flex\",\n alignItems: \"center\",\n gap: \"8px\",\n cursor: \"pointer\",\n }}\n >\n <input\n type=\"checkbox\"\n checked={showLabels}\n onChange={(e) => onShowLabelsChange(e.target.checked)}\n style={{\n width: \"16px\",\n height: \"16px\",\n accentColor: colorToHex(KOREAN_COLORS.PRIMARY_CYAN),\n }}\n data-testid=\"show-labels-checkbox\"\n />\n <span style={{ fontSize }}>라벨 표시 | Show Labels</span>\n </label>\n <label\n style={{\n display: \"flex\",\n alignItems: \"center\",\n gap: \"8px\",\n cursor: \"pointer\",\n }}\n >\n <input\n type=\"checkbox\"\n checked={animated}\n onChange={(e) => onAnimatedChange(e.target.checked)}\n style={{\n width: \"16px\",\n height: \"16px\",\n accentColor: colorToHex(KOREAN_COLORS.PRIMARY_CYAN),\n }}\n data-testid=\"animated-checkbox\"\n />\n <span style={{ fontSize }}>애니메이션 | Animations</span>\n </label>\n </div>\n </div>\n\n {/* Reset filters button */}\n {(severityFilters.length > 0 ||\n regionFilter !== \"all\" ||\n searchQuery !== \"\") && (\n <div style={{ marginBottom: \"14px\" }}>\n <button\n onClick={() => {\n onSeverityFiltersChange([]);\n onRegionFilterChange(\"all\");\n setSearchQuery(\"\");\n }}\n style={{\n width: \"100%\",\n height: buttonHeight - 4,\n background: `${colorToHex(\n KOREAN_COLORS.UI_BACKGROUND_MEDIUM\n )}`,\n border: `2px solid ${colorToHex(\n KOREAN_COLORS.ACCENT_ORANGE\n )}`,\n borderRadius: \"6px\",\n color: colorToHex(KOREAN_COLORS.ACCENT_ORANGE),\n fontSize: smallFontSize,\n cursor: \"pointer\",\n transition: \"all 0.2s ease\",\n fontWeight: \"bold\",\n }}\n onMouseEnter={(e) => {\n e.currentTarget.style.background = `${colorToHex(\n KOREAN_COLORS.ACCENT_ORANGE\n )}20`;\n e.currentTarget.style.transform = \"translateY(-1px)\";\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.background = `${colorToHex(\n KOREAN_COLORS.UI_BACKGROUND_MEDIUM\n )}`;\n e.currentTarget.style.transform = \"translateY(0)\";\n }}\n data-testid=\"reset-filters-button\"\n >\n 🔄 필터 초기화 | Reset Filters\n </button>\n </div>\n )}\n\n {/* Scale slider */}\n <div>\n <div\n style={{\n fontSize,\n marginBottom: \"6px\",\n color: colorToHex(KOREAN_COLORS.ACCENT_CYAN),\n }}\n >\n 크기 | Scale: {scale.toFixed(1)}x\n </div>\n <input\n type=\"range\"\n min=\"0.5\"\n max=\"2.0\"\n step=\"0.1\"\n value={scale}\n onChange={(e) => onScaleChange(parseFloat(e.target.value))}\n style={{\n width: \"100%\",\n accentColor: colorToHex(KOREAN_COLORS.PRIMARY_CYAN),\n }}\n data-testid=\"scale-slider\"\n />\n </div>\n\n {/* Statistics */}\n <div\n style={{\n marginTop: \"12px\",\n paddingTop: \"12px\",\n borderTop: `1px solid ${colorToHex(\n KOREAN_COLORS.PRIMARY_CYAN\n )}44`,\n fontSize: smallFontSize,\n color: colorToHex(KOREAN_COLORS.TEXT_SECONDARY),\n }}\n >\n <div>\n 머리: {stats.byRegion.head} | 몸통: {stats.byRegion.torso}\n </div>\n <div>\n 팔: {stats.byRegion.arms} | 다리: {stats.byRegion.legs}\n </div>\n </div>\n </>\n )}\n </div>\n </Html>\n );\n};\n\nexport default VitalPointOverlayControlsHtml;\n"],"mappings":"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"}
1
+ {"version":3,"file":"VitalPointOverlayControlsHtml.js","names":[],"sources":["../../../../../src/components/shared/three/ui/VitalPointOverlayControlsHtml.tsx"],"sourcesContent":["/**\n * VitalPointOverlayControlsHtml - UI controls for vital point visualization\n *\n * Provides comprehensive controls for the 70-point vital point overlay system:\n * - Toggle overlay visibility\n * - Filter by severity level\n * - Filter by body region\n * - Search vital points\n * - Adjust marker scale\n * - Toggle labels\n * - Toggle animations\n *\n * @module components/shared/three/ui/VitalPointOverlayControlsHtml\n */\n\nimport { Html } from \"@react-three/drei\";\nimport React, { useCallback, useMemo, useState } from \"react\";\nimport {\n KOREAN_VITAL_POINTS,\n getVitalPointsStats,\n} from \"../../../../systems/vitalpoint/KoreanVitalPoints\";\nimport { VitalPointSeverity } from \"../../../../types/common\";\nimport { FONT_FAMILY, KOREAN_COLORS } from \"../../../../types/constants\";\nimport { hexColorToCSS, hexToRgbaString } from \"../../../../utils/colorUtils\";\nimport type { BodyRegionFilter } from \"../effects/VitalPointMarkers3D\";\n\nexport type { BodyRegionFilter } from \"../effects/VitalPointMarkers3D\";\n\n/**\n * Props for VitalPointOverlayControlsHtml component\n */\nexport interface VitalPointOverlayControlsProps {\n /** Whether overlay is currently visible */\n readonly visible: boolean;\n /** Callback when visibility changes */\n readonly onVisibleChange: (visible: boolean) => void;\n /** Current severity filters */\n readonly severityFilters: VitalPointSeverity[];\n /** Callback when severity filters change */\n readonly onSeverityFiltersChange: (filters: VitalPointSeverity[]) => void;\n /** Current region filter */\n readonly regionFilter: BodyRegionFilter;\n /** Callback when region filter changes */\n readonly onRegionFilterChange: (filter: BodyRegionFilter) => void;\n /** Current search query */\n readonly searchQuery?: string;\n /** Callback when search query changes */\n readonly onSearchQueryChange?: (query: string) => void;\n /** Whether labels are shown */\n readonly showLabels: boolean;\n /** Callback when label visibility changes */\n readonly onShowLabelsChange: (show: boolean) => void;\n /** Whether animations are enabled */\n readonly animated: boolean;\n /** Callback when animation state changes */\n readonly onAnimatedChange: (animated: boolean) => void;\n /** Marker scale multiplier */\n readonly scale: number;\n /** Callback when scale changes */\n readonly onScaleChange: (scale: number) => void;\n /** Whether on mobile device */\n readonly isMobile?: boolean;\n /**\n * Screen position for the control panel.\n *\n * All values must be valid CSS position values, such as `\"20px\"`, `\"10%\"`, `\"1rem\"`, or `\"auto\"`.\n * These are applied directly to the `style` of the Html overlay container.\n */\n readonly screenPosition?: {\n /** CSS `top` position (e.g., \"20px\", \"10%\", \"1rem\", \"auto\") */\n top?: string;\n /** CSS `left` position (e.g., \"20px\", \"10%\", \"1rem\", \"auto\") */\n left?: string;\n /** CSS `right` position (e.g., \"20px\", \"10%\", \"1rem\", \"auto\") */\n right?: string;\n /** CSS `bottom` position (e.g., \"20px\", \"10%\", \"1rem\", \"auto\") */\n bottom?: string;\n };\n}\n\n/**\n * Convert numeric color to CSS hex string\n */\n/**\n * Get color for severity level\n */\nconst getSeverityColor = (severity: VitalPointSeverity): string => {\n switch (severity) {\n case VitalPointSeverity.LETHAL:\n return hexColorToCSS(KOREAN_COLORS.NEGATIVE_RED);\n case VitalPointSeverity.CRITICAL:\n return hexColorToCSS(KOREAN_COLORS.HEALTH_LOW);\n case VitalPointSeverity.MAJOR:\n return hexColorToCSS(KOREAN_COLORS.ACCENT_GOLD);\n case VitalPointSeverity.MODERATE:\n return hexColorToCSS(KOREAN_COLORS.TRIGRAM_GEON_PRIMARY);\n case VitalPointSeverity.MINOR:\n return hexColorToCSS(KOREAN_COLORS.POSITIVE_GREEN);\n default:\n return hexColorToCSS(KOREAN_COLORS.NEON_CYAN);\n }\n};\n\n/**\n * VitalPointOverlayControlsHtml Component\n * Provides comprehensive UI for vital point visualization control\n */\nexport const VitalPointOverlayControlsHtml: React.FC<\n VitalPointOverlayControlsProps\n> = ({\n visible,\n onVisibleChange,\n severityFilters,\n onSeverityFiltersChange,\n regionFilter,\n onRegionFilterChange,\n searchQuery: externalSearchQuery,\n onSearchQueryChange,\n showLabels,\n onShowLabelsChange,\n animated,\n onAnimatedChange,\n scale,\n onScaleChange,\n isMobile = false,\n screenPosition,\n}) => {\n const [expanded, setExpanded] = useState(false);\n\n // Use internal state if no external control provided\n const [internalSearchQuery, setInternalSearchQuery] = useState(\"\");\n const searchQuery = externalSearchQuery ?? internalSearchQuery;\n const setSearchQuery = onSearchQueryChange ?? setInternalSearchQuery;\n\n // Get system statistics\n const stats = useMemo(() => getVitalPointsStats(), []);\n\n // Default screen position - left side, below player 1 status (accounting for stance indicator)\n const defaultPosition: {\n top?: string;\n left?: string;\n right?: string;\n bottom?: string;\n } = useMemo(\n () => ({\n top: isMobile ? \"180px\" : \"220px\",\n left: isMobile ? \"10px\" : \"20px\",\n }),\n [isMobile]\n );\n\n const finalPosition = screenPosition ?? defaultPosition;\n\n // Get filtered count\n const filteredCount = useMemo(() => {\n let points = [...KOREAN_VITAL_POINTS];\n\n // Filter by severity\n if (severityFilters.length > 0) {\n points = points.filter((vp) => severityFilters.includes(vp.severity));\n }\n\n // Filter by region\n if (regionFilter !== \"all\") {\n if (regionFilter === \"arms\") {\n // Match both left and right arm vital points\n points = points.filter(\n (vp) =>\n vp.id.startsWith(\"arm_left_\") || vp.id.startsWith(\"arm_right_\")\n );\n } else if (regionFilter === \"legs\") {\n // Match both left and right leg vital points\n points = points.filter(\n (vp) =>\n vp.id.startsWith(\"leg_left_\") || vp.id.startsWith(\"leg_right_\")\n );\n } else {\n // Simple prefix match for head_ or torso_\n const prefix = `${regionFilter}_`;\n points = points.filter((vp) => vp.id.startsWith(prefix));\n }\n }\n\n // Filter by search query\n if (searchQuery) {\n const query = searchQuery.toLowerCase();\n points = points.filter(\n (vp) =>\n vp.names.korean.toLowerCase().includes(query) ||\n vp.names.english.toLowerCase().includes(query) ||\n vp.names.romanized.toLowerCase().includes(query) ||\n vp.id.toLowerCase().includes(query)\n );\n }\n\n return points.length;\n }, [severityFilters, regionFilter, searchQuery]);\n\n // Toggle severity filter\n const toggleSeverityFilter = useCallback(\n (severity: VitalPointSeverity) => {\n const newFilters = severityFilters.includes(severity)\n ? severityFilters.filter((s) => s !== severity)\n : [...severityFilters, severity];\n onSeverityFiltersChange(newFilters);\n },\n [severityFilters, onSeverityFiltersChange]\n );\n\n // Severity options\n const severityOptions: VitalPointSeverity[] = [\n VitalPointSeverity.LETHAL,\n VitalPointSeverity.CRITICAL,\n VitalPointSeverity.MAJOR,\n VitalPointSeverity.MODERATE,\n VitalPointSeverity.MINOR,\n ];\n\n // Region options\n const regionOptions: {\n value: BodyRegionFilter;\n label: string;\n korean: string;\n }[] = [\n { value: \"all\", label: \"All Regions\", korean: \"전체\" },\n { value: \"head\", label: \"Head\", korean: \"머리\" },\n { value: \"torso\", label: \"Torso\", korean: \"몸통\" },\n { value: \"arms\", label: \"Arms\", korean: \"팔\" },\n { value: \"legs\", label: \"Legs\", korean: \"다리\" },\n ];\n\n // Panel styles\n const panelWidth = isMobile ? 280 : 350;\n const buttonHeight = isMobile ? 32 : 36;\n const fontSize = isMobile ? 11 : 13;\n const smallFontSize = isMobile ? 9 : 10;\n\n return (\n <Html fullscreen style={{ pointerEvents: \"none\" }}>\n <div\n style={{\n position: \"absolute\",\n top: finalPosition.top,\n left: finalPosition.left,\n right: finalPosition.right,\n bottom: finalPosition.bottom,\n width: panelWidth,\n background: `${hexColorToCSS(KOREAN_COLORS.UI_BACKGROUND_DARK)}f0`,\n border: `2px solid ${hexColorToCSS(KOREAN_COLORS.PRIMARY_CYAN)}`,\n borderRadius: \"12px\",\n padding: isMobile ? \"12px\" : \"16px\",\n fontFamily: FONT_FAMILY.KOREAN,\n color: hexColorToCSS(KOREAN_COLORS.TEXT_PRIMARY),\n boxShadow: `0 0 30px ${hexColorToCSS(\n KOREAN_COLORS.PRIMARY_CYAN\n )}40, inset 0 0 20px ${hexColorToCSS(\n KOREAN_COLORS.UI_BACKGROUND_DARK\n )}80`,\n transition: \"all 0.3s ease\",\n pointerEvents: \"all\",\n zIndex: 200,\n }}\n data-testid=\"vital-point-overlay-controls\"\n >\n {/* Header with toggle */}\n <div\n style={{\n display: \"flex\",\n justifyContent: \"space-between\",\n alignItems: \"center\",\n marginBottom: \"12px\",\n paddingBottom: \"12px\",\n borderBottom: `1px solid ${hexColorToCSS(\n KOREAN_COLORS.PRIMARY_CYAN\n )}40`,\n background: `linear-gradient(90deg, ${hexColorToCSS(\n KOREAN_COLORS.UI_BACKGROUND_DARK\n )}00 0%, ${hexColorToCSS(\n KOREAN_COLORS.PRIMARY_CYAN\n )}10 50%, ${hexColorToCSS(KOREAN_COLORS.UI_BACKGROUND_DARK)}00 100%)`,\n }}\n >\n <div>\n <div style={{ fontSize: isMobile ? 14 : 16, fontWeight: \"bold\" }}>\n 급소 오버레이 | Vital Points\n </div>\n <div\n style={{\n fontSize: smallFontSize,\n color: hexColorToCSS(KOREAN_COLORS.TEXT_SECONDARY),\n marginTop: \"2px\",\n }}\n >\n {filteredCount} / {stats.total} 표시 | Showing\n </div>\n </div>\n <button\n onClick={() => setExpanded(!expanded)}\n style={{\n background: `linear-gradient(135deg, ${hexColorToCSS(\n KOREAN_COLORS.UI_BACKGROUND_MEDIUM\n )} 0%, ${hexColorToCSS(KOREAN_COLORS.UI_BACKGROUND_DARK)} 100%)`,\n border: `2px solid ${hexColorToCSS(KOREAN_COLORS.PRIMARY_CYAN)}`,\n borderRadius: \"6px\",\n padding: \"8px 14px\",\n color: hexColorToCSS(KOREAN_COLORS.TEXT_PRIMARY),\n fontSize,\n cursor: \"pointer\",\n transition: \"all 0.2s ease\",\n boxShadow: `0 2px 8px ${hexColorToCSS(\n KOREAN_COLORS.PRIMARY_CYAN\n )}30`,\n }}\n onMouseEnter={(e) => {\n e.currentTarget.style.transform = \"scale(1.05)\";\n e.currentTarget.style.boxShadow = `0 4px 12px ${hexColorToCSS(\n KOREAN_COLORS.PRIMARY_CYAN\n )}50`;\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.transform = \"scale(1)\";\n e.currentTarget.style.boxShadow = `0 2px 8px ${hexColorToCSS(\n KOREAN_COLORS.PRIMARY_CYAN\n )}30`;\n }}\n data-testid=\"toggle-expand-button\"\n >\n {expanded ? \"▼\" : \"▶\"}\n </button>\n </div>\n\n {/* Main toggle */}\n <div style={{ marginBottom: \"14px\" }}>\n <button\n onClick={() => onVisibleChange(!visible)}\n style={{\n width: \"100%\",\n height: buttonHeight,\n background: visible\n ? `linear-gradient(135deg, ${hexColorToCSS(\n KOREAN_COLORS.ACCENT_GOLD\n )} 0%, ${hexColorToCSS(KOREAN_COLORS.SECONDARY_YELLOW)} 100%)`\n : `linear-gradient(135deg, ${hexColorToCSS(\n KOREAN_COLORS.UI_BACKGROUND_MEDIUM\n )} 0%, ${hexColorToCSS(KOREAN_COLORS.UI_BACKGROUND_DARK)} 100%)`,\n border: `2px solid ${\n visible\n ? hexColorToCSS(KOREAN_COLORS.ACCENT_GOLD)\n : hexColorToCSS(KOREAN_COLORS.PRIMARY_CYAN)\n }`,\n borderRadius: \"8px\",\n color: visible ? hexColorToCSS(KOREAN_COLORS.KOREAN_BLACK) : hexColorToCSS(KOREAN_COLORS.TEXT_PRIMARY),\n fontSize: isMobile ? 13 : 15,\n fontWeight: \"bold\",\n cursor: \"pointer\",\n transition: \"all 0.3s ease\",\n boxShadow: visible\n ? `0 4px 16px ${hexColorToCSS(\n KOREAN_COLORS.ACCENT_GOLD\n )}60, inset 0 2px 4px ${hexToRgbaString(KOREAN_COLORS.TEXT_PRIMARY, 0.2)}`\n : `0 2px 8px ${hexColorToCSS(KOREAN_COLORS.PRIMARY_CYAN)}30`,\n textTransform: \"uppercase\",\n letterSpacing: \"0.5px\",\n }}\n onMouseEnter={(e) => {\n e.currentTarget.style.transform = \"translateY(-2px)\";\n e.currentTarget.style.boxShadow = visible\n ? `0 6px 20px ${hexColorToCSS(KOREAN_COLORS.ACCENT_GOLD)}80`\n : `0 4px 12px ${hexColorToCSS(KOREAN_COLORS.PRIMARY_CYAN)}50`;\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.transform = \"translateY(0)\";\n e.currentTarget.style.boxShadow = visible\n ? `0 4px 16px ${hexColorToCSS(KOREAN_COLORS.ACCENT_GOLD)}60`\n : `0 2px 8px ${hexColorToCSS(KOREAN_COLORS.PRIMARY_CYAN)}30`;\n }}\n data-testid=\"toggle-visibility-button\"\n >\n {visible ? \"✓ 활성화 | Enabled\" : \"비활성화 | Disabled\"}\n </button>\n </div>\n\n {/* Expanded controls */}\n {expanded && visible && (\n <>\n {/* Severity filters */}\n <div style={{ marginBottom: \"14px\" }}>\n <div\n style={{\n fontSize,\n marginBottom: \"8px\",\n color: hexColorToCSS(KOREAN_COLORS.ACCENT_CYAN),\n fontWeight: \"600\",\n textTransform: \"uppercase\",\n letterSpacing: \"1px\",\n }}\n >\n 심각도 필터 | Severity Filter\n </div>\n <div\n style={{\n display: \"flex\",\n flexWrap: \"wrap\",\n gap: \"8px\",\n }}\n >\n {severityOptions.map((severity) => {\n const isActive = severityFilters.includes(severity);\n const severityColor = getSeverityColor(severity);\n return (\n <button\n key={severity}\n onClick={() => toggleSeverityFilter(severity)}\n style={{\n background: isActive\n ? `linear-gradient(135deg, ${severityColor} 0%, ${severityColor}cc 100%)`\n : `${hexColorToCSS(KOREAN_COLORS.UI_BACKGROUND_MEDIUM)}`,\n border: `2px solid ${severityColor}`,\n borderRadius: \"6px\",\n padding: \"6px 12px\",\n color: hexColorToCSS(KOREAN_COLORS.TEXT_PRIMARY),\n fontSize: smallFontSize,\n cursor: \"pointer\",\n opacity: isActive ? 1 : 0.6,\n transition: \"all 0.2s ease\",\n fontWeight: isActive ? \"bold\" : \"normal\",\n boxShadow: isActive\n ? `0 2px 8px ${severityColor}50`\n : \"none\",\n }}\n onMouseEnter={(e) => {\n e.currentTarget.style.opacity = \"1\";\n e.currentTarget.style.transform = \"scale(1.05)\";\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.opacity = isActive ? \"1\" : \"0.6\";\n e.currentTarget.style.transform = \"scale(1)\";\n }}\n data-testid={`severity-filter-${severity}`}\n >\n {severity}\n </button>\n );\n })}\n </div>\n </div>\n\n {/* Region filter */}\n <div style={{ marginBottom: \"14px\" }}>\n <div\n style={{\n fontSize,\n marginBottom: \"8px\",\n color: hexColorToCSS(KOREAN_COLORS.ACCENT_CYAN),\n fontWeight: \"600\",\n textTransform: \"uppercase\",\n letterSpacing: \"1px\",\n }}\n >\n 부위 필터 | Region Filter\n </div>\n <div\n style={{\n display: \"flex\",\n flexWrap: \"wrap\",\n gap: \"8px\",\n }}\n >\n {regionOptions.map((option) => {\n const isActive = regionFilter === option.value;\n return (\n <button\n key={option.value}\n onClick={() => onRegionFilterChange(option.value)}\n style={{\n background: isActive\n ? `linear-gradient(135deg, ${hexColorToCSS(\n KOREAN_COLORS.PRIMARY_CYAN\n )} 0%, ${hexColorToCSS(\n KOREAN_COLORS.ACCENT_BLUE\n )} 100%)`\n : `${hexColorToCSS(KOREAN_COLORS.UI_BACKGROUND_MEDIUM)}`,\n border: `2px solid ${hexColorToCSS(\n KOREAN_COLORS.PRIMARY_CYAN\n )}`,\n borderRadius: \"6px\",\n padding: \"6px 12px\",\n color: hexColorToCSS(KOREAN_COLORS.TEXT_PRIMARY),\n fontSize: smallFontSize,\n cursor: \"pointer\",\n opacity: isActive ? 1 : 0.6,\n transition: \"all 0.2s ease\",\n fontWeight: isActive ? \"bold\" : \"normal\",\n boxShadow: isActive\n ? `0 2px 8px ${hexColorToCSS(\n KOREAN_COLORS.PRIMARY_CYAN\n )}50`\n : \"none\",\n }}\n onMouseEnter={(e) => {\n e.currentTarget.style.opacity = \"1\";\n e.currentTarget.style.transform = \"scale(1.05)\";\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.opacity = isActive ? \"1\" : \"0.6\";\n e.currentTarget.style.transform = \"scale(1)\";\n }}\n data-testid={`region-filter-${option.value}`}\n >\n {option.korean} | {option.label}\n </button>\n );\n })}\n </div>\n </div>\n\n {/* Search box with clear button */}\n <div style={{ marginBottom: \"14px\" }}>\n <div\n style={{\n fontSize,\n marginBottom: \"8px\",\n color: hexColorToCSS(KOREAN_COLORS.ACCENT_CYAN),\n fontWeight: \"600\",\n textTransform: \"uppercase\",\n letterSpacing: \"1px\",\n }}\n >\n 검색 | Search\n </div>\n <div style={{ position: \"relative\" }}>\n <input\n type=\"text\"\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n placeholder=\"급소 이름... | Point name...\"\n style={{\n width: \"100%\",\n height: buttonHeight,\n background: `${hexColorToCSS(\n KOREAN_COLORS.UI_BACKGROUND_MEDIUM\n )}`,\n border: `2px solid ${hexColorToCSS(\n KOREAN_COLORS.PRIMARY_CYAN\n )}40`,\n borderRadius: \"8px\",\n padding: \"0 40px 0 14px\", // Add right padding for clear button\n color: hexColorToCSS(KOREAN_COLORS.TEXT_PRIMARY),\n fontSize,\n fontFamily: FONT_FAMILY.KOREAN,\n transition: \"all 0.2s ease\",\n outline: \"none\",\n }}\n onFocus={(e) => {\n e.currentTarget.style.borderColor = hexColorToCSS(\n KOREAN_COLORS.PRIMARY_CYAN\n );\n e.currentTarget.style.boxShadow = `0 0 12px ${hexColorToCSS(\n KOREAN_COLORS.PRIMARY_CYAN\n )}40`;\n }}\n onBlur={(e) => {\n e.currentTarget.style.borderColor = `${hexColorToCSS(\n KOREAN_COLORS.PRIMARY_CYAN\n )}40`;\n e.currentTarget.style.boxShadow = \"none\";\n }}\n data-testid=\"search-input\"\n />\n {/* Clear button */}\n {searchQuery && (\n <button\n onClick={() => setSearchQuery(\"\")}\n style={{\n position: \"absolute\",\n right: \"8px\",\n top: \"50%\",\n transform: \"translateY(-50%)\",\n background: \"transparent\",\n border: \"none\",\n color: hexColorToCSS(KOREAN_COLORS.TEXT_SECONDARY),\n cursor: \"pointer\",\n fontSize: \"16px\",\n padding: \"4px\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n borderRadius: \"4px\",\n transition: \"all 0.2s ease\",\n }}\n onMouseEnter={(e) => {\n e.currentTarget.style.color = hexColorToCSS(\n KOREAN_COLORS.ACCENT_RED\n );\n e.currentTarget.style.background = `${hexColorToCSS(\n KOREAN_COLORS.UI_BACKGROUND_MEDIUM\n )}`;\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.color = hexColorToCSS(\n KOREAN_COLORS.TEXT_SECONDARY\n );\n e.currentTarget.style.background = \"transparent\";\n }}\n data-testid=\"search-clear-button\"\n title=\"Clear search\"\n >\n ✕\n </button>\n )}\n </div>\n </div>\n\n {/* Display options */}\n <div style={{ marginBottom: \"14px\" }}>\n <div\n style={{\n fontSize,\n marginBottom: \"8px\",\n color: hexColorToCSS(KOREAN_COLORS.ACCENT_CYAN),\n fontWeight: \"600\",\n textTransform: \"uppercase\",\n letterSpacing: \"1px\",\n }}\n >\n 표시 옵션 | Display Options\n </div>\n <div\n style={{ display: \"flex\", flexDirection: \"column\", gap: \"8px\" }}\n >\n <label\n style={{\n display: \"flex\",\n alignItems: \"center\",\n gap: \"8px\",\n cursor: \"pointer\",\n }}\n >\n <input\n type=\"checkbox\"\n checked={showLabels}\n onChange={(e) => onShowLabelsChange(e.target.checked)}\n style={{\n width: \"16px\",\n height: \"16px\",\n accentColor: hexColorToCSS(KOREAN_COLORS.PRIMARY_CYAN),\n }}\n data-testid=\"show-labels-checkbox\"\n />\n <span style={{ fontSize }}>라벨 표시 | Show Labels</span>\n </label>\n <label\n style={{\n display: \"flex\",\n alignItems: \"center\",\n gap: \"8px\",\n cursor: \"pointer\",\n }}\n >\n <input\n type=\"checkbox\"\n checked={animated}\n onChange={(e) => onAnimatedChange(e.target.checked)}\n style={{\n width: \"16px\",\n height: \"16px\",\n accentColor: hexColorToCSS(KOREAN_COLORS.PRIMARY_CYAN),\n }}\n data-testid=\"animated-checkbox\"\n />\n <span style={{ fontSize }}>애니메이션 | Animations</span>\n </label>\n </div>\n </div>\n\n {/* Reset filters button */}\n {(severityFilters.length > 0 ||\n regionFilter !== \"all\" ||\n searchQuery !== \"\") && (\n <div style={{ marginBottom: \"14px\" }}>\n <button\n onClick={() => {\n onSeverityFiltersChange([]);\n onRegionFilterChange(\"all\");\n setSearchQuery(\"\");\n }}\n style={{\n width: \"100%\",\n height: buttonHeight - 4,\n background: `${hexColorToCSS(\n KOREAN_COLORS.UI_BACKGROUND_MEDIUM\n )}`,\n border: `2px solid ${hexColorToCSS(\n KOREAN_COLORS.ACCENT_ORANGE\n )}`,\n borderRadius: \"6px\",\n color: hexColorToCSS(KOREAN_COLORS.ACCENT_ORANGE),\n fontSize: smallFontSize,\n cursor: \"pointer\",\n transition: \"all 0.2s ease\",\n fontWeight: \"bold\",\n }}\n onMouseEnter={(e) => {\n e.currentTarget.style.background = `${hexColorToCSS(\n KOREAN_COLORS.ACCENT_ORANGE\n )}20`;\n e.currentTarget.style.transform = \"translateY(-1px)\";\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.background = `${hexColorToCSS(\n KOREAN_COLORS.UI_BACKGROUND_MEDIUM\n )}`;\n e.currentTarget.style.transform = \"translateY(0)\";\n }}\n data-testid=\"reset-filters-button\"\n >\n 🔄 필터 초기화 | Reset Filters\n </button>\n </div>\n )}\n\n {/* Scale slider */}\n <div>\n <div\n style={{\n fontSize,\n marginBottom: \"6px\",\n color: hexColorToCSS(KOREAN_COLORS.ACCENT_CYAN),\n }}\n >\n 크기 | Scale: {scale.toFixed(1)}x\n </div>\n <input\n type=\"range\"\n min=\"0.5\"\n max=\"2.0\"\n step=\"0.1\"\n value={scale}\n onChange={(e) => onScaleChange(parseFloat(e.target.value))}\n style={{\n width: \"100%\",\n accentColor: hexColorToCSS(KOREAN_COLORS.PRIMARY_CYAN),\n }}\n data-testid=\"scale-slider\"\n />\n </div>\n\n {/* Statistics */}\n <div\n style={{\n marginTop: \"12px\",\n paddingTop: \"12px\",\n borderTop: `1px solid ${hexColorToCSS(\n KOREAN_COLORS.PRIMARY_CYAN\n )}44`,\n fontSize: smallFontSize,\n color: hexColorToCSS(KOREAN_COLORS.TEXT_SECONDARY),\n }}\n >\n <div>\n 머리: {stats.byRegion.head} | 몸통: {stats.byRegion.torso}\n </div>\n <div>\n 팔: {stats.byRegion.arms} | 다리: {stats.byRegion.legs}\n </div>\n </div>\n </>\n )}\n </div>\n </Html>\n );\n};\n\nexport default VitalPointOverlayControlsHtml;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsFA,IAAM,oBAAoB,aAAyC;AACjE,SAAQ,UAAR;EACE,KAAK,mBAAmB,OACtB,QAAO,cAAc,cAAc,aAAa;EAClD,KAAK,mBAAmB,SACtB,QAAO,cAAc,cAAc,WAAW;EAChD,KAAK,mBAAmB,MACtB,QAAO,cAAc,cAAc,YAAY;EACjD,KAAK,mBAAmB,SACtB,QAAO,cAAc,cAAc,qBAAqB;EAC1D,KAAK,mBAAmB,MACtB,QAAO,cAAc,cAAc,eAAe;EACpD,QACE,QAAO,cAAc,cAAc,UAAU;;;;;;;AAQnD,IAAa,iCAER,EACH,SACA,iBACA,iBACA,yBACA,cACA,sBACA,aAAa,qBACb,qBACA,YACA,oBACA,UACA,kBACA,OACA,eACA,WAAW,OACX,qBACI;CACJ,MAAM,CAAC,UAAU,eAAe,SAAS,MAAM;CAG/C,MAAM,CAAC,qBAAqB,0BAA0B,SAAS,GAAG;CAClE,MAAM,cAAc,uBAAuB;CAC3C,MAAM,iBAAiB,uBAAuB;CAG9C,MAAM,QAAQ,cAAc,qBAAqB,EAAE,EAAE,CAAC;CAGtD,MAAM,kBAKF,eACK;EACL,KAAK,WAAW,UAAU;EAC1B,MAAM,WAAW,SAAS;EAC3B,GACD,CAAC,SAAS,CACX;CAED,MAAM,gBAAgB,kBAAkB;CAGxC,MAAM,gBAAgB,cAAc;EAClC,IAAI,SAAS,CAAC,GAAG,oBAAoB;AAGrC,MAAI,gBAAgB,SAAS,EAC3B,UAAS,OAAO,QAAQ,OAAO,gBAAgB,SAAS,GAAG,SAAS,CAAC;AAIvE,MAAI,iBAAiB,MACnB,KAAI,iBAAiB,OAEnB,UAAS,OAAO,QACb,OACC,GAAG,GAAG,WAAW,YAAY,IAAI,GAAG,GAAG,WAAW,aAAa,CAClE;WACQ,iBAAiB,OAE1B,UAAS,OAAO,QACb,OACC,GAAG,GAAG,WAAW,YAAY,IAAI,GAAG,GAAG,WAAW,aAAa,CAClE;OACI;GAEL,MAAM,SAAS,GAAG,aAAa;AAC/B,YAAS,OAAO,QAAQ,OAAO,GAAG,GAAG,WAAW,OAAO,CAAC;;AAK5D,MAAI,aAAa;GACf,MAAM,QAAQ,YAAY,aAAa;AACvC,YAAS,OAAO,QACb,OACC,GAAG,MAAM,OAAO,aAAa,CAAC,SAAS,MAAM,IAC7C,GAAG,MAAM,QAAQ,aAAa,CAAC,SAAS,MAAM,IAC9C,GAAG,MAAM,UAAU,aAAa,CAAC,SAAS,MAAM,IAChD,GAAG,GAAG,aAAa,CAAC,SAAS,MAAM,CACtC;;AAGH,SAAO,OAAO;IACb;EAAC;EAAiB;EAAc;EAAY,CAAC;CAGhD,MAAM,uBAAuB,aAC1B,aAAiC;AAIhC,0BAHmB,gBAAgB,SAAS,SAAS,GACjD,gBAAgB,QAAQ,MAAM,MAAM,SAAS,GAC7C,CAAC,GAAG,iBAAiB,SAAS,CACC;IAErC,CAAC,iBAAiB,wBAAwB,CAC3C;CAGD,MAAM,kBAAwC;EAC5C,mBAAmB;EACnB,mBAAmB;EACnB,mBAAmB;EACnB,mBAAmB;EACnB,mBAAmB;EACpB;CAGD,MAAM,gBAIA;EACJ;GAAE,OAAO;GAAO,OAAO;GAAe,QAAQ;GAAM;EACpD;GAAE,OAAO;GAAQ,OAAO;GAAQ,QAAQ;GAAM;EAC9C;GAAE,OAAO;GAAS,OAAO;GAAS,QAAQ;GAAM;EAChD;GAAE,OAAO;GAAQ,OAAO;GAAQ,QAAQ;GAAK;EAC7C;GAAE,OAAO;GAAQ,OAAO;GAAQ,QAAQ;GAAM;EAC/C;CAGD,MAAM,aAAa,WAAW,MAAM;CACpC,MAAM,eAAe,WAAW,KAAK;CACrC,MAAM,WAAW,WAAW,KAAK;CACjC,MAAM,gBAAgB,WAAW,IAAI;AAErC,QACE,oBAAC,MAAD;EAAM,YAAA;EAAW,OAAO,EAAE,eAAe,QAAQ;YAC/C,qBAAC,OAAD;GACE,OAAO;IACL,UAAU;IACV,KAAK,cAAc;IACnB,MAAM,cAAc;IACpB,OAAO,cAAc;IACrB,QAAQ,cAAc;IACtB,OAAO;IACP,YAAY,GAAG,cAAc,cAAc,mBAAmB,CAAC;IAC/D,QAAQ,aAAa,cAAc,cAAc,aAAa;IAC9D,cAAc;IACd,SAAS,WAAW,SAAS;IAC7B,YAAY,YAAY;IACxB,OAAO,cAAc,cAAc,aAAa;IAChD,WAAW,YAAY,cACrB,cAAc,aACf,CAAC,qBAAqB,cACrB,cAAc,mBACf,CAAC;IACF,YAAY;IACZ,eAAe;IACf,QAAQ;IACT;GACD,eAAY;aAvBd;IA0BE,qBAAC,OAAD;KACE,OAAO;MACL,SAAS;MACT,gBAAgB;MAChB,YAAY;MACZ,cAAc;MACd,eAAe;MACf,cAAc,aAAa,cACzB,cAAc,aACf,CAAC;MACF,YAAY,0BAA0B,cACpC,cAAc,mBACf,CAAC,SAAS,cACT,cAAc,aACf,CAAC,UAAU,cAAc,cAAc,mBAAmB,CAAC;MAC7D;eAfH,CAiBE,qBAAC,OAAD,EAAA,UAAA,CACE,oBAAC,OAAD;MAAK,OAAO;OAAE,UAAU,WAAW,KAAK;OAAI,YAAY;OAAQ;gBAAE;MAE5D,CAAA,EACN,qBAAC,OAAD;MACE,OAAO;OACL,UAAU;OACV,OAAO,cAAc,cAAc,eAAe;OAClD,WAAW;OACZ;gBALH;OAOG;OAAc;OAAI,MAAM;OAAM;OAC3B;QACF,EAAA,CAAA,EACN,oBAAC,UAAD;MACE,eAAe,YAAY,CAAC,SAAS;MACrC,OAAO;OACL,YAAY,2BAA2B,cACrC,cAAc,qBACf,CAAC,OAAO,cAAc,cAAc,mBAAmB,CAAC;OACzD,QAAQ,aAAa,cAAc,cAAc,aAAa;OAC9D,cAAc;OACd,SAAS;OACT,OAAO,cAAc,cAAc,aAAa;OAChD;OACA,QAAQ;OACR,YAAY;OACZ,WAAW,aAAa,cACtB,cAAc,aACf,CAAC;OACH;MACD,eAAe,MAAM;AACnB,SAAE,cAAc,MAAM,YAAY;AAClC,SAAE,cAAc,MAAM,YAAY,cAAc,cAC9C,cAAc,aACf,CAAC;;MAEJ,eAAe,MAAM;AACnB,SAAE,cAAc,MAAM,YAAY;AAClC,SAAE,cAAc,MAAM,YAAY,aAAa,cAC7C,cAAc,aACf,CAAC;;MAEJ,eAAY;gBAEX,WAAW,MAAM;MACX,CAAA,CACL;;IAGN,oBAAC,OAAD;KAAK,OAAO,EAAE,cAAc,QAAQ;eAClC,oBAAC,UAAD;MACE,eAAe,gBAAgB,CAAC,QAAQ;MACxC,OAAO;OACL,OAAO;OACP,QAAQ;OACR,YAAY,UACR,2BAA2B,cACzB,cAAc,YACf,CAAC,OAAO,cAAc,cAAc,iBAAiB,CAAC,UACvD,2BAA2B,cACzB,cAAc,qBACf,CAAC,OAAO,cAAc,cAAc,mBAAmB,CAAC;OAC7D,QAAQ,aACN,UACI,cAAc,cAAc,YAAY,GACxC,cAAc,cAAc,aAAa;OAE/C,cAAc;OACd,OAAO,UAAU,cAAc,cAAc,aAAa,GAAG,cAAc,cAAc,aAAa;OACtG,UAAU,WAAW,KAAK;OAC1B,YAAY;OACZ,QAAQ;OACR,YAAY;OACZ,WAAW,UACP,cAAc,cACZ,cAAc,YACf,CAAC,sBAAsB,gBAAgB,cAAc,cAAc,GAAI,KACxE,aAAa,cAAc,cAAc,aAAa,CAAC;OAC3D,eAAe;OACf,eAAe;OAChB;MACD,eAAe,MAAM;AACnB,SAAE,cAAc,MAAM,YAAY;AAClC,SAAE,cAAc,MAAM,YAAY,UAC9B,cAAc,cAAc,cAAc,YAAY,CAAC,MACvD,cAAc,cAAc,cAAc,aAAa,CAAC;;MAE9D,eAAe,MAAM;AACnB,SAAE,cAAc,MAAM,YAAY;AAClC,SAAE,cAAc,MAAM,YAAY,UAC9B,cAAc,cAAc,cAAc,YAAY,CAAC,MACvD,aAAa,cAAc,cAAc,aAAa,CAAC;;MAE7D,eAAY;gBAEX,UAAU,oBAAoB;MACxB,CAAA;KACL,CAAA;IAGL,YAAY,WACX,qBAAA,UAAA,EAAA,UAAA;KAEE,qBAAC,OAAD;MAAK,OAAO,EAAE,cAAc,QAAQ;gBAApC,CACE,oBAAC,OAAD;OACE,OAAO;QACL;QACA,cAAc;QACd,OAAO,cAAc,cAAc,YAAY;QAC/C,YAAY;QACZ,eAAe;QACf,eAAe;QAChB;iBACF;OAEK,CAAA,EACN,oBAAC,OAAD;OACE,OAAO;QACL,SAAS;QACT,UAAU;QACV,KAAK;QACN;iBAEA,gBAAgB,KAAK,aAAa;QACjC,MAAM,WAAW,gBAAgB,SAAS,SAAS;QACnD,MAAM,gBAAgB,iBAAiB,SAAS;AAChD,eACE,oBAAC,UAAD;SAEE,eAAe,qBAAqB,SAAS;SAC7C,OAAO;UACL,YAAY,WACR,2BAA2B,cAAc,OAAO,cAAc,YAC9D,GAAG,cAAc,cAAc,qBAAqB;UACxD,QAAQ,aAAa;UACrB,cAAc;UACd,SAAS;UACT,OAAO,cAAc,cAAc,aAAa;UAChD,UAAU;UACV,QAAQ;UACR,SAAS,WAAW,IAAI;UACxB,YAAY;UACZ,YAAY,WAAW,SAAS;UAChC,WAAW,WACP,aAAa,cAAc,MAC3B;UACL;SACD,eAAe,MAAM;AACnB,YAAE,cAAc,MAAM,UAAU;AAChC,YAAE,cAAc,MAAM,YAAY;;SAEpC,eAAe,MAAM;AACnB,YAAE,cAAc,MAAM,UAAU,WAAW,MAAM;AACjD,YAAE,cAAc,MAAM,YAAY;;SAEpC,eAAa,mBAAmB;mBAE/B;SACM,EA9BF,SA8BE;SAEX;OACE,CAAA,CACF;;KAGN,qBAAC,OAAD;MAAK,OAAO,EAAE,cAAc,QAAQ;gBAApC,CACE,oBAAC,OAAD;OACE,OAAO;QACL;QACA,cAAc;QACd,OAAO,cAAc,cAAc,YAAY;QAC/C,YAAY;QACZ,eAAe;QACf,eAAe;QAChB;iBACF;OAEK,CAAA,EACN,oBAAC,OAAD;OACE,OAAO;QACL,SAAS;QACT,UAAU;QACV,KAAK;QACN;iBAEA,cAAc,KAAK,WAAW;QAC7B,MAAM,WAAW,iBAAiB,OAAO;AACzC,eACE,qBAAC,UAAD;SAEE,eAAe,qBAAqB,OAAO,MAAM;SACjD,OAAO;UACL,YAAY,WACR,2BAA2B,cACzB,cAAc,aACf,CAAC,OAAO,cACP,cAAc,YACf,CAAC,UACF,GAAG,cAAc,cAAc,qBAAqB;UACxD,QAAQ,aAAa,cACnB,cAAc,aACf;UACD,cAAc;UACd,SAAS;UACT,OAAO,cAAc,cAAc,aAAa;UAChD,UAAU;UACV,QAAQ;UACR,SAAS,WAAW,IAAI;UACxB,YAAY;UACZ,YAAY,WAAW,SAAS;UAChC,WAAW,WACP,aAAa,cACX,cAAc,aACf,CAAC,MACF;UACL;SACD,eAAe,MAAM;AACnB,YAAE,cAAc,MAAM,UAAU;AAChC,YAAE,cAAc,MAAM,YAAY;;SAEpC,eAAe,MAAM;AACnB,YAAE,cAAc,MAAM,UAAU,WAAW,MAAM;AACjD,YAAE,cAAc,MAAM,YAAY;;SAEpC,eAAa,iBAAiB,OAAO;mBApCvC;UAsCG,OAAO;UAAO;UAAI,OAAO;UACnB;WAtCF,OAAO,MAsCL;SAEX;OACE,CAAA,CACF;;KAGN,qBAAC,OAAD;MAAK,OAAO,EAAE,cAAc,QAAQ;gBAApC,CACE,oBAAC,OAAD;OACE,OAAO;QACL;QACA,cAAc;QACd,OAAO,cAAc,cAAc,YAAY;QAC/C,YAAY;QACZ,eAAe;QACf,eAAe;QAChB;iBACF;OAEK,CAAA,EACN,qBAAC,OAAD;OAAK,OAAO,EAAE,UAAU,YAAY;iBAApC,CACE,oBAAC,SAAD;QACE,MAAK;QACL,OAAO;QACP,WAAW,MAAM,eAAe,EAAE,OAAO,MAAM;QAC/C,aAAY;QACZ,OAAO;SACL,OAAO;SACP,QAAQ;SACR,YAAY,GAAG,cACb,cAAc,qBACf;SACD,QAAQ,aAAa,cACnB,cAAc,aACf,CAAC;SACF,cAAc;SACd,SAAS;SACT,OAAO,cAAc,cAAc,aAAa;SAChD;SACA,YAAY,YAAY;SACxB,YAAY;SACZ,SAAS;SACV;QACD,UAAU,MAAM;AACd,WAAE,cAAc,MAAM,cAAc,cAClC,cAAc,aACf;AACD,WAAE,cAAc,MAAM,YAAY,YAAY,cAC5C,cAAc,aACf,CAAC;;QAEJ,SAAS,MAAM;AACb,WAAE,cAAc,MAAM,cAAc,GAAG,cACrC,cAAc,aACf,CAAC;AACF,WAAE,cAAc,MAAM,YAAY;;QAEpC,eAAY;QACZ,CAAA,EAED,eACC,oBAAC,UAAD;QACE,eAAe,eAAe,GAAG;QACjC,OAAO;SACL,UAAU;SACV,OAAO;SACP,KAAK;SACL,WAAW;SACX,YAAY;SACZ,QAAQ;SACR,OAAO,cAAc,cAAc,eAAe;SAClD,QAAQ;SACR,UAAU;SACV,SAAS;SACT,SAAS;SACT,YAAY;SACZ,gBAAgB;SAChB,cAAc;SACd,YAAY;SACb;QACD,eAAe,MAAM;AACnB,WAAE,cAAc,MAAM,QAAQ,cAC5B,cAAc,WACf;AACD,WAAE,cAAc,MAAM,aAAa,GAAG,cACpC,cAAc,qBACf;;QAEH,eAAe,MAAM;AACnB,WAAE,cAAc,MAAM,QAAQ,cAC5B,cAAc,eACf;AACD,WAAE,cAAc,MAAM,aAAa;;QAErC,eAAY;QACZ,OAAM;kBACP;QAEQ,CAAA,CAEP;SACF;;KAGN,qBAAC,OAAD;MAAK,OAAO,EAAE,cAAc,QAAQ;gBAApC,CACE,oBAAC,OAAD;OACE,OAAO;QACL;QACA,cAAc;QACd,OAAO,cAAc,cAAc,YAAY;QAC/C,YAAY;QACZ,eAAe;QACf,eAAe;QAChB;iBACF;OAEK,CAAA,EACN,qBAAC,OAAD;OACE,OAAO;QAAE,SAAS;QAAQ,eAAe;QAAU,KAAK;QAAO;iBADjE,CAGE,qBAAC,SAAD;QACE,OAAO;SACL,SAAS;SACT,YAAY;SACZ,KAAK;SACL,QAAQ;SACT;kBANH,CAQE,oBAAC,SAAD;SACE,MAAK;SACL,SAAS;SACT,WAAW,MAAM,mBAAmB,EAAE,OAAO,QAAQ;SACrD,OAAO;UACL,OAAO;UACP,QAAQ;UACR,aAAa,cAAc,cAAc,aAAa;UACvD;SACD,eAAY;SACZ,CAAA,EACF,oBAAC,QAAD;SAAM,OAAO,EAAE,UAAU;mBAAE;SAA0B,CAAA,CAC/C;WACR,qBAAC,SAAD;QACE,OAAO;SACL,SAAS;SACT,YAAY;SACZ,KAAK;SACL,QAAQ;SACT;kBANH,CAQE,oBAAC,SAAD;SACE,MAAK;SACL,SAAS;SACT,WAAW,MAAM,iBAAiB,EAAE,OAAO,QAAQ;SACnD,OAAO;UACL,OAAO;UACP,QAAQ;UACR,aAAa,cAAc,cAAc,aAAa;UACvD;SACD,eAAY;SACZ,CAAA,EACF,oBAAC,QAAD;SAAM,OAAO,EAAE,UAAU;mBAAE;SAAyB,CAAA,CAC9C;UACJ;SACF;;MAGJ,gBAAgB,SAAS,KACzB,iBAAiB,SACjB,gBAAgB,OAChB,oBAAC,OAAD;MAAK,OAAO,EAAE,cAAc,QAAQ;gBAClC,oBAAC,UAAD;OACE,eAAe;AACb,gCAAwB,EAAE,CAAC;AAC3B,6BAAqB,MAAM;AAC3B,uBAAe,GAAG;;OAEpB,OAAO;QACL,OAAO;QACP,QAAQ,eAAe;QACvB,YAAY,GAAG,cACb,cAAc,qBACf;QACD,QAAQ,aAAa,cACnB,cAAc,cACf;QACD,cAAc;QACd,OAAO,cAAc,cAAc,cAAc;QACjD,UAAU;QACV,QAAQ;QACR,YAAY;QACZ,YAAY;QACb;OACD,eAAe,MAAM;AACnB,UAAE,cAAc,MAAM,aAAa,GAAG,cACpC,cAAc,cACf,CAAC;AACF,UAAE,cAAc,MAAM,YAAY;;OAEpC,eAAe,MAAM;AACnB,UAAE,cAAc,MAAM,aAAa,GAAG,cACpC,cAAc,qBACf;AACD,UAAE,cAAc,MAAM,YAAY;;OAEpC,eAAY;iBACb;OAEQ,CAAA;MACL,CAAA;KAIR,qBAAC,OAAD,EAAA,UAAA,CACE,qBAAC,OAAD;MACE,OAAO;OACL;OACA,cAAc;OACd,OAAO,cAAc,cAAc,YAAY;OAChD;gBALH;OAMC;OACc,MAAM,QAAQ,EAAE;OAAC;OAC1B;SACN,oBAAC,SAAD;MACE,MAAK;MACL,KAAI;MACJ,KAAI;MACJ,MAAK;MACL,OAAO;MACP,WAAW,MAAM,cAAc,WAAW,EAAE,OAAO,MAAM,CAAC;MAC1D,OAAO;OACL,OAAO;OACP,aAAa,cAAc,cAAc,aAAa;OACvD;MACD,eAAY;MACZ,CAAA,CACE,EAAA,CAAA;KAGN,qBAAC,OAAD;MACE,OAAO;OACL,WAAW;OACX,YAAY;OACZ,WAAW,aAAa,cACtB,cAAc,aACf,CAAC;OACF,UAAU;OACV,OAAO,cAAc,cAAc,eAAe;OACnD;gBATH,CAWE,qBAAC,OAAD,EAAA,UAAA;OAAK;OACE,MAAM,SAAS;OAAK;OAAQ,MAAM,SAAS;OAC5C,EAAA,CAAA,EACN,qBAAC,OAAD,EAAA,UAAA;OAAK;OACC,MAAM,SAAS;OAAK;OAAQ,MAAM,SAAS;OAC3C,EAAA,CAAA,CACF;;KACL,EAAA,CAAA;IAED;;EACD,CAAA"}
@@ -10,6 +10,46 @@
10
10
  * - Pointer events handling
11
11
  * - Backdrop blur effects
12
12
  *
13
+ * ## Z-Index Stacking Order (Combat HUD Layers)
14
+ *
15
+ * The combat screen renders multiple overlapping HUD panels. The stacking
16
+ * order is managed by the Z_INDEX constants from LayoutTypes.ts:
17
+ *
18
+ * | Layer | Z-Index | Description |
19
+ * |---------------------|---------|------------------------------------|
20
+ * | BACKGROUND | 0 | 3D scene background |
21
+ * | ARENA | 10 | Combat arena mesh |
22
+ * | PLAYERS | 20 | Character models |
23
+ * | EFFECTS | 30 | Particles and VFX |
24
+ * | HUD_BACKGROUND | 40 | HUD panel backgrounds |
25
+ * | HUD (default) | 50 | Left/Right/Top/Bottom HUD panels |
26
+ * | TECHNIQUE_BAR | 55 | Technique bar overlay |
27
+ * | HUD_OVERLAY | 60 | PlayerStateOverlay and sub-HUDs |
28
+ * | MOBILE_CONTROLS | 100 | Touch controls on mobile |
29
+ * | MODAL | 200 | Modal dialogs |
30
+ * | TOOLTIP | 300 | Tooltips and hints |
31
+ * | PAUSE_MENU | 1000 | Pause menu overlay |
32
+ * | LOADING | 2000 | Loading screens |
33
+ * | DEBUG | 9000 | Performance debug overlay |
34
+ *
35
+ * All BaseHUDContainer instances default to Z_INDEX.HUD (50). Parent
36
+ * screens can override via the `zIndex` prop when a different layer
37
+ * is needed (e.g., overlays at HUD_OVERLAY = 60).
38
+ *
39
+ * ## Viewport Breakpoints (Expected HUD Sizes)
40
+ *
41
+ * | Viewport | Width | Left/Right HUD | Top HUD | Bottom HUD |
42
+ * |---------------------|----------|----------------|---------|------------|
43
+ * | Small Phone (≤375) | ≤375px | ~120-150px | ~50px | ~90px |
44
+ * | Mobile (<768) | <768px | ~180-200px | ~60px | ~110px |
45
+ * | Tablet (768-1199) | 768-1199 | ~220-260px | ~65px | ~120px |
46
+ * | Desktop (≥1200) | ≥1200px | ~260-300px | ~70px | ~130px |
47
+ * | 4K (≥1920) | ≥1920px | ~300-400px | ~80px | ~140px |
48
+ *
49
+ * Width/height values are passed by the parent screen and scaled via
50
+ * positionScale multipliers. This table documents the expected ranges
51
+ * produced by CombatScreen3D and TrainingScreen3D layout calculations.
52
+ *
13
53
  * @module components/shared/ui
14
54
  * @korean 기본HUD컨테이너 - 공통 패턴을 가진 재사용 가능한 HUD 컨테이너
15
55
  */
@@ -1 +1 @@
1
- {"version":3,"file":"BaseHUDContainer.d.ts","sourceRoot":"","sources":["../../../../src/components/shared/ui/BaseHUDContainer.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAK1B;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,GAAG,QAAQ,CAAC;AAE9D;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,qDAAqD;IACrD,QAAQ,CAAC,QAAQ,EAAE,WAAW,CAAC;IAC/B,iFAAiF;IACjF,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,uBAAuB;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,iDAAiD;IACjD,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,iCAAiC;IACjC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,qCAAqC;IACrC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,2BAA2B;IAC3B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,4BAA4B;IAC5B,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IACrC,qBAAqB;IACrB,QAAQ,CAAC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IACnC,0BAA0B;IAC1B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,gBAAgB,EAAE,KAAK,CAAC,EAAE,CAAC,qBAAqB,CAwF5D,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
1
+ {"version":3,"file":"BaseHUDContainer.d.ts","sourceRoot":"","sources":["../../../../src/components/shared/ui/BaseHUDContainer.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsDG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAK1B;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,GAAG,QAAQ,CAAC;AAE9D;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,qDAAqD;IACrD,QAAQ,CAAC,QAAQ,EAAE,WAAW,CAAC;IAC/B,iFAAiF;IACjF,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,uBAAuB;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,iDAAiD;IACjD,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,iCAAiC;IACjC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,qCAAqC;IACrC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,2BAA2B;IAC3B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,4BAA4B;IAC5B,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IACrC,qBAAqB;IACrB,QAAQ,CAAC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IACnC,0BAA0B;IAC1B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,gBAAgB,EAAE,KAAK,CAAC,EAAE,CAAC,qBAAqB,CAyG5D,CAAC;AAEF,eAAe,gBAAgB,CAAC"}