@ihabdevteam/core 0.345.0 → 0.347.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.
- package/CHANGELOG.md +89 -2
- package/dist/components.cjs.js +14 -14
- package/dist/components.esm.css +2 -2
- package/dist/hooks.cjs.js +3 -3
- package/dist/i18n.cjs.js +3 -3
- package/dist/ihabdevteam-core.cjs.js +177 -177
- package/dist/ihabdevteam-core.d.ts +1 -9
- package/dist/packages/core/src/components/Popup/Popup.js +12 -1
- package/dist/packages/core/src/components/Training/TrainingPagePopup.js +8 -1
- package/dist/packages/core/src/components/Training/TrainingStepAudioPlayer.css +14 -3
- package/dist/packages/core/src/components/Training/TrainingStepAudioPlayer.js +17 -5
- package/dist/packages/core/src/components/Word/WordSearchResultBlock.css +8 -0
- package/dist/packages/core/src/components/Word/WordSearchResultBlock.js +22 -10
- package/dist/packages/core/src/i18n/locales/en.json +2 -1
- package/dist/packages/core/src/i18n/locales/ko.json +2 -1
- package/dist/packages/core/src/types/wordSearch.d.ts +0 -8
- package/dist/packages/core/src/utils/phonemePairSearch.js +1 -4
- package/dist/packages/core/src/version.d.ts +1 -1
- package/dist/packages/core/src/version.js +1 -1
- package/dist/utils.cjs.js +3 -3
- package/package.json +1 -1
|
@@ -1754,7 +1754,7 @@ export declare function copyText(text: string): Promise<boolean>;
|
|
|
1754
1754
|
*
|
|
1755
1755
|
* 값은 손으로 적고 `test/coreVersion.test.ts` 가 package.json 과 어긋나면 운다.
|
|
1756
1756
|
*/
|
|
1757
|
-
export declare const CORE_VERSION = "0.
|
|
1757
|
+
export declare const CORE_VERSION = "0.347.0";
|
|
1758
1758
|
|
|
1759
1759
|
/** @public */
|
|
1760
1760
|
declare const coreDefault: Record<string, unknown>;
|
|
@@ -11126,14 +11126,6 @@ export declare interface WordSearchResultGroup {
|
|
|
11126
11126
|
export declare interface WordSearchResultItem {
|
|
11127
11127
|
id: string;
|
|
11128
11128
|
words: WordSearchResultWord[];
|
|
11129
|
-
/**
|
|
11130
|
-
* 갈린 자리 — **옛 한글 이름**(`'초성'`·`'모음'`·`'종성'`·`'활음'`)이다.
|
|
11131
|
-
*
|
|
11132
|
-
* @deprecated `diffSectionCode` 를 쓴다 — 같은 자리를 코드로 가리킨다. 2026-10-13 안쪽을
|
|
11133
|
-
* 코드로 옮겼지만 **소비자가 이 값으로 묶음을 가른다**(말귀 `WordPairsPage` 의 `GROUP_ORDER`).
|
|
11134
|
-
* 혼자 바꾸면 그쪽 묶음이 조용히 빈다. 소비자가 옮긴 뒤 걷는다.
|
|
11135
|
-
*/
|
|
11136
|
-
diffSection?: string;
|
|
11137
11129
|
/** 갈린 자리 — 코드(`'onset'`·`'vowel'`·`'coda'`·`'glide'`). 이쪽이 정본이다. */
|
|
11138
11130
|
diffSectionCode?: PhonemeSectionCode;
|
|
11139
11131
|
diffHasEmpty?: boolean;
|
|
@@ -88,10 +88,21 @@ export default function Popup(props) {
|
|
|
88
88
|
// 원래 포커스였던 요소로 되돌린다(다른 창으로 옮겨 갔으면 그대로 둔다).
|
|
89
89
|
// 단, 자식이 자체 autoFocus를 쓰는 경우(SaveTablePopup 등)는 이미 그쪽으로 포커스가
|
|
90
90
|
// 가 있을 것이므로(브라우저가 커밋 시점에 처리) 뺏지 않는다.
|
|
91
|
+
/* **부른 자리는 그리는 중에 적는다**(말귀 청 (310), 2026-10-19).
|
|
92
|
+
전에는 아래 효과 안에서 `document.activeElement` 를 읽었다. 그런데 자식의 `autoFocus` 는
|
|
93
|
+
브라우저가 **커밋 시점에** 처리하고 효과는 그 **뒤에** 돈다 — 그래서 적히는 것이 부른
|
|
94
|
+
사람이 아니라 **그 자식**이고, 닫을 때는 이미 사라진 자리라 거는 것이 아무 일도 안 했다.
|
|
95
|
+
|
|
96
|
+
`useRef` 의 **초깃값**은 첫 그림에서 한 번만 셈되니 자식의 `autoFocus` 보다 먼저다.
|
|
97
|
+
|
|
98
|
+
**각각은 초록이었다** — *"자식이 초점을 쥔다"* 도 참, *"닫으면 되돌린다"* 도 참인데
|
|
99
|
+
겹치면 깨졌다. 그래서 몇 달 안 보였다(`test/popupFocusReturnWithAutoFocus.test.tsx`
|
|
100
|
+
가 둘을 **한 시험 안에서** 함께 센다). */
|
|
101
|
+
const previouslyFocusedRef = useRef(typeof document === 'undefined' ? null : document.activeElement);
|
|
91
102
|
useEffect(() => {
|
|
92
103
|
if (inline)
|
|
93
104
|
return;
|
|
94
|
-
const previouslyFocused =
|
|
105
|
+
const previouslyFocused = previouslyFocusedRef.current;
|
|
95
106
|
const panel = panelRef.current;
|
|
96
107
|
rememberFocusReturn(panel, previouslyFocused);
|
|
97
108
|
if (panel && !panel.contains(document.activeElement)) {
|
|
@@ -322,9 +322,16 @@ function TrainingPagePopupInner(props) {
|
|
|
322
322
|
끌어왔고**, 닫을 때 포커스를 되돌리는 코드는 없어 뒤 화면에서 포커스를 잃었다. */
|
|
323
323
|
const onBackRef = useRef(onBack);
|
|
324
324
|
useEffect(() => { onBackRef.current = onBack; });
|
|
325
|
+
/* **부른 자리는 그리는 중에 적는다**(말귀 청 (310), 2026-10-19).
|
|
326
|
+
전에는 이 효과 안에서 `document.activeElement` 를 읽었다. 자식의 `autoFocus` 는
|
|
327
|
+
브라우저가 **커밋 시점에** 처리하고 효과는 그 **뒤에** 돌아, 적히는 것이 부른 사람이
|
|
328
|
+
아니라 **그 자식**이었다 — 닫을 때는 이미 사라진 자리라 거는 것이 아무 일도 안 했다.
|
|
329
|
+
`useRef` 의 초깃값은 첫 그림에서 한 번만 셈되니 자식의 `autoFocus` 보다 먼저다.
|
|
330
|
+
`Popup.tsx` 와 같은 고침이다(같은 흠이 두 자리에 있었다). */
|
|
331
|
+
const previouslyFocusedRef = useRef(typeof document === 'undefined' ? null : document.activeElement);
|
|
325
332
|
useEffect(() => {
|
|
326
333
|
const panel = panelRef.current;
|
|
327
|
-
const previouslyFocused =
|
|
334
|
+
const previouslyFocused = previouslyFocusedRef.current;
|
|
328
335
|
rememberFocusReturn(panel, previouslyFocused);
|
|
329
336
|
if (panel) {
|
|
330
337
|
const firstEl = Array.from(panel.querySelectorAll(FOCUSABLE_SELECTOR)).filter(el => !el.closest('[aria-hidden="true"]'))[0];
|
|
@@ -248,11 +248,22 @@
|
|
|
248
248
|
box-shadow: 0 0 6px rgba(0,0,0,0.4);
|
|
249
249
|
}
|
|
250
250
|
|
|
251
|
-
/*
|
|
252
|
-
|
|
251
|
+
/* **눈에서 감추되 낭독기에는 남긴다**(사장님 결정 (다), 2026-10-19 — (305)).
|
|
252
|
+
전에는 `display: none` 이었다. 글자는 안 보였지만 **접근성 나무에서도 빠져** 기다리는
|
|
253
|
+
동안 아무 말도 안 읽혔다 — 위의 `role="status"`·`aria-live` 가 헛돌았다.
|
|
254
|
+
저장소 처음부터 그랬다(`speechear 복사본`).
|
|
255
|
+
|
|
256
|
+
이 저장소가 쓰는 감추는 규약(`Table.css`·`Board.css`·`WordTable.css` 의 `clip-path: inset(50%)`)
|
|
257
|
+
을 그대로 쓴다 — 자리를 안 차지하고 보이지 않지만 **읽힌다.**
|
|
258
|
+
`display: none` 이나 `visibility: hidden` 으로 바꾸면 읽히는 것이 다시 사라진다. */
|
|
253
259
|
.audio-player__loading .audio-player__loading-text,
|
|
254
260
|
.audio-player__panel.is-loading .audio-player__loading-text {
|
|
255
|
-
|
|
261
|
+
position: absolute;
|
|
262
|
+
width: 1px;
|
|
263
|
+
height: 1px;
|
|
264
|
+
overflow: hidden;
|
|
265
|
+
clip-path: inset(50%);
|
|
266
|
+
white-space: nowrap;
|
|
256
267
|
}
|
|
257
268
|
|
|
258
269
|
.audio-player__loading-spinner {
|
|
@@ -18,7 +18,9 @@ import Toast from '../Toast';
|
|
|
18
18
|
import Button from '../Button';
|
|
19
19
|
import Icon from '../Icon';
|
|
20
20
|
import InfoTooltip from '../Tooltip/InfoTooltip';
|
|
21
|
-
|
|
21
|
+
/* `STATUS_MESSAGES` 를 더는 안 들여온다 — 걸음마다 다른 글을 화면에 쓰지 않기 때문이다
|
|
22
|
+
(사장님 결정 (다), 2026-10-19). 그 표는 **지우지 않았다**: 개발 판의 자취와 색이 쓴다. */
|
|
23
|
+
import { ANNOUNCED_STATUSES } from './trainingStepAudioPlayerTypes';
|
|
22
24
|
import { wasKeyboardPress } from '../../hooks/inputModality';
|
|
23
25
|
import { measureAudioDuration, waitForCanPlay } from './audioWaits';
|
|
24
26
|
import { useWaveReady } from './useWaveReady';
|
|
@@ -1418,10 +1420,20 @@ export default function TrainingStepAudioPlayer({ btnLabel, soundSrc, bucketPath
|
|
|
1418
1420
|
const fillLeftPercent = Math.max(0, Math.min(100, (progress || 0) * 100));
|
|
1419
1421
|
const fillClass = cx('audio-player__fill', fillVisibleFlag ? 'is-visible' : '', fillExpandedFlag ? 'is-expanded' : '', fillPausedFlag ? 'is-paused' : '', isFillOutAnimating ? 'is-out' : '');
|
|
1420
1422
|
return (_jsxs("div", { className: `audio-player ${isDisabled ? 'is-disabled' : ''}${className ? ` ${className}` : ''}`, role: "group", "aria-label": typeof label === 'string' ? label : undefined, children: [_jsxs("div", { className: `audio-player__panel ${displayedPlaying ? "is-playing" : ""} ${isFetchingUrl ? "is-loading" : ""} ${stateLocal !== "default" ? "is-revealed" : ""}`, children: [isFetchingUrl ? (
|
|
1421
|
-
/*
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1423
|
+
/* **기다리는 동안 글자를 안 띄운다**(사장님 결정 (다), 2026-10-19 — (305)).
|
|
1424
|
+
전에는 걸음마다 글이 바뀌었다: `소리를 만들고 있습니다…` 가 **2ms 뒤에**
|
|
1425
|
+
`리소스 매핑 중…` 으로 덮이고 `서명 URL 조회…` · `다운로드 중…` 까지 늘어섰다
|
|
1426
|
+
(말귀 실측 2026-10-19: 캐시가 다 찬 가장 짧은 판에서도 94ms · 746ms).
|
|
1427
|
+
사장님 규칙은 **개발자의 말을 화면에 쓰지 않는다**다.
|
|
1428
|
+
|
|
1429
|
+
골라 둔 길 (가)(사람 말 한 마디로 묶기)가 아니라 **글을 아예 없애는** 쪽으로
|
|
1430
|
+
정하셨다 — 깜박이는 글은 한 마디로 묶어도 읽을 틈이 없다. 눈에는 동그라미만,
|
|
1431
|
+
**낭독기에는 한 마디**를 남긴다(그쪽은 깜박임이 없다).
|
|
1432
|
+
|
|
1433
|
+
`ANNOUNCED_STATUSES` 는 그대로 쓴다(사장님 결정 (나), 2026-10-05) — `준비 완료`
|
|
1434
|
+
처럼 일이 끝난 뒤의 알림은 읽히지 않는다. 걸음 이름과 그 글(`STATUS_MESSAGES`)은
|
|
1435
|
+
**지우지 않는다**: 개발 판의 자취와 색이 그것을 쓴다. */
|
|
1436
|
+
_jsxs("div", { className: "audio-player__loading", role: ANNOUNCED_STATUSES[fetchDebugStatus] ? 'status' : undefined, "aria-live": ANNOUNCED_STATUSES[fetchDebugStatus] ? 'polite' : 'off', children: [_jsx("span", { className: "audio-player__loading-spinner", "aria-hidden": "true" }), _jsx("span", { className: "audio-player__loading-text", children: t('training.audioStatus.preparing', { defaultValue: '소리를 준비하고 있습니다…' }) })] })) : null, stateLocal === "active" ? (_jsx("button", { className: "audio-player__button", type: "button",
|
|
1425
1437
|
/* 막힌 단계(아직 차례가 아닌 듣기·합성 주소를 받는 중)는 CSS pointer-events 로만 막아, Tab 으로 와서 Enter 를
|
|
1426
1438
|
누르면 소리를 받으러 가거나 "가져오지 못했습니다" 를 띄웠다 — 마우스와 같게 키보드도 막는다 */
|
|
1427
1439
|
disabled: isDisabled,
|
|
@@ -135,6 +135,14 @@
|
|
|
135
135
|
gap: 10px;
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
+
/* 묶음 이름 머리(말귀 청 (308)) — 앱이 `초성 대비`·`모음 대비` 처럼 넘긴다.
|
|
139
|
+
**글자 크기는 여기서 안 정한다** — 이 저장소는 `styles.css` 의 공용 클래스(`caption-1b`)로
|
|
140
|
+
준다(`date-picker__dow caption-1` 따위가 그 꼴이다). 세우다 `--font-label-m` 이라는
|
|
141
|
+
**없는 토큰**을 적었는데, 없는 변수는 조용히 아무 꼴도 안 내서 깨진 줄 몰랐다. */
|
|
142
|
+
.word-search-result-block__group-label {
|
|
143
|
+
color: var(--color-gray);
|
|
144
|
+
}
|
|
145
|
+
|
|
138
146
|
.word-search-result-block__group + .word-search-result-block__group {
|
|
139
147
|
margin-top: var(--spacing-l);
|
|
140
148
|
padding-top: var(--spacing-l);
|
|
@@ -3,7 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
3
3
|
import { cx } from '../../utils/cx';
|
|
4
4
|
import { compareKorean } from '../../utils/textSort';
|
|
5
5
|
import { useEscToClose } from '../../hooks/useEscToClose';
|
|
6
|
-
import { useState, useCallback, useMemo, useEffect } from 'react';
|
|
6
|
+
import { useState, useCallback, useMemo, useEffect, useId } from 'react';
|
|
7
7
|
import { useTranslation } from 'react-i18next';
|
|
8
8
|
import './WordSearchResultBlock.css';
|
|
9
9
|
import Button from '../Button';
|
|
@@ -126,6 +126,9 @@ export default function WordSearchResultBlock({ titleIcon, resultCount, resultUn
|
|
|
126
126
|
const pageClamped = Math.min(page, totalPages);
|
|
127
127
|
const pagedItems = useMemo(() => sortedItems.slice((pageClamped - 1) * pageSize, pageClamped * pageSize), [sortedItems, pageClamped, pageSize]);
|
|
128
128
|
// 현재 페이지의 그룹 항목을 그룹 순서대로 재그룹핑한다(그룹 헤더 유지; 페이지 경계에서 한 그룹이 나뉠 수 있음).
|
|
129
|
+
/* 묶음 이름표의 id 뿌리 — 한 쪽에 이 묶음이 둘 이상 설 수 있어 `useId()` 로 가른다.
|
|
130
|
+
이름 자체를 꼬리로 붙인다(한 쪽 안에서 `Map` 열쇠라 겹치지 않는다). */
|
|
131
|
+
const groupLabelBaseId = useId();
|
|
129
132
|
const pagedGroups = useMemo(() => {
|
|
130
133
|
if (!groups)
|
|
131
134
|
return null;
|
|
@@ -180,15 +183,24 @@ export default function WordSearchResultBlock({ titleIcon, resultCount, resultUn
|
|
|
180
183
|
과 아주 같은 h4 로 맞췄는데, 그러니 결과 덩이의 머리가 그 위 판 제목들(`검색 옵션` ·
|
|
181
184
|
`단어 검색 설정`)과 같은 급이 되어 **무엇이 무엇의 머리인지 안 보였다.** 거르개 판은
|
|
182
185
|
결과를 고르는 도구고 이 덩이는 그 결과라, 나란한 것이 아니라 아래 딸린 것이다. */
|
|
183
|
-
_jsxs(Section, { type: sectionType, className: cx('word-search-result-block', className), children: [_jsxs("div", { className: "word-search-result-block__inner", children: [(titleLabel || toolbar || discNode) && (_jsxs("div", { className: "word-search-result-block__head", children: [_jsxs("div", { className: "word-search-result-block__head-main", children: [titleIcon && _jsx(Icon, { icon: titleIcon, className: "word-search-result-block__head-icon" }), titleLabel && _jsx("span", { className: "word-search-result-block__title h3", children: titleLabel })] }), toolbar && _jsx("div", { className: "word-search-result-block__head-actions", children: toolbar }), discNode && _jsx("div", { className: "word-search-result-block__head-disc body-3", children: discNode })] })), (groups ? groups.length === 0 : items.length === 0) && (_jsxs("div", { className: "word-search-result-block__empty-wrap", children: [_jsx("p", { className: "word-search-result-block__empty body-3", children: t('wordSearch.emptyResult', '검색 결과가 없습니다.') }), emptyHint && _jsx("p", { className: "word-search-result-block__empty-hint body-3", children: emptyHint })] })), groups ? ((pagedGroups ?? []).map((group) => (
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
186
|
+
_jsxs(Section, { type: sectionType, className: cx('word-search-result-block', className), children: [_jsxs("div", { className: "word-search-result-block__inner", children: [(titleLabel || toolbar || discNode) && (_jsxs("div", { className: "word-search-result-block__head", children: [_jsxs("div", { className: "word-search-result-block__head-main", children: [titleIcon && _jsx(Icon, { icon: titleIcon, className: "word-search-result-block__head-icon" }), titleLabel && _jsx("span", { className: "word-search-result-block__title h3", children: titleLabel })] }), toolbar && _jsx("div", { className: "word-search-result-block__head-actions", children: toolbar }), discNode && _jsx("div", { className: "word-search-result-block__head-disc body-3", children: discNode })] })), (groups ? groups.length === 0 : items.length === 0) && (_jsxs("div", { className: "word-search-result-block__empty-wrap", children: [_jsx("p", { className: "word-search-result-block__empty body-3", children: t('wordSearch.emptyResult', '검색 결과가 없습니다.') }), emptyHint && _jsx("p", { className: "word-search-result-block__empty-hint body-3", children: emptyHint })] })), groups ? ((pagedGroups ?? []).map((group) => (
|
|
187
|
+
/* **묶음 이름을 머리로 그린다**(말귀 청 (308), 2026-10-19).
|
|
188
|
+
`pagedGroups` 가 `label` 을 들고 왔는데 **그리는 자리에서 안 썼다** — 받은 이름이
|
|
189
|
+
`key` 로만 쓰이고 사라져, 보는 사람은 어느 자리가 갈린 쌍인지 모른 채 한 덩어리로
|
|
190
|
+
봤다(말귀 실측: 묶음 네 개를 넘겼는데 `__group` 이 1 개, 이름 글자 0 건, 1,159쌍).
|
|
191
|
+
`pagedGroups` 가 쪽마다 다시 묶으므로 **쪽 경계에서 나뉘면 나뉜 쪽에도 다시 선다.**
|
|
192
|
+
|
|
193
|
+
이름을 `aria-labelledby` 로 가리킨다 — `IconGridMenu` 가 쓰는 규약이다.
|
|
194
|
+
`role="group"` 은 **이름이 없으면 낭독기가 묶음을 안 알린다**(`ColorPicker` 의 주석). */
|
|
195
|
+
_jsxs("div", { className: "word-search-result-block__group", role: "group", "aria-labelledby": `${groupLabelBaseId}-${group.label}`, children: [_jsx("div", { className: "word-search-result-block__group-label caption-1b", id: `${groupLabelBaseId}-${group.label}`, children: group.label }), _jsx("div", { className: "word-search-result-block__items", children: group.items.map((item) => {
|
|
196
|
+
const isSelected = selected.includes(item.id);
|
|
197
|
+
return (_jsx("div", { onMouseEnter: e => {
|
|
198
|
+
openHover(e.currentTarget);
|
|
199
|
+
setHoverWords(item.words ?? []);
|
|
200
|
+
}, onMouseLeave: handleHoverLeave, children: _jsx(Button, { htmlType: "button", size: "word", align: "grid", label: (item.words ?? []).map(w => w.label), subLabel: (item.words ?? []).some(w => w.subLabel)
|
|
201
|
+
? (item.words ?? []).map(w => w.subLabel)
|
|
202
|
+
: undefined, selected: isSelected, onClick: () => handleClick(item.id), minWidth: 200 }) }, item.id));
|
|
203
|
+
}) })] }, group.label)))) : (_jsx("div", { className: "word-search-result-block__items", children: pagedItems.map((item) => {
|
|
192
204
|
const isSelected = selected.includes(item.id);
|
|
193
205
|
return (_jsx("div", { onMouseEnter: e => {
|
|
194
206
|
openHover(e.currentTarget);
|
|
@@ -470,7 +470,8 @@
|
|
|
470
470
|
"measuring_duration": "Measuring length…",
|
|
471
471
|
"ready": "Ready",
|
|
472
472
|
"error": "Error",
|
|
473
|
-
"synthesizing": "Preparing the sound…"
|
|
473
|
+
"synthesizing": "Preparing the sound…",
|
|
474
|
+
"preparing": "Preparing sound…"
|
|
474
475
|
}
|
|
475
476
|
},
|
|
476
477
|
"trainingSettings": {
|
|
@@ -19,14 +19,6 @@ export interface WordSearchResultWord {
|
|
|
19
19
|
export interface WordSearchResultItem {
|
|
20
20
|
id: string;
|
|
21
21
|
words: WordSearchResultWord[];
|
|
22
|
-
/**
|
|
23
|
-
* 갈린 자리 — **옛 한글 이름**(`'초성'`·`'모음'`·`'종성'`·`'활음'`)이다.
|
|
24
|
-
*
|
|
25
|
-
* @deprecated `diffSectionCode` 를 쓴다 — 같은 자리를 코드로 가리킨다. 2026-10-13 안쪽을
|
|
26
|
-
* 코드로 옮겼지만 **소비자가 이 값으로 묶음을 가른다**(말귀 `WordPairsPage` 의 `GROUP_ORDER`).
|
|
27
|
-
* 혼자 바꾸면 그쪽 묶음이 조용히 빈다. 소비자가 옮긴 뒤 걷는다.
|
|
28
|
-
*/
|
|
29
|
-
diffSection?: string;
|
|
30
22
|
/** 갈린 자리 — 코드(`'onset'`·`'vowel'`·`'coda'`·`'glide'`). 이쪽이 정본이다. */
|
|
31
23
|
diffSectionCode?: import('../utils/phonemeSectionCodes').PhonemeSectionCode;
|
|
32
24
|
diffHasEmpty?: boolean;
|
|
@@ -16,7 +16,7 @@ function decompose(char) {
|
|
|
16
16
|
/* 음절 자리 코드는 **아래 모듈**에 있다 — `phonemeConvert` 도 함께 쓰는데, 여기 두면
|
|
17
17
|
`phonemePairSearch ↔ phonemeConvert` 돌림 import 가 된다(한 번 그렇게 해 시험 338 꾸러미가
|
|
18
18
|
무너졌다, 2026-10-13). */
|
|
19
|
-
import { toPhonemeCodes,
|
|
19
|
+
import { toPhonemeCodes, isPhonemeSectionCode, PHONEME_SECTION_CODES, PHONEME_NONE } from './phonemeSectionCodes';
|
|
20
20
|
export { toPhonemeCodes, PHONEME_NONE } from './phonemeSectionCodes';
|
|
21
21
|
const SECTION_KEYS = PHONEME_SECTION_CODES;
|
|
22
22
|
const NONE = PHONEME_NONE;
|
|
@@ -444,8 +444,6 @@ function searchPairsCore(selectedList, words, debug, enforcePosition, phonemic =
|
|
|
444
444
|
for (const sec of senses.get(second) ?? [second]) {
|
|
445
445
|
results.push({
|
|
446
446
|
id: `${wordKey(f)}|${wordKey(sec)}`,
|
|
447
|
-
/* 옛 이름을 내보내고 코드를 나란히 — 소비자가 옛 값으로 묶음을 가른다 */
|
|
448
|
-
diffSection: toLegacyPhonemeKey(sDiffSection),
|
|
449
447
|
diffSectionCode: sDiffSection,
|
|
450
448
|
diffHasEmpty: sDiffHasEmpty,
|
|
451
449
|
words: [wordCells(f), wordCells(sec)],
|
|
@@ -689,7 +687,6 @@ preferAsTarget) {
|
|
|
689
687
|
const kt = wordKey(t), kc = wordKey(c);
|
|
690
688
|
results.push({
|
|
691
689
|
id: kt < kc ? `${kt}|${kc}` : `${kc}|${kt}`,
|
|
692
|
-
diffSection: toLegacyPhonemeKey(item.diffSection),
|
|
693
690
|
diffSectionCode: item.diffSection,
|
|
694
691
|
diffHasEmpty: item.diffHasEmpty,
|
|
695
692
|
words: [wordCells(t), wordCells(c)],
|