@mondrianai/runyourai-design-system 0.0.26 → 0.0.27

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/README.md CHANGED
@@ -1,56 +1,33 @@
1
1
  # @mondrianai/runyourai-design-system
2
2
 
3
- RunYourAI Design System — React component library built on [shadcn/ui](https://ui.shadcn.com/) components, [Radix UI](https://www.radix-ui.com/) primitives, and [Tailwind CSS v4](https://tailwindcss.com/).
3
+ RunYourAI Design System — [shadcn/ui](https://ui.shadcn.com/) · [Radix UI](https://www.radix-ui.com/) · [Tailwind CSS v4](https://tailwindcss.com/) 기반 React 컴포넌트 라이브러리입니다.
4
4
 
5
- ## Requirements
5
+ | 항목 | 내용 |
6
+ | --- | --- |
7
+ | 최종 수정 | 2026-08-11 (Seulki Yun) |
6
8
 
7
- - React 18+
8
- - Next.js 15+
9
- - Tailwind CSS v4
10
-
11
- ## Installation
12
-
13
- ```bash
14
- npm install @mondrianai/runyourai-design-system
15
- # or
16
- yarn add @mondrianai/runyourai-design-system
17
- ```
18
-
19
- ## Local Development (yalc)
20
-
21
- 소비 앱에서 로컬 빌드를 테스트할 때는 [yalc](https://github.com/wclr/yalc)를 사용합니다.
22
-
23
- ```bash
24
- # 최초 1회: 소비 앱에 yalc 연결
25
- # (design system 레포에서)
26
- yalc publish
27
- # (소비 앱 레포에서)
28
- yalc add @mondrianai/runyourai-design-system
29
-
30
- # 이후 변경사항 반영 시 (design system 레포에서)
31
- npm run build && yalc push
32
- # 소비 앱 dev 서버 재시작 필요
33
- ```
9
+ ---
34
10
 
35
- > **주의**: `sidebar.tsx`, `button.tsx` 등 컴포넌트 소스를 수정한 경우, 반드시 `npm run build && yalc push` 를 실행해야 소비 앱에 변경사항이 반영됩니다. 소스 파일을 수정해도 소비 앱의 dev server hot-reload만으로는 dist가 갱신되지 않습니다.
11
+ 소비 앱은 `main-frontend`(React 18 · antd 5)와 `core-frontend`(Next.js 16 · React 19 · antd 6) 두 곳입니다. 이 레포는 라이브러리이면서 동시에 **쇼케이스 앱**(`src/app/`)을 겸하고 있어, `yarn dev` 로 모든 컴포넌트를 눈으로 확인할 수 있습니다.
36
12
 
37
- ---
13
+ | 항목 | 값 |
14
+ | --- | --- |
15
+ | Peer deps | React 18+ · Tailwind CSS v4 |
16
+ | 배포 | Bitbucket Pipelines — `v*` 태그 push 시 npm publish |
17
+ | 로컬 연동 | [yalc](https://github.com/wclr/yalc) |
38
18
 
39
- ## Setup
19
+ ## 소비 앱에 설치하기
40
20
 
41
21
  ### 1. 패키지 설치
42
22
 
43
23
  ```bash
44
- npm install @mondrianai/runyourai-design-system tailwindcss
45
- npm install --save-dev @tailwindcss/postcss
46
- # or
47
24
  yarn add @mondrianai/runyourai-design-system tailwindcss
48
25
  yarn add --dev @tailwindcss/postcss
49
26
  ```
50
27
 
51
28
  ### 2. PostCSS 설정
52
29
 
53
- 프로젝트 루트에 `postcss.config.mjs`를 생성합니다.
30
+ 프로젝트 루트에 `postcss.config.mjs` 를 만듭니다.
54
31
 
55
32
  ```js
56
33
  // postcss.config.mjs
@@ -65,7 +42,7 @@ export default config;
65
42
 
66
43
  ### 3. Next.js (Turbopack) 설정
67
44
 
68
- Next.js 15+ / 16의 기본 번들러인 Turbopack은 CSS `@import`에서 `package.json`의 `exports` 서브패스를 해석하지 못합니다. `next.config.ts`에 아래 `resolveAlias`를 추가해야 합니다.
45
+ Next.js 15+ / 16 의 기본 번들러인 Turbopack 은 CSS `@import` 에서 `package.json` 의 `exports` 서브패스를 해석하지 못합니다. `next.config.ts` 에 `resolveAlias` 를 추가합니다.
69
46
 
70
47
  ```ts
71
48
  // next.config.ts
@@ -85,11 +62,11 @@ const nextConfig: NextConfig = {
85
62
  export default nextConfig;
86
63
  ```
87
64
 
88
- > Turbopack을 사용하지 않는 경우(webpack 기반 커스텀 설정 등)에는 이 단계를 건너뜁니다.
65
+ > Turbopack 을 쓰지 않으면(webpack 기반 커스텀 설정 등) 이 단계는 건너뜁니다.
89
66
 
90
67
  ### 4. Tailwind CSS 설정
91
68
 
92
- `globals.css`에 아래 내용을 추가합니다. 디자인 토큰, Tailwind 테마, 애니메이션, 다크모드 설정이 모두 적용됩니다.
69
+ `globals.css` 에 아래를 추가하면 디자인 토큰 · Tailwind 테마 · 애니메이션 · 다크모드가 모두 적용됩니다.
93
70
 
94
71
  ```css
95
72
  /* globals.css */
@@ -98,60 +75,53 @@ export default nextConfig;
98
75
  @import '@mondrianai/runyourai-design-system/tailwind-theme';
99
76
 
100
77
  /* globals.css 위치 기준 node_modules 상대 경로 — 프로젝트 구조에 따라 조정하세요
101
- * globals.css가 app/globals.css 에 있는 경우: ../node_modules/...
102
- * globals.css가 src/app/globals.css 에 있는 경우: ../../node_modules/...
78
+ * app/globals.css → ../node_modules/...
79
+ * src/app/globals.css → ../../node_modules/...
103
80
  */
104
81
  @source "../node_modules/@mondrianai/runyourai-design-system/dist";
105
82
  ```
106
83
 
107
- > **`@import` 순서 주의**
108
- >
109
- > - `url(...)` 형태의 외부 폰트 import가 있다면 `@import 'tailwindcss'` **앞에** 위치시킵니다.
110
- > - `@mondrianai/runyourai-design-system/styles`, `@mondrianai/runyourai-design-system/tailwind-theme`는 `@import 'tailwindcss'` **뒤에** 위치시킵니다.
111
-
112
- > **디자인 토큰 오버라이드 주의**
113
- >
114
- > `:root`에서 디자인 시스템 CSS 변수를 오버라이드할 때, **컴포넌트 표면색 토큰**(`--background`, `--card`, `--popover` 등)을 페이지 배경색 설정 목적으로 변경하면 안 됩니다. 이 변수들은 `Button` outline, `Card`, `Popover` 등 컴포넌트의 배경색에 직접 사용됩니다.
115
- >
116
- > **페이지 배경색**은 CSS 토큰 오버라이드 대신 `html`/`body`에 직접 지정하세요.
117
- >
118
- > ```css
119
- > /* ❌ 잘못된 예 — Button outline 등 컴포넌트 배경색까지 바뀜 */
120
- > :root {
121
- > --background: #fdfdfd;
122
- > }
123
- >
124
- > /* ✅ 올바른 예 — 페이지 배경만 변경, 컴포넌트 토큰 무영향 */
125
- > html, body {
126
- > background-color: #fdfdfd;
127
- > }
128
- > ```
129
-
130
- > **기존 CSS 리셋 주의 (Tailwind v4)**
131
- > 프로젝트에 `* { margin: 0; padding: 0; }` 같은 전역 리셋이 있다면 반드시 `@layer base`로 감싸야 합니다. Tailwind v4에서 레이어 밖의 스타일은 `@layer utilities`보다 우선순위가 높아 `px-*`, `py-*` 등의 유틸리티 클래스가 무효화됩니다.
132
- >
133
- > ```css
134
- > @layer base {
135
- > *,
136
- > *::before,
137
- > *::after {
138
- > box-sizing: border-box;
139
- > }
140
- >
141
- > * {
142
- > margin: 0;
143
- > padding: 0;
144
- > }
145
- > }
146
- > ```
84
+ **`@import` 순서** — `url(...)` 형태의 외부 폰트 import 는 `@import 'tailwindcss'` **앞에**, 디자인 시스템 두 줄은 **뒤에** 둡니다.
85
+
86
+ **디자인 토큰 오버라이드** — `:root` 에서 **컴포넌트 표면색 토큰**(`--background` · `--card` · `--popover` 등)을 페이지 배경색 목적으로 바꾸면 안 됩니다. 이 변수들은 `Button` outline · `Card` · `Popover` 의 배경색에 직접 쓰입니다.
87
+
88
+ ```css
89
+ /* ❌ Button outline 등 컴포넌트 배경색까지 바뀜 */
90
+ :root {
91
+ --background: #fdfdfd;
92
+ }
93
+
94
+ /* ✅ 페이지 배경만 변경, 컴포넌트 토큰 무영향 */
95
+ html,
96
+ body {
97
+ background-color: #fdfdfd;
98
+ }
99
+ ```
100
+
101
+ **기존 CSS 리셋** — `* { margin: 0; padding: 0; }` 같은 전역 리셋이 있다면 반드시 `@layer base` 로 감쌉니다. Tailwind v4 에서 레이어 밖 스타일은 `@layer utilities` 보다 우선순위가 높아 `px-*` · `py-*` 가 무효화됩니다.
102
+
103
+ ```css
104
+ @layer base {
105
+ *,
106
+ *::before,
107
+ *::after {
108
+ box-sizing: border-box;
109
+ }
110
+
111
+ * {
112
+ margin: 0;
113
+ padding: 0;
114
+ }
115
+ }
116
+ ```
147
117
 
148
118
  ### 5. Provider 설정
149
119
 
150
- `Tooltip` 등 Radix UI 기반 컴포넌트가 올바르게 동작하려면 앱 최상단에 `Providers`를 감싸야 합니다.
120
+ `Tooltip` · `toast()` 등 Radix · sonner 기반 컴포넌트가 동작하려면 앱 최상단을 `RyaiDsProvider` 로 감쌉니다.
151
121
 
152
122
  ```tsx
153
123
  // app/layout.tsx
154
- import { Providers } from '@mondrianai/runyourai-design-system';
124
+ import { RyaiDsProvider } from '@mondrianai/runyourai-design-system';
155
125
 
156
126
  export default function RootLayout({
157
127
  children,
@@ -161,19 +131,21 @@ export default function RootLayout({
161
131
  return (
162
132
  <html lang='ko'>
163
133
  <body>
164
- <Providers>{children}</Providers>
134
+ <RyaiDsProvider>{children}</RyaiDsProvider>
165
135
  </body>
166
136
  </html>
167
137
  );
168
138
  }
169
139
  ```
170
140
 
171
- ## Usage
141
+ > antd 를 함께 쓴다면 **antd `ConfigProvider` 바깥**에 둡니다.
142
+
143
+ ## 사용
172
144
 
173
145
  ```tsx
174
146
  import { Button, Badge, Card } from '@mondrianai/runyourai-design-system';
175
147
 
176
- export default function Page() {
148
+ export function Page() {
177
149
  return (
178
150
  <Card>
179
151
  <Badge text='New' />
@@ -183,78 +155,59 @@ export default function Page() {
183
155
  }
184
156
  ```
185
157
 
186
- ### Utilities
187
-
188
158
  ```tsx
189
159
  import { cn } from '@mondrianai/runyourai-design-system/utils';
190
160
 
191
161
  <div className={cn('base-class', isActive && 'active-class')} />;
192
162
  ```
193
163
 
194
- ## Components
195
-
196
- | Component | Description |
197
- | ------------------------------------- | ------------------------- |
198
- | `Accordion` | 펼침/접힘 컨텐츠 영역 |
199
- | `AlertDialog` | 확인/취소 모달 다이얼로그 |
200
- | `Alert` | 인라인 알림 메시지 |
201
- | `Avatar` / `AvatarGroup` | 유저 아바타 및 그룹 |
202
- | `Badge` | 상태·카테고리 배지 |
203
- | `Breadcrumb` | 페이지 경로 탐색 |
204
- | `Button` | 버튼 |
205
- | `Card` | 카드 컨테이너 |
206
- | `Checkbox` / `CheckboxCard` | 체크박스 |
207
- | `ChoiceCardGroup` / `ChoiceCardItem` | 선택형 카드 그룹 |
208
- | `Collapsible` | 접을 수 있는 컨텐츠 |
209
- | `Combobox` | 검색 가능한 선택 입력 |
210
- | `Field` / `FieldSection` / `FieldRow` | 폼 필드 레이아웃 |
211
- | `GroupedSelect` | 그룹 구분이 있는 셀렉트 |
212
- | `Header` | 앱 상단 헤더 |
213
- | `Icon` | Lucide 아이콘 래퍼 |
214
- | `MarkdownMessage` | 마크다운 렌더링 메시지 |
215
- | `Menubar` | 메뉴바 |
216
- | `MessageFooter` | 메시지 하단 액션 영역 |
217
- | `NumberBadge` | 숫자 배지 |
218
- | `SegmentedControl` | 세그먼트 탭 컨트롤 |
219
- | `Select` | 셀렉트 박스 |
220
- | `Sheet` | 사이드 슬라이드 패널 |
221
- | `Sidebar` | 앱 사이드바 |
222
- | `Slider` | 슬라이더 |
223
- | `Stepper` | 단계 진행 표시 |
224
- | `Switch` / `SwitchField` | 토글 스위치 |
225
- | `Tooltip` / `TooltipWithIcon` | 툴팁 |
226
- | `UserMessageBubble` | 채팅 메시지 말풍선 |
227
-
228
- ## Icons
229
-
230
- ### Custom Icons
164
+ 아이콘도 같은 진입점에서 가져옵니다. `size-*` · `text-*` 클래스로 크기와 색을 제어합니다.
231
165
 
232
166
  ```tsx
233
167
  import {
234
- SparklesIcon,
235
- TrashIcon,
236
- StarIcon,
168
+ SparklesIcon, // 커스텀 아이콘
169
+ ClaudeIcon, // 브랜드 아이콘
170
+ RyaiLogoIcon, // 로고
237
171
  } from '@mondrianai/runyourai-design-system';
238
172
 
239
173
  <SparklesIcon className='size-4 text-primary' />;
240
174
  ```
241
175
 
242
- ### Brand Icons
176
+ **제공 컴포넌트 목록**은 여기 적지 않습니다 — 목록은 금방 낡습니다. `src/components/index.ts` 의 export 를 보거나, `yarn dev` 로 쇼케이스 앱을 띄워 확인하세요.
243
177
 
244
- ```tsx
245
- import {
246
- ClaudeIcon,
247
- OpenAiIcon,
248
- GeminiIcon,
249
- } from '@mondrianai/runyourai-design-system';
178
+ ## 로컬 개발
250
179
 
251
- <ClaudeIcon className='size-6' />;
180
+ ```bash
181
+ yarn install
182
+ yarn dev # 쇼케이스 앱 (port 3999)
183
+ yarn build # 라이브러리 빌드 — tsc --noEmit && tsup
184
+ yarn lint
252
185
  ```
253
186
 
254
- ### Logo
187
+ 소비 앱에서 로컬 빌드를 테스트할 때는 yalc 를 씁니다.
255
188
 
256
- ```tsx
257
- import { RyaiLogoIcon } from '@mondrianai/runyourai-design-system';
189
+ ```bash
190
+ # 디자인 시스템 레포에서 (최초 1회)
191
+ yarn build && yalc publish
192
+ # 소비 앱에서 (최초 1회)
193
+ yalc add @mondrianai/runyourai-design-system
258
194
 
259
- <RyaiLogoIcon />;
195
+ # 이후 변경마다 — 디자인 시스템 레포에서
196
+ yarn build && yalc push
260
197
  ```
198
+
199
+ > **소스만 고치면 소비 앱에 반영되지 않습니다.** 소비 앱은 `dist/` 를 보므로 반드시 `yarn build` 를 거쳐야 합니다. 최초 세팅·트러블슈팅은 [개발 사이클](./docs/operations/dev-cycle.md).
200
+
201
+ ## 문서
202
+
203
+ | 문서 | 내용 |
204
+ | --- | --- |
205
+ | [`AGENTS.md`](./AGENTS.md) | **작업 규칙** — 코드를 만지기 전에 읽습니다. 디자이너·개발자 갈래가 나뉩니다 |
206
+ | [`docs/`](./docs/README.md) | 문서 인덱스 — 디자인 · 아키텍처 · 운영 · 계획 |
207
+ | [Figma 구현](./docs/design/figma-implementation.md) | 시안을 1px 오차 없이 옮기는 기준 |
208
+ | [디자인 토큰](./docs/design/tokens.md) | `variables.json` → CSS 세 파일 동기화 |
209
+ | [컴포넌트 API](./docs/architecture/component-api.md) | 공개 표면 설계 원칙 |
210
+ | [개발 사이클](./docs/operations/dev-cycle.md) | 구현 후 마무리 · yalc 반영 · 트러블슈팅 |
211
+ | [릴리스](./docs/operations/release.md) | 버전 결정 · 태그 · npm 배포 |
212
+
213
+ 디렉터리 구조와 설정 항목은 문서로 옮겨두지 않습니다. `src/` 와 `tsup.config.ts` · `package.json` 을 직접 보세요. 왜 그렇게 되어 있는지는 위 문서에 있습니다.
package/dist/index.d.mts CHANGED
@@ -114,7 +114,7 @@ declare function BreadcrumbSeparator({ iconName, className, ...props }: React__d
114
114
 
115
115
  declare const buttonVariants: (props?: ({
116
116
  variant?: "link" | "primary" | "secondary" | "outline" | "ghost" | "destructive" | "ghostDestructive" | null | undefined;
117
- size?: "rounded" | "default" | "icon" | "pill" | null | undefined;
117
+ size?: "rounded" | "default" | "sm" | "icon" | "pill" | null | undefined;
118
118
  } & class_variance_authority_types.ClassProp) | undefined) => string;
119
119
  interface ButtonProps extends React$1.ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> {
120
120
  /** Radix Slot 패턴 — true 이면 자식 요소에 props를 병합 */
@@ -819,7 +819,7 @@ type SelectProps = SingleSelectProps | MultiSelectProps;
819
819
  * - 열린 상태에서 listbox에 포커스가 이동하고 `aria-activedescendant`로 활성 옵션을 연결한다.
820
820
  * - 키보드: ↑/↓/Home/End 이동, Enter/Space 선택(다중은 토글), Esc 닫힘, 타입어헤드 점프.
821
821
  * - 역할: 컨테이너 `role="listbox"`(+다중 시 `aria-multiselectable`), 옵션 `role="option"`+`aria-selected`.
822
- * 배경·설계 근거: {@link ../../../docs/todo/select-a11y-keyboard-aria.md}
822
+ * 배경·설계 근거: {@link ../../../docs/plans/select-a11y-keyboard-aria.md}
823
823
  */
824
824
  declare function Select(props: SelectProps): react_jsx_runtime.JSX.Element;
825
825
 
@@ -999,7 +999,7 @@ interface TabsProps<T extends string = string> {
999
999
  *
1000
1000
  * NOTE: 콘텐츠 패널은 사용처가 바깥에서 `current`로 렌더하므로 Tabs.Content(및 aria-controls↔
1001
1001
  * aria-labelledby 연결)가 없다. 키보드·역할 a11y는 확보했으나 탭↔패널 연결은 미완.
1002
- * 구 pill 사용처 이관 및 릴리스 체크리스트: docs/todo/tabs-segmented-control.md
1002
+ * 구 pill 사용처 이관 및 릴리스 체크리스트: docs/plans/tabs-segmented-control.md
1003
1003
  */
1004
1004
  declare function Tabs<T extends string = string>({ allTabs, current, onTabChange, block, className, }: TabsProps<T>): react_jsx_runtime.JSX.Element;
1005
1005
 
@@ -1396,6 +1396,15 @@ type ToastOptions = {
1396
1396
  duration?: number;
1397
1397
  /** 표시 위치. 미지정 시 `<Toaster />` 기본값(top-center). */
1398
1398
  position?: ExternalToast['position'];
1399
+ /**
1400
+ * 기본 너비(356px)에서 줄바꿈하지 않고 문구 길이만큼 토스트를 넓힌다.
1401
+ * 목록처럼 길이가 가변적인 문구(예: 지원하지 않는 파일 형식 나열)에 사용한다.
1402
+ *
1403
+ * 무한정 넓어지지는 않는다. `--toast-nowrap-max`(기본 `min(90vw, 720px)`)에서
1404
+ * 멈추고, 그보다 긴 문구는 그 지점부터 다시 줄바꿈한다. 좁은 뷰포트에서
1405
+ * 토스트가 화면 밖으로 나가지 않도록 하는 안전장치다.
1406
+ */
1407
+ nowrap?: boolean;
1399
1408
  /** 토스트 컨테이너에 추가할 클래스 */
1400
1409
  className?: string;
1401
1410
  };
@@ -1443,7 +1452,7 @@ type ToastPromiseMessages<T> = {
1443
1452
  * error: '실패',
1444
1453
  * });
1445
1454
  */
1446
- declare function promiseToast<T>(promise: Promise<T> | (() => Promise<T>), messages: ToastPromiseMessages<T>): string | number;
1455
+ declare function promiseToast<T>(promise: Promise<T> | (() => Promise<T>), messages: ToastPromiseMessages<T>, options?: ToastMethodOptions): string | number;
1447
1456
  /**
1448
1457
  * 이미 떠 있는 하나의 토스트를 이후 상태로 갱신하는 핸들.
1449
1458
  * `toast.loading()`이 반환하며, `{ id }`를 옵션으로 넘겨 다시 지정할 필요 없이
package/dist/index.d.ts CHANGED
@@ -114,7 +114,7 @@ declare function BreadcrumbSeparator({ iconName, className, ...props }: React__d
114
114
 
115
115
  declare const buttonVariants: (props?: ({
116
116
  variant?: "link" | "primary" | "secondary" | "outline" | "ghost" | "destructive" | "ghostDestructive" | null | undefined;
117
- size?: "rounded" | "default" | "icon" | "pill" | null | undefined;
117
+ size?: "rounded" | "default" | "sm" | "icon" | "pill" | null | undefined;
118
118
  } & class_variance_authority_types.ClassProp) | undefined) => string;
119
119
  interface ButtonProps extends React$1.ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> {
120
120
  /** Radix Slot 패턴 — true 이면 자식 요소에 props를 병합 */
@@ -819,7 +819,7 @@ type SelectProps = SingleSelectProps | MultiSelectProps;
819
819
  * - 열린 상태에서 listbox에 포커스가 이동하고 `aria-activedescendant`로 활성 옵션을 연결한다.
820
820
  * - 키보드: ↑/↓/Home/End 이동, Enter/Space 선택(다중은 토글), Esc 닫힘, 타입어헤드 점프.
821
821
  * - 역할: 컨테이너 `role="listbox"`(+다중 시 `aria-multiselectable`), 옵션 `role="option"`+`aria-selected`.
822
- * 배경·설계 근거: {@link ../../../docs/todo/select-a11y-keyboard-aria.md}
822
+ * 배경·설계 근거: {@link ../../../docs/plans/select-a11y-keyboard-aria.md}
823
823
  */
824
824
  declare function Select(props: SelectProps): react_jsx_runtime.JSX.Element;
825
825
 
@@ -999,7 +999,7 @@ interface TabsProps<T extends string = string> {
999
999
  *
1000
1000
  * NOTE: 콘텐츠 패널은 사용처가 바깥에서 `current`로 렌더하므로 Tabs.Content(및 aria-controls↔
1001
1001
  * aria-labelledby 연결)가 없다. 키보드·역할 a11y는 확보했으나 탭↔패널 연결은 미완.
1002
- * 구 pill 사용처 이관 및 릴리스 체크리스트: docs/todo/tabs-segmented-control.md
1002
+ * 구 pill 사용처 이관 및 릴리스 체크리스트: docs/plans/tabs-segmented-control.md
1003
1003
  */
1004
1004
  declare function Tabs<T extends string = string>({ allTabs, current, onTabChange, block, className, }: TabsProps<T>): react_jsx_runtime.JSX.Element;
1005
1005
 
@@ -1396,6 +1396,15 @@ type ToastOptions = {
1396
1396
  duration?: number;
1397
1397
  /** 표시 위치. 미지정 시 `<Toaster />` 기본값(top-center). */
1398
1398
  position?: ExternalToast['position'];
1399
+ /**
1400
+ * 기본 너비(356px)에서 줄바꿈하지 않고 문구 길이만큼 토스트를 넓힌다.
1401
+ * 목록처럼 길이가 가변적인 문구(예: 지원하지 않는 파일 형식 나열)에 사용한다.
1402
+ *
1403
+ * 무한정 넓어지지는 않는다. `--toast-nowrap-max`(기본 `min(90vw, 720px)`)에서
1404
+ * 멈추고, 그보다 긴 문구는 그 지점부터 다시 줄바꿈한다. 좁은 뷰포트에서
1405
+ * 토스트가 화면 밖으로 나가지 않도록 하는 안전장치다.
1406
+ */
1407
+ nowrap?: boolean;
1399
1408
  /** 토스트 컨테이너에 추가할 클래스 */
1400
1409
  className?: string;
1401
1410
  };
@@ -1443,7 +1452,7 @@ type ToastPromiseMessages<T> = {
1443
1452
  * error: '실패',
1444
1453
  * });
1445
1454
  */
1446
- declare function promiseToast<T>(promise: Promise<T> | (() => Promise<T>), messages: ToastPromiseMessages<T>): string | number;
1455
+ declare function promiseToast<T>(promise: Promise<T> | (() => Promise<T>), messages: ToastPromiseMessages<T>, options?: ToastMethodOptions): string | number;
1447
1456
  /**
1448
1457
  * 이미 떠 있는 하나의 토스트를 이후 상태로 갱신하는 핸들.
1449
1458
  * `toast.loading()`이 반환하며, `{ id }`를 옵션으로 넘겨 다시 지정할 필요 없이
package/dist/index.js CHANGED
@@ -716,12 +716,15 @@ var buttonVariants = (0, import_class_variance_authority.cva)(
716
716
  /**
717
717
  * size — 버튼 크기
718
718
  * default h-9 (36px) px-4 텍스트 버튼 표준
719
+ * sm h-8 (32px) px-3 py-1.5 표 안 액션 등 좁은 자리용 (Figma 6320:6085)
719
720
  * icon 36×36 정사각형 아이콘 전용, rounded-lg 유지 / hover: opacity-80
720
721
  * rounded 36×36 원형 아이콘 전용, rounded-full / hover: opacity-80
721
722
  * pill px-3 py-1.5 rounded-full Rounded-text 필셋 스타일
722
723
  */
723
724
  size: {
724
725
  default: "h-9 px-4 py-2",
726
+ /** Small — Figma node 6320:6085. 높이 32px, 패딩만 default 와 다르다 */
727
+ sm: "h-8 px-3 py-1.5",
725
728
  icon: "size-9 p-0 hover:opacity-80",
726
729
  rounded: "size-9 p-0 rounded-full hover:opacity-80",
727
730
  pill: "px-3 py-1.5 rounded-full"
@@ -1051,8 +1054,8 @@ function AvatarGroup({ avatars, size = "md", className }) {
1051
1054
 
1052
1055
  // src/components/ui/badge.tsx
1053
1056
  var import_jsx_runtime7 = require("react/jsx-runtime");
1054
- var TEXT_BADGE_BASE = "inline-flex w-fit shrink-0 items-center justify-center gap-1 h-5.5 rounded-lg px-2.5 py-0.5 text-xs font-medium leading-xs tracking-tight whitespace-nowrap";
1055
- var BADGE_ICON_BASE = "size-3";
1057
+ var TEXT_BADGE_BASE = "inline-flex w-fit shrink-0 items-center justify-center gap-1 rounded-full px-2.5 py-1 text-sm font-medium leading-5 tracking-tight whitespace-nowrap";
1058
+ var BADGE_ICON_BASE = "size-4";
1056
1059
  var BADGE_VARIANT_STYLES = {
1057
1060
  default: "bg-primary text-primary-foreground",
1058
1061
  secondary: "bg-secondary text-secondary-foreground",
@@ -4725,13 +4728,13 @@ MenubarSeparator.displayName = import_radix_ui13.Menubar.Separator.displayName;
4725
4728
 
4726
4729
  // src/components/ui/number-badge.tsx
4727
4730
  var import_jsx_runtime34 = require("react/jsx-runtime");
4728
- var NUMBER_BADGE_BASE = "inline-flex shrink-0 items-center justify-center rounded-full font-mono text-xs font-medium leading-xs whitespace-nowrap";
4731
+ var NUMBER_BADGE_BASE = "inline-flex h-7 shrink-0 items-center justify-center rounded-full font-mono text-sm font-medium leading-4 whitespace-nowrap";
4729
4732
  function NumberBadge({
4730
4733
  count,
4731
4734
  variant = "default",
4732
4735
  className
4733
4736
  }) {
4734
- const sizeClass = count < 10 ? "size-5" : "h-5 min-w-5 px-2";
4737
+ const sizeClass = count < 10 ? "w-5" : "min-w-5 px-2";
4735
4738
  return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
4736
4739
  "span",
4737
4740
  {
@@ -7236,29 +7239,63 @@ var TYPE_TOAST = {
7236
7239
  info: import_sonner.toast.info,
7237
7240
  error: import_sonner.toast.error
7238
7241
  };
7242
+ var WIDTH_CLASS = {
7243
+ /**
7244
+ * 내용 크기로 줄이고, --width(356px)를 넘으면 줄바꿈.
7245
+ *
7246
+ * w-fit으로 줄이면 absolute+left/right auto라 컨테이너 좌측 끝에 붙어 왼쪽으로
7247
+ * 치우친다. left/right:0 + margin-inline:auto로 컨테이너 안에서 다시 중앙 정렬한다.
7248
+ * (left:50%로 잡으면 fit-content의 available space가 컨테이너의 절반으로 줄어
7249
+ * 토스트가 178px에서 줄바꿈해 버리므로 이 조합이어야 한다.)
7250
+ */
7251
+ fit: "!w-fit !max-w-[var(--width)] data-[x-position=center]:!left-0 data-[x-position=center]:!right-0 data-[x-position=center]:!mx-auto",
7252
+ /**
7253
+ * 한 줄로 늘리고, --toast-nowrap-max에서 멈춘 뒤 줄바꿈.
7254
+ *
7255
+ * 컨테이너(--width)보다 넓어질 수 있어서 fit의 중앙 정렬 방식을 쓸 수 없다.
7256
+ * left:0 + right:0 + margin auto는 over-constrained가 되고, CSS 규격상 마진이
7257
+ * 음수가 되어야 하는 경우 ltr에서는 margin-left가 0으로 고정되어 좌측으로 쏠린다.
7258
+ * 대신 left:50% + translate로 잡는다. Tailwind v4의 translate 유틸리티는
7259
+ * `transform`이 아니라 `translate` 속성을 쓰므로, sonner가 스택 애니메이션에
7260
+ * 사용하는 transform과 겹치지 않고 합성된다.
7261
+ */
7262
+ nowrap: "!w-max !max-w-[var(--toast-nowrap-max)] data-[x-position=center]:!left-1/2 data-[x-position=center]:!right-auto data-[x-position=center]:-translate-x-1/2"
7263
+ };
7264
+ function toSonnerOptions(options) {
7265
+ const _a = options != null ? options : {}, { nowrap, className } = _a, rest = __objRest(_a, ["nowrap", "className"]);
7266
+ return __spreadProps(__spreadValues({}, rest), {
7267
+ className: cn(nowrap ? WIDTH_CLASS.nowrap : WIDTH_CLASS.fit, className)
7268
+ });
7269
+ }
7239
7270
  function baseToast(message, options) {
7240
7271
  const _a = options != null ? options : {}, { type } = _a, rest = __objRest(_a, ["type"]);
7241
- if (type) return TYPE_TOAST[type](message, rest);
7242
- return (0, import_sonner.toast)(message, rest);
7272
+ const sonnerOptions = toSonnerOptions(rest);
7273
+ if (type) return TYPE_TOAST[type](message, sonnerOptions);
7274
+ return (0, import_sonner.toast)(message, sonnerOptions);
7243
7275
  }
7244
7276
  function resolveMessage(message, arg) {
7245
7277
  return typeof message === "function" ? message(arg) : message;
7246
7278
  }
7247
- function promiseToast(promise, messages) {
7279
+ function promiseToast(promise, messages, options) {
7248
7280
  const running = typeof promise === "function" ? promise() : promise;
7249
- const id = import_sonner.toast.loading(messages.loading, {
7281
+ const sonnerOptions = toSonnerOptions(options);
7282
+ const id = import_sonner.toast.loading(messages.loading, __spreadProps(__spreadValues({}, sonnerOptions), {
7250
7283
  description: messages.description
7251
- });
7284
+ }));
7252
7285
  running.then(
7253
- (data) => import_sonner.toast.success(resolveMessage(messages.success, data), { id })
7286
+ (data) => import_sonner.toast.success(resolveMessage(messages.success, data), __spreadProps(__spreadValues({}, sonnerOptions), {
7287
+ id
7288
+ }))
7254
7289
  ).catch(
7255
- (error) => import_sonner.toast.error(resolveMessage(messages.error, error), { id })
7290
+ (error) => import_sonner.toast.error(resolveMessage(messages.error, error), __spreadProps(__spreadValues({}, sonnerOptions), {
7291
+ id
7292
+ }))
7256
7293
  );
7257
7294
  return id;
7258
7295
  }
7259
7296
  function makeToastHandle(id) {
7260
7297
  const update = (fn) => (message, options) => {
7261
- fn(message, __spreadProps(__spreadValues({}, options), { id }));
7298
+ fn(message, __spreadProps(__spreadValues({}, toSonnerOptions(options)), { id }));
7262
7299
  };
7263
7300
  return {
7264
7301
  id,
@@ -7293,7 +7330,7 @@ var toast = Object.assign(baseToast, {
7293
7330
  * onError: () => t.error('저장 실패'),
7294
7331
  * });
7295
7332
  */
7296
- loading: (message, options) => makeToastHandle(import_sonner.toast.loading(message, options)),
7333
+ loading: (message, options) => makeToastHandle(import_sonner.toast.loading(message, toSonnerOptions(options))),
7297
7334
  /** Promise/async 함수를 받아 로딩→성공/실패로 자동 전환. */
7298
7335
  promise: promiseToast,
7299
7336
  /** id로 특정 토스트를 닫는다. 인자 없으면 전체 닫기. */
@@ -7321,16 +7358,18 @@ function Toaster(_a) {
7321
7358
  style: {
7322
7359
  "--normal-bg": "var(--popover)",
7323
7360
  "--normal-text": "var(--popover-foreground)",
7324
- "--normal-border": "var(--border)"
7361
+ "--normal-border": "var(--border)",
7362
+ // `nowrap` 토스트의 상한. 뷰포트 밖으로 나가지 않도록 90vw로 막고,
7363
+ // 대화면에서 과도하게 길어지지 않도록 절대값도 함께 건다.
7364
+ "--toast-nowrap-max": "min(90vw, 720px)"
7325
7365
  },
7326
7366
  toastOptions: {
7327
7367
  classNames: {
7328
- // 세로 패딩만 약간 축소(16→12px). 너비는 내용에 맞추고(fit-content),
7329
- // 기본 너비(--width, 356px)를 넘어서면 줄바꿈. (sonner 기본 규칙보다 특이도가 필요해 ! 사용)
7330
- // w-fit으로 토스트를 내용 크기로 줄이면 absolute+left/right auto라 컨테이너 좌측
7331
- // 끝에 붙어 왼쪽으로 치우친다. left/right:0 + margin-inline:auto로 컨테이너 안에서
7332
- // 다시 중앙 정렬한다(transform은 stacking 애니메이션이 쓰므로 건드리지 않는다).
7333
- toast: "!w-fit !max-w-[var(--width)] !py-3 data-[x-position=center]:!left-0 data-[x-position=center]:!right-0 data-[x-position=center]:!mx-auto",
7368
+ // 세로 패딩만 약간 축소(16→12px).
7369
+ // 너비·중앙 정렬은 토스트마다 달라지므로 여기 두지 않는다. sonner의 내부 cn은
7370
+ // 단순 join이라 전역 클래스와 개별 클래스가 충돌해도 정리해 주지 않는다.
7371
+ // `toast()`에서 cn()(tailwind-merge)으로 합쳐 WIDTH_CLASS로 붙인다.
7372
+ toast: "!py-3",
7334
7373
  title: "text-sm font-medium leading-5 tracking-tight break-words",
7335
7374
  description: "text-sm leading-5 tracking-tight text-muted-foreground break-words"
7336
7375
  }