sh-ui-cli 0.68.0 → 0.68.1

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.
@@ -2,6 +2,21 @@
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.68.1",
7
+ "date": "2026-05-09",
8
+ "title": "fix(tokens) — --ring / --danger-hover / --shadow-menu 정식 등록 (Phase A 후속)",
9
+ "type": "patch",
10
+ "highlights": [
11
+ "**`--ring` 토큰 신설** — 58 개 컴포넌트가 focus outline 으로 사용하던 변수가 미정의였음 (시각적으로 outline 색이 빈 값으로 들어가 거의 안 보이던 silent breakage). v0.68.0 doctor 가 발견. 이번 릴리즈로 light=neutral.400 / dark=neutral.500 (preset 별 derive) 으로 정식 매핑.",
12
+ "**`--danger-hover` 토큰 신설** — Button danger 변종 hover 색 변화가 없던 silent breakage. light=red.700 / dark=red.500 추가, 기존 `--danger` 와 자연스러운 진/연 차이.",
13
+ "**`--shadow-menu` 토큰 의미화** — DropdownMenu / Select / ContextMenu 그림자. 이전엔 inject 단계 hardcode 였는데 semantic.json 의 `{shadow.lg}` 참조로 정식화 — `sh-ui add tokens` (buildTokens 경로) 와 `sh-ui create` (inject 경로) 둘 다 동일하게 emit.",
14
+ "**Flutter 측 `ShUiColorTokens.ring` / `.dangerHover` 필드 추가** — DART_FIELD_SOURCES 에 `fallback` 메타 추가, 옛 base64 theme 처럼 키가 누락된 경우 `danger` / `foreground-subtle` 로 안전하게 폴백. 기존 `sh_ui_encode_theme` 산출물도 깨지지 않음.",
15
+ "**옵셔널 키 정책** — `ring`, `danger-hover` 는 OPTIONAL_TOKEN_KEYS 로 등록. light/dark 모두 정의된 preset 만 inject 시 emit (NEUTRAL/slate 정식 정의, rose/emerald/violet 은 NEUTRAL spread 로 자동 상속). 옛 base64 theme 디코드는 strict 검증을 우회해 backward 호환.",
16
+ "**v0.68.0 doctor 가 즉시 활용** — fresh `sh-ui create` + `sh-ui doctor` 가 ✓ 모든 검사 통과."
17
+ ],
18
+ "url": "https://github.com/sanghyeonKim0201/sh-ui/releases/tag/v0.68.1"
19
+ },
5
20
  {
6
21
  "version": "0.68.0",
7
22
  "date": "2026-05-09",
@@ -20,6 +20,8 @@ class ShUiColorTokens {
20
20
  final Color primaryHover;
21
21
  final Color danger;
22
22
  final Color dangerForeground;
23
+ final Color dangerHover;
24
+ final Color ring;
23
25
 
24
26
  const ShUiColorTokens({
25
27
  required this.background,
@@ -37,6 +39,8 @@ class ShUiColorTokens {
37
39
  required this.primaryHover,
38
40
  required this.danger,
39
41
  required this.dangerForeground,
42
+ required this.dangerHover,
43
+ required this.ring,
40
44
  });
41
45
 
42
46
  static const light = ShUiColorTokens(
@@ -55,6 +59,8 @@ class ShUiColorTokens {
55
59
  primaryHover: Color(0xFF262626),
56
60
  danger: Color(0xFFDC2626),
57
61
  dangerForeground: Color(0xFFFFFFFF),
62
+ dangerHover: Color(0xFFB91C1C),
63
+ ring: Color(0xFFA3A3A3),
58
64
  );
59
65
 
60
66
  static const dark = ShUiColorTokens(
@@ -73,6 +79,8 @@ class ShUiColorTokens {
73
79
  primaryHover: Color(0xFFE5E5E5),
74
80
  danger: Color(0xFFDC2626),
75
81
  dangerForeground: Color(0xFFFFFFFF),
82
+ dangerHover: Color(0xFFEF4444),
83
+ ring: Color(0xFF737373),
76
84
  );
77
85
  }
78
86
 
@@ -194,12 +202,14 @@ class ShUiShadowTokens {
194
202
  final List<BoxShadow> md;
195
203
  final List<BoxShadow> lg;
196
204
  final List<BoxShadow> xl;
205
+ final List<BoxShadow> menu;
197
206
 
198
207
  const ShUiShadowTokens({
199
208
  required this.sm,
200
209
  required this.md,
201
210
  required this.lg,
202
211
  required this.xl,
212
+ required this.menu,
203
213
  });
204
214
 
205
215
  static const tokens = ShUiShadowTokens(
@@ -207,6 +217,7 @@ class ShUiShadowTokens {
207
217
  md: <BoxShadow>[BoxShadow(offset: Offset(0.0, 4.0), blurRadius: 12.0, spreadRadius: 0.0, color: Color(0x1F000000))],
208
218
  lg: <BoxShadow>[BoxShadow(offset: Offset(0.0, 8.0), blurRadius: 24.0, spreadRadius: 0.0, color: Color(0x26000000))],
209
219
  xl: <BoxShadow>[BoxShadow(offset: Offset(0.0, 16.0), blurRadius: 48.0, spreadRadius: 0.0, color: Color(0x2E000000))],
220
+ menu: <BoxShadow>[BoxShadow(offset: Offset(0.0, 8.0), blurRadius: 24.0, spreadRadius: 0.0, color: Color(0x26000000))],
210
221
  );
211
222
  }
212
223
 
@@ -48,7 +48,8 @@
48
48
 
49
49
  "red": {
50
50
  "500": { "$value": "#EF4444", "$type": "color" },
51
- "600": { "$value": "#DC2626", "$type": "color" }
51
+ "600": { "$value": "#DC2626", "$type": "color" },
52
+ "700": { "$value": "#B91C1C", "$type": "color" }
52
53
  }
53
54
  },
54
55
 
@@ -25,8 +25,10 @@
25
25
  },
26
26
  "danger": {
27
27
  "default": { "$value": "{color.red.600}", "$type": "color" },
28
- "foreground": { "$value": "{color.white}", "$type": "color" }
29
- }
28
+ "foreground": { "$value": "{color.white}", "$type": "color" },
29
+ "hover": { "$value": "{color.red.700}", "$type": "color" }
30
+ },
31
+ "ring": { "$value": "{color.{base}.400}", "$type": "color" }
30
32
  },
31
33
 
32
34
  "dark": {
@@ -53,8 +55,10 @@
53
55
  },
54
56
  "danger": {
55
57
  "default": { "$value": "{color.red.600}", "$type": "color" },
56
- "foreground": { "$value": "{color.white}", "$type": "color" }
57
- }
58
+ "foreground": { "$value": "{color.white}", "$type": "color" },
59
+ "hover": { "$value": "{color.red.500}", "$type": "color" }
60
+ },
61
+ "ring": { "$value": "{color.{base}.500}", "$type": "color" }
58
62
  },
59
63
 
60
64
  "radius": {
@@ -94,10 +98,11 @@
94
98
  },
95
99
 
96
100
  "shadow": {
97
- "sm": { "$value": "{shadow.sm}", "$type": "shadow" },
98
- "md": { "$value": "{shadow.md}", "$type": "shadow" },
99
- "lg": { "$value": "{shadow.lg}", "$type": "shadow" },
100
- "xl": { "$value": "{shadow.xl}", "$type": "shadow" }
101
+ "sm": { "$value": "{shadow.sm}", "$type": "shadow" },
102
+ "md": { "$value": "{shadow.md}", "$type": "shadow" },
103
+ "lg": { "$value": "{shadow.lg}", "$type": "shadow" },
104
+ "xl": { "$value": "{shadow.xl}", "$type": "shadow" },
105
+ "menu": { "$value": "{shadow.lg}", "$type": "shadow" }
101
106
  },
102
107
 
103
108
  "duration": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sh-ui-cli",
3
- "version": "0.68.0",
3
+ "version": "0.68.1",
4
4
  "description": "sh-ui CLI — 프로젝트 스캐폴드(create) + 컴포넌트 추가(add/list/remove) + IDE-내 AI용 MCP 서버",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -10,11 +10,15 @@ const TOKEN_KEYS = [
10
10
 
11
11
  // 옵셔널 색 토큰 — 누락 OK. 입력에 들어 있으면 hex 검증 + inject 시 CSS 변수로 emit.
12
12
  // 사용자가 success/warning/info 상태 컬러를 커스텀하고 싶을 때 사용.
13
- // Dart ShUiColorTokens 아직 미반영(웹 한정).
13
+ // v0.68.1+ 부터 ring / danger-hover 옵셔널로 — 기존 base64 theme 은 누락된 상태로
14
+ // 디코드되고, 신규 preset 은 light/dark 모두 정의해 inject 가 emit (DART 도 OPTIONAL 키
15
+ // 가 light/dark 양쪽에 있으면 ShUiColorTokens 의 ring/dangerHover 필드에 자동 매핑).
14
16
  const OPTIONAL_TOKEN_KEYS = [
15
17
  'success', 'success-foreground',
16
18
  'warning', 'warning-foreground',
17
19
  'info', 'info-foreground',
20
+ 'danger-hover',
21
+ 'ring',
18
22
  ];
19
23
 
20
24
  /**
@@ -92,12 +92,20 @@ const DART_FIELD_SOURCES = [
92
92
  { field: 'primaryHover', key: 'primary-hover' },
93
93
  { field: 'danger', key: 'danger' },
94
94
  { field: 'dangerForeground', key: 'danger-foreground' },
95
+ // 아래 둘은 v0.68.1+ 신설. theme 객체에 없으면 fallback 키의 값으로 안전하게 채움 —
96
+ // 기존 base64 테마(이전 버전이 만든 것)도 깨지지 않도록.
97
+ { field: 'dangerHover', key: 'danger-hover', fallback: 'danger' },
98
+ { field: 'ring', key: 'ring', fallback: 'foreground-subtle' },
95
99
  ];
96
100
 
97
101
  const buildDartStaticConst = (mode, self) => {
98
- const lines = DART_FIELD_SOURCES.map(({ field, key }) =>
99
- ` ${field}: ${toDartColor(self[key])},`,
100
- ).join('\n');
102
+ const lines = DART_FIELD_SOURCES.map(({ field, key, fallback }) => {
103
+ const value = self[key] ?? (fallback ? self[fallback] : undefined);
104
+ if (!value) {
105
+ throw new Error(`inject: ${mode}.${key} 누락 (fallback ${fallback ?? '없음'} 도 없음)`);
106
+ }
107
+ return ` ${field}: ${toDartColor(value)},`;
108
+ }).join('\n');
101
109
  return [
102
110
  ` static const ${mode} = ShUiColorTokens(`,
103
111
  lines,
@@ -22,6 +22,8 @@ const NEUTRAL_LIGHT = {
22
22
  'primary-hover': '#262626',
23
23
  'danger': '#DC2626',
24
24
  'danger-foreground': '#FFFFFF',
25
+ 'danger-hover': '#B91C1C',
26
+ 'ring': '#A3A3A3',
25
27
  };
26
28
 
27
29
  const NEUTRAL_DARK = {
@@ -40,6 +42,8 @@ const NEUTRAL_DARK = {
40
42
  'primary-hover': '#E5E5E5',
41
43
  'danger': '#DC2626',
42
44
  'danger-foreground': '#FFFFFF',
45
+ 'danger-hover': '#EF4444',
46
+ 'ring': '#737373',
43
47
  };
44
48
 
45
49
  export const THEME_PRESETS = {
@@ -67,6 +71,8 @@ export const THEME_PRESETS = {
67
71
  'primary-hover': '#4338CA',
68
72
  'danger': '#DC2626',
69
73
  'danger-foreground': '#FFFFFF',
74
+ 'danger-hover': '#B91C1C',
75
+ 'ring': '#94A3B8',
70
76
  },
71
77
  dark: {
72
78
  'background': '#0F172A',
@@ -84,6 +90,8 @@ export const THEME_PRESETS = {
84
90
  'primary-hover': '#A5B4FC',
85
91
  'danger': '#F87171',
86
92
  'danger-foreground': '#450A0A',
93
+ 'danger-hover': '#FCA5A5',
94
+ 'ring': '#64748B',
87
95
  },
88
96
  radius: 0.375,
89
97
  // 정보 밀도 ↑ — 본문 14px 부터, 컨트롤 36px (대시보드/관리자 인상)
@@ -20,6 +20,8 @@ class ShUiColorTokens {
20
20
  final Color primaryHover;
21
21
  final Color danger;
22
22
  final Color dangerForeground;
23
+ final Color dangerHover;
24
+ final Color ring;
23
25
 
24
26
  const ShUiColorTokens({
25
27
  required this.background,
@@ -37,6 +39,8 @@ class ShUiColorTokens {
37
39
  required this.primaryHover,
38
40
  required this.danger,
39
41
  required this.dangerForeground,
42
+ required this.dangerHover,
43
+ required this.ring,
40
44
  });
41
45
 
42
46
  // sh-ui:theme-colors-start
@@ -56,6 +60,8 @@ class ShUiColorTokens {
56
60
  primaryHover: Color(0xFF262626),
57
61
  danger: Color(0xFFDC2626),
58
62
  dangerForeground: Color(0xFFFFFFFF),
63
+ dangerHover: Color(0xFFB91C1C),
64
+ ring: Color(0xFFA3A3A3),
59
65
  );
60
66
 
61
67
  static const dark = ShUiColorTokens(
@@ -74,6 +80,8 @@ class ShUiColorTokens {
74
80
  primaryHover: Color(0xFFE5E5E5),
75
81
  danger: Color(0xFFDC2626),
76
82
  dangerForeground: Color(0xFFFFFFFF),
83
+ dangerHover: Color(0xFFEF4444),
84
+ ring: Color(0xFF737373),
77
85
  );
78
86
  // sh-ui:theme-colors-end
79
87
  }