@ihabdevteam/core 0.128.0 → 0.130.0

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 (115) hide show
  1. package/CHANGELOG.md +77 -0
  2. package/dist/components.cjs.js +177 -177
  3. package/dist/components.esm.css +2 -2
  4. package/dist/hooks.cjs.js +1 -1
  5. package/dist/i18n.cjs.js +3 -3
  6. package/dist/ihabdevteam-core-tokens.css +1 -1
  7. package/dist/ihabdevteam-core.cjs.js +182 -182
  8. package/dist/ihabdevteam-core.css +1 -1
  9. package/dist/ihabdevteam-core.d.ts +268 -66
  10. package/dist/packages/core/src/components/AudioMeter.js +29 -15
  11. package/dist/packages/core/src/components/Avatar.js +6 -2
  12. package/dist/packages/core/src/components/Badge.d.ts +5 -1
  13. package/dist/packages/core/src/components/Badge.js +19 -5
  14. package/dist/packages/core/src/components/BulkBar.d.ts +2 -1
  15. package/dist/packages/core/src/components/BulkBar.js +9 -2
  16. package/dist/packages/core/src/components/Calendar.js +1 -1
  17. package/dist/packages/core/src/components/ColorPicker.d.ts +3 -1
  18. package/dist/packages/core/src/components/ColorPicker.js +4 -2
  19. package/dist/packages/core/src/components/DatePicker.d.ts +2 -1
  20. package/dist/packages/core/src/components/DatePicker.js +9 -4
  21. package/dist/packages/core/src/components/Effect/EffectPartyPopper.d.ts +9 -0
  22. package/dist/packages/core/src/components/Effect/EffectPartyPopper.js +30 -25
  23. package/dist/packages/core/src/components/EmptyState.js +3 -1
  24. package/dist/packages/core/src/components/ErrorState.js +3 -1
  25. package/dist/packages/core/src/components/FilterPanel.js +7 -0
  26. package/dist/packages/core/src/components/Header.d.ts +6 -0
  27. package/dist/packages/core/src/components/Header.js +25 -1
  28. package/dist/packages/core/src/components/Icon.css +22 -0
  29. package/dist/packages/core/src/components/Icon.d.ts +7 -3
  30. package/dist/packages/core/src/components/Icon.js +7 -1
  31. package/dist/packages/core/src/components/IconGridMenu.css +4 -2
  32. package/dist/packages/core/src/components/IconGridMenu.d.ts +1 -1
  33. package/dist/packages/core/src/components/IconGridMenu.js +6 -3
  34. package/dist/packages/core/src/components/LandingHeader.js +6 -3
  35. package/dist/packages/core/src/components/Loader.d.ts +1 -1
  36. package/dist/packages/core/src/components/Loader.js +7 -2
  37. package/dist/packages/core/src/components/NoticeBox.d.ts +5 -0
  38. package/dist/packages/core/src/components/NoticeBox.js +3 -1
  39. package/dist/packages/core/src/components/Pagination.d.ts +2 -2
  40. package/dist/packages/core/src/components/Pagination.js +2 -2
  41. package/dist/packages/core/src/components/PanelRow.js +5 -1
  42. package/dist/packages/core/src/components/PassCard/PassCard.d.ts +10 -1
  43. package/dist/packages/core/src/components/PaymentPlan.d.ts +8 -1
  44. package/dist/packages/core/src/components/Popup/ConfirmPopup.d.ts +1 -1
  45. package/dist/packages/core/src/components/Popup/ListPopup.d.ts +8 -1
  46. package/dist/packages/core/src/components/Popup/ListPopup.js +17 -2
  47. package/dist/packages/core/src/components/Popup/Popup.css +1 -1
  48. package/dist/packages/core/src/components/Popup/Popup.d.ts +1 -1
  49. package/dist/packages/core/src/components/Popup/TrainingResultPopup.js +3 -2
  50. package/dist/packages/core/src/components/Popup/WordDataUpdatePopup.d.ts +4 -1
  51. package/dist/packages/core/src/components/Popup/WordDataUpdatePopup.js +8 -3
  52. package/dist/packages/core/src/components/Popup/customerDetailUtils.d.ts +7 -2
  53. package/dist/packages/core/src/components/Popup/customerDetailUtils.js +20 -3
  54. package/dist/packages/core/src/components/ProgressIndicator.d.ts +2 -5
  55. package/dist/packages/core/src/components/PromoBanner.js +23 -6
  56. package/dist/packages/core/src/components/RadioGroup.d.ts +3 -1
  57. package/dist/packages/core/src/components/RadioGroup.js +2 -2
  58. package/dist/packages/core/src/components/Report/ReportDocument.d.ts +4 -1
  59. package/dist/packages/core/src/components/Report/ReportDocument.js +4 -1
  60. package/dist/packages/core/src/components/Section.d.ts +7 -1
  61. package/dist/packages/core/src/components/Section.js +6 -2
  62. package/dist/packages/core/src/components/Table.d.ts +1 -1
  63. package/dist/packages/core/src/components/TextField.css +2 -1
  64. package/dist/packages/core/src/components/TextField.js +10 -2
  65. package/dist/packages/core/src/components/Toggle.js +4 -1
  66. package/dist/packages/core/src/components/Tooltip/DebugTooltip.d.ts +1 -6
  67. package/dist/packages/core/src/components/Tooltip/DebugTooltip.js +4 -7
  68. package/dist/packages/core/src/components/Tooltip/Tooltip.js +11 -1
  69. package/dist/packages/core/src/components/Training/AudioPlayerWaveForm.js +3 -3
  70. package/dist/packages/core/src/components/Training/TrainingBlock.d.ts +8 -3
  71. package/dist/packages/core/src/components/Training/TrainingStepConfirmWithText.js +10 -11
  72. package/dist/packages/core/src/components/Training/trainingBlockButtonTypes.d.ts +4 -4
  73. package/dist/packages/core/src/components/Training/trainingScenario.d.ts +6 -2
  74. package/dist/packages/core/src/components/Word/WordSearchResultBlock.js +3 -1
  75. package/dist/packages/core/src/components/Word/WordTableBlock.js +6 -0
  76. package/dist/packages/core/src/components/Word/WordTableButton.d.ts +2 -2
  77. package/dist/packages/core/src/components/Word/WordTableButton.js +1 -1
  78. package/dist/packages/core/src/components/Word/wordTableBlockTypes.d.ts +1 -1
  79. package/dist/packages/core/src/components/Word/wordTableScoring.d.ts +1 -4
  80. package/dist/packages/core/src/components/Word/wordTableScoring.js +1 -4
  81. package/dist/packages/core/src/components/WordTable.d.ts +1 -1
  82. package/dist/packages/core/src/components/WordTable.js +3 -1
  83. package/dist/packages/core/src/components.d.ts +3 -0
  84. package/dist/packages/core/src/components.js +5 -0
  85. package/dist/packages/core/src/hooks/useDeviceManagement.js +11 -2
  86. package/dist/packages/core/src/hooks/useFontScale.js +3 -1
  87. package/dist/packages/core/src/hooks/useLucideIcon.d.ts +3 -4
  88. package/dist/packages/core/src/hooks/useLucideIcon.js +3 -4
  89. package/dist/packages/core/src/hooks/usePasswordChange.js +20 -7
  90. package/dist/packages/core/src/hooks/useVisitAllPanel.d.ts +7 -0
  91. package/dist/packages/core/src/hooks/useVisitAllPanel.js +8 -0
  92. package/dist/packages/core/src/hooks/useWordMeanings.js +3 -0
  93. package/dist/packages/core/src/hooks/useWordTrainingFilter.d.ts +5 -0
  94. package/dist/packages/core/src/hooks/useWordTrainingFilter.js +23 -33
  95. package/dist/packages/core/src/i18n/locales/en.json +32 -5
  96. package/dist/packages/core/src/i18n/locales/ko.json +32 -5
  97. package/dist/packages/core/src/tokens.css +2 -1
  98. package/dist/packages/core/src/utils/CategoryData.js +2 -2
  99. package/dist/packages/core/src/utils/assets.js +7 -3
  100. package/dist/packages/core/src/utils/clipboard.js +16 -2
  101. package/dist/packages/core/src/utils/errorMessage.d.ts +2 -1
  102. package/dist/packages/core/src/utils/errorMessage.js +19 -2
  103. package/dist/packages/core/src/utils/getTrainingData.d.ts +8 -1
  104. package/dist/packages/core/src/utils/getTrainingData.js +8 -1
  105. package/dist/packages/core/src/utils/koreanSyllable.d.ts +15 -12
  106. package/dist/packages/core/src/utils/koreanSyllable.js +16 -13
  107. package/dist/packages/core/src/utils/lucideIconSet.d.ts +4 -3
  108. package/dist/packages/core/src/utils/lucideIconSet.js +4 -3
  109. package/dist/packages/core/src/utils/ttsResolver.d.ts +5 -0
  110. package/dist/packages/core/src/utils/wordTrainingGenerators.d.ts +3 -3
  111. package/dist/packages/core/src/utils/wordTrainingGenerators.js +28 -7
  112. package/dist/packages/core/src/version.d.ts +1 -1
  113. package/dist/packages/core/src/version.js +1 -1
  114. package/dist/utils.cjs.js +2 -2
  115. package/package.json +4 -2
@@ -225,6 +225,10 @@ export declare interface BadgeProps {
225
225
  */
226
226
  size?: 'small' | 'medium' | 'large' | BadgeDotSize;
227
227
  className?: string;
228
+ /**
229
+ * 접근성 이름. dot 은 주지 않으면 모양에 따라 '선택됨'·'제거'·'제외'·'N번째 선택' 으로 짓고,
230
+ * bar 는 주면 그 이름으로 읽힌다(안 주면 글자 그대로 읽힌다).
231
+ */
228
232
  'aria-label'?: string;
229
233
  label?: string;
230
234
  children?: ReactNode;
@@ -252,7 +256,7 @@ export declare interface BadgeProps {
252
256
  /** 우측 아이콘 (bar 전용). Lucide 아이콘 이름 또는 ReactNode */
253
257
  iconRight?: ReactNode | string;
254
258
  /**
255
- * 아이콘 종류 (dot 전용)
259
+ * 아이콘 종류 (dot 전용). `number` 가 없고 이것도 없으면 `check` 로 그린다.
256
260
  * - `check` — 선택/완료 (파란 체크)
257
261
  * - `remove` — 제거 (빨간 ×)
258
262
  * - `minus` — 비활성/제외 (회색 —)
@@ -648,12 +652,13 @@ export declare interface BulkAction {
648
652
  onClick: () => void;
649
653
  }
650
654
 
651
- export declare function BulkBar({ count, actions, onClear, clearLabel, visible, content, max, }: BulkBarProps): JSX.Element;
655
+ export declare function BulkBar({ count, actions, onClear, clearLabel: clearLabelProp, visible, content, max, }: BulkBarProps): JSX.Element;
652
656
 
653
657
  export declare interface BulkBarProps {
654
658
  count: number;
655
659
  actions: BulkAction[];
656
660
  onClear: () => void;
661
+ /** 선택 해제 단추 글자. 기본은 사전의 `bulkBar.clearSelection`('선택 해제') */
657
662
  clearLabel?: string;
658
663
  /** count > 0 일 때 자동으로 visible */
659
664
  visible?: boolean;
@@ -1063,7 +1068,7 @@ export declare interface CodeBoxProps {
1063
1068
  }
1064
1069
 
1065
1070
  /** @public */
1066
- export declare function ColorPicker({ value, onChange, palette, size, className, }: ColorPickerProps): JSX.Element;
1071
+ export declare function ColorPicker({ value, onChange, palette, size, className, ariaLabel, }: ColorPickerProps): JSX.Element;
1067
1072
 
1068
1073
  /** @public */
1069
1074
  export declare interface ColorPickerProps {
@@ -1076,6 +1081,8 @@ export declare interface ColorPickerProps {
1076
1081
  /** 스와치 크기 — Button과 동일한 체계 (32 / 40 / 48), radius도 Button rounded와 동일하게 맞춤 */
1077
1082
  size?: ColorPickerSize;
1078
1083
  className?: string;
1084
+ /** 묶음의 이름(예: '글자 색') — 낭독기가 무엇의 색을 고르는지 읽는다 */
1085
+ ariaLabel?: string;
1079
1086
  }
1080
1087
 
1081
1088
  /** @public */
@@ -1115,7 +1122,14 @@ export declare interface CompositionFilter {
1115
1122
  /** @public 단어 구성요소 종류 (초성/중성/종성) */
1116
1123
  declare type CompositionPart = 'cho' | 'jung' | 'jong';
1117
1124
 
1118
- /** @public */
1125
+ /**
1126
+ * 오늘·어제 세트를 뽑아 `TODAY_SETS`·`YESTERDAY_SETS` 에 채운다(`loadTrainingData` 가 부른다).
1127
+ *
1128
+ * **어제 세트는 "어제 뽑힌 세트" 가 아니다** — 캐시가 메모리에만 있어, 날이 바뀌면 어제 몫도 따로 새로 뽑는다.
1129
+ * 사용자에게 보이는 오늘·어제 세트는 사용자와 날짜로 시드를 정하는 `useDailyTrainingSelection` 을 쓴다
1130
+ * (같은 날 같은 사용자면 늘 같은 세트, 어제 세트도 어제와 같다).
1131
+ * @public
1132
+ */
1119
1133
  export declare function computeDailySelection({ counts, forceRefresh }?: ComputeDailySelectionOptions): {
1120
1134
  today: {
1121
1135
  [k: string]: unknown[];
@@ -1230,7 +1244,7 @@ export declare interface ConfirmPopupProps {
1230
1244
  onClose: () => void;
1231
1245
  /** portal 없이 부모 컨테이너에 제자리 렌더 (목업/데모용) */
1232
1246
  inline?: boolean;
1233
- /** 패널 최대 가로폭. 확인창 기본은 'small'(640px) */
1247
+ /** 패널 최대 가로폭. 확인창 기본은 'small'(480px) */
1234
1248
  size?: 'small' | 'medium' | 'large';
1235
1249
  className?: string;
1236
1250
  }
@@ -1341,7 +1355,7 @@ export declare function copyText(text: string): Promise<boolean>;
1341
1355
  *
1342
1356
  * 값은 손으로 적고 `test/coreVersion.test.ts` 가 package.json 과 어긋나면 운다.
1343
1357
  */
1344
- export declare const CORE_VERSION = "0.128.0";
1358
+ export declare const CORE_VERSION = "0.130.0";
1345
1359
 
1346
1360
  /** @public */
1347
1361
  declare const coreDefault: Record<string, unknown>;
@@ -1493,6 +1507,11 @@ export declare interface CreateTTSResolverOptions {
1493
1507
  storagePrefix?: string;
1494
1508
  voice?: string;
1495
1509
  speed?: number;
1510
+ /**
1511
+ * @deprecated 리졸버가 쓰지 않는다 — 파일 이름에도, `/api/tts` 요청에도 들어가지 않는다
1512
+ * (그 개발 서버도 pitch 를 받지 않는다). `generator` 에 넘기는 opts 에 그대로 실려 갈 뿐이라,
1513
+ * 음높이가 필요하면 generator 가 직접 다룬다. 쓰는 곳이 없어 구현하지 않았다.
1514
+ */
1496
1515
  pitch?: number;
1497
1516
  /** Optional function to generate/upload TTS audio when missing. Should return a public URL when done. */
1498
1517
  generator?: (text: string, destPath: string, opts?: CreateTTSResolverOptions) => Promise<string | null>;
@@ -1538,7 +1557,7 @@ export declare function cx(...parts: (string | false | null | undefined)[]): str
1538
1557
 
1539
1558
  export declare type DataLevel = 0 | 1 | 2 | 3;
1540
1559
 
1541
- export declare function DatePicker({ value, onChange, size, min, max, disabled, placeholder, className, }: DatePickerProps): JSX.Element;
1560
+ export declare function DatePicker({ value, onChange, size, min, max, disabled, placeholder: placeholderProp, className, }: DatePickerProps): JSX.Element;
1542
1561
 
1543
1562
  /** @public */
1544
1563
  export declare interface DatePickerProps {
@@ -1548,6 +1567,7 @@ export declare interface DatePickerProps {
1548
1567
  min?: string;
1549
1568
  max?: string;
1550
1569
  disabled?: boolean;
1570
+ /** 값이 없을 때 보이는 문구. 기본은 사전의 `datePicker.chooseDate`('날짜 선택') */
1551
1571
  placeholder?: string;
1552
1572
  className?: string;
1553
1573
  }
@@ -1561,12 +1581,7 @@ export declare function dateToDb(app: string | null | undefined): string | undef
1561
1581
  /**
1562
1582
  * 결과 팝업을 실제 훈련 없이 열어보는 개발용 단추 넷.
1563
1583
  *
1564
- * 예전에는 파일 안의 `const DEBUG = false` 로만 켤 수 있었다. 모노레포 시절에는
1565
- * packages/core 를 그 자리에서 고칠 수 있었으니 말이 됐는데, **패키지로 분리되면서
1566
- * 켤 방법이 사라졌다** — 소비자가 node_modules 를 고치지 않는 한 언제나 null 이다.
1567
- * 실제로 speechear-v2 가 이것을 렌더하고 있고, 아무것도 그려지지 않는다.
1568
- *
1569
- * 그래서 스위치를 밖으로 낸다. 기본값은 그대로 꺼짐이라 지금 쓰는 곳은 안 바뀐다.
1584
+ * `enabled` 를 주어야 단추가 선다. 기본은 꺼짐이다.
1570
1585
  *
1571
1586
  * @example
1572
1587
  * <DebugResultLauncher enabled={import.meta.env.DEV} renderResultPopup={…} />
@@ -1589,6 +1604,8 @@ declare type Decomp = (ReturnType<typeof parseSyllable>)[];
1589
1604
 
1590
1605
  export declare const DEFAULT_LOCALE = "ko";
1591
1606
 
1607
+ export declare const DEFAULT_NOISE_SETTINGS: CoreNoiseSettings;
1608
+
1592
1609
  /** @public */
1593
1610
  export declare function deriveNoiseContexts(trainingData: Record<string, unknown> | null | undefined): NoiseContext_2[];
1594
1611
 
@@ -1821,14 +1838,32 @@ export declare interface DropdownProps {
1821
1838
  ariaLabel?: string;
1822
1839
  }
1823
1840
 
1841
+ /**
1842
+ * @public 소음이 **들리는 크기**(0~2, 원본 소음 파일 기준) = mainVolume × 비율. 켜짐 여부는 보지
1843
+ * 않는다. 결과 기록·파형이 쓴다. 소음 `<audio>` 에 거는 값은 `noiseElementVolume`.
1844
+ */
1845
+ export declare function effectiveNoiseVolume(settings: 비율설정): number;
1846
+
1847
+ /** @public 문제 소리 볼륨(0~1) = mainVolume. 비율과 상관없다. */
1848
+ export declare function effectiveSpeechVolume(settings: 비율설정): number;
1849
+
1824
1850
  /** @public */
1825
1851
  export declare function EffectPartyPopper({ active, count, duration, strength, // multiplier for speed/spread
1826
1852
  onComplete, trigger, }: {
1853
+ /** true 인 동안 그린다. false 면 아무것도 안 그린다. */
1827
1854
  active?: boolean;
1855
+ /** 조각 수. 좌우로 반씩 나눈다. 기본 40. */
1828
1856
  count?: number;
1857
+ /**
1858
+ * 조각 수명의 기준(ms). 값에 비례해 모든 조각이 더 오래 또는 더 짧게 날다 사라진다 — 기본 2000 에서
1859
+ * 조각 하나는 약 1.5~5.5초 난다(조각마다 다르고, strength 가 크면 조금 길다).
1860
+ */
1829
1861
  duration?: number;
1862
+ /** 속도·퍼짐 배율. 기본 1. */
1830
1863
  strength?: number;
1864
+ /** 모든 조각이 사라지면 부른다(움직임 최소화 설정이면 바로 부른다). */
1831
1865
  onComplete?: () => void;
1866
+ /** 값이 바뀌면 다시 터진다. */
1832
1867
  trigger?: number;
1833
1868
  }): JSX.Element | null;
1834
1869
 
@@ -2149,8 +2184,13 @@ export declare function formatDateLongLocale(iso: string, lang: string): string;
2149
2184
  /** "2024. 01. 15. 오후 03:30" (2-digit month + time) */
2150
2185
  export declare function formatDatetimeKo(iso: string): string;
2151
2186
 
2152
- /** 초 → "M분 S초"(또는 "S초") */
2153
- export declare function formatDuration(sec: number | null | undefined): string;
2187
+ /**
2188
+ * 초 → "M분 S초"(또는 "M분"·"S초"). 없거나 0 이하면 '-'.
2189
+ *
2190
+ * 문구는 지금 언어로(`customers.duration.*`, 사전에 없으면 한국어). `durationStr` 를 주면
2191
+ * 그 함수가 만든 글을 그대로 쓴다 — `calcAge` 의 `ageStr` 와 같은 결.
2192
+ */
2193
+ export declare function formatDuration(sec: number | null | undefined, durationStr?: (m: number, s: number) => string): string;
2154
2194
 
2155
2195
  /**
2156
2196
  * 한국 연락처를 010-1234-5678 형식으로 정규화.
@@ -2245,7 +2285,8 @@ export declare function getElementVolume(el: HTMLMediaElement): number;
2245
2285
  * 곳곳에 흩어져 있어 여기 한 벌로 모은다.
2246
2286
  *
2247
2287
  * 문자열도 그대로 쓴다 — `throw '문구'` 로 던지는 자리가 있다. 끝내 못 읽는
2248
- * 값(숫자·null·빈 객체·빈 문구)일 때만 fallback 을 쓴다.
2288
+ * 값(숫자·null·빈 객체·빈 문구)일 때만 fallback 을 쓴다. fallback 을 주지 않으면 지금
2289
+ * 언어의 '알 수 없는 오류'(`common.unknownError`), 주면 그 문구 그대로.
2249
2290
  *
2250
2291
  * 회원 앱은 `src/libs/errorUtils.ts` 가 이것을 다시 내보낸다 — 한 벌만 둔다.
2251
2292
  * @public
@@ -2386,8 +2427,14 @@ export declare function hasStructureContrast(A: Decomp, B: Decomp): boolean;
2386
2427
  export declare function Header({ activeKey, onChange, items, malgwiItems: _malgwiItems, managerItems: _managerItems, unreadCount, onDebug, onNotification, onAccount, onSetting, onHelp, appMode, onAppModeChange }: {
2387
2428
  activeKey?: string | null;
2388
2429
  onChange?: (key?: string) => void;
2430
+ /** 지금 모드의 내비 항목. 데스크톱은 SideNav, 모바일은 BottomNav 가 이 목록을 그린다. */
2389
2431
  items?: SideNavItem[];
2432
+ /**
2433
+ * @deprecated 읽지 않는다 — 지금 모드의 목록을 `items` 로 넘긴다.
2434
+ * 모드 전환 때 부르는 쪽이 `items` 를 바꿔 넣으므로 헤더가 두 목록을 따로 들고 있을 까닭이 없다.
2435
+ */
2390
2436
  malgwiItems?: SideNavItem[];
2437
+ /** @deprecated 읽지 않는다 — 지금 모드의 목록을 `items` 로 넘긴다(`malgwiItems` 와 같다). */
2391
2438
  managerItems?: SideNavItem[];
2392
2439
  unreadCount?: number;
2393
2440
  onDebug?: () => void;
@@ -2437,7 +2484,7 @@ export declare interface IconGridMenuCategory {
2437
2484
  /** @public */
2438
2485
  export declare interface IconGridMenuProps {
2439
2486
  categories?: IconGridMenuCategory[];
2440
- /** 현재 선택된 카테고리 key (controlled) */
2487
+ /** 현재 선택된 카테고리 key (controlled). `null` 이면 아무것도 고르지 않은 상태, 주지 않으면 첫 카테고리로 시작한다 */
2441
2488
  selected?: string | null;
2442
2489
  onSelect?: (key: string | null) => void;
2443
2490
  /** 버튼 크기 — `medium`(기본, 68px) / `small`(48px) */
@@ -2456,8 +2503,8 @@ export declare interface IconProps {
2456
2503
  /**
2457
2504
  * 감싸는 `<span>` 의 클래스. 주면 기본 이름을 **갈아치운다** — 덧붙이지 않는다.
2458
2505
  *
2459
- * 기본값 `iconRenderer-icon` 을 비롯한 네 `iconRenderer-*` 이름은 **패키지에
2460
- * 제 규칙이 하나도 없다**(실측: 자기 규칙 0개). 붙잡을 이름일 뿐이고 크기는
2506
+ * 기본값 `iconRenderer-icon` 을 비롯한 `iconRenderer-*` 이름은 마스크(`-mask`)를 빼면
2507
+ * **패키지에 제 규칙이 없다**. 붙잡을 이름일 뿐이고 크기는
2461
2508
  * `size`, 그 밖의 모양은 `wrapperStyle` 이나 소비자 CSS 로 간다. 그래서
2462
2509
  * `className="iconRenderer-icon"` 이라고 적어도 아무 모양도 따라오지 않는다.
2463
2510
  *
@@ -2468,7 +2515,10 @@ export declare interface IconProps {
2468
2515
  className?: string;
2469
2516
  /** `<img>` 로 그릴 때의 클래스. 기본 `iconRenderer-icon-image` — 규칙 없음. */
2470
2517
  imgClassName?: string;
2471
- /** 마스크로 그릴 때의 클래스. 기본 `iconRenderer-icon-mask` — 규칙 없음. */
2518
+ /**
2519
+ * 마스크로 그릴 때의 클래스. 기본 `iconRenderer-icon-mask` — 글자색으로 칠하고 크기는 `size`(없으면 20px).
2520
+ * 주면 기본 이름을 갈아치우므로 마스크 규칙(`mask-image: var(--icon-url)` 등)도 그 클래스가 대야 한다.
2521
+ */
2472
2522
  maskClassName?: string;
2473
2523
  /** 인라인 SVG 로 그릴 때의 클래스. 기본 `iconRenderer-icon-svg` — 규칙 없음. */
2474
2524
  svgClassName?: string;
@@ -2699,6 +2749,12 @@ export declare interface LeaderboardRow {
2699
2749
  isMe: boolean;
2700
2750
  }
2701
2751
 
2752
+ /** @public */
2753
+ export declare const LEVEL_MAX = 9;
2754
+
2755
+ /** @public */
2756
+ export declare const levelToPct: (level: number) => number;
2757
+
2702
2758
  /** @public */
2703
2759
  export declare function ListenStep({ _stepIndex, _totalSteps, ...rest }: StepWrapperProps): JSX.Element;
2704
2760
 
@@ -2718,7 +2774,7 @@ export declare interface LoaderProps {
2718
2774
  size?: number | string;
2719
2775
  /** 진행률(0–100). `variant='bar'`일 때 적용됩니다. */
2720
2776
  progress?: number | null;
2721
- /** aria-label 및 시각적 레이블 */
2777
+ /** 접근성 이름(aria-label). 화면에 글자로 나오지는 않는다. 안 주면 i18n 의 `loader.loading`(기본 'Loading'). */
2722
2778
  label?: string;
2723
2779
  }
2724
2780
 
@@ -2763,7 +2819,10 @@ export declare function lsBytes(keys: string[]): number;
2763
2819
  export declare const MARK_ICON: Record<ScoreMark, string>;
2764
2820
 
2765
2821
  /**
2766
- * 고른 표시의 단추 변형.
2822
+ * 고른 표시의 단추 변형(○ main · △ warn · ✕ danger).
2823
+ *
2824
+ * **모양(아이콘)이 이미 셋을 가르고 색은 거들 뿐이다** — 색만으로 구분하게 두면
2825
+ * 색을 잘 못 가리는 사람에게는 세 표시가 같아진다.
2767
2826
  *
2768
2827
  * △ 는 `sub`(청록)였는데 **파랑 옆에서 '좋음' 계열로 읽혔다** — ○ 와 △ 가 나란히
2769
2828
  * 있으면 둘 다 통과한 것처럼 보인다. 애매함은 주의색이 맞다.
@@ -2803,19 +2862,21 @@ export declare function marksForSave(marks: Record<string, ScoreMark | null | un
2803
2862
  export declare function matchesChoseongSearch(text: string, search: string): boolean;
2804
2863
 
2805
2864
  /**
2806
- * @public 음절 패턴 미니 DSL 매칭. 쉼표(,)로 나눈 조각을 자리(음절) 단위로 자동 분해하며,
2807
- * **분해된 총 자리 수는 반드시 단어의 음절 수와 같아야** 매칭된다. 한 자리는 다음 중 하나:
2865
+ * @public 음절 패턴 미니 DSL 매칭. 쉼표(,)로 나눈 조각을 연속된 음절 자리들로 풀고, 그 자리들이 단어의
2866
+ * **어느 위치에서든 연속으로** 맞으면 매칭된다(포함 매칭 — 단어 음절 수가 자리 수보다 많아도 된다).
2867
+ * 한 자리는 다음 중 하나:
2808
2868
  * - 빈 자리(쉼표 사이가 비어 있음) — 무관(아무 음절이나 허용).
2809
- * - 완성 음절 한 글자(예: '가') — 그 자리 음절이 정확히 이 글자와 같아야 함.
2869
+ * - 완성 음절 한 글자(예: '가', '강') — 받침 없는 글자는 초성·중성만 같으면 되고(받침 무관: '수' → 수·술·숙),
2870
+ * 받침 있는 글자는 정확히 같아야 함.
2810
2871
  * - 자음/모음 한 글자(예: 'ㄴ', 'ㅏ') — 자음이면 그 자리 초성이, 모음이면 그 자리 중성이 정확히 그
2811
2872
  * 자모여야 함(나머지는 무관).
2812
- * - 초성·중성·종성 3자모 템플릿(예: '__ㄱ', 쉼표로 나눈 조각이 정확히 3글자일 때만) — 각 자리가 '_'면
2813
- * 무관, 자모면 그 위치(초성/중성/종성)가 정확히 그 자모여야 함. 종성 자리 '_'는 받침 유무 모두 허용.
2814
- * 쉼표 사이에 완성 음절·자음·모음을 이어 붙이면(예: '가ㄴ') 한 글자씩 각각 한 음절 자리로 자동 분해된다
2815
- * (3글자로 붙이면 템플릿과 구분이 안 되므로 예외 — 그 경우는 쉼표로 나눠 쓴다).
2816
- * 예: '가,,다' → 3음절, 첫 음절 '가' · 가운데 무관 · 끝 음절 '다'.
2817
- * 예: '__ㄱ,고' → 2음절, 첫 음절 종성이 'ㄱ'(초성·중성 무관) · 끝 음절 '고'.
2818
- * 예: '가ㄴ,,다'(= '가,ㄴ,,다'와 동일) → 4음절, 첫 음절 '가' · 둘째 음절 초성 'ㄴ' · 셋째 무관 · 넷째 '다'.
2873
+ * - 한 음절 템플릿(조각에 '_'가 하나라도 있을 때, 1~4글자) — 초성·중성·종성 순으로 읽고, '_'나 생략한 뒤
2874
+ * 자리는 무관, 자모면 그 위치가 정확히 그 자모여야 함. 종성은 겹받침 두 자모('ㄹㄱ' = ㄺ)까지.
2875
+ * 예: '__ㄱ' = 종성 ㄱ, 'ㄱ_' = 초성 ㄱ, 'ㄱ_ㄱ' = 초성 ㄱ·종성 ㄱ.
2876
+ * '_' 없이 완성 음절·자음·모음을 이어 붙이면(예: '가ㄴ') 한 글자씩 각각 한 음절 자리로 분해된다.
2877
+ * 예: '가,,다' → 첫 음절 '가'(받침 무관) · 가운데 무관 · 끝 음절 '다'인 연속 3음절을 포함한 단어.
2878
+ * 예: '__ㄱ,고' → 종성 'ㄱ'인 음절 바로 뒤에 '고'가 오는 단어(먹고·박고구마 …).
2879
+ * 예: '가ㄴ,,다'(= '가,ㄴ,,다'와 동일) → 연속 4음절 '가' · 초성 'ㄴ' · 무관 · '다'.
2819
2880
  * 해석할 수 없는 글자(자음·모음·완성 음절이 아님)가 섞이면 그 자리는 항상 매칭 실패 처리되어 결과가
2820
2881
  * 0건이 된다(조용히 무관 처리하지 않는다 — 제약이 무시된 채 결과가 나오는 오해를 막기 위해).
2821
2882
  */
@@ -2869,10 +2930,23 @@ export declare type MergedVisit = {
2869
2930
  status: VisitAllStatus;
2870
2931
  };
2871
2932
 
2933
+ /**
2934
+ * @public 설정에 조각을 덮는다 — `{ ...base, ...patch }` 에 비율 규칙 하나를 더한 것.
2935
+ *
2936
+ * **조각에 `level` 은 있는데 `noiseRatio` 가 없으면** 그 조각은 옛 모양이다(저장된 옛 기록,
2937
+ * 옛 `noiseOverride`). 그대로 덮으면 base 의 `noiseRatio`(기본값 1 등)가 남아 level 을 가려
2938
+ * 버리므로, 덮은 결과의 level·mainVolume 으로 비율을 셈해 **그 자리에서 박아 둔다** —
2939
+ * 박지 않고 비워 두면 뒤에 전체 소리만 바꿀 때 비율이 따라 변해 소음이 옛날처럼 제자리에 선다.
2940
+ * `mainVolume` 은 그대로다 — 옛 뜻(문제 소리 = mainVolume, 소음 = level/9)이 새 셈(문제 = M, 소음 = M×R)
2941
+ * 에서 들리던 크기 그대로 남는다(0.123.0 의 "R>1 이면 mainVolume = level/9" 는 0.125.0 에서 걷음).
2942
+ * `{ ...DEFAULT_NOISE_SETTINGS, ...saved }` 대신 이것을 쓴다.
2943
+ */
2944
+ export declare function mergeNoiseSettings(base: CoreNoiseSettings, patch?: Partial<CoreNoiseSettings> | null): CoreNoiseSettings;
2945
+
2872
2946
  /**
2873
2947
  * @public 합치기 문제 **표시** 문장 — 미리보기·러너 공용. 음절/음소 모두 원문(자모는 자모 그대로: 'ㄱ','ㅏ')로 표시한다.
2874
2948
  * TTS로 읽을 땐 자모를 낱자 이름으로 바꿔야 하지만(그건 jamoToNamesForSpeech가 합성 경로에서 처리), 화면 표기는 자모 유지.
2875
- * 예: ['아','냐'] → `"아"와 "냐"를 합치면 어떤 단어가 되나요?` / ['ㄱ','ㅏ'] → `"ㄱ"와 "ㅏ"를 합치면 어떤 소리가 되나요?`
2949
+ * 예: ['아','냐'] → `"아"와 "냐"를 합치면 어떤 단어가 되나요?` / ['ㄱ','ㅏ'] → `"ㄱ"과 "ㅏ"를 합치면 어떤 소리가 되나요?`
2876
2950
  */
2877
2951
  export declare function mergeQuestionText(parts: string[], phoneme?: boolean): string;
2878
2952
 
@@ -2995,6 +3069,12 @@ export declare const NO_CODA = "__NO_CODA__";
2995
3069
  /** @public */
2996
3070
  export declare const NOISE_DATAS: unknown[];
2997
3071
 
3072
+ /** @public 소음 비율 상한(배). */
3073
+ export declare const NOISE_RATIO_MAX = 2;
3074
+
3075
+ /** @public 소음 비율 칸(배). */
3076
+ export declare const NOISE_RATIO_STEP = 0.1;
3077
+
2998
3078
  /** @public */
2999
3079
  export declare interface NoiseContext {
3000
3080
  sentence: string;
@@ -3006,6 +3086,42 @@ declare interface NoiseContext_2 {
3006
3086
  contextCode?: string;
3007
3087
  }
3008
3088
 
3089
+ /**
3090
+ * @public 소음 `<audio>` 볼륨(0~1) = min(1, 들리는 크기 ÷ gain). gain 은 소음 파일이 원본보다 몇 배
3091
+ * 큰지 — 기본은 `configureStorage({ noiseFileGain })`(없으면 1).
3092
+ */
3093
+ export declare function noiseElementVolume(settings: 비율설정, gain?: number): number;
3094
+
3095
+ /** @public */
3096
+ export declare const NoisePlayerContext: Context<NoisePlayerContextValue | null>;
3097
+
3098
+ /** @public */
3099
+ export declare type NoisePlayerContextValue = {
3100
+ settings: CoreNoiseSettings;
3101
+ setSettings: (next: Partial<CoreNoiseSettings>) => void;
3102
+ /** 오디오 레벨 미터용 AnalyserNode 등록(오디오 플레이어가 호출). */
3103
+ registerAnalyser?: (which: 'main' | 'noise', analyser: AnalyserNode | null) => void;
3104
+ /** 문장(메인) 오디오 미터용 AnalyserNode. */
3105
+ mainAnalyser?: AnalyserNode | null;
3106
+ /** 소음 오디오 미터용 AnalyserNode. */
3107
+ noiseAnalyser?: AnalyserNode | null;
3108
+ /** 문장(메인) 재생 속도(playbackRate). 0.5–1.5, 기본 1. */
3109
+ playbackRate: number;
3110
+ setPlaybackRate: (rate: number) => void;
3111
+ /** 전체 소리 = 문제 소리 볼륨. 0–1, 기본 1. 소음은 이 값 × 소음 비율(noiseElementVolume 참고). */
3112
+ mainVolume: number;
3113
+ setMainVolume: (v: number) => void;
3114
+ };
3115
+
3116
+ /**
3117
+ * @public 설정의 소음 비율(배). `noiseRatio` 가 있으면 그것(0~2 로 자름), 없으면 **옛 값에서
3118
+ * 들리던 그대로** 옮긴다 — (level/9) ÷ mainVolume 을 0~2 로 자르고 0.1 단위 반올림.
3119
+ * mainVolume 이 0 이면 옛 값으로는 비율을 알 수 없어 1.
3120
+ */
3121
+ export declare function noiseRatioOf(settings: Pick<CoreNoiseSettings, 'level' | 'mainVolume'> & {
3122
+ noiseRatio?: number;
3123
+ }): number;
3124
+
3009
3125
  /** @public 소음설정 영속 저장소. 앱에서 Supabase 연결을 주입한다. */
3010
3126
  export declare interface NoiseSettingsStore {
3011
3127
  /** 현재 훈련 대상(본인 또는 고객)의 저장된 소음설정. 없으면 null. */
@@ -3056,9 +3172,14 @@ export declare function NoticeBox({ title, lines, notices, children, variant, di
3056
3172
  export declare interface NoticeBoxProps {
3057
3173
  title?: string;
3058
3174
  lines?: string[];
3175
+ /** 여러 안내를 한 상자에 차례로 싣는다. 주면 `title`·`lines`·`children` 대신 이것을 그린다. */
3059
3176
  notices?: Array<{
3060
3177
  title?: string;
3061
3178
  lines?: string[];
3179
+ /**
3180
+ * 효과 없음 — 상자 모양은 바깥 `variant` 하나로만 정해진다.
3181
+ * @deprecated 읽히지 않는 값이다. 항목마다 모양을 달리 그리는 스타일이 없다 — 모양이 다른 안내는 NoticeBox 를 따로 둔다.
3182
+ */
3062
3183
  variant?: string;
3063
3184
  children?: ReactNode;
3064
3185
  }> | null;
@@ -3153,8 +3274,8 @@ declare type PageTitleProps = {
3153
3274
  };
3154
3275
 
3155
3276
  /**
3156
- * 페이지네이션 컨트롤 (‹ 현재/전체 ›).
3157
- * CustomersPage·TrainingSettingsPage 등 테이블 페이지에서 공유.
3277
+ * 페이지네이션 컨트롤 (‹ 현재/전체 ›). 표 아래에 두고 `page`·`totalPages` 를 넘기면
3278
+ * 이전·다음 단추가 `onChange` 로 1~`totalPages` 안의 쪽 번호를 돌려준다. 끝에서는 그쪽 단추가 꺼진다.
3158
3279
  *
3159
3280
  * @public
3160
3281
  */
@@ -3509,7 +3630,12 @@ export declare interface PassCardProps {
3509
3630
  /** 기간 토큰(예: '30일'). 미지정 시 product에서 도출. */
3510
3631
  plan?: string;
3511
3632
  type?: PassCardType;
3633
+ /**
3634
+ * @deprecated 읽지 않는다 — 카드는 시작일을 그리지 않고, 남은 기간은 `endDate`(또는 `remainingSeconds`)로만 센다.
3635
+ * 넘겨도 화면은 같다.
3636
+ */
3512
3637
  startDate?: string;
3638
+ /** 끝나는 날(YYYY-MM-DD). `state` 가 active·expired 이고 `remainingSeconds` 가 없으면 이 날까지 남은 날수로 사용량을 센다. */
3513
3639
  endDate?: string;
3514
3640
  /**
3515
3641
  * 전환으로 일시정지되어 남은 시간(초)이 알려진 경우에만 지정.
@@ -3519,8 +3645,12 @@ export declare interface PassCardProps {
3519
3645
  */
3520
3646
  remainingSeconds?: number;
3521
3647
  state?: PassCardState;
3648
+ /** @deprecated 읽지 않는다 — 대기 순서는 카드에 그리지 않는다. 넘겨도 화면은 같다. */
3522
3649
  queueIndex?: number;
3523
- /** 배경 컴포넌트. 기본값: PassCardBgTwoCircle */
3650
+ /**
3651
+ * 배경 컴포넌트. 안 주면 종류·기간·라벨로 고른다 — 말귀(`speechear`)는 늘 PassCardBgTwoCircle,
3652
+ * 말귀 매니저(`manager`)는 1년이면 PassCardBgTalk, 라벨이 오픈특가면 PassCardBgSoundMoveV1, 그 밖에는 PassCardBgSoundMove.
3653
+ */
3524
3654
  bg?: ReactNode;
3525
3655
  /** true면 배경만 표시하고 콘텐츠(로고·플랜·카피라이트)를 숨김 */
3526
3656
  bgOnly?: boolean;
@@ -3580,8 +3710,15 @@ export declare function PaymentPlan(props: PaymentPlanProps): JSX.Element;
3580
3710
  export declare interface PaymentPlanData {
3581
3711
  id: string | number;
3582
3712
  title?: string;
3583
- /** 서비스명(말귀/말귀매니저). 카드 로고·표기용. title에서 파싱하지 않고 별도 저장. */
3713
+ /**
3714
+ * 서비스명(말귀/말귀매니저). 이 컴포넌트는 읽지 않는다 — 카드 로고는 `group` 으로 고른다.
3715
+ * 부르는 쪽이 상품 목록을 나누거나 표기할 때 쓰도록 자리만 둔 칸이다.
3716
+ */
3584
3717
  service?: string;
3718
+ /**
3719
+ * 상품 묶음. `'org'` 면 이용권 카드를 말귀 매니저 모양으로, 그 밖(`'personal'` 등)이면 말귀 모양으로 그린다.
3720
+ */
3721
+ group?: 'personal' | 'org' | (string & {});
3585
3722
  /** 플랜 티어명(스탠다드/라이트/프로 등). 카드 헤드라인용. */
3586
3723
  tier?: string;
3587
3724
  subtitle?: string;
@@ -3632,6 +3769,9 @@ export declare interface PaymentPlanTier {
3632
3769
  [k: string]: unknown;
3633
3770
  }
3634
3771
 
3772
+ /** @public */
3773
+ export declare const pctToLevel: (pct: number) => number;
3774
+
3635
3775
  /**
3636
3776
  * 인물 소개 카드 — 원형 사진 위에 이름·직함, 아래에 경력 목록을 세로로 쌓는다.
3637
3777
  * 회사 소개의 운영진 섹션, 연구진 목록 등에 사용.
@@ -3876,7 +4016,14 @@ export declare interface PopupListProps<T extends {
3876
4016
  selectedId?: string | number;
3877
4017
  /** 현재 선택된 아이템 id Set — 다중 선택 강조 (`selectedId`보다 우선) */
3878
4018
  selectedIds?: Set<string | number>;
3879
- /** 행 클릭 후에도 팝업을 닫지 않음 (다중 선택용). 기본 false */
4019
+ /**
4020
+ * 효과 없음 — ListPopup 은 행을 골라도 **스스로 닫지 않는다**(닫기는 늘 `onSelect`·`onClose` 를
4021
+ * 받은 쪽이 정한다). 그래서 이 값은 읽지 않는다.
4022
+ *
4023
+ * @deprecated 읽히지 않는 값이다. 예전 JSDoc 은 "기본 false = 고르면 닫힘" 이라 했지만 그렇게
4024
+ * 동작한 적이 없고, 그 뜻대로 고치면 행 클릭이 아무것도 안 하는 화면(행 안 단추로만 보내는 창 등)이
4025
+ * 누르는 순간 닫힌다. 고른 뒤 닫으려면 `onSelect` 안에서 직접 닫는다.
4026
+ */
3880
4027
  keepOpenOnSelect?: boolean;
3881
4028
  /** 행 선택 콜백 */
3882
4029
  onSelect: (item: T) => void;
@@ -3968,7 +4115,7 @@ export declare interface PopupProps {
3968
4115
  * 패널 최대 가로폭 제한. 미지정 시 기본(820px) 유지.
3969
4116
  * - `small` 480px (확인/알림류)
3970
4117
  * - `medium` 680px (입력 폼)
3971
- * - `large` 880px (리스트/다중 콘텐츠)
4118
+ * - `large` 1080px (리스트/다중 콘텐츠)
3972
4119
  * 더 넓거나 무제한이 필요하면 className으로 `.popup-panel` max-width를 직접 override.
3973
4120
  */
3974
4121
  size?: 'small' | 'medium' | 'large';
@@ -4112,11 +4259,8 @@ export declare interface ProgressIndicatorProps {
4112
4259
  */
4113
4260
  announce?: boolean;
4114
4261
  /**
4115
- * 이름표를 직접 준다. 안 주면 i18n 의 `progress.steps` 로 짓는다.
4116
- *
4117
- * i18n 을 쓰지 않는 앱에서는 그 값이 한글 기본값으로 떨어진다 —
4118
- * 화면은 영문인데 이름표만 한글이 되는 자리가 실제로 있었다
4119
- * (sound-localization-system 의 병원용 빌드).
4262
+ * 이름표를 직접 준다. 안 주면 i18n 의 `progress.steps`('진행 단계 N / M')로 짓는다 —
4263
+ * i18n 사전을 싣지 않는 앱은 이름표가 한글 기본값으로 떨어지므로 번역한 글을 여기 넘긴다.
4120
4264
  */
4121
4265
  ariaLabel?: string;
4122
4266
  }
@@ -4392,7 +4536,7 @@ export declare interface Question {
4392
4536
  export declare function Radio({ checked, onChange, name, value, disabled, label, className }: RadioProps): JSX.Element;
4393
4537
 
4394
4538
  /** @public */
4395
- export declare function RadioGroup({ options, value, onChange, name, disabled, direction, className, }: RadioGroupProps): JSX.Element;
4539
+ export declare function RadioGroup({ options, value, onChange, name, disabled, direction, className, ariaLabel, }: RadioGroupProps): JSX.Element;
4396
4540
 
4397
4541
  /** @public */
4398
4542
  export declare type RadioGroupDirection = 'vertical' | 'horizontal';
@@ -4413,6 +4557,8 @@ export declare interface RadioGroupProps {
4413
4557
  disabled?: boolean;
4414
4558
  direction?: RadioGroupDirection;
4415
4559
  className?: string;
4560
+ /** 묶음의 이름 — 낭독기가 "무엇을 고르는 라디오 묶음" 인지 읽는다. 보이는 제목이 따로 있어도 이어지지 않으면 준다. */
4561
+ ariaLabel?: string;
4416
4562
  }
4417
4563
 
4418
4564
  /** @public */
@@ -4543,7 +4689,10 @@ export declare interface ReportBlock {
4543
4689
  * 바닥(로고·저작권 + 쪽 번호)이 반복된다.
4544
4690
  *
4545
4691
  * 재는 방식이라 글꼴이 늦게 오거나 이미지가 나중에 뜨면 쪽이 밀릴 수 있다.
4546
- * 그 경우 `blocks` 를 그대로 두고 다시 그리면 다시 잰다.
4692
+ * 다시 재는 때는 블록들의 `id`·`section`·`group` 구성이 바뀔 때와 처음 그릴 때뿐이다 —
4693
+ * 같은 구성으로 다시 그리거나 `content` 만 바뀌어서는 다시 재지 않는다(렌더마다 재면
4694
+ * 재기 → 쪽 나눔 → 렌더가 끝없이 돈다). 글꼴·이미지가 다 온 뒤 다시 재야 하면
4695
+ * `key` 를 바꿔 새로 붙인다.
4547
4696
  *
4548
4697
  * 화면에 그린 쪽 수는 다른 도구로 만든 PDF 와 다를 수 있다. 같은 내용을 그리는
4549
4698
  * 높이가 서로 달라서인데, 쪽 수까지 맞춰야 하면 양쪽 본문 높이를 견줘야 한다.
@@ -4848,10 +4997,14 @@ export declare function saveNoiseSettings(settings: CoreNoiseSettings): void;
4848
4997
  * 리액트가 다른 것을 같은 것으로 보고 상태를 잘못 물려준다. 그래서 **주어진 이름이
4849
4998
  * 있으면 반드시 그것을 쓴다**는 게 이 함수들의 약속이다.
4850
4999
  */
4851
- /** @public */
5000
+ /**
5001
+ * @public 시나리오의 한 쪽. `steps` 는 시나리오(`scenario.pages`)에서는 **ScenarioStep 객체**다 — 팝업이
5002
+ * 그것을 `ScenarioStep` 으로 읽는다(key·type·props). 자식 요소(`<Page>`)로 쪽을 짠 경우에만 팝업이 안에서
5003
+ * 단계 요소(ReactNode)를 담는다.
5004
+ */
4852
5005
  export declare type ScenarioPage = {
4853
5006
  id?: string;
4854
- steps?: ReactNode[];
5007
+ steps?: ScenarioStep[] | ReactNode[];
4855
5008
  pageTop?: ReactNode | null;
4856
5009
  pageBottom?: ReactNode | null;
4857
5010
  } & Record<string, unknown>;
@@ -4996,7 +5149,7 @@ export declare interface SearchSummaryProps {
4996
5149
  export declare type SearchSummaryStatus = 'ready' | 'guide' | 'error';
4997
5150
 
4998
5151
  /** @public */
4999
- export declare function Section({ type, title, icon, action, disc, children, className, style }: SectionProps): JSX.Element;
5152
+ export declare function Section({ type, title, headingLevel, icon, action, disc, children, className, style }: SectionProps): JSX.Element;
5000
5153
 
5001
5154
  /**
5002
5155
  * 섹션·페이지의 머리 — 머리말 · 제목 · 설명.
@@ -5034,6 +5187,12 @@ export declare type SectionHeaderTone = 'dark' | 'light';
5034
5187
  export declare interface SectionProps {
5035
5188
  type?: SectionType;
5036
5189
  title?: ReactNode;
5190
+ /**
5191
+ * 제목을 이 단계의 제목 태그(`h1`~`h6`)로 그린다 — **주는 것을 권한다.** 페이지 제목 아래 구획이면 2, 그 아래면 3 처럼 문서의 제목 순서에 맞춰 주면 낭독기의 제목 목록으로 이 구획에 바로 갈 수 있다.
5192
+ *
5193
+ * 주지 않으면 `div` 로 그려 제목 목록에 안 나온다. 글자 모양은 어느 쪽이든 `type` 이 정한다.
5194
+ */
5195
+ headingLevel?: 1 | 2 | 3 | 4 | 5 | 6;
5037
5196
  icon?: string;
5038
5197
  action?: ReactNode;
5039
5198
  disc?: ReactNode;
@@ -5510,7 +5669,7 @@ export declare interface SoundCompassProps {
5510
5669
 
5511
5670
  /**
5512
5671
  * @public 음절의 실제 소리(표준발음). pronMap에 있으면 그것, 없으면(합성 음절) 끝소리 규칙 발음, 그래도 없으면 철자.
5513
- * (예: '밡'·'밖'은 모두 [박] — 소리 기준 중복 판정에 쓴다.)
5672
+ * (예: '밖'·'밗'은 모두 [박] — 소리 기준 중복 판정에 쓴다.)
5514
5673
  */
5515
5674
  export declare function soundOf(syll: string, pronMap: Map<string, string>): string;
5516
5675
 
@@ -5821,7 +5980,7 @@ export declare const SYLLABLE_MIN = 5;
5821
5980
 
5822
5981
  /**
5823
5982
  * @public 정답 음절(분해 cs)의 지정 파트를 다른 음소로 바꿔 대립쌍 음절 n개 생성.
5824
- * 중복 배제는 '실제 발음(soundOf)' 기준 — 소리가 같은 오답(밡/밖→박)이 함께 나오지 않게 한다.
5983
+ * 중복 배제는 '실제 발음(soundOf)' 기준 — 소리가 같은 오답(밖/밗→박)이 함께 나오지 않게 한다.
5825
5984
  */
5826
5985
  export declare function syllableContrasts(cs: Syllable, part: 'cho' | 'jung' | 'jong', correct: string, n: number, pronMap: Map<string, string>): string[];
5827
5986
 
@@ -6017,7 +6176,7 @@ export declare type TableProps<T extends {
6017
6176
  headerActions?: ReactNode;
6018
6177
  /** 행이 없을 때 body 영역에 표시할 콘텐츠 */
6019
6178
  empty?: ReactNode;
6020
- /** true면 rows/empty 대신 스피너 표시 (fields 헤더도 숨김) */
6179
+ /** true면 rows/empty 대신 스피너 표시. 제목 머리와 fields 머리는 그대로 둔다. */
6021
6180
  loading?: boolean;
6022
6181
  /** 드래그 오버 강조 */
6023
6182
  dragOver?: boolean;
@@ -6646,15 +6805,15 @@ export declare interface TrainingBlockButtonProps {
6646
6805
  completed?: boolean;
6647
6806
  /**
6648
6807
  * 버튼 레이아웃 크기.
6649
- * - `square` (기본값): 200×200px 정사각형 카드
6808
+ * - `square` (기본값): 200×200px 정사각형 카드(좁은 화면에선 190px·반 폭으로 줄어든다)
6650
6809
  * - `small`: 120×120px 축소 카드 (행 펼치기 영역 등 좁은 공간용)
6651
- * - `list`: 전체 너비 × 60px 가로형 리스트 항목
6652
- * - `compact`: 툴팁 등 좁은 공간용 소형 리스트 항목 (44px, 아이콘 24px)
6810
+ * - `list`: 너비 200px · 최소 높이 68px 가로형 항목
6811
+ * - `compact`: 툴팁 등 좁은 공간용 소형 리스트 항목 (전체 너비 × 높이 40px, 작은 아이콘)
6653
6812
  */
6654
6813
  size?: 'square' | 'small' | 'list' | 'compact';
6655
6814
  renderPopup?: (props: TrainingBlockButtonPopupProps) => ReactNode;
6656
6815
  completedIds?: Set<string>;
6657
- /** 선택 상태 ID Set. 완료 뱃지 없이 테두리 강조만 표시. */
6816
+ /** 선택 상태 ID Set. 포함된 항목(완료가 아닌 것)에 체크 배지를 표시 — 완료와 같은 배지이고, `hideCheckBadge` 면 숨는다. */
6658
6817
  selectedIds?: Set<string>;
6659
6818
  /** 호버 시 표시할 문제/문장 텍스트. 제공 시 DescriptionTooltip 으로 렌더. */
6660
6819
  tooltipText?: string | string[];
@@ -6691,19 +6850,24 @@ export declare interface TrainingBlockProps {
6691
6850
  type?: TrainingTypeDescriptor;
6692
6851
  trainingDatas?: TrainingItem[] | unknown[];
6693
6852
  depth?: string | string[];
6853
+ /**
6854
+ * @deprecated 쓰이지 않는다 — 한 번에 보이는 카드 수는 20개로 고정이고 나머지는 '더보기'로 20개씩 늘린다.
6855
+ * 소비처가 항목 수(`trainingDatas.length`)를 넘기고 있어, 이 값을 페이지 크기로 살리면 20개 끊기가 사라진다.
6856
+ */
6694
6857
  count?: number;
6695
6858
  /** 버튼 클릭 시 열릴 팝업 렌더링 함수. TrainingBlockButton의 renderPopup과 동일. */
6696
6859
  renderPopup?: (props: TrainingBlockButtonPopupProps) => ReactNode;
6697
6860
  /** 오늘 완료된 훈련 항목 ID Set. completedIds에 포함된 항목은 완료 상태로 표시됩니다. */
6698
6861
  completedIds?: Set<string>;
6699
- /** 선택 상태 ID Set. 완료 뱃지 없이 테두리 강조만 표시. */
6862
+ /** 선택 상태 ID Set. 포함된 항목(완료가 아닌 것)에 체크 배지를 표시 — 완료와 같은 배지다. */
6700
6863
  selectedIds?: Set<string>;
6701
6864
  /** 훈련 항목 완료 시 호출되는 콜백. itemId는 완료된 TrainingItem의 id입니다. */
6702
6865
  onItemComplete?: (itemId: string) => void;
6703
6866
  /**
6704
6867
  * 버튼 레이아웃 크기.
6705
- * - `square` (기본값): 200×200px 정사각형 카드
6706
- * - `list`: 전체 너비 × 80px 가로형 리스트 항목
6868
+ * - `square` (기본값): 200×200px 정사각형 카드(좁은 화면에선 190px·반 폭으로 줄어든다)
6869
+ * - `small`: 120×120px 축소 카드
6870
+ * - `list`: 너비 200px · 최소 높이 68px 가로형 항목
6707
6871
  */
6708
6872
  size?: 'square' | 'list' | 'small';
6709
6873
  /** true 이면 버튼 호버 시 DescriptionTooltip 으로 문제 텍스트를 표시. 기본값 false. */
@@ -7761,6 +7925,9 @@ export declare function useI18n(): I18nContextValue;
7761
7925
  /** @public */
7762
7926
  export declare function useMediaQuery(query: string): boolean;
7763
7927
 
7928
+ /** @public */
7929
+ export declare function useNoisePlayer(): NoisePlayerContextValue | null;
7930
+
7764
7931
  /**
7765
7932
  * 클라이언트 페이지네이션 — 목록·페이지크기·현재 페이지로 현재 쪽 항목·총 쪽수·보정된
7766
7933
  * 현재 쪽을 낸다. 목록이 줄어 page 가 총 쪽수를 넘으면 마지막 쪽으로 조인다(curPage).
@@ -7964,6 +8131,11 @@ export declare function useVisitAllPanel(mergedVisits: MergedVisit[]): {
7964
8131
  setVapSearch: Dispatch<SetStateAction<string>>;
7965
8132
  vapPageSize: string;
7966
8133
  setVapPageSize: Dispatch<SetStateAction<string>>;
8134
+ /** 번역된 개수 보기 옵션 — `VAP_PAGE_SIZE_OPTIONS` 와 값은 같고 라벨만 지금 언어로 */
8135
+ vapPageSizeOptions: {
8136
+ label: string;
8137
+ value: string;
8138
+ }[];
7967
8139
  vapStatus: string;
7968
8140
  setVapStatus: Dispatch<SetStateAction<string>>;
7969
8141
  vapPage: number;
@@ -8187,8 +8359,9 @@ export declare function useWordTrainingFilter(groupKey: string, caps: WordTraini
8187
8359
 
8188
8360
  /**
8189
8361
  * @public 단어 검색 '글자 입력'의 형식 유효성 검사.
8190
- * 허용: 완성 음절('가'), 초성/중성 자모('ㄱ'·'ㅏ'), 초성·중성·종성 3자모 템플릿('__ㄱ', 정확히 3글자),
8191
- * 쉼표(,) 구분(빈 자리는 '무관'). 그 외(예: '_' 오용, 라틴 문자, 한 자리에 음소 4개 이상인 '___ㄱ')는 무효.
8362
+ * 허용: 완성 음절('가'), 초성/중성 자모('ㄱ'·'ㅏ'), '_'가 든 한 음절 템플릿(1~4글자, 초성·중성·종성 순 —
8363
+ * '__ㄱ'·'ㄱ_'·'ㄱ_ㄹㄱ'), 쉼표(,) 구분(빈 자리는 '무관'). 그 외(예: 5글자 이상 템플릿, 템플릿 자리에 맞지 않는
8364
+ * 자모('ㅏ_'), 라틴 문자)는 무효.
8192
8365
  * @returns 유효하면 `{ ok: true }`, 아니면 `{ ok: false }`(호출부가 안내 문구를 표시).
8193
8366
  */
8194
8367
  export declare function validateSyllableSearchInput(input: string): {
@@ -8199,6 +8372,8 @@ export declare function validateSyllableSearchInput(input: string): {
8199
8372
  export declare const valueToJamos: (v: string) => string[];
8200
8373
 
8201
8374
  /**
8375
+ * 개수 보기 옵션의 값 목록. 라벨은 한국어 고정이다 — 화면에는 훅이 돌려주는
8376
+ * 번역된 `vapPageSizeOptions` 를 쓴다.
8202
8377
  * @public
8203
8378
  */
8204
8379
  export declare const VAP_PAGE_SIZE_OPTIONS: {
@@ -8234,6 +8409,21 @@ export declare type VisitItem = {
8234
8409
  /** @public */
8235
8410
  export declare type VisitStatus = 'visited' | 'scheduled' | 'noVisit';
8236
8411
 
8412
+ /** @public 눈금 끝(배). */
8413
+ export declare const WAVE_A_MAX = 2;
8414
+
8415
+ /** @public 1배 막대 높이(px) — 지금까지의 파형 칸 높이. */
8416
+ export declare const WAVE_H1_PX = 45;
8417
+
8418
+ /** @public 2배 막대 높이(px) — 카드 윗변·조작 줄을 넘지 않는 최대. */
8419
+ export declare const WAVE_HMAX_PX = 74;
8420
+
8421
+ /** @public 막대 크기 a(0~2, 넘으면 자름) → 픽셀 높이. */
8422
+ export declare function waveBarPx(a: number): number;
8423
+
8424
+ /** @public 막대 `--val` — 1배 높이(H1)에 대한 배율. 막대 ::after 는 H1 높이를 scaleY(--val) 한다. */
8425
+ export declare function waveBarVal(a: number): number;
8426
+
8237
8427
  /** 낱말 하나에 대해 쌓인 성적. */
8238
8428
  export declare interface WeakItem {
8239
8429
  label: string;
@@ -8385,13 +8575,16 @@ export declare function WordDataProvider({ words, loading, error, statusMessage,
8385
8575
  }): JSX.Element;
8386
8576
 
8387
8577
  /** @public */
8388
- export declare function WordDataUpdatePopup({ title, progress, onDone, onRetry, inline }: WordDataUpdatePopupProps): JSX.Element;
8578
+ export declare function WordDataUpdatePopup({ title, progress, onDone, onRetry, onClose, inline }: WordDataUpdatePopupProps): JSX.Element;
8389
8579
 
8390
8580
  declare interface WordDataUpdatePopupProps {
8391
8581
  title: string;
8392
8582
  progress: WordDataProgress;
8393
8583
  onDone: () => void;
8584
+ /** 오류 때 '다시 시도' 단추가 부른다. 없으면 그 자리에 '닫기' 단추가 선다. */
8394
8585
  onRetry?: () => void;
8586
+ /** 오류인데 `onRetry` 가 없을 때 '닫기' 단추가 부른다. 없으면 `onDone` 을 부른다. */
8587
+ onClose?: () => void;
8395
8588
  inline?: boolean;
8396
8589
  }
8397
8590
 
@@ -8429,11 +8622,16 @@ declare interface WordEntry_2 {
8429
8622
  /** 설정 팝업 취소 시 되돌릴 필터 스냅샷 */
8430
8623
  export declare interface WordFilterSnapshot {
8431
8624
  composition: WordComposition;
8625
+ /** 뜰 때의 대립 갯수. composition 에서 파생되므로 되돌릴 때는 읽지 않는다. */
8432
8626
  contrastCount: number;
8433
8627
  excludeNoImage: boolean;
8434
8628
  sameStructureOnly: boolean;
8435
8629
  phonemeView: 'char' | 'pron' | 'direct';
8436
8630
  gradeFilter: string[];
8631
+ /** 품사 필터. 없으면(예전에 뜬 스냅샷) 되돌릴 때 지금 값을 그대로 둔다. */
8632
+ posFilter?: string[];
8633
+ /** 어종 필터. 없으면(예전에 뜬 스냅샷) 되돌릴 때 지금 값을 그대로 둔다. */
8634
+ wordTypeFilter?: string[];
8437
8635
  }
8438
8636
 
8439
8637
  /** @public 단어의 모든 한글 음절에 종성(받침)이 없는지 (열린 음절로만 구성) */
@@ -8645,7 +8843,7 @@ export declare interface WordTableBlockProps {
8645
8843
  onReorder?: (fromIndex: number, toIndex: number) => void;
8646
8844
  /** display 모드에서도 칸 수 조절 허용 */
8647
8845
  showCellCount?: boolean;
8648
- /** display 모드에서 빈 슬롯에 추가하기 버튼 표시 */
8846
+ /** display 모드에서 칸 수 안의 빈 칸에 추가하기 버튼 표시 → `renderWordSearch` 로 고른 첫 낱말을 새 항목으로 넘긴다. 칸 수는 `showCellCount` 로만 늘릴 수 있어 **함께 줘야** 빈 칸이 생긴다(없으면 칸 수 = 항목 수라 단추가 뜰 자리가 없다). */
8649
8847
  onAddItem?: (item: WordSearchResultItem) => void;
8650
8848
  className?: string;
8651
8849
  /** 저장 팝업 렌더러 */
@@ -8691,11 +8889,11 @@ export declare interface WordTableButtonProps {
8691
8889
  createdAt?: string;
8692
8890
  /** 스토어 발행 배지 표시 */
8693
8891
  published?: boolean;
8694
- /** 발행 배지 라벨(i18n 주입) */
8892
+ /** 발행 배지 라벨. 없으면 사전 `myWordTable.publishedBadge`(기본 '발행됨'). */
8695
8893
  publishedLabel?: string;
8696
8894
  /** 활성 공유 링크 존재 배지 표시(썸네일 우상단) */
8697
8895
  shared?: boolean;
8698
- /** 공유 배지 라벨(i18n 주입) */
8896
+ /** 공유 배지 라벨. 없으면 사전 `myWordTable.sharedBadge`(기본 '공유 중'). */
8699
8897
  sharedLabel?: string;
8700
8898
  /** 최근 생성·미열람 표 표시 — 우상단 원형 배지(circle badge) */
8701
8899
  isNew?: boolean;
@@ -8831,7 +9029,7 @@ export declare interface WordTableProps {
8831
9029
  onRemoveItem?: (id: string) => void;
8832
9030
  /** 표시 항목 드래그 재정렬 */
8833
9031
  onReorder?: (fromIndex: number, toIndex: number) => void;
8834
- /** 표시 모드 빈칸에 '추가' 버튼 노출 */
9032
+ /** 표시 모드에서 `count` 안의 빈칸(항목 수 뒤 ~ count)에 '추가' 버튼 노출. count 가 항목 수와 같으면 그런 칸이 없다. `onActivateCell` 필요. */
8835
9033
  showFillerAdd?: boolean;
8836
9034
  /** 그리드 콘텐츠 DOM ref — 인쇄/캡처용 */
8837
9035
  contentRef?: Ref<HTMLDivElement>;
@@ -8954,4 +9152,8 @@ export declare type WordTrainingTypeKey = 'SAME_SOUND_PICK' | 'SAME_OR_DIFF' | '
8954
9152
  */
8955
9153
  export declare function wrongItems(items: readonly TrainingItemResult[]): TrainingItemResult[];
8956
9154
 
9155
+ declare type 비율설정 = Pick<CoreNoiseSettings, 'level' | 'mainVolume'> & {
9156
+ noiseRatio?: number;
9157
+ };
9158
+
8957
9159
  export { }