@ihabdevteam/core 0.323.0 → 0.324.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.
@@ -1678,7 +1678,7 @@ export declare function copyText(text: string): Promise<boolean>;
1678
1678
  *
1679
1679
  * 값은 손으로 적고 `test/coreVersion.test.ts` 가 package.json 과 어긋나면 운다.
1680
1680
  */
1681
- export declare const CORE_VERSION = "0.323.0";
1681
+ export declare const CORE_VERSION = "0.324.0";
1682
1682
 
1683
1683
  /** @public */
1684
1684
  declare const coreDefault: Record<string, unknown>;
@@ -10700,7 +10700,7 @@ export declare interface WordAttributeFilterKeys {
10700
10700
  }
10701
10701
 
10702
10702
  /** @public 공용 속성 필터 섹션 코드 */
10703
- export declare type WordAttributeSectionCode = 'grade' | 'syllables' | 'pos' | 'wordType';
10703
+ export declare type WordAttributeSectionCode = 'grade' | 'syllables' | 'pos' | 'wordType' | 'field';
10704
10704
 
10705
10705
  /** 정답 단어 구성 선택 상태 (정답 단어에만 적용 — 오답은 대립 관계로 결정) */
10706
10706
  export declare interface WordComposition {
@@ -922,6 +922,7 @@
922
922
  "modifier": "Adverb/Determiner"
923
923
  },
924
924
  "wordType": "Word Type",
925
+ "field": "Semantic Category",
925
926
  "type": {
926
927
  "native": "Native Korean",
927
928
  "sino": "Sino-Korean",
@@ -909,6 +909,7 @@
909
909
  "modifier": "수식언(부사, 관형사)"
910
910
  },
911
911
  "wordType": "유형",
912
+ "field": "의미범주",
912
913
  "type": {
913
914
  "native": "고유어",
914
915
  "sino": "한자어",
@@ -127,6 +127,7 @@ const DEFAULT_PAIR_FILTER_KEYS = {
127
127
  syllable5plus: '5음절 이상',
128
128
  partOfSpeech: '품사',
129
129
  wordType: '유형',
130
+ field: '의미범주',
130
131
  posMap: POS_GROUP_MAP_KO,
131
132
  typeMap: WORD_TYPE_MAP_KO,
132
133
  };
@@ -135,13 +136,14 @@ function collect(filterSelected, keys) {
135
136
  const hit = collected.get(filterSelected);
136
137
  if (hit && hit.keys === keys)
137
138
  return hit.value;
138
- const value = { grades: [], syls: [], poses: [], types: [] };
139
- const bySection = { grade: value.grades, syllables: value.syls, pos: value.poses, wordType: value.types };
139
+ const value = { grades: [], syls: [], poses: [], types: [], fields: [] };
140
+ const bySection = { grade: value.grades, syllables: value.syls, pos: value.poses, wordType: value.types, field: value.fields };
140
141
  for (const [label, raw] of Object.entries(filterSelected ?? {})) {
141
142
  /* 섹션은 넘긴 keys 의 라벨이면 그것, 아니면 어느 언어·옛 라벨·코드든 읽는다(wordAttributeSectionOf) */
142
143
  const s = label === keys.difficulty ? 'grade' : label === keys.syllables ? 'syllables'
143
144
  : label === keys.partOfSpeech ? 'pos' : label === keys.wordType ? 'wordType'
144
- : wordAttributeSectionOf(label);
145
+ : label === keys.field ? 'field'
146
+ : wordAttributeSectionOf(label);
145
147
  if (!s)
146
148
  continue;
147
149
  for (const v of Array.isArray(raw) ? raw : typeof raw === 'string' ? [raw] : [])
@@ -193,8 +195,14 @@ export function applyFilters(filterSelected, words, keys = DEFAULT_PAIR_FILTER_K
193
195
  return words.filter(entry => {
194
196
  if (!matchesFilters({ grade: entry.grade, syllables: [...entry.word].length, pos: entry.pos, wordType: entry.wordType }, filterSelected, keys))
195
197
  return false;
196
- const fields = filterSelected['의미범주'];
197
- if (fields?.length) {
198
+ /* **의미범주도 다른 넷과 같은 길로 읽는다**(사장님 지시 2026-10-14, 말귀 실측으로 다리는
199
+ 필요 없음이 확인됐다 — 앱 소스 0건·저장 구성 416줄 0건).
200
+
201
+ 전에는 `filterSelected['의미범주']` 로 **그 다섯 글자만** 찾았다. 다른 절은 어느 언어
202
+ 라벨이든 읽는데 이 하나만 안 그랬고, 영어 라벨로 부르면 **이 필터만 조용히 안 걸렸다**
203
+ (에러도 없다). 같은 꼴을 등급·음절에서 이미 겪어 고쳤던 자리다. */
204
+ const { fields } = collect(filterSelected, keys);
205
+ if (fields.length) {
198
206
  const normalized = (entry.field ?? '').replace(/전문\(|\)/g, '');
199
207
  if (!fields.some(f => normalized.includes(f)))
200
208
  return false;
@@ -25,6 +25,8 @@ export interface PairFilterKeys {
25
25
  syllable5plus: string;
26
26
  partOfSpeech: string;
27
27
  wordType: string;
28
+ /** 의미범주 절의 이름표. **안 줘도 된다** — 안 주면 어느 언어 라벨이든 사전으로 읽는다(0.324.0). */
29
+ field?: string;
28
30
  posMap: Record<string, string[]>;
29
31
  typeMap: Record<string, string>;
30
32
  }
@@ -1,5 +1,5 @@
1
1
  /** @public 공용 속성 필터 섹션 코드 */
2
- export type WordAttributeSectionCode = 'grade' | 'syllables' | 'pos' | 'wordType';
2
+ export type WordAttributeSectionCode = 'grade' | 'syllables' | 'pos' | 'wordType' | 'field';
3
3
  /**
4
4
  * **등급이 빈 단어를 가리키는 코드** — `'none'`((205), 사장님 지시 2026-10-12).
5
5
  *
@@ -11,6 +11,18 @@
11
11
  * (useWordTrainingFilter 의 POS_CODES·toAttrCodes 와 같은 코드다.)
12
12
  */
13
13
  import i18n from '../i18n';
14
+ /**
15
+ * **값이 코드가 아닌 절** — 지금은 `field`(의미범주) 하나다.
16
+ *
17
+ * 다른 넷은 값도 코드다(`'1'`·`'5+'`·`'noun'`·`'sino'`). 이 절의 값은 **사전이 들고 있는
18
+ * 분야 글자 자신**(`의학`·`법률`…)이라 미리 셀 수가 없고, 맞출 때도 같은지가 아니라
19
+ * **들어 있는지**로 본다(`entry.field` 가 `전문(의학)` 꼴이다).
20
+ *
21
+ * 그래서 값은 **그대로 지나간다** — 코드로 바꾸지도, 라벨로 되돌리지도 않는다. 이것을 안
22
+ * 가르면 저장할 때 `wordAttributeValueCodeOf` 가 `null` 을 내어 **고른 값이 통째로
23
+ * 버려지고**, 되읽을 때도 사전에 없어 사라진다. 둘 다 조용히 일어난다.
24
+ */
25
+ const FREE_VALUE_SECTIONS = new Set(['field']);
14
26
  /**
15
27
  * **등급이 빈 단어를 가리키는 코드** — `'none'`((205), 사장님 지시 2026-10-12).
16
28
  *
@@ -59,13 +71,14 @@ export function matchesGradeCodes(codes, grade) {
59
71
  return set.has(GRADE_NONE);
60
72
  return set.has(String(grade));
61
73
  }
62
- const sectionCodes = ['grade', 'syllables', 'pos', 'wordType'];
74
+ const sectionCodes = ['grade', 'syllables', 'pos', 'wordType', 'field'];
63
75
  /** 등급 코드 — `'none'` 은 **등급이 빈 단어**다(값이 아니라 **빔**이다. GRADE_NONE 주석을 볼 것) */
64
76
  const gradeValues = ['1', '2', '3', '4', 'none'];
65
77
  const syllableValues = ['1', '2', '3', '4', '5+'];
66
78
  const posValues = ['noun', 'verb', 'modifier'];
67
79
  const wordTypeValues = ['native', 'sino', 'foreign', 'mixed'];
68
- const valuesBySection = { grade: gradeValues, syllables: syllableValues, pos: posValues, wordType: wordTypeValues };
80
+ /* `field` 는 값이 자유라 셀 목록이 없다 — `FREE_VALUE_SECTIONS` 를 볼 것 */
81
+ const valuesBySection = { grade: gradeValues, syllables: syllableValues, pos: posValues, wordType: wordTypeValues, field: [] };
69
82
  /** 품사 코드 → 사전 품사 약자(단어의 pos 에 들어 있는지 본다) · 어종 코드 → 사전 어종 값 */
70
83
  export const POS_CODE_TARGETS = { noun: ['명', '수'], verb: ['동', '형'], modifier: ['부', '관'] };
71
84
  export const WORD_TYPE_CODE_VALUES = { native: '고유', sino: '한자', foreign: '외래', mixed: '혼종' };
@@ -91,6 +104,7 @@ function dictFor(lng) {
91
104
  syllables: t('wordSearch.filter.syllables', '음절 수'),
92
105
  pos: t('wordSearch.filter.partOfSpeech', '품사'),
93
106
  wordType: t('wordSearch.filter.wordType', '유형'),
107
+ field: t('wordSearch.filter.field', '의미범주'),
94
108
  },
95
109
  value: {
96
110
  grade: {
@@ -113,6 +127,8 @@ function dictFor(lng) {
113
127
  foreign: t('wordSearch.filter.type.foreign', '외래어'),
114
128
  mixed: t('wordSearch.filter.type.mixed', '혼종어'),
115
129
  },
130
+ /* 값이 자유로운 절 — 라벨이 곧 값이라 표가 비어 있다(`wordAttributeValueLabelOf` 가 코드를 그대로 돌려준다) */
131
+ field: {},
116
132
  },
117
133
  };
118
134
  dictCache.set(lng, d);
@@ -136,6 +152,9 @@ export function wordAttributeSectionOf(label) {
136
152
  }
137
153
  /** 한 섹션의 값(코드·어느 언어 라벨·옛 라벨) → 값 코드. 못 읽으면 null. */
138
154
  export function wordAttributeValueCodeOf(section, value) {
155
+ /* 값이 자유로운 절은 **그대로 지나간다** — 코드가 따로 없다(`FREE_VALUE_SECTIONS`) */
156
+ if (FREE_VALUE_SECTIONS.has(section))
157
+ return value;
139
158
  if (valuesBySection[section].includes(value))
140
159
  return value;
141
160
  for (const lng of languages()) {
@@ -191,7 +210,8 @@ export function wordAttributeFilterFromCodes(codes, lng = i18n.language) {
191
210
  const d = dictFor(lng || 'ko');
192
211
  const out = {};
193
212
  for (const s of sectionCodes) {
194
- const labels = (codes?.[s] ?? []).map((c) => d.value[s][c]).filter((x) => !!x);
213
+ /* 자유 절은 값이 곧 라벨이다 — 사전에 없다고 버리면 저장했다 되읽을 때 사라진다 */
214
+ const labels = (codes?.[s] ?? []).map((c) => d.value[s][c] ?? (FREE_VALUE_SECTIONS.has(s) ? c : undefined)).filter((x) => !!x);
195
215
  if (labels.length)
196
216
  out[d.section[s]] = labels;
197
217
  }
@@ -46,4 +46,4 @@
46
46
  *
47
47
  * 값은 손으로 적고 `test/coreVersion.test.ts` 가 package.json 과 어긋나면 운다.
48
48
  */
49
- export declare const CORE_VERSION = "0.323.0";
49
+ export declare const CORE_VERSION = "0.324.0";
@@ -46,4 +46,4 @@
46
46
  *
47
47
  * 값은 손으로 적고 `test/coreVersion.test.ts` 가 package.json 과 어긋나면 운다.
48
48
  */
49
- export const CORE_VERSION = '0.323.0';
49
+ export const CORE_VERSION = '0.324.0';