sh-ui-cli 0.80.1 → 0.81.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 (38) hide show
  1. package/data/changelog/versions.json +17 -0
  2. package/data/registry/react/components/button/index.tailwind.tsx +1 -1
  3. package/data/registry/react/components/button/styles.css +1 -0
  4. package/data/registry/react/components/button/styles.css.ts +1 -0
  5. package/data/registry/react/components/button/styles.module.css +1 -0
  6. package/data/registry/react/components/combobox/index.tailwind.tsx +1 -1
  7. package/data/registry/react/components/combobox/styles.css +1 -1
  8. package/data/registry/react/components/combobox/styles.module.css +1 -1
  9. package/data/registry/react/components/context-menu/index.tailwind.tsx +1 -1
  10. package/data/registry/react/components/context-menu/styles.css +1 -1
  11. package/data/registry/react/components/context-menu/styles.module.css +1 -1
  12. package/data/registry/react/components/dropdown-menu/index.module.tsx +22 -7
  13. package/data/registry/react/components/dropdown-menu/index.tailwind.tsx +18 -3
  14. package/data/registry/react/components/dropdown-menu/index.tsx +24 -7
  15. package/data/registry/react/components/dropdown-menu/styles.css +1 -1
  16. package/data/registry/react/components/dropdown-menu/styles.module.css +1 -1
  17. package/data/registry/react/components/markdown-editor/index.module.tsx +1 -1
  18. package/data/registry/react/components/markdown-editor/index.tailwind.tsx +1 -1
  19. package/data/registry/react/components/markdown-editor/index.tsx +1 -1
  20. package/data/registry/react/components/select/index.tailwind.tsx +1 -1
  21. package/data/registry/react/components/select/styles.css +1 -1
  22. package/data/registry/react/components/select/styles.module.css +1 -1
  23. package/data/registry/react/components/sidebar/index.module.tsx +1 -1
  24. package/data/registry/react/components/sidebar/index.tailwind.tsx +1 -1
  25. package/data/registry/react/tokens-used.json +13 -13
  26. package/data/tokens/src/primitives.json +3 -3
  27. package/package.json +1 -1
  28. package/src/add.mjs +13 -1
  29. package/src/create/generator.js +34 -1
  30. package/templates/monorepo/packages/typescript-config/base.json +0 -1
  31. package/templates/nextjs-standalone/_arch/flat/lib/styles/tokens.css +2 -2
  32. package/templates/nextjs-standalone/_arch/flat/tsconfig.json +0 -1
  33. package/templates/nextjs-standalone/_arch/fsd/src/shared/styles/tokens.css +2 -2
  34. package/templates/nextjs-standalone/_arch/fsd/tsconfig.json +0 -1
  35. package/templates/nextjs-standalone/_arch/mes/src/lib/styles/tokens.css +2 -2
  36. package/templates/nextjs-standalone/_arch/mes/tsconfig.json +0 -1
  37. package/templates/ui-app-template/src/styles/globals.css +10 -0
  38. package/templates/ui-app-template/src/styles/tokens.css +2 -2
@@ -2,6 +2,23 @@
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
3
  "$description": "sh-ui 릴리즈 노트 단일 소스. docs(React)와 showcase(Flutter)가 함께 읽는다. 새 릴리즈마다 맨 앞에 추가.",
4
4
  "versions": [
5
+ {
6
+ "version": "0.81.0",
7
+ "date": "2026-05-13",
8
+ "title": "atlas P0/P1 일괄 — monorepo create_project · registry · 디자인 톤",
9
+ "type": "minor",
10
+ "highlights": [
11
+ "**monorepo `create_project` 자동완성 흐름** — (1) ui-core `sh-ui.config.json` 의 `cssFramework` 가 항상 `plain` 으로 emit 되던 결함 수정 (호출 시점 인자가 ui-core/ui-app 양쪽에 반영), (2) ui-app `globals.css` 의 `@source` 에 ui-core 경로 누락 수정 (`../../../../ui-core/src/**/*.{ts,tsx}` 자동 박힘), (3) utility CSS 5종 (`base · focus-ring · animations · breakpoints · z-index`) 을 스캐폴드 시점에 ui-core 에 자동 emit + ui-app `globals.css` 에 sentinel-bounded `@import` 자동 박기. `create → dev` 가 매뉴얼 보정 없이 굴러감.",
12
+ "**registry `.tsx` cross-import 잔존 6건 일괄 제거** — v0.80.1 에서 sidebar/plain 한 개만 고쳤던 누락 후속. sidebar `tailwind/module` + markdown-editor `plain/tailwind/module` + docs 듀얼카피 markdown-editor 의 `../code-editor/index.tsx` / `../popover/index.tsx` 를 확장자 없는 형태로 통일. Next.js + TS 디폴트(`allowImportingTsExtensions: false`) 에서 빌드 깨지던 결함 해소.",
13
+ "**DropdownMenu / Select / Combobox / ContextMenu z-index `--z-dropdown` (200) → `--z-popover` (500)** — Sidebar offcanvas drawer 의 `--z-overlay` (300) 아래로 깔려 안 보이던 결함 수정. 드로어 안에 있는 메뉴/콤보가 정상 위로 뜸. 듀얼 카피본까지 일괄.",
14
+ "**DropdownMenuLabel free-standing 자동 self-wrap** — Base UI 의 `Menu.GroupLabel` 이 `Menu.Group` 부재 시 \"MenuGroupRootContext is missing\" 으로 런타임 throw 하던 마이그레이션 함정 해소. 자체 Context 로 부모 Group 감지, 없으면 Label 이 스스로 `Menu.Group` 으로 wrap. 명시적 Group 안에 두면 중첩 없이 그대로 동작 — shadcn 코드 그대로 옮겨와도 OK.",
15
+ "**디폴트 control 사이즈 다운 (40 → 36)** — `--control-md` 가 데스크탑 SaaS/대시보드 톤에 비해 통통하다는 atlas 측정(`size='sm'` 24건, `size='lg'` 0건) 반영. `--control-sm/md/lg` 를 `2rem / 2.25rem / 2.5rem` (32/36/40) 로. 모바일 터치 점프(44) 는 그대로. 기존 프로젝트의 `tokens.css` 는 미변경 — 신규 프로젝트만 새 디폴트.",
16
+ "**Button `white-space: nowrap`** — 좁은 컨테이너에서 한글 라벨이 줄바꿈되며 버튼 높이 60px+ 로 부풀던 결함 수정. 4 변종 (plain/tailwind/module/vanilla-extract) + 듀얼카피 일괄.",
17
+ "**`noUncheckedIndexedAccess` 디폴트 off** — 스캐폴드의 `typescript-config/base.json` + nextjs-standalone 3 arch 의 tsconfig 에서 제거. registry 컴포넌트들이 index access 결과를 narrowing 없이 쓰는 패턴이라 신규 사용자가 첫 `typecheck` 부터 실패하던 결함 해소.",
18
+ "**`add tokens` 가 ui-core 에 대해 던지는 에러 메시지에 가이드 추가** — `paths.tokens 가 설정에 없습니다` 만 던지던 것을 v0.65 layout 설명 + `cwd` 를 `packages/ui/ui-apps/ui-<name>` 로 바꾸라는 한 줄 안내로 보강."
19
+ ],
20
+ "url": "https://github.com/sanghyeonKim0201/sh-ui/releases/tag/v0.81.0"
21
+ },
5
22
  {
6
23
  "version": "0.80.1",
7
24
  "date": "2026-05-12",
@@ -3,7 +3,7 @@ import { cn } from "@SH_UI_UTILS@";
3
3
  import { cva, type VariantProps } from "class-variance-authority";
4
4
 
5
5
  const buttonVariants = cva(
6
- "inline-flex items-center justify-center gap-[var(--space-2)] border border-transparent rounded-[var(--radius)] font-medium leading-none cursor-pointer select-none transition-[background-color,color,border-color] duration-[var(--duration-fast)] disabled:opacity-[var(--opacity-disabled)] disabled:pointer-events-none focus-visible:outline-2 focus-visible:outline-ring focus-visible:outline-offset-2 active:scale-[0.97]",
6
+ "inline-flex items-center justify-center gap-[var(--space-2)] border border-transparent rounded-[var(--radius)] font-medium leading-none whitespace-nowrap cursor-pointer select-none transition-[background-color,color,border-color] duration-[var(--duration-fast)] disabled:opacity-[var(--opacity-disabled)] disabled:pointer-events-none focus-visible:outline-2 focus-visible:outline-ring focus-visible:outline-offset-2 active:scale-[0.97]",
7
7
  {
8
8
  variants: {
9
9
  variant: {
@@ -7,6 +7,7 @@
7
7
  border-radius: var(--radius);
8
8
  font-weight: var(--weight-medium);
9
9
  line-height: 1;
10
+ white-space: nowrap;
10
11
  cursor: pointer;
11
12
  transition: background-color var(--duration-fast), color var(--duration-fast), border-color var(--duration-fast),
12
13
  transform 80ms ease-out, filter 80ms;
@@ -9,6 +9,7 @@ export const button = style({
9
9
  borderRadius: "var(--radius)",
10
10
  fontWeight: "var(--weight-medium)",
11
11
  lineHeight: 1,
12
+ whiteSpace: "nowrap",
12
13
  cursor: "pointer",
13
14
  transition:
14
15
  "background-color var(--duration-fast), color var(--duration-fast), border-color var(--duration-fast), transform 80ms ease-out, filter 80ms",
@@ -7,6 +7,7 @@
7
7
  border-radius: var(--radius);
8
8
  font-weight: var(--weight-medium);
9
9
  line-height: 1;
10
+ white-space: nowrap;
10
11
  cursor: pointer;
11
12
  transition: background-color var(--duration-fast), color var(--duration-fast), border-color var(--duration-fast),
12
13
  transform 80ms ease-out, filter 80ms;
@@ -53,7 +53,7 @@ export const ComboboxContent = React.forwardRef<
53
53
  return (
54
54
  <BaseCombobox.Portal container={container}>
55
55
  <BaseCombobox.Positioner
56
- className="z-[var(--z-dropdown)] outline-none w-[var(--anchor-width)]"
56
+ className="z-[var(--z-popover)] outline-none w-[var(--anchor-width)]"
57
57
  sideOffset={sideOffset}
58
58
  align="start"
59
59
  >
@@ -31,7 +31,7 @@
31
31
 
32
32
  /* ───── Popup ───── */
33
33
  .sh-ui-combobox__positioner {
34
- z-index: var(--z-dropdown);
34
+ z-index: var(--z-popover);
35
35
  outline: none;
36
36
  width: var(--anchor-width);
37
37
  }
@@ -31,7 +31,7 @@
31
31
 
32
32
  /* ───── Popup ───── */
33
33
  .combobox__positioner {
34
- z-index: var(--z-dropdown);
34
+ z-index: var(--z-popover);
35
35
  outline: none;
36
36
  width: var(--anchor-width);
37
37
  }
@@ -29,7 +29,7 @@ export const ContextMenuContent = React.forwardRef<HTMLDivElement, ContextMenuCo
29
29
  function ContextMenuContent({ className, children, container, ...props }, ref) {
30
30
  return (
31
31
  <BaseContextMenu.Portal container={container}>
32
- <BaseContextMenu.Positioner className="outline-none z-[var(--z-dropdown)]">
32
+ <BaseContextMenu.Positioner className="outline-none z-[var(--z-popover)]">
33
33
  <BaseContextMenu.Popup ref={ref} className={cn(contentClasses, className)} {...props}>
34
34
  {children}
35
35
  </BaseContextMenu.Popup>
@@ -10,7 +10,7 @@
10
10
 
11
11
  .sh-ui-cm__positioner {
12
12
  outline: none;
13
- z-index: var(--z-dropdown);
13
+ z-index: var(--z-popover);
14
14
  }
15
15
 
16
16
  .sh-ui-cm__content {
@@ -10,7 +10,7 @@
10
10
 
11
11
  .cm__positioner {
12
12
  outline: none;
13
- z-index: var(--z-dropdown);
13
+ z-index: var(--z-popover);
14
14
  }
15
15
 
16
16
  .cm__content {
@@ -149,18 +149,26 @@ export const DropdownMenuRadioItem = React.forwardRef<
149
149
  );
150
150
  });
151
151
 
152
- /* ───────── Group / Label ───────── */
152
+ /* ───────── Group / Label ─────────
153
+ *
154
+ * Base UI Menu.GroupLabel 은 Menu.Group 안에서만 동작 — free-standing 시 throw.
155
+ * DropdownMenuLabel 이 부모 Group 부재 시 자체 Group 으로 self-wrap.
156
+ */
157
+
158
+ const InDmGroupContext = React.createContext(false);
153
159
 
154
160
  export const DropdownMenuGroup = React.forwardRef<
155
161
  HTMLDivElement,
156
162
  WithStringClassName<React.ComponentPropsWithoutRef<typeof BaseMenu.Group>>
157
163
  >(function DropdownMenuGroup({ className, ...props }, ref) {
158
164
  return (
159
- <BaseMenu.Group
160
- ref={ref}
161
- className={cn(styles.dm__group, className)}
162
- {...props}
163
- />
165
+ <InDmGroupContext.Provider value={true}>
166
+ <BaseMenu.Group
167
+ ref={ref}
168
+ className={cn(styles.dm__group, className)}
169
+ {...props}
170
+ />
171
+ </InDmGroupContext.Provider>
164
172
  );
165
173
  });
166
174
 
@@ -168,13 +176,20 @@ export const DropdownMenuLabel = React.forwardRef<
168
176
  HTMLDivElement,
169
177
  WithStringClassName<React.ComponentPropsWithoutRef<typeof BaseMenu.GroupLabel>>
170
178
  >(function DropdownMenuLabel({ className, ...props }, ref) {
171
- return (
179
+ const inGroup = React.useContext(InDmGroupContext);
180
+ const label = (
172
181
  <BaseMenu.GroupLabel
173
182
  ref={ref}
174
183
  className={cn(styles.dm__label, className)}
175
184
  {...props}
176
185
  />
177
186
  );
187
+ if (inGroup) return label;
188
+ return (
189
+ <InDmGroupContext.Provider value={true}>
190
+ <BaseMenu.Group>{label}</BaseMenu.Group>
191
+ </InDmGroupContext.Provider>
192
+ );
178
193
  });
179
194
 
180
195
  /* ───────── Separator ─────────
@@ -52,7 +52,7 @@ export const DropdownMenuContent = React.forwardRef<HTMLDivElement, DropdownMenu
52
52
  return (
53
53
  <BaseMenu.Portal container={container}>
54
54
  <BaseMenu.Positioner
55
- className="outline-none z-[var(--z-dropdown)]"
55
+ className="outline-none z-[var(--z-popover)]"
56
56
  side={side}
57
57
  align={align}
58
58
  sideOffset={sideOffset}
@@ -120,18 +120,27 @@ export const DropdownMenuRadioItem = React.forwardRef<
120
120
  );
121
121
  });
122
122
 
123
+ // Base UI Menu.GroupLabel 은 Menu.Group 안에 있어야만 동작 — free-standing 시 throw.
124
+ // DropdownMenuLabel 이 부모 Group 부재 시 자체 Group 으로 self-wrap 하도록 Context 로 감지.
125
+ const InDmGroupContext = React.createContext(false);
126
+
123
127
  export const DropdownMenuGroup = React.forwardRef<
124
128
  HTMLDivElement,
125
129
  WithStringClassName<React.ComponentPropsWithoutRef<typeof BaseMenu.Group>>
126
130
  >(function DropdownMenuGroup({ className, ...props }, ref) {
127
- return <BaseMenu.Group ref={ref} className={cn("p-0", className)} {...props} />;
131
+ return (
132
+ <InDmGroupContext.Provider value={true}>
133
+ <BaseMenu.Group ref={ref} className={cn("p-0", className)} {...props} />
134
+ </InDmGroupContext.Provider>
135
+ );
128
136
  });
129
137
 
130
138
  export const DropdownMenuLabel = React.forwardRef<
131
139
  HTMLDivElement,
132
140
  WithStringClassName<React.ComponentPropsWithoutRef<typeof BaseMenu.GroupLabel>>
133
141
  >(function DropdownMenuLabel({ className, ...props }, ref) {
134
- return (
142
+ const inGroup = React.useContext(InDmGroupContext);
143
+ const label = (
135
144
  <BaseMenu.GroupLabel
136
145
  ref={ref}
137
146
  className={cn(
@@ -141,6 +150,12 @@ export const DropdownMenuLabel = React.forwardRef<
141
150
  {...props}
142
151
  />
143
152
  );
153
+ if (inGroup) return label;
154
+ return (
155
+ <InDmGroupContext.Provider value={true}>
156
+ <BaseMenu.Group>{label}</BaseMenu.Group>
157
+ </InDmGroupContext.Provider>
158
+ );
144
159
  });
145
160
 
146
161
  export const DropdownMenuSeparator = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
@@ -149,18 +149,28 @@ export const DropdownMenuRadioItem = React.forwardRef<
149
149
  );
150
150
  });
151
151
 
152
- /* ───────── Group / Label ───────── */
152
+ /* ───────── Group / Label ─────────
153
+ *
154
+ * Base UI Menu.GroupLabel 은 Menu.Group 안에 있어야만 동작 (없으면 런타임 throw —
155
+ * "MenuGroupRootContext is missing"). shadcn 등에선 Label 이 free-standing 으로도
156
+ * 쓰여서 마이그레이션 시 사용자가 잘 찔린다. DropdownMenuLabel 이 부모 Group 부재 시
157
+ * 자체로 Group 으로 self-wrap 하도록 Context 로 감지.
158
+ */
159
+
160
+ const InDmGroupContext = React.createContext(false);
153
161
 
154
162
  export const DropdownMenuGroup = React.forwardRef<
155
163
  HTMLDivElement,
156
164
  WithStringClassName<React.ComponentPropsWithoutRef<typeof BaseMenu.Group>>
157
165
  >(function DropdownMenuGroup({ className, ...props }, ref) {
158
166
  return (
159
- <BaseMenu.Group
160
- ref={ref}
161
- className={cn("sh-ui-dm__group", className)}
162
- {...props}
163
- />
167
+ <InDmGroupContext.Provider value={true}>
168
+ <BaseMenu.Group
169
+ ref={ref}
170
+ className={cn("sh-ui-dm__group", className)}
171
+ {...props}
172
+ />
173
+ </InDmGroupContext.Provider>
164
174
  );
165
175
  });
166
176
 
@@ -168,13 +178,20 @@ export const DropdownMenuLabel = React.forwardRef<
168
178
  HTMLDivElement,
169
179
  WithStringClassName<React.ComponentPropsWithoutRef<typeof BaseMenu.GroupLabel>>
170
180
  >(function DropdownMenuLabel({ className, ...props }, ref) {
171
- return (
181
+ const inGroup = React.useContext(InDmGroupContext);
182
+ const label = (
172
183
  <BaseMenu.GroupLabel
173
184
  ref={ref}
174
185
  className={cn("sh-ui-dm__label", className)}
175
186
  {...props}
176
187
  />
177
188
  );
189
+ if (inGroup) return label;
190
+ return (
191
+ <InDmGroupContext.Provider value={true}>
192
+ <BaseMenu.Group>{label}</BaseMenu.Group>
193
+ </InDmGroupContext.Provider>
194
+ );
178
195
  });
179
196
 
180
197
  /* ───────── Separator ─────────
@@ -17,7 +17,7 @@
17
17
 
18
18
  .sh-ui-dm__positioner {
19
19
  outline: none;
20
- z-index: var(--z-dropdown);
20
+ z-index: var(--z-popover);
21
21
  }
22
22
 
23
23
  .sh-ui-dm__content {
@@ -17,7 +17,7 @@
17
17
 
18
18
  .dm__positioner {
19
19
  outline: none;
20
- z-index: var(--z-dropdown);
20
+ z-index: var(--z-popover);
21
21
  }
22
22
 
23
23
  .dm__content {
@@ -3,7 +3,7 @@
3
3
  import { useState } from "react";
4
4
  import ReactMarkdown from "react-markdown";
5
5
  import remarkGfm from "remark-gfm";
6
- import { CodeEditor } from "../code-editor/index.tsx";
6
+ import { CodeEditor } from "../code-editor";
7
7
  import styles from "./styles.module.css";
8
8
 
9
9
  import { cn } from "@SH_UI_UTILS@";
@@ -3,7 +3,7 @@
3
3
  import { useState } from "react";
4
4
  import ReactMarkdown from "react-markdown";
5
5
  import remarkGfm from "remark-gfm";
6
- import { CodeEditor } from "../code-editor/index.tsx";
6
+ import { CodeEditor } from "../code-editor";
7
7
 
8
8
  import { cn } from "@SH_UI_UTILS@";
9
9
  export interface MarkdownEditorProps {
@@ -3,7 +3,7 @@
3
3
  import { useState } from "react";
4
4
  import ReactMarkdown from "react-markdown";
5
5
  import remarkGfm from "remark-gfm";
6
- import { CodeEditor } from "../code-editor/index.tsx";
6
+ import { CodeEditor } from "../code-editor";
7
7
  import "./styles.css";
8
8
 
9
9
  import { cn } from "@SH_UI_UTILS@";
@@ -76,7 +76,7 @@ export const SelectContent = React.forwardRef<
76
76
  }
77
77
  >(({ className, children, container, ...props }, ref) => (
78
78
  <BaseSelect.Portal container={container}>
79
- <BaseSelect.Positioner className="outline-none z-[var(--z-dropdown)]" sideOffset={4} align="start">
79
+ <BaseSelect.Positioner className="outline-none z-[var(--z-popover)]" sideOffset={4} align="start">
80
80
  <BaseSelect.Popup
81
81
  ref={ref}
82
82
  className={cn(
@@ -64,7 +64,7 @@
64
64
  /* 팝업 */
65
65
  .sh-ui-select__positioner {
66
66
  outline: none;
67
- z-index: var(--z-dropdown);
67
+ z-index: var(--z-popover);
68
68
  }
69
69
 
70
70
  .sh-ui-select__content {
@@ -64,7 +64,7 @@
64
64
  /* 팝업 */
65
65
  .select__positioner {
66
66
  outline: none;
67
- z-index: var(--z-dropdown);
67
+ z-index: var(--z-popover);
68
68
  }
69
69
 
70
70
  .select__content {
@@ -3,7 +3,7 @@
3
3
  import * as React from "react";
4
4
  import { cn } from "@SH_UI_UTILS@";
5
5
  import { ChevronRightIcon, PanelLeftIcon } from "lucide-react";
6
- import { Popover, PopoverContent, PopoverTrigger } from "../popover/index.tsx";
6
+ import { Popover, PopoverContent, PopoverTrigger } from "../popover";
7
7
  import styles from "./styles.module.css";
8
8
 
9
9
  const SIDEBAR_COOKIE_NAME = "sidebar_state";
@@ -3,7 +3,7 @@
3
3
  import * as React from "react";
4
4
  import { cn } from "@SH_UI_UTILS@";
5
5
  import { ChevronRightIcon, PanelLeftIcon } from "lucide-react";
6
- import { Popover, PopoverContent, PopoverTrigger } from "../popover/index.tsx";
6
+ import { Popover, PopoverContent, PopoverTrigger } from "../popover";
7
7
 
8
8
  const SIDEBAR_COOKIE_NAME = "sidebar_state";
9
9
  const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7;
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$description": "컴포넌트별 토큰 의존성 (var(--*) 추출). build-registry-tokens.mjs 가 자동 생성.",
3
- "$generated": "2026-05-11T07:02:18.245Z",
3
+ "$generated": "2026-05-12T23:53:11.079Z",
4
4
  "components": {
5
5
  "button": {
6
6
  "plain": [
@@ -600,7 +600,7 @@
600
600
  "--text-sm",
601
601
  "--text-xs",
602
602
  "--weight-semibold",
603
- "--z-dropdown"
603
+ "--z-popover"
604
604
  ],
605
605
  "tailwind": [
606
606
  "--border-width-strong",
@@ -614,7 +614,7 @@
614
614
  "--space-3",
615
615
  "--text-sm",
616
616
  "--text-xs",
617
- "--z-dropdown"
617
+ "--z-popover"
618
618
  ],
619
619
  "css-modules": [
620
620
  "--background",
@@ -636,7 +636,7 @@
636
636
  "--text-sm",
637
637
  "--text-xs",
638
638
  "--weight-semibold",
639
- "--z-dropdown"
639
+ "--z-popover"
640
640
  ],
641
641
  "vanilla-extract": []
642
642
  },
@@ -1034,7 +1034,7 @@
1034
1034
  "--text-xs",
1035
1035
  "--weight-medium",
1036
1036
  "--weight-semibold",
1037
- "--z-dropdown"
1037
+ "--z-popover"
1038
1038
  ],
1039
1039
  "tailwind": [
1040
1040
  "--border-width-strong",
@@ -1048,7 +1048,7 @@
1048
1048
  "--space-3",
1049
1049
  "--text-sm",
1050
1050
  "--text-xs",
1051
- "--z-dropdown"
1051
+ "--z-popover"
1052
1052
  ],
1053
1053
  "css-modules": [
1054
1054
  "--background",
@@ -1072,7 +1072,7 @@
1072
1072
  "--text-xs",
1073
1073
  "--weight-medium",
1074
1074
  "--weight-semibold",
1075
- "--z-dropdown"
1075
+ "--z-popover"
1076
1076
  ],
1077
1077
  "vanilla-extract": []
1078
1078
  },
@@ -1202,7 +1202,7 @@
1202
1202
  "--text-sm",
1203
1203
  "--text-xs",
1204
1204
  "--weight-semibold",
1205
- "--z-dropdown"
1205
+ "--z-popover"
1206
1206
  ],
1207
1207
  "tailwind": [
1208
1208
  "--border-width-strong",
@@ -1213,7 +1213,7 @@
1213
1213
  "--space-2",
1214
1214
  "--text-sm",
1215
1215
  "--text-xs",
1216
- "--z-dropdown"
1216
+ "--z-popover"
1217
1217
  ],
1218
1218
  "css-modules": [
1219
1219
  "--background",
@@ -1230,7 +1230,7 @@
1230
1230
  "--text-sm",
1231
1231
  "--text-xs",
1232
1232
  "--weight-semibold",
1233
- "--z-dropdown"
1233
+ "--z-popover"
1234
1234
  ],
1235
1235
  "vanilla-extract": []
1236
1236
  },
@@ -1248,7 +1248,7 @@
1248
1248
  "--text-sm",
1249
1249
  "--text-xs",
1250
1250
  "--weight-semibold",
1251
- "--z-dropdown"
1251
+ "--z-popover"
1252
1252
  ],
1253
1253
  "tailwind": [
1254
1254
  "--opacity-disabled",
@@ -1258,7 +1258,7 @@
1258
1258
  "--space-2",
1259
1259
  "--text-sm",
1260
1260
  "--text-xs",
1261
- "--z-dropdown"
1261
+ "--z-popover"
1262
1262
  ],
1263
1263
  "css-modules": [
1264
1264
  "--background",
@@ -1273,7 +1273,7 @@
1273
1273
  "--text-sm",
1274
1274
  "--text-xs",
1275
1275
  "--weight-semibold",
1276
- "--z-dropdown"
1276
+ "--z-popover"
1277
1277
  ],
1278
1278
  "vanilla-extract": []
1279
1279
  },
@@ -113,9 +113,9 @@
113
113
  },
114
114
 
115
115
  "controlHeight": {
116
- "sm": { "$value": "2rem", "$type": "dimension" },
117
- "md": { "$value": "2.5rem", "$type": "dimension" },
118
- "lg": { "$value": "3rem", "$type": "dimension" }
116
+ "sm": { "$value": "2rem", "$type": "dimension" },
117
+ "md": { "$value": "2.25rem", "$type": "dimension" },
118
+ "lg": { "$value": "2.5rem", "$type": "dimension" }
119
119
  },
120
120
 
121
121
  "borderWidth": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sh-ui-cli",
3
- "version": "0.80.1",
3
+ "version": "0.81.0",
4
4
  "description": "sh-ui CLI — 프로젝트 스캐폴드(create) + 컴포넌트 추가(add/list/remove) + IDE-내 AI용 MCP 서버",
5
5
  "license": "MIT",
6
6
  "repository": {
package/src/add.mjs CHANGED
@@ -180,7 +180,19 @@ async function writeOrDiff({ dest, content, cwd, diffMode, summary, conflictReso
180
180
  /** 특수 컴포넌트: 설정으로 토큰 파일 생성 */
181
181
  async function addTokens(config, cwd, diffMode, summary, conflictResolver) {
182
182
  const destRel = config.paths?.tokens;
183
- if (!destRel) throw new Error("paths.tokens 가 설정에 없습니다.");
183
+ if (!destRel) {
184
+ // v0.65+ monorepo: ui-core 는 tokens-only 마커가 아니라 컴포넌트-only.
185
+ // tokens 는 ui-apps/ui-<name>/ 의 역할. cwd 가 ui-core 면 안내를 명확히.
186
+ const cwdLower = cwd.replace(/\\/g, '/').toLowerCase();
187
+ if (cwdLower.endsWith('/packages/ui/ui-core')) {
188
+ throw new Error(
189
+ "paths.tokens 가 설정에 없습니다.\n" +
190
+ " ui-core 는 컴포넌트 단일 SoT 라 tokens 를 보관하지 않습니다 (v0.65+ layout).\n" +
191
+ " tokens 는 ui-app 의 역할 — cwd 를 packages/ui/ui-apps/ui-<name>/ 로 바꾸세요.",
192
+ );
193
+ }
194
+ throw new Error("paths.tokens 가 설정에 없습니다.");
195
+ }
184
196
  const dest = resolve(cwd, destRel);
185
197
 
186
198
  // theme.base === 'custom' 이면 토큰 빌더가 color.custom.X 스케일을 못 찾아 throw 한다 —
@@ -104,7 +104,7 @@ import {
104
104
  buildDartEasesBlock,
105
105
  buildDartGradientsBlock,
106
106
  } from './theme/inject.js';
107
- import { getTemplatesRoot } from '../paths.mjs';
107
+ import { getTemplatesRoot, getRegistryRoot } from '../paths.mjs';
108
108
  import {
109
109
  CSS_FRAMEWORK_DEFAULT,
110
110
  CSS_FRAMEWORKS_SUPPORTED,
@@ -714,6 +714,39 @@ async function generateMonorepo(targetDir, projectName, plugins, { yes = false,
714
714
  const uiAppDir = path.join(targetDir, 'packages', 'ui', 'ui-apps', `ui-${appName}`);
715
715
  await injectCssTheme(uiAppDir, theme);
716
716
  await patchShUiConfig(path.join(uiAppDir, 'sh-ui.config.json'), css, themeBase);
717
+
718
+ // ui-core sh-ui.config.json 의 cssFramework 도 동일하게 맞춘다 (v0.81.0+).
719
+ // 안 그러면 후속 add_component 호출이 plain 변종으로 fallback.
720
+ const uiCoreConfigPath = path.join(targetDir, 'packages', 'ui', 'ui-core', 'sh-ui.config.json');
721
+ await patchShUiConfig(uiCoreConfigPath, css, themeBase);
722
+
723
+ // ui-core 에 utility CSS 5종 자동 설치 (v0.81.0+).
724
+ // ui-app/globals.css 가 이 5개 파일을 @import 하므로 안 깔리면 빌드 깨짐.
725
+ // create → dev 사이에 사용자가 매뉴얼 `add base focus-ring …` 호출 안 해도 굴러가게 함.
726
+ await prefetchUiCoreUtilityCss(path.join(targetDir, 'packages', 'ui', 'ui-core'));
727
+ }
728
+
729
+ /**
730
+ * v0.81.0+ — monorepo create_project 직후 ui-core 에 베이스 유틸 CSS 를 자동 emit.
731
+ *
732
+ * ui-app-template/src/styles/globals.css 가 `@workspace/ui-core/styles/{base,focus-ring,
733
+ * animations,breakpoints,z-index}.css` 를 sentinel 블록 안에서 @import 하므로 이 파일들이
734
+ * 존재해야 Tailwind v4 빌드가 깨지지 않는다. 외부 의존성 없는 5개 CSS 파일이라 registry
735
+ * 라우팅 거치지 않고 직접 카피 — add.mjs 의 framework 매칭 / placeholder 치환 로직이
736
+ * 필요 없다 (모두 plain CSS, 토큰 변수만 참조).
737
+ */
738
+ async function prefetchUiCoreUtilityCss(uiCoreDir) {
739
+ const stylesDir = path.join(uiCoreDir, 'src', 'styles');
740
+ await fs.ensureDir(stylesDir);
741
+ const registryRoot = getRegistryRoot('react');
742
+ const names = ['base', 'focus-ring', 'animations', 'breakpoints', 'z-index'];
743
+ for (const name of names) {
744
+ const src = path.join(registryRoot, 'components', name, `${name}.css`);
745
+ const dest = path.join(stylesDir, `${name}.css`);
746
+ if (await fs.pathExists(src) && !(await fs.pathExists(dest))) {
747
+ await fs.copy(src, dest);
748
+ }
749
+ }
717
750
  }
718
751
 
719
752
  async function generateApp(targetDir, appName, port, plugins, arch, css = 'tailwind') {
@@ -11,7 +11,6 @@
11
11
  "module": "NodeNext",
12
12
  "moduleDetection": "force",
13
13
  "moduleResolution": "NodeNext",
14
- "noUncheckedIndexedAccess": true,
15
14
  "resolveJsonModule": true,
16
15
  "skipLibCheck": true,
17
16
  "strict": true,
@@ -145,8 +145,8 @@
145
145
  /* sh-ui:theme-ease-end */
146
146
  /* sh-ui:theme-control-start */
147
147
  --control-sm: 2rem;
148
- --control-md: 2.5rem;
149
- --control-lg: 3rem;
148
+ --control-md: 2.25rem;
149
+ --control-lg: 2.5rem;
150
150
  /* sh-ui:theme-control-end */
151
151
  /* sh-ui:theme-border-width-start */
152
152
  --border-width: 1px;
@@ -13,7 +13,6 @@
13
13
  "resolveJsonModule": true,
14
14
  "skipLibCheck": true,
15
15
  "strict": true,
16
- "noUncheckedIndexedAccess": true,
17
16
  "allowJs": true,
18
17
  "jsx": "preserve",
19
18
  "noEmit": true,
@@ -145,8 +145,8 @@
145
145
  /* sh-ui:theme-ease-end */
146
146
  /* sh-ui:theme-control-start */
147
147
  --control-sm: 2rem;
148
- --control-md: 2.5rem;
149
- --control-lg: 3rem;
148
+ --control-md: 2.25rem;
149
+ --control-lg: 2.5rem;
150
150
  /* sh-ui:theme-control-end */
151
151
  /* sh-ui:theme-border-width-start */
152
152
  --border-width: 1px;
@@ -13,7 +13,6 @@
13
13
  "resolveJsonModule": true,
14
14
  "skipLibCheck": true,
15
15
  "strict": true,
16
- "noUncheckedIndexedAccess": true,
17
16
  "allowJs": true,
18
17
  "jsx": "preserve",
19
18
  "noEmit": true,
@@ -112,8 +112,8 @@
112
112
  /* sh-ui:theme-ease-end */
113
113
  /* sh-ui:theme-control-start */
114
114
  --control-sm: 2rem;
115
- --control-md: 2.5rem;
116
- --control-lg: 3rem;
115
+ --control-md: 2.25rem;
116
+ --control-lg: 2.5rem;
117
117
  /* sh-ui:theme-control-end */
118
118
  /* sh-ui:theme-border-width-start */
119
119
  --border-width: 1px;
@@ -13,7 +13,6 @@
13
13
  "resolveJsonModule": true,
14
14
  "skipLibCheck": true,
15
15
  "strict": true,
16
- "noUncheckedIndexedAccess": true,
17
16
  "allowJs": true,
18
17
  "jsx": "preserve",
19
18
  "noEmit": true,
@@ -1,10 +1,20 @@
1
1
  @import 'tailwindcss';
2
2
 
3
3
  @source "../../../../../../apps/**/*.{ts,tsx}";
4
+ @source "../../../../ui-core/src/**/*.{ts,tsx}";
4
5
  @source "../**/*.{ts,tsx}";
5
6
 
6
7
  @import './tokens.css';
7
8
 
9
+ /* sh-ui:utility-imports-start — v0.81+ create_project 가 자동 설치하는 베이스 유틸 CSS.
10
+ * 이 블록 안의 줄들은 add_component 가 멱등하게 관리. 직접 편집은 가능하지만 sentinel 은 유지. */
11
+ @import '@workspace/ui-core/styles/base.css';
12
+ @import '@workspace/ui-core/styles/focus-ring.css';
13
+ @import '@workspace/ui-core/styles/animations.css';
14
+ @import '@workspace/ui-core/styles/breakpoints.css';
15
+ @import '@workspace/ui-core/styles/z-index.css';
16
+ /* sh-ui:utility-imports-end */
17
+
8
18
  @custom-variant dark (&:is(.dark *));
9
19
 
10
20
  /* Tailwind v4 — 토큰 CSS 변수를 utility 클래스로 노출.
@@ -145,8 +145,8 @@
145
145
  /* sh-ui:theme-ease-end */
146
146
  /* sh-ui:theme-control-start */
147
147
  --control-sm: 2rem;
148
- --control-md: 2.5rem;
149
- --control-lg: 3rem;
148
+ --control-md: 2.25rem;
149
+ --control-lg: 2.5rem;
150
150
  /* sh-ui:theme-control-end */
151
151
  /* sh-ui:theme-border-width-start */
152
152
  --border-width: 1px;