@ihabdevteam/core 0.193.0 → 0.194.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.
@@ -1626,7 +1626,7 @@ export declare function copyText(text: string): Promise<boolean>;
1626
1626
  *
1627
1627
  * 값은 손으로 적고 `test/coreVersion.test.ts` 가 package.json 과 어긋나면 운다.
1628
1628
  */
1629
- export declare const CORE_VERSION = "0.193.0";
1629
+ export declare const CORE_VERSION = "0.194.0";
1630
1630
 
1631
1631
  /** @public */
1632
1632
  declare const coreDefault: Record<string, unknown>;
@@ -4889,6 +4889,35 @@ export declare interface PopupProps {
4889
4889
 
4890
4890
  export declare const POS_LABEL: Record<string, string>;
4891
4891
 
4892
+ /**
4893
+ * **늦게 받는 화면 청크를 한가할 때 미리 받아 둔다.**
4894
+ *
4895
+ * `lazyPage` 로 미룬 화면은 **그리려는 순간** 청크를 받는다. 그 사이 화면에는 동그라미만 돌고
4896
+ * 제목도 구획도 하나도 없다(말귀 실측 2026-09-20: 고객 상세가 누른 뒤 **364ms** 동안 빈 화면.
4897
+ * 사용자가 짚었다 — "고객을 누르면 pagetitle, 차트섹션들이 하나도 안 보이는 순간이 있는데").
4898
+ *
4899
+ * 그래서 **누르기 전에** 받아 둔다. 목록이 선 뒤 한가할 때 같은 모듈을 한 번 부르면 번들러가
4900
+ * 캐시하고, 누르는 순간 `lazy` 가 곧바로 풀려 **뼈대가 먼저 선다** — 자료를 기다리는 일은 그
4901
+ * 뼈대 안에서 일어난다.
4902
+ *
4903
+ * 지키는 것 둘:
4904
+ * - **첫 그리기를 안 건드린다.** 화면이 선 뒤 한가한 틈에만 받는다(`requestIdleCallback`).
4905
+ * 그 함수는 **사파리에 없다** — 없으면 짧게 미뤄(300ms) 그리는 일을 막지 않을 만큼만 늦춘다.
4906
+ * - **실패를 삼킨다.** 미리 받기가 안 되면 누를 때 받으면 된다(그러면 예전과 같을 뿐이다).
4907
+ * 그것 때문에 오류 화면을 띄울 일이 아니다.
4908
+ *
4909
+ * 돌려주는 것은 **그만두는 함수**다 — 효과에서 그대로 돌려주면 화면을 떠날 때 예약이 걷힌다.
4910
+ *
4911
+ * ```ts
4912
+ * useEffect(() => prefetchChunk(() => import('./pages/CustomerDetail')), [])
4913
+ * ```
4914
+ *
4915
+ * 두 앱(회원앱 · 어드민)이 한 벌씩 들고 있던 것이다 — 번들이 갈려 앱끼리는 합칠 수 없는
4916
+ * 자리라 코어로 왔다(2026-09-25). 브라우저 차이를 아는 일은 `chunkSkew`·`lazyPage` 와 같은 결이다.
4917
+ * @public
4918
+ */
4919
+ export declare function prefetchChunk(...loads: readonly (() => Promise<unknown>)[]): () => void;
4920
+
4892
4921
  /**
4893
4922
  * @public
4894
4923
  * 여러 단어의 이미지 존재 여부를 사전 확인 (병렬). localStorage에 영구 저장됨.
@@ -9038,6 +9067,29 @@ export declare function usePagedList<T>(items: T[], perPage: number, page: numbe
9038
9067
  curPage: number;
9039
9068
  };
9040
9069
 
9070
+ /**
9071
+ * **거르는 조건이 바뀌면 첫 쪽으로 돌아간다.** `usePagedList` 의 짝이다.
9072
+ *
9073
+ * 안 돌리면 **3쪽을 보던 채로 검색어를 바꿨을 때 결과가 한 쪽뿐이라 빈 표가 뜬다** — 찾는 것이
9074
+ * 없어서가 아니라 **없는 쪽을 보고 있어서**다. 화면 셋(어드민 문장·낱말 자료, 회원앱 낱말 관리)이
9075
+ * 같은 세 줄을 각자 적고 있었고, 그것을 한 벌로 모은 뒤에도 두 앱에 한 벌씩 남아 있었다
9076
+ * (번들이 갈려 앱끼리는 합칠 수 없는 자리다 — 2026-09-25 코어로).
9077
+ *
9078
+ * **효과(`useEffect`)가 아니라 렌더 중에 고친다.** 효과로 미루면 한 번은 옛 쪽 번호로 그려진 뒤
9079
+ * 다음 프레임에 1쪽이 되어, 새 조건의 결과가 **빈 표로 한 번 깜빡인다.** 렌더 중 상태 조정은
9080
+ * React 가 이 꼴을 위해 내준 길이다(같은 컴포넌트를 바로 다시 그린다).
9081
+ *
9082
+ * ```ts
9083
+ * usePageResetOnChange(`${검색어}|${갈래}|${정렬}`, setPage)
9084
+ * const { pageItems, totalPages } = usePagedList(걸러낸것, 쪽크기, page)
9085
+ * ```
9086
+ *
9087
+ * @param 열쇠 조건을 하나로 이어 붙인 글 — 이것이 달라지면 첫 쪽이다
9088
+ * @param setPage 쪽 번호를 쥔 쪽에서 준다
9089
+ * @public
9090
+ */
9091
+ export declare function usePageResetOnChange(열쇠: string, setPage: (n: number) => void): void;
9092
+
9041
9093
  /**
9042
9094
  * 비밀번호 변경 폼 상태와 핸들러를 캡슐화한 훅.
9043
9095
  * showModal 상태는 컴포넌트가 직접 관리 (훅 밖에 두어야 모달 열기 트리거 제어 가능).
@@ -0,0 +1,22 @@
1
+ /**
2
+ * **거르는 조건이 바뀌면 첫 쪽으로 돌아간다.** `usePagedList` 의 짝이다.
3
+ *
4
+ * 안 돌리면 **3쪽을 보던 채로 검색어를 바꿨을 때 결과가 한 쪽뿐이라 빈 표가 뜬다** — 찾는 것이
5
+ * 없어서가 아니라 **없는 쪽을 보고 있어서**다. 화면 셋(어드민 문장·낱말 자료, 회원앱 낱말 관리)이
6
+ * 같은 세 줄을 각자 적고 있었고, 그것을 한 벌로 모은 뒤에도 두 앱에 한 벌씩 남아 있었다
7
+ * (번들이 갈려 앱끼리는 합칠 수 없는 자리다 — 2026-09-25 코어로).
8
+ *
9
+ * **효과(`useEffect`)가 아니라 렌더 중에 고친다.** 효과로 미루면 한 번은 옛 쪽 번호로 그려진 뒤
10
+ * 다음 프레임에 1쪽이 되어, 새 조건의 결과가 **빈 표로 한 번 깜빡인다.** 렌더 중 상태 조정은
11
+ * React 가 이 꼴을 위해 내준 길이다(같은 컴포넌트를 바로 다시 그린다).
12
+ *
13
+ * ```ts
14
+ * usePageResetOnChange(`${검색어}|${갈래}|${정렬}`, setPage)
15
+ * const { pageItems, totalPages } = usePagedList(걸러낸것, 쪽크기, page)
16
+ * ```
17
+ *
18
+ * @param 열쇠 조건을 하나로 이어 붙인 글 — 이것이 달라지면 첫 쪽이다
19
+ * @param setPage 쪽 번호를 쥔 쪽에서 준다
20
+ * @public
21
+ */
22
+ export declare function usePageResetOnChange(열쇠: string, setPage: (n: number) => void): void;
@@ -0,0 +1,30 @@
1
+ 'use client';
2
+ import { useState } from 'react';
3
+ /**
4
+ * **거르는 조건이 바뀌면 첫 쪽으로 돌아간다.** `usePagedList` 의 짝이다.
5
+ *
6
+ * 안 돌리면 **3쪽을 보던 채로 검색어를 바꿨을 때 결과가 한 쪽뿐이라 빈 표가 뜬다** — 찾는 것이
7
+ * 없어서가 아니라 **없는 쪽을 보고 있어서**다. 화면 셋(어드민 문장·낱말 자료, 회원앱 낱말 관리)이
8
+ * 같은 세 줄을 각자 적고 있었고, 그것을 한 벌로 모은 뒤에도 두 앱에 한 벌씩 남아 있었다
9
+ * (번들이 갈려 앱끼리는 합칠 수 없는 자리다 — 2026-09-25 코어로).
10
+ *
11
+ * **효과(`useEffect`)가 아니라 렌더 중에 고친다.** 효과로 미루면 한 번은 옛 쪽 번호로 그려진 뒤
12
+ * 다음 프레임에 1쪽이 되어, 새 조건의 결과가 **빈 표로 한 번 깜빡인다.** 렌더 중 상태 조정은
13
+ * React 가 이 꼴을 위해 내준 길이다(같은 컴포넌트를 바로 다시 그린다).
14
+ *
15
+ * ```ts
16
+ * usePageResetOnChange(`${검색어}|${갈래}|${정렬}`, setPage)
17
+ * const { pageItems, totalPages } = usePagedList(걸러낸것, 쪽크기, page)
18
+ * ```
19
+ *
20
+ * @param 열쇠 조건을 하나로 이어 붙인 글 — 이것이 달라지면 첫 쪽이다
21
+ * @param setPage 쪽 번호를 쥔 쪽에서 준다
22
+ * @public
23
+ */
24
+ export function usePageResetOnChange(열쇠, setPage) {
25
+ const [본열쇠, set본열쇠] = useState(열쇠);
26
+ if (본열쇠 !== 열쇠) {
27
+ set본열쇠(열쇠);
28
+ setPage(1);
29
+ }
30
+ }
@@ -23,6 +23,7 @@ export * from './hooks/useEscToClose';
23
23
  export * from './hooks/useWordMeanings';
24
24
  export * from './hooks/useHoverAnchor';
25
25
  export * from './hooks/usePagedList';
26
+ export * from './hooks/usePageResetOnChange';
26
27
  export * from './hooks/useToastState';
27
28
  export { useTrainingTypeLabels } from './hooks/useTrainingTypeLabels';
28
29
  export { CORE_VERSION } from './version';
@@ -22,6 +22,7 @@ export * from './hooks/useEscToClose';
22
22
  export * from './hooks/useWordMeanings';
23
23
  export * from './hooks/useHoverAnchor';
24
24
  export * from './hooks/usePagedList';
25
+ export * from './hooks/usePageResetOnChange';
25
26
  // 토스트 상태(문구 + 종류 + 재실행 번호). 회원 앱의 useToast 가 이 위에 얹혀 있다 —
26
27
  // 같은 관용구를 두 벌로 두지 않으려면 패키지 밖에서도 보여야 한다.
27
28
  export * from './hooks/useToastState';
@@ -0,0 +1,28 @@
1
+ /**
2
+ * **늦게 받는 화면 청크를 한가할 때 미리 받아 둔다.**
3
+ *
4
+ * `lazyPage` 로 미룬 화면은 **그리려는 순간** 청크를 받는다. 그 사이 화면에는 동그라미만 돌고
5
+ * 제목도 구획도 하나도 없다(말귀 실측 2026-09-20: 고객 상세가 누른 뒤 **364ms** 동안 빈 화면.
6
+ * 사용자가 짚었다 — "고객을 누르면 pagetitle, 차트섹션들이 하나도 안 보이는 순간이 있는데").
7
+ *
8
+ * 그래서 **누르기 전에** 받아 둔다. 목록이 선 뒤 한가할 때 같은 모듈을 한 번 부르면 번들러가
9
+ * 캐시하고, 누르는 순간 `lazy` 가 곧바로 풀려 **뼈대가 먼저 선다** — 자료를 기다리는 일은 그
10
+ * 뼈대 안에서 일어난다.
11
+ *
12
+ * 지키는 것 둘:
13
+ * - **첫 그리기를 안 건드린다.** 화면이 선 뒤 한가한 틈에만 받는다(`requestIdleCallback`).
14
+ * 그 함수는 **사파리에 없다** — 없으면 짧게 미뤄(300ms) 그리는 일을 막지 않을 만큼만 늦춘다.
15
+ * - **실패를 삼킨다.** 미리 받기가 안 되면 누를 때 받으면 된다(그러면 예전과 같을 뿐이다).
16
+ * 그것 때문에 오류 화면을 띄울 일이 아니다.
17
+ *
18
+ * 돌려주는 것은 **그만두는 함수**다 — 효과에서 그대로 돌려주면 화면을 떠날 때 예약이 걷힌다.
19
+ *
20
+ * ```ts
21
+ * useEffect(() => prefetchChunk(() => import('./pages/CustomerDetail')), [])
22
+ * ```
23
+ *
24
+ * 두 앱(회원앱 · 어드민)이 한 벌씩 들고 있던 것이다 — 번들이 갈려 앱끼리는 합칠 수 없는
25
+ * 자리라 코어로 왔다(2026-09-25). 브라우저 차이를 아는 일은 `chunkSkew`·`lazyPage` 와 같은 결이다.
26
+ * @public
27
+ */
28
+ export declare function prefetchChunk(...loads: readonly (() => Promise<unknown>)[]): () => void;
@@ -0,0 +1,44 @@
1
+ /**
2
+ * **늦게 받는 화면 청크를 한가할 때 미리 받아 둔다.**
3
+ *
4
+ * `lazyPage` 로 미룬 화면은 **그리려는 순간** 청크를 받는다. 그 사이 화면에는 동그라미만 돌고
5
+ * 제목도 구획도 하나도 없다(말귀 실측 2026-09-20: 고객 상세가 누른 뒤 **364ms** 동안 빈 화면.
6
+ * 사용자가 짚었다 — "고객을 누르면 pagetitle, 차트섹션들이 하나도 안 보이는 순간이 있는데").
7
+ *
8
+ * 그래서 **누르기 전에** 받아 둔다. 목록이 선 뒤 한가할 때 같은 모듈을 한 번 부르면 번들러가
9
+ * 캐시하고, 누르는 순간 `lazy` 가 곧바로 풀려 **뼈대가 먼저 선다** — 자료를 기다리는 일은 그
10
+ * 뼈대 안에서 일어난다.
11
+ *
12
+ * 지키는 것 둘:
13
+ * - **첫 그리기를 안 건드린다.** 화면이 선 뒤 한가한 틈에만 받는다(`requestIdleCallback`).
14
+ * 그 함수는 **사파리에 없다** — 없으면 짧게 미뤄(300ms) 그리는 일을 막지 않을 만큼만 늦춘다.
15
+ * - **실패를 삼킨다.** 미리 받기가 안 되면 누를 때 받으면 된다(그러면 예전과 같을 뿐이다).
16
+ * 그것 때문에 오류 화면을 띄울 일이 아니다.
17
+ *
18
+ * 돌려주는 것은 **그만두는 함수**다 — 효과에서 그대로 돌려주면 화면을 떠날 때 예약이 걷힌다.
19
+ *
20
+ * ```ts
21
+ * useEffect(() => prefetchChunk(() => import('./pages/CustomerDetail')), [])
22
+ * ```
23
+ *
24
+ * 두 앱(회원앱 · 어드민)이 한 벌씩 들고 있던 것이다 — 번들이 갈려 앱끼리는 합칠 수 없는
25
+ * 자리라 코어로 왔다(2026-09-25). 브라우저 차이를 아는 일은 `chunkSkew`·`lazyPage` 와 같은 결이다.
26
+ * @public
27
+ */
28
+ export function prefetchChunk(...loads) {
29
+ let 취소됨 = false;
30
+ const 받기 = () => { if (!취소됨)
31
+ for (const load of loads)
32
+ void load().catch(() => { }); };
33
+ const idle = globalThis.requestIdleCallback;
34
+ if (idle) {
35
+ const id = idle(받기, { timeout: 2000 });
36
+ return () => {
37
+ 취소됨 = true;
38
+ const cancel = globalThis.cancelIdleCallback;
39
+ cancel?.(id);
40
+ };
41
+ }
42
+ const id = setTimeout(받기, 300);
43
+ return () => { 취소됨 = true; clearTimeout(id); };
44
+ }
@@ -57,4 +57,5 @@ export { PLAN_FEATURES, PLAN_LIMITS, TIER_RANK, getPlanTier, isManagerTier, type
57
57
  export { WORD_SUGGESTION_WEEKLY_QUOTA, WORD_SUGGESTION_ACCOUNT_LIMIT, WORD_SUGGESTION_EXISTING_ERROR, isThisWeekPick, } from './utils/wordSuggestionPolicy';
58
58
  export { recoverFromSkew, skewReloadPending } from './utils/chunkSkew';
59
59
  export { lazyPage } from './utils/lazyPage';
60
+ export { prefetchChunk } from './utils/prefetchChunk';
60
61
  export { CORE_VERSION } from './version';
@@ -46,4 +46,5 @@ export { PLAN_FEATURES, PLAN_LIMITS, TIER_RANK, getPlanTier, isManagerTier, } fr
46
46
  export { WORD_SUGGESTION_WEEKLY_QUOTA, WORD_SUGGESTION_ACCOUNT_LIMIT, WORD_SUGGESTION_EXISTING_ERROR, isThisWeekPick, } from './utils/wordSuggestionPolicy';
47
47
  export { recoverFromSkew, skewReloadPending } from './utils/chunkSkew';
48
48
  export { lazyPage } from './utils/lazyPage';
49
+ export { prefetchChunk } from './utils/prefetchChunk';
49
50
  export { CORE_VERSION } from './version';
@@ -46,4 +46,4 @@
46
46
  *
47
47
  * 값은 손으로 적고 `test/coreVersion.test.ts` 가 package.json 과 어긋나면 운다.
48
48
  */
49
- export declare const CORE_VERSION = "0.193.0";
49
+ export declare const CORE_VERSION = "0.194.0";
@@ -46,4 +46,4 @@
46
46
  *
47
47
  * 값은 손으로 적고 `test/coreVersion.test.ts` 가 package.json 과 어긋나면 운다.
48
48
  */
49
- export const CORE_VERSION = '0.193.0';
49
+ export const CORE_VERSION = '0.194.0';