sh-ui-cli 0.45.1 → 0.45.3

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 (93) hide show
  1. package/data/changelog/versions.json +25 -0
  2. package/data/registry/react/components/accordion/index.tailwind.tsx +5 -7
  3. package/data/registry/react/components/accordion/index.tsx +5 -7
  4. package/data/registry/react/components/avatar/index.tailwind.tsx +4 -6
  5. package/data/registry/react/components/avatar/index.tsx +4 -6
  6. package/data/registry/react/components/badge/index.tailwind.tsx +2 -4
  7. package/data/registry/react/components/badge/index.tsx +2 -4
  8. package/data/registry/react/components/breadcrumb/index.tailwind.tsx +8 -10
  9. package/data/registry/react/components/breadcrumb/index.tsx +8 -10
  10. package/data/registry/react/components/button/index.tailwind.tsx +2 -1
  11. package/data/registry/react/components/button/index.tsx +3 -4
  12. package/data/registry/react/components/calendar/index.tailwind.tsx +10 -12
  13. package/data/registry/react/components/calendar/index.tsx +9 -11
  14. package/data/registry/react/components/card/index.tailwind.tsx +8 -10
  15. package/data/registry/react/components/card/index.tsx +8 -10
  16. package/data/registry/react/components/carousel/index.tailwind.tsx +7 -9
  17. package/data/registry/react/components/carousel/index.tsx +7 -9
  18. package/data/registry/react/components/checkbox/index.tailwind.tsx +3 -5
  19. package/data/registry/react/components/checkbox/index.tsx +3 -5
  20. package/data/registry/react/components/code-editor/index.tailwind.tsx +2 -4
  21. package/data/registry/react/components/code-editor/index.tsx +2 -4
  22. package/data/registry/react/components/code-panel/index.tailwind.tsx +5 -7
  23. package/data/registry/react/components/code-panel/index.tsx +5 -7
  24. package/data/registry/react/components/color-picker/index.tailwind.tsx +7 -6
  25. package/data/registry/react/components/color-picker/index.tsx +7 -6
  26. package/data/registry/react/components/combobox/index.tailwind.tsx +8 -10
  27. package/data/registry/react/components/combobox/index.tsx +8 -10
  28. package/data/registry/react/components/context-menu/index.tailwind.tsx +10 -12
  29. package/data/registry/react/components/context-menu/index.tsx +10 -12
  30. package/data/registry/react/components/date-picker/index.tailwind.tsx +7 -9
  31. package/data/registry/react/components/date-picker/index.tsx +7 -9
  32. package/data/registry/react/components/dialog/index.tailwind.tsx +6 -8
  33. package/data/registry/react/components/dialog/index.tsx +6 -8
  34. package/data/registry/react/components/dropdown-menu/index.tailwind.tsx +10 -12
  35. package/data/registry/react/components/dropdown-menu/index.tsx +10 -12
  36. package/data/registry/react/components/file-upload/index.tailwind.tsx +6 -8
  37. package/data/registry/react/components/file-upload/index.tsx +6 -8
  38. package/data/registry/react/components/form/field.tailwind.tsx +2 -1
  39. package/data/registry/react/components/form/field.tsx +2 -3
  40. package/data/registry/react/components/header/index.tailwind.tsx +17 -19
  41. package/data/registry/react/components/header/index.tsx +17 -19
  42. package/data/registry/react/components/input/index.tailwind.tsx +4 -6
  43. package/data/registry/react/components/input/index.tsx +4 -6
  44. package/data/registry/react/components/label/index.tailwind.tsx +6 -8
  45. package/data/registry/react/components/label/index.tsx +6 -8
  46. package/data/registry/react/components/markdown-editor/index.tailwind.tsx +2 -4
  47. package/data/registry/react/components/markdown-editor/index.tsx +2 -4
  48. package/data/registry/react/components/menubar/index.tailwind.tsx +2 -4
  49. package/data/registry/react/components/menubar/index.tsx +2 -4
  50. package/data/registry/react/components/numeric-input/index.tailwind.tsx +2 -4
  51. package/data/registry/react/components/numeric-input/index.tsx +2 -4
  52. package/data/registry/react/components/page-toc/index.tailwind.tsx +3 -2
  53. package/data/registry/react/components/page-toc/index.tsx +2 -3
  54. package/data/registry/react/components/pagination/index.tailwind.tsx +8 -10
  55. package/data/registry/react/components/pagination/index.tsx +8 -10
  56. package/data/registry/react/components/popover/index.tailwind.tsx +4 -6
  57. package/data/registry/react/components/popover/index.tsx +4 -6
  58. package/data/registry/react/components/progress/index.tailwind.tsx +3 -5
  59. package/data/registry/react/components/progress/index.tsx +2 -4
  60. package/data/registry/react/components/radio/index.tailwind.tsx +3 -5
  61. package/data/registry/react/components/radio/index.tsx +3 -5
  62. package/data/registry/react/components/rich-text-editor/index.tailwind.tsx +3 -5
  63. package/data/registry/react/components/rich-text-editor/index.tsx +3 -5
  64. package/data/registry/react/components/select/index.tailwind.tsx +8 -10
  65. package/data/registry/react/components/select/index.tsx +8 -10
  66. package/data/registry/react/components/separator/index.tailwind.tsx +2 -4
  67. package/data/registry/react/components/separator/index.tsx +2 -4
  68. package/data/registry/react/components/sidebar/index.tailwind.tsx +32 -43
  69. package/data/registry/react/components/sidebar/index.tsx +29 -46
  70. package/data/registry/react/components/skeleton/index.tailwind.tsx +2 -4
  71. package/data/registry/react/components/skeleton/index.tsx +2 -4
  72. package/data/registry/react/components/slider/index.tailwind.tsx +5 -7
  73. package/data/registry/react/components/slider/index.tsx +5 -7
  74. package/data/registry/react/components/spinner/index.tailwind.tsx +3 -5
  75. package/data/registry/react/components/spinner/index.tsx +2 -4
  76. package/data/registry/react/components/switch/index.tailwind.tsx +3 -5
  77. package/data/registry/react/components/switch/index.tsx +2 -4
  78. package/data/registry/react/components/tabs/index.tailwind.tsx +6 -8
  79. package/data/registry/react/components/tabs/index.tsx +6 -8
  80. package/data/registry/react/components/textarea/index.tailwind.tsx +2 -4
  81. package/data/registry/react/components/textarea/index.tsx +2 -4
  82. package/data/registry/react/components/toggle/index.tailwind.tsx +4 -6
  83. package/data/registry/react/components/toggle/index.tsx +4 -6
  84. package/data/registry/react/components/tooltip/index.tailwind.tsx +2 -4
  85. package/data/registry/react/components/tooltip/index.tsx +2 -4
  86. package/data/registry/react/lib/cn.tailwind.ts +17 -0
  87. package/data/registry/react/peer-versions.json +3 -1
  88. package/data/registry/react/registry.json +159 -43
  89. package/package.json +1 -1
  90. package/src/add.mjs +25 -1
  91. package/src/mcp-init.mjs +13 -5
  92. package/src/mcp.mjs +20 -15
  93. package/templates/ui-app-template/sh-ui.config.json +5 -0
@@ -1,16 +1,14 @@
1
1
  import * as React from "react";
2
2
 
3
+ import { cn } from "@SH_UI_UTILS@";
3
4
  type DivProps = React.HTMLAttributes<HTMLDivElement>;
4
5
 
5
- function mergeClass(base: string, extra?: string) {
6
- return extra ? `${base} ${extra}` : base;
7
- }
8
6
 
9
7
  export const Card = React.forwardRef<HTMLDivElement, DivProps>(
10
8
  ({ className, ...props }, ref) => (
11
9
  <div
12
10
  ref={ref}
13
- className={mergeClass(
11
+ className={cn(
14
12
  "flex flex-col gap-[var(--space-6)] py-[var(--space-6)] bg-background text-foreground border border-border rounded-[var(--radius)] max-sm:gap-[var(--space-4)] max-sm:py-[var(--space-4)]",
15
13
  className,
16
14
  )}
@@ -25,7 +23,7 @@ export const CardHeader = React.forwardRef<HTMLDivElement, DivProps>(
25
23
  <div
26
24
  ref={ref}
27
25
  data-slot="card-header"
28
- className={mergeClass(
26
+ className={cn(
29
27
  "grid grid-cols-1 auto-rows-auto gap-y-1.5 px-[var(--space-6)] has-[[data-slot=card-action]]:grid-cols-[1fr_auto] max-sm:px-[var(--space-4)]",
30
28
  className,
31
29
  )}
@@ -39,7 +37,7 @@ export const CardTitle = React.forwardRef<HTMLDivElement, DivProps>(
39
37
  ({ className, ...props }, ref) => (
40
38
  <div
41
39
  ref={ref}
42
- className={mergeClass(
40
+ className={cn(
43
41
  "text-[length:var(--text-base)] font-semibold leading-tight tracking-tight",
44
42
  className,
45
43
  )}
@@ -53,7 +51,7 @@ export const CardDescription = React.forwardRef<HTMLDivElement, DivProps>(
53
51
  ({ className, ...props }, ref) => (
54
52
  <div
55
53
  ref={ref}
56
- className={mergeClass(
54
+ className={cn(
57
55
  "text-[length:var(--text-sm)] leading-normal text-foreground-muted",
58
56
  className,
59
57
  )}
@@ -72,7 +70,7 @@ export const CardAction = React.forwardRef<HTMLDivElement, DivProps>(
72
70
  <div
73
71
  ref={ref}
74
72
  data-slot="card-action"
75
- className={mergeClass(
73
+ className={cn(
76
74
  "col-start-2 row-span-2 self-start justify-self-end",
77
75
  className,
78
76
  )}
@@ -86,7 +84,7 @@ export const CardContent = React.forwardRef<HTMLDivElement, DivProps>(
86
84
  ({ className, ...props }, ref) => (
87
85
  <div
88
86
  ref={ref}
89
- className={mergeClass(
87
+ className={cn(
90
88
  "px-[var(--space-6)] text-[length:var(--text-sm)] leading-relaxed max-sm:px-[var(--space-4)]",
91
89
  className,
92
90
  )}
@@ -100,7 +98,7 @@ export const CardFooter = React.forwardRef<HTMLDivElement, DivProps>(
100
98
  ({ className, ...props }, ref) => (
101
99
  <div
102
100
  ref={ref}
103
- className={mergeClass(
101
+ className={cn(
104
102
  "px-[var(--space-6)] flex items-center gap-[var(--space-2)] max-sm:px-[var(--space-4)] max-sm:flex-wrap",
105
103
  className,
106
104
  )}
@@ -1,15 +1,13 @@
1
1
  import * as React from "react";
2
+ import { cn } from "@SH_UI_UTILS@";
2
3
  import "./styles.css";
3
4
 
4
5
  type DivProps = React.HTMLAttributes<HTMLDivElement>;
5
6
 
6
- function mergeClass(base: string, extra?: string) {
7
- return extra ? `${base} ${extra}` : base;
8
- }
9
7
 
10
8
  export const Card = React.forwardRef<HTMLDivElement, DivProps>(
11
9
  ({ className, ...props }, ref) => (
12
- <div ref={ref} className={mergeClass("sh-ui-card", className)} {...props} />
10
+ <div ref={ref} className={cn("sh-ui-card", className)} {...props} />
13
11
  ),
14
12
  );
15
13
  Card.displayName = "Card";
@@ -19,7 +17,7 @@ export const CardHeader = React.forwardRef<HTMLDivElement, DivProps>(
19
17
  <div
20
18
  ref={ref}
21
19
  data-slot="card-header"
22
- className={mergeClass("sh-ui-card__header", className)}
20
+ className={cn("sh-ui-card__header", className)}
23
21
  {...props}
24
22
  />
25
23
  ),
@@ -30,7 +28,7 @@ export const CardTitle = React.forwardRef<HTMLDivElement, DivProps>(
30
28
  ({ className, ...props }, ref) => (
31
29
  <div
32
30
  ref={ref}
33
- className={mergeClass("sh-ui-card__title", className)}
31
+ className={cn("sh-ui-card__title", className)}
34
32
  {...props}
35
33
  />
36
34
  ),
@@ -41,7 +39,7 @@ export const CardDescription = React.forwardRef<HTMLDivElement, DivProps>(
41
39
  ({ className, ...props }, ref) => (
42
40
  <div
43
41
  ref={ref}
44
- className={mergeClass("sh-ui-card__description", className)}
42
+ className={cn("sh-ui-card__description", className)}
45
43
  {...props}
46
44
  />
47
45
  ),
@@ -56,7 +54,7 @@ export const CardAction = React.forwardRef<HTMLDivElement, DivProps>(
56
54
  ({ className, ...props }, ref) => (
57
55
  <div
58
56
  ref={ref}
59
- className={mergeClass("sh-ui-card__action", className)}
57
+ className={cn("sh-ui-card__action", className)}
60
58
  {...props}
61
59
  />
62
60
  ),
@@ -67,7 +65,7 @@ export const CardContent = React.forwardRef<HTMLDivElement, DivProps>(
67
65
  ({ className, ...props }, ref) => (
68
66
  <div
69
67
  ref={ref}
70
- className={mergeClass("sh-ui-card__content", className)}
68
+ className={cn("sh-ui-card__content", className)}
71
69
  {...props}
72
70
  />
73
71
  ),
@@ -78,7 +76,7 @@ export const CardFooter = React.forwardRef<HTMLDivElement, DivProps>(
78
76
  ({ className, ...props }, ref) => (
79
77
  <div
80
78
  ref={ref}
81
- className={mergeClass("sh-ui-card__footer", className)}
79
+ className={cn("sh-ui-card__footer", className)}
82
80
  {...props}
83
81
  />
84
82
  ),
@@ -2,10 +2,8 @@
2
2
 
3
3
  import * as React from "react";
4
4
 
5
- function cx(...args: (string | undefined | false | null)[]) {
6
- return args.filter(Boolean).join(" ");
7
- }
8
5
 
6
+ import { cn } from "@SH_UI_UTILS@";
9
7
  type Orientation = "horizontal" | "vertical";
10
8
 
11
9
  interface CarouselContextValue {
@@ -144,7 +142,7 @@ export const Carousel = React.forwardRef<HTMLDivElement, CarouselProps>(
144
142
  <CarouselContext.Provider value={value}>
145
143
  <div
146
144
  ref={ref}
147
- className={cx("relative w-full", className)}
145
+ className={cn("relative w-full", className)}
148
146
  data-orientation={orientation}
149
147
  role="region"
150
148
  aria-roledescription="carousel"
@@ -171,7 +169,7 @@ export const CarouselContent = React.forwardRef<HTMLDivElement, React.HTMLAttrib
171
169
  return (
172
170
  <div
173
171
  ref={mergedRef}
174
- className={cx(
172
+ className={cn(
175
173
  "flex gap-[var(--space-4)] overflow-x-auto overflow-y-hidden snap-x snap-mandatory scroll-smooth [scrollbar-width:none] [-ms-overflow-style:none] [-webkit-overflow-scrolling:touch] overscroll-x-contain [&::-webkit-scrollbar]:hidden motion-reduce:scroll-auto",
176
174
  orientation === "vertical" && "flex-col overflow-x-hidden overflow-y-auto snap-y snap-mandatory h-80",
177
175
  className,
@@ -193,7 +191,7 @@ export const CarouselItem = React.forwardRef<HTMLDivElement, React.HTMLAttribute
193
191
  ref={ref}
194
192
  role="group"
195
193
  aria-roledescription="slide"
196
- className={cx(
194
+ className={cn(
197
195
  "flex-[0_0_100%] min-w-0 snap-start snap-always",
198
196
  orientation === "vertical" && "basis-auto",
199
197
  className,
@@ -218,7 +216,7 @@ export const CarouselPrevious = React.forwardRef<HTMLButtonElement, React.Button
218
216
  ref={ref}
219
217
  type="button"
220
218
  aria-label="이전"
221
- className={cx(
219
+ className={cn(
222
220
  navClasses,
223
221
  orientation === "horizontal" ? "-left-4" : "-top-4 left-1/2",
224
222
  className,
@@ -248,7 +246,7 @@ export const CarouselNext = React.forwardRef<HTMLButtonElement, React.ButtonHTML
248
246
  ref={ref}
249
247
  type="button"
250
248
  aria-label="다음"
251
- className={cx(
249
+ className={cn(
252
250
  navClasses,
253
251
  orientation === "horizontal" ? "-right-4" : "-bottom-4 left-1/2 [top:auto]",
254
252
  className,
@@ -282,7 +280,7 @@ export const CarouselIndicators = React.forwardRef<HTMLDivElement, CarouselIndic
282
280
  ref={ref}
283
281
  role="tablist"
284
282
  aria-label="슬라이드 선택"
285
- className={cx(
283
+ className={cn(
286
284
  "flex justify-center items-center gap-[var(--space-2)] mt-[var(--space-3)]",
287
285
  orientation === "vertical" && "absolute top-1/2 right-2 mt-0 flex-col -translate-y-1/2",
288
286
  className,
@@ -3,10 +3,8 @@
3
3
  import * as React from "react";
4
4
  import "./styles.css";
5
5
 
6
- function cx(...args: (string | undefined | false | null)[]) {
7
- return args.filter(Boolean).join(" ");
8
- }
9
6
 
7
+ import { cn } from "@SH_UI_UTILS@";
10
8
  type Orientation = "horizontal" | "vertical";
11
9
 
12
10
  interface CarouselContextValue {
@@ -259,7 +257,7 @@ export const Carousel = React.forwardRef<HTMLDivElement, CarouselProps>(
259
257
  <CarouselContext.Provider value={value}>
260
258
  <div
261
259
  ref={ref}
262
- className={cx("sh-ui-carousel", className)}
260
+ className={cn("sh-ui-carousel", className)}
263
261
  data-orientation={orientation}
264
262
  role="region"
265
263
  aria-roledescription="carousel"
@@ -292,7 +290,7 @@ export const CarouselContent = React.forwardRef<
292
290
  return (
293
291
  <div
294
292
  ref={mergedRef}
295
- className={cx("sh-ui-carousel__content", className)}
293
+ className={cn("sh-ui-carousel__content", className)}
296
294
  data-orientation={orientation}
297
295
  {...props}
298
296
  />
@@ -313,7 +311,7 @@ export const CarouselItem = React.forwardRef<
313
311
  ref={ref}
314
312
  role="group"
315
313
  aria-roledescription="slide"
316
- className={cx("sh-ui-carousel__item", className)}
314
+ className={cn("sh-ui-carousel__item", className)}
317
315
  data-orientation={orientation}
318
316
  {...props}
319
317
  />
@@ -332,7 +330,7 @@ export const CarouselPrevious = React.forwardRef<
332
330
  ref={ref}
333
331
  type="button"
334
332
  aria-label="이전"
335
- className={cx("sh-ui-carousel__nav", "sh-ui-carousel__nav--prev", className)}
333
+ className={cn("sh-ui-carousel__nav", "sh-ui-carousel__nav--prev", className)}
336
334
  data-orientation={orientation}
337
335
  disabled={disabled || atStart || count === 0}
338
336
  onClick={(e) => {
@@ -368,7 +366,7 @@ export const CarouselNext = React.forwardRef<
368
366
  ref={ref}
369
367
  type="button"
370
368
  aria-label="다음"
371
- className={cx("sh-ui-carousel__nav", "sh-ui-carousel__nav--next", className)}
369
+ className={cn("sh-ui-carousel__nav", "sh-ui-carousel__nav--next", className)}
372
370
  data-orientation={orientation}
373
371
  disabled={disabled || atEnd || count === 0}
374
372
  onClick={(e) => {
@@ -410,7 +408,7 @@ export const CarouselIndicators = React.forwardRef<
410
408
  ref={ref}
411
409
  role="tablist"
412
410
  aria-label="슬라이드 선택"
413
- className={cx("sh-ui-carousel__indicators", className)}
411
+ className={cn("sh-ui-carousel__indicators", className)}
414
412
  data-orientation={orientation}
415
413
  {...props}
416
414
  >
@@ -2,10 +2,8 @@ import * as React from "react";
2
2
  import { Checkbox as BaseCheckbox } from "@base-ui/react/checkbox";
3
3
  import { CheckboxGroup as BaseCheckboxGroup } from "@base-ui/react/checkbox-group";
4
4
 
5
- function cx(...args: (string | undefined | false)[]) {
6
- return args.filter(Boolean).join(" ");
7
- }
8
5
 
6
+ import { cn } from "@SH_UI_UTILS@";
9
7
  export type CheckboxProps = Omit<
10
8
  React.ComponentPropsWithoutRef<typeof BaseCheckbox.Root>,
11
9
  "className"
@@ -17,7 +15,7 @@ export const Checkbox = React.forwardRef<HTMLElement, CheckboxProps>(
17
15
  ({ className, ...props }, ref) => (
18
16
  <BaseCheckbox.Root
19
17
  ref={ref}
20
- className={cx(
18
+ className={cn(
21
19
  "inline-flex items-center justify-center w-[1.125rem] h-[1.125rem] border border-border-strong rounded-[calc(var(--radius)-2px)] bg-background text-primary-foreground cursor-pointer shrink-0 transition-[background-color,border-color] duration-[var(--duration-fast)] hover:not-data-[disabled]:border-foreground focus-visible:outline-[length:var(--border-width-strong)] focus-visible:outline-foreground focus-visible:outline-offset-2 data-[checked]:bg-primary data-[checked]:border-primary data-[indeterminate]:bg-primary data-[indeterminate]:border-primary data-[disabled]:opacity-[var(--opacity-disabled)] data-[disabled]:cursor-not-allowed motion-reduce:transition-none [@media(hover:none)_and_(pointer:coarse)]:w-5 [@media(hover:none)_and_(pointer:coarse)]:h-5",
22
20
  className,
23
21
  )}
@@ -43,7 +41,7 @@ export const CheckboxGroup = React.forwardRef<HTMLDivElement, CheckboxGroupProps
43
41
  ({ className, orientation = "vertical", ...props }, ref) => (
44
42
  <BaseCheckboxGroup
45
43
  ref={ref}
46
- className={cx(
44
+ className={cn(
47
45
  "flex gap-2.5",
48
46
  orientation === "vertical" ? "flex-col" : "flex-row flex-wrap",
49
47
  className,
@@ -3,10 +3,8 @@ import { Checkbox as BaseCheckbox } from "@base-ui/react/checkbox";
3
3
  import { CheckboxGroup as BaseCheckboxGroup } from "@base-ui/react/checkbox-group";
4
4
  import "./styles.css";
5
5
 
6
- function cx(...args: (string | undefined | false)[]) {
7
- return args.filter(Boolean).join(" ");
8
- }
9
6
 
7
+ import { cn } from "@SH_UI_UTILS@";
10
8
  /* ───────────── Checkbox ───────────── */
11
9
 
12
10
  export type CheckboxProps = Omit<
@@ -24,7 +22,7 @@ export const Checkbox = React.forwardRef<HTMLElement, CheckboxProps>(
24
22
  ({ className, ...props }, ref) => (
25
23
  <BaseCheckbox.Root
26
24
  ref={ref}
27
- className={cx("sh-ui-checkbox", className)}
25
+ className={cn("sh-ui-checkbox", className)}
28
26
  {...props}
29
27
  >
30
28
  <BaseCheckbox.Indicator className="sh-ui-checkbox__indicator">
@@ -60,7 +58,7 @@ export const CheckboxGroup = React.forwardRef<HTMLDivElement, CheckboxGroupProps
60
58
  ({ className, orientation = "vertical", ...props }, ref) => (
61
59
  <BaseCheckboxGroup
62
60
  ref={ref}
63
- className={cx("sh-ui-checkbox-group", className)}
61
+ className={cn("sh-ui-checkbox-group", className)}
64
62
  data-orientation={orientation}
65
63
  {...props}
66
64
  />
@@ -10,6 +10,7 @@ import { css as cssLang } from "@codemirror/lang-css";
10
10
  import { html } from "@codemirror/lang-html";
11
11
  import { markdown } from "@codemirror/lang-markdown";
12
12
 
13
+ import { cn } from "@SH_UI_UTILS@";
13
14
  export type CodeEditorLanguage =
14
15
  | "text" | "javascript" | "typescript" | "jsx" | "tsx"
15
16
  | "json" | "css" | "html" | "markdown";
@@ -30,9 +31,6 @@ export interface CodeEditorProps {
30
31
  "aria-labelledby"?: string;
31
32
  }
32
33
 
33
- function cx(...args: (string | undefined | false | null)[]) {
34
- return args.filter(Boolean).join(" ");
35
- }
36
34
 
37
35
  function languageExtension(language: CodeEditorLanguage): Extension {
38
36
  switch (language) {
@@ -136,7 +134,7 @@ export function CodeEditor({
136
134
  return (
137
135
  <div
138
136
  ref={hostRef}
139
- className={cx(
137
+ className={cn(
140
138
  "sh-ui-code-editor relative border border-border rounded-[var(--radius)] bg-background text-[0.8125rem] leading-relaxed overflow-hidden transition-[border-color] duration-[var(--duration-fast)] focus-within:border-foreground focus-within:outline-[length:var(--border-width-strong)] focus-within:outline-foreground focus-within:outline-offset-2 data-[readonly]:bg-background-subtle motion-reduce:transition-none",
141
139
  className,
142
140
  )}
@@ -11,6 +11,7 @@ import { html } from "@codemirror/lang-html";
11
11
  import { markdown } from "@codemirror/lang-markdown";
12
12
  import "./styles.css";
13
13
 
14
+ import { cn } from "@SH_UI_UTILS@";
14
15
  export type CodeEditorLanguage =
15
16
  | "text"
16
17
  | "javascript"
@@ -59,9 +60,6 @@ export interface CodeEditorProps {
59
60
  "aria-labelledby"?: string;
60
61
  }
61
62
 
62
- function cx(...args: (string | undefined | false | null)[]) {
63
- return args.filter(Boolean).join(" ");
64
- }
65
63
 
66
64
  function languageExtension(language: CodeEditorLanguage): Extension {
67
65
  switch (language) {
@@ -219,7 +217,7 @@ export function CodeEditor({
219
217
  return (
220
218
  <div
221
219
  ref={hostRef}
222
- className={cx("sh-ui-code-editor", className)}
220
+ className={cn("sh-ui-code-editor", className)}
223
221
  data-readonly={readOnly || undefined}
224
222
  style={
225
223
  {
@@ -1,10 +1,8 @@
1
1
  import { codeToHtml } from "shiki";
2
2
  import { CodePanelCopyButton } from "./copy";
3
3
 
4
- function cx(...args: (string | undefined | false | null)[]) {
5
- return args.filter(Boolean).join(" ");
6
- }
7
4
 
5
+ import { cn } from "@SH_UI_UTILS@";
8
6
  export interface CodePanelProps
9
7
  extends Omit<React.HTMLAttributes<HTMLDivElement>, "children"> {
10
8
  code?: string;
@@ -21,7 +19,7 @@ const rootClasses =
21
19
  export async function CodePanel({
22
20
  code, language = "text", filename, showLineNumbers = true, hideCopy, className, children, ...rest
23
21
  }: CodePanelProps) {
24
- const classes = cx(rootClasses, className);
22
+ const classes = cn(rootClasses, className);
25
23
 
26
24
  if (children !== undefined) {
27
25
  return <div className={classes} {...rest}>{children}</div>;
@@ -52,7 +50,7 @@ export async function CodePanel({
52
50
  export function CodePanelHeader({ className, children, ...props }: React.HTMLAttributes<HTMLDivElement>) {
53
51
  return (
54
52
  <div
55
- className={cx(
53
+ className={cn(
56
54
  "flex items-center justify-between gap-[var(--space-2)] py-[var(--space-2)] pl-[var(--space-4)] pr-[var(--space-3)] border-b border-border bg-background-muted text-[length:var(--text-xs)] text-foreground-muted",
57
55
  className,
58
56
  )}
@@ -65,7 +63,7 @@ export function CodePanelHeader({ className, children, ...props }: React.HTMLAtt
65
63
 
66
64
  export function CodePanelFilename({ className, children, ...props }: React.HTMLAttributes<HTMLSpanElement>) {
67
65
  return (
68
- <span className={cx("font-mono text-foreground", className)} {...props}>{children}</span>
66
+ <span className={cn("font-mono text-foreground", className)} {...props}>{children}</span>
69
67
  );
70
68
  }
71
69
 
@@ -93,7 +91,7 @@ export async function CodePanelBody({
93
91
 
94
92
  return (
95
93
  <div
96
- className={cx(
94
+ className={cn(
97
95
  "overflow-x-auto [&_pre]:m-0 [&_pre]:py-[var(--space-3)] [&_pre]:px-[var(--space-4)] [&_pre]:!bg-transparent [&_pre]:font-mono [&_pre]:text-[length:inherit] [&_pre]:leading-[inherit] [&_pre]:border-none [&_pre]:rounded-none [&_code]:bg-transparent [&_code]:p-0 [&_code]:text-[length:inherit] [&_code]:block [&_.shiki]:!text-[var(--shiki-light)] [&_.shiki_span]:!text-[var(--shiki-light)] [&_.shiki]:!bg-transparent [&_.shiki_span]:!bg-transparent [.dark_&_.shiki]:!text-[var(--shiki-dark)] [.dark_&_.shiki_span]:!text-[var(--shiki-dark)] data-[line-numbers]:[&_pre_code]:[counter-reset:step] data-[line-numbers]:[&_pre_code_.line]:before:[content:counter(step)] data-[line-numbers]:[&_pre_code_.line]:before:[counter-increment:step] data-[line-numbers]:[&_pre_code_.line]:before:inline-block data-[line-numbers]:[&_pre_code_.line]:before:w-7 data-[line-numbers]:[&_pre_code_.line]:before:mr-[var(--space-4)] data-[line-numbers]:[&_pre_code_.line]:before:text-right data-[line-numbers]:[&_pre_code_.line]:before:text-foreground-muted data-[line-numbers]:[&_pre_code_.line]:before:opacity-70 data-[line-numbers]:[&_pre_code_.line]:before:select-none",
98
96
  className,
99
97
  )}
@@ -2,10 +2,8 @@ import { codeToHtml } from "shiki";
2
2
  import { CodePanelCopyButton } from "./copy";
3
3
  import "./styles.css";
4
4
 
5
- function cx(...args: (string | undefined | false | null)[]) {
6
- return args.filter(Boolean).join(" ");
7
- }
8
5
 
6
+ import { cn } from "@SH_UI_UTILS@";
9
7
  export interface CodePanelProps
10
8
  extends Omit<React.HTMLAttributes<HTMLDivElement>, "children"> {
11
9
  /** 하이라이팅할 코드 문자열. children을 제공하지 않을 때 필수. */
@@ -56,7 +54,7 @@ export async function CodePanel({
56
54
  children,
57
55
  ...rest
58
56
  }: CodePanelProps) {
59
- const classes = cx("sh-ui-code", className);
57
+ const classes = cn("sh-ui-code", className);
60
58
 
61
59
  if (children !== undefined) {
62
60
  return (
@@ -104,7 +102,7 @@ export function CodePanelHeader({
104
102
  ...props
105
103
  }: React.HTMLAttributes<HTMLDivElement>) {
106
104
  return (
107
- <div className={cx("sh-ui-code__header", className)} {...props}>
105
+ <div className={cn("sh-ui-code__header", className)} {...props}>
108
106
  {children}
109
107
  </div>
110
108
  );
@@ -119,7 +117,7 @@ export function CodePanelFilename({
119
117
  ...props
120
118
  }: React.HTMLAttributes<HTMLSpanElement>) {
121
119
  return (
122
- <span className={cx("sh-ui-code__filename", className)} {...props}>
120
+ <span className={cn("sh-ui-code__filename", className)} {...props}>
123
121
  {children}
124
122
  </span>
125
123
  );
@@ -182,7 +180,7 @@ export async function CodePanelBody({
182
180
 
183
181
  return (
184
182
  <div
185
- className={cx("sh-ui-code__body", className)}
183
+ className={cn("sh-ui-code__body", className)}
186
184
  data-line-numbers={showLineNumbers || undefined}
187
185
  dangerouslySetInnerHTML={{ __html: html }}
188
186
  {...rest}
@@ -2,6 +2,7 @@
2
2
 
3
3
  import * as React from "react";
4
4
 
5
+ import { cn } from "@SH_UI_UTILS@";
5
6
  interface HSV { h: number; s: number; v: number; }
6
7
  interface HSVA extends HSV { a: number; }
7
8
 
@@ -136,7 +137,7 @@ export function ColorPicker({
136
137
 
137
138
  return (
138
139
  <ColorPickerContext.Provider value={ctx}>
139
- <div className={["flex flex-col gap-2.5 w-full select-none", className].filter(Boolean).join(" ")} {...rest}>
140
+ <div className={cn("flex flex-col gap-2.5 w-full select-none", className)} {...rest}>
140
141
  {children ?? (<><ColorPickerSaturation /><ColorPickerHue /><ColorPickerHex /></>)}
141
142
  </div>
142
143
  </ColorPickerContext.Provider>
@@ -157,7 +158,7 @@ export function ColorPickerSaturation({ className, style, ...rest }: ColorPicker
157
158
  <div
158
159
  ref={drag.ref}
159
160
  onPointerDown={drag.onPointerDown}
160
- className={["relative w-full aspect-[4/3] rounded-[var(--radius)] cursor-crosshair overflow-hidden touch-none", className].filter(Boolean).join(" ")}
161
+ className={cn("relative w-full aspect-[4/3] rounded-[var(--radius)] cursor-crosshair overflow-hidden touch-none", className)}
161
162
  style={{ background: pureHueHex, ...style }}
162
163
  role="slider"
163
164
  aria-label="채도/명도"
@@ -187,7 +188,7 @@ export function ColorPickerHue({ className, ...rest }: ColorPickerHueProps) {
187
188
  <div
188
189
  ref={drag.ref}
189
190
  onPointerDown={drag.onPointerDown}
190
- className={["relative w-full h-3.5 rounded-full cursor-pointer touch-none bg-[linear-gradient(to_right,#f00_0%,#ff0_16.66%,#0f0_33.33%,#0ff_50%,#00f_66.66%,#f0f_83.33%,#f00_100%)]", className].filter(Boolean).join(" ")}
191
+ className={cn("relative w-full h-3.5 rounded-full cursor-pointer touch-none bg-[linear-gradient(to_right,#f00_0%,#ff0_16.66%,#0f0_33.33%,#0ff_50%,#00f_66.66%,#f0f_83.33%,#f00_100%)]", className)}
191
192
  role="slider"
192
193
  aria-label="색조"
193
194
  aria-valuemin={0} aria-valuemax={360} aria-valuenow={Math.round(hsva.h)}
@@ -215,7 +216,7 @@ export function ColorPickerAlpha({ className, style, ...rest }: ColorPickerAlpha
215
216
  <div
216
217
  ref={drag.ref}
217
218
  onPointerDown={drag.onPointerDown}
218
- className={["relative w-full h-3.5 rounded-full cursor-pointer touch-none overflow-hidden bg-white bg-[length:8px_8px] bg-[position:0_0,0_4px,4px_-4px,-4px_0] [background-image:linear-gradient(45deg,#ccc_25%,transparent_25%),linear-gradient(-45deg,#ccc_25%,transparent_25%),linear-gradient(45deg,transparent_75%,#ccc_75%),linear-gradient(-45deg,transparent_75%,#ccc_75%)]", className].filter(Boolean).join(" ")}
219
+ className={cn("relative w-full h-3.5 rounded-full cursor-pointer touch-none overflow-hidden bg-white bg-[length:8px_8px] bg-[position:0_0,0_4px,4px_-4px,-4px_0] [background-image:linear-gradient(45deg,#ccc_25%,transparent_25%),linear-gradient(-45deg,#ccc_25%,transparent_25%),linear-gradient(45deg,transparent_75%,#ccc_75%),linear-gradient(-45deg,transparent_75%,#ccc_75%)]", className)}
219
220
  role="slider"
220
221
  aria-label="투명도"
221
222
  aria-valuemin={0} aria-valuemax={100} aria-valuenow={Math.round(hsva.a * 100)}
@@ -248,7 +249,7 @@ export function ColorPickerHex({ className, showSwatch = true, ...rest }: ColorP
248
249
 
249
250
  return (
250
251
  <div
251
- className={["flex items-center gap-[var(--space-2)]", className].filter(Boolean).join(" ")}
252
+ className={cn("flex items-center gap-[var(--space-2)]", className)}
252
253
  {...rest}
253
254
  >
254
255
  {showSwatch && (
@@ -285,7 +286,7 @@ export function ColorPickerSwatches({ className, colors, ...rest }: ColorPickerS
285
286
  <div
286
287
  role="group"
287
288
  aria-label="미리 준비된 색상"
288
- className={["flex flex-wrap gap-1.5", className].filter(Boolean).join(" ")}
289
+ className={cn("flex flex-wrap gap-1.5", className)}
289
290
  {...rest}
290
291
  >
291
292
  {colors.map((c) => {
@@ -1,6 +1,7 @@
1
1
  "use client";
2
2
 
3
3
  import * as React from "react";
4
+ import { cn } from "@SH_UI_UTILS@";
4
5
  import "./styles.css";
5
6
 
6
7
  /* ───────────── types ───────────── */
@@ -234,7 +235,7 @@ export function ColorPicker({
234
235
  return (
235
236
  <ColorPickerContext.Provider value={ctx}>
236
237
  <div
237
- className={["sh-ui-color-picker", className].filter(Boolean).join(" ")}
238
+ className={cn("sh-ui-color-picker", className)}
238
239
  {...rest}
239
240
  >
240
241
  {children ?? (
@@ -271,7 +272,7 @@ export function ColorPickerSaturation({
271
272
  <div
272
273
  ref={drag.ref}
273
274
  onPointerDown={drag.onPointerDown}
274
- className={["sh-ui-color-picker__sv", className].filter(Boolean).join(" ")}
275
+ className={cn("sh-ui-color-picker__sv", className)}
275
276
  style={{ background: pureHueHex, ...style }}
276
277
  role="slider"
277
278
  aria-label="채도/명도"
@@ -309,7 +310,7 @@ export function ColorPickerHue({ className, ...rest }: ColorPickerHueProps) {
309
310
  <div
310
311
  ref={drag.ref}
311
312
  onPointerDown={drag.onPointerDown}
312
- className={["sh-ui-color-picker__hue", className].filter(Boolean).join(" ")}
313
+ className={cn("sh-ui-color-picker__hue", className)}
313
314
  role="slider"
314
315
  aria-label="색조"
315
316
  aria-valuemin={0}
@@ -341,7 +342,7 @@ export function ColorPickerAlpha({ className, style, ...rest }: ColorPickerAlpha
341
342
  <div
342
343
  ref={drag.ref}
343
344
  onPointerDown={drag.onPointerDown}
344
- className={["sh-ui-color-picker__alpha", className].filter(Boolean).join(" ")}
345
+ className={cn("sh-ui-color-picker__alpha", className)}
345
346
  role="slider"
346
347
  aria-label="투명도"
347
348
  aria-valuemin={0}
@@ -392,7 +393,7 @@ export function ColorPickerHex({
392
393
 
393
394
  return (
394
395
  <div
395
- className={["sh-ui-color-picker__row", className].filter(Boolean).join(" ")}
396
+ className={cn("sh-ui-color-picker__row", className)}
396
397
  {...rest}
397
398
  >
398
399
  {showSwatch && (
@@ -442,7 +443,7 @@ export function ColorPickerSwatches({
442
443
  <div
443
444
  role="group"
444
445
  aria-label="미리 준비된 색상"
445
- className={["sh-ui-color-picker__swatches", className].filter(Boolean).join(" ")}
446
+ className={cn("sh-ui-color-picker__swatches", className)}
446
447
  {...rest}
447
448
  >
448
449
  {colors.map((c) => {