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.
- package/data/changelog/versions.json +15 -0
- package/data/registry/flutter/foundation/sh_ui_tokens.dart +11 -0
- package/data/tokens/src/primitives.json +2 -1
- package/data/tokens/src/semantic.json +13 -8
- package/package.json +1 -1
- package/src/create/theme/decode.js +5 -1
- package/src/create/theme/inject.js +11 -3
- package/src/create/theme/presets.js +8 -0
- package/templates/flutter-standalone/lib/sh_ui/foundation/sh_ui_tokens.dart +8 -0
|
@@ -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
|
|
|
@@ -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":
|
|
98
|
-
"md":
|
|
99
|
-
"lg":
|
|
100
|
-
"xl":
|
|
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
|
@@ -10,11 +10,15 @@ const TOKEN_KEYS = [
|
|
|
10
10
|
|
|
11
11
|
// 옵셔널 색 토큰 — 누락 OK. 입력에 들어 있으면 hex 검증 + inject 시 CSS 변수로 emit.
|
|
12
12
|
// 사용자가 success/warning/info 상태 컬러를 커스텀하고 싶을 때 사용.
|
|
13
|
-
//
|
|
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
|
-
|
|
100
|
-
|
|
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
|
}
|