sh-ui-cli 0.42.0 → 0.43.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.
- package/README.md +6 -1
- package/data/changelog/versions.json +25 -0
- package/data/registry/flutter/registry.json +1 -1
- package/data/registry/react/components/button/index.tailwind.tsx +70 -0
- package/data/registry/react/components/calendar/index.tsx +26 -20
- package/data/registry/react/components/calendar/styles.css +30 -44
- package/data/registry/react/components/card/index.tailwind.tsx +111 -0
- package/data/registry/react/components/input/index.tailwind.tsx +405 -0
- package/data/registry/react/peer-versions.json +1 -0
- package/data/registry/react/registry.json +31 -8
- package/data/tokens/build.mjs +66 -0
- package/package.json +1 -1
- package/src/add.mjs +54 -6
- package/src/api.d.ts +14 -0
- package/src/api.js +4 -0
- package/src/constants.js +19 -0
- package/src/create/cli-args.js +18 -2
- package/src/create/generator.js +55 -6
- package/src/create/index.mjs +3 -1
- package/src/init.mjs +25 -7
- package/src/mcp.mjs +13 -2
- package/templates/flutter-standalone/sh-ui.config.json +1 -1
- package/templates/nextjs-standalone/app/globals.css +1 -21
- package/templates/nextjs-standalone/sh-ui.config.json +1 -1
- package/templates/ui-app-template/sh-ui.config.json +1 -1
- package/templates/ui-app-template/src/styles/globals.css +1 -21
package/src/api.d.ts
CHANGED
|
@@ -10,18 +10,32 @@ export type ThemeBase = 'neutral' | 'zinc' | 'slate';
|
|
|
10
10
|
export type ThemeRadius = 'none' | 'sm' | 'md' | 'lg' | 'xl' | 'full';
|
|
11
11
|
export type ThemeMode = 'light-dark' | 'light' | 'dark';
|
|
12
12
|
|
|
13
|
+
/** 현재 실제로 동작하는 CSS 프레임워크.
|
|
14
|
+
* - plain: 모든 컴포넌트가 plain 변종 보유.
|
|
15
|
+
* - tailwind: 일부 컴포넌트가 utility-class 변종 보유 — 미지원 컴포넌트는 add 시 plain 으로 자동 fallback. */
|
|
16
|
+
export type CssFrameworkSupported = 'plain' | 'tailwind';
|
|
17
|
+
/** 향후 추가 예정 — UI 에서 "곧 지원" 으로 노출되지만 CLI 는 거부. */
|
|
18
|
+
export type CssFrameworkPlanned = 'css-modules' | 'vanilla-extract';
|
|
19
|
+
/** 알려진 전체 (validation 메시지용). */
|
|
20
|
+
export type CssFramework = CssFrameworkSupported | CssFrameworkPlanned;
|
|
21
|
+
|
|
13
22
|
export const CREATE_PLATFORMS: readonly CreatePlatform[];
|
|
14
23
|
export const CREATE_STRUCTURES: readonly CreateStructure[];
|
|
15
24
|
export const INIT_PLATFORMS: readonly InitPlatform[];
|
|
16
25
|
export const THEME_BASES: readonly ThemeBase[];
|
|
17
26
|
export const THEME_RADII: readonly ThemeRadius[];
|
|
18
27
|
export const THEME_MODES: readonly ThemeMode[];
|
|
28
|
+
export const CSS_FRAMEWORKS_SUPPORTED: readonly CssFrameworkSupported[];
|
|
29
|
+
export const CSS_FRAMEWORKS_PLANNED: readonly CssFrameworkPlanned[];
|
|
30
|
+
export const CSS_FRAMEWORKS_ALL: readonly CssFramework[];
|
|
31
|
+
export const CSS_FRAMEWORK_DEFAULT: CssFrameworkSupported;
|
|
19
32
|
|
|
20
33
|
export const INIT_DEFAULTS: {
|
|
21
34
|
platform: InitPlatform;
|
|
22
35
|
base: ThemeBase;
|
|
23
36
|
radius: ThemeRadius;
|
|
24
37
|
mode: ThemeMode;
|
|
38
|
+
cssFramework: CssFrameworkSupported;
|
|
25
39
|
};
|
|
26
40
|
|
|
27
41
|
export type PluginManifest = {
|
package/src/api.js
CHANGED
package/src/constants.js
CHANGED
|
@@ -21,6 +21,24 @@ export const THEME_RADII = ['none', 'sm', 'md', 'lg', 'xl', 'full'];
|
|
|
21
21
|
|
|
22
22
|
export const THEME_MODES = ['light-dark', 'light', 'dark'];
|
|
23
23
|
|
|
24
|
+
// ─── CSS 프레임워크 (변종 시스템 — 1단계: 그릇만) ───
|
|
25
|
+
|
|
26
|
+
// 현재 실제로 동작하는 값.
|
|
27
|
+
// - plain: CSS custom properties + 일반 .css 파일 (모든 컴포넌트 변종 보유)
|
|
28
|
+
// - tailwind: utility class TSX 변종 (button/card/input 부터 시작, 점진적 확대 — 변종이 없는 컴포넌트는 add 시 plain 으로 자동 fallback)
|
|
29
|
+
export const CSS_FRAMEWORKS_SUPPORTED = ['plain', 'tailwind'];
|
|
30
|
+
|
|
31
|
+
// 향후 추가 예정. 사용자가 이 값을 주면 친절 에러로 안내.
|
|
32
|
+
export const CSS_FRAMEWORKS_PLANNED = ['css-modules', 'vanilla-extract'];
|
|
33
|
+
|
|
34
|
+
// 알려진 전체 — 검증 시 supported 와 planned 둘 다 인지하기 위함.
|
|
35
|
+
export const CSS_FRAMEWORKS_ALL = [
|
|
36
|
+
...CSS_FRAMEWORKS_SUPPORTED,
|
|
37
|
+
...CSS_FRAMEWORKS_PLANNED,
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
export const CSS_FRAMEWORK_DEFAULT = 'plain';
|
|
41
|
+
|
|
24
42
|
// ─── 기본값 ───
|
|
25
43
|
|
|
26
44
|
export const INIT_DEFAULTS = {
|
|
@@ -28,4 +46,5 @@ export const INIT_DEFAULTS = {
|
|
|
28
46
|
base: 'neutral',
|
|
29
47
|
radius: 'md',
|
|
30
48
|
mode: 'light-dark',
|
|
49
|
+
cssFramework: CSS_FRAMEWORK_DEFAULT,
|
|
31
50
|
};
|
package/src/create/cli-args.js
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
CREATE_PLATFORMS,
|
|
3
|
+
CREATE_STRUCTURES,
|
|
4
|
+
CSS_FRAMEWORKS_SUPPORTED,
|
|
5
|
+
CSS_FRAMEWORKS_PLANNED,
|
|
6
|
+
} from '../constants.js';
|
|
2
7
|
import { allPlugins } from './plugins/index.js';
|
|
3
8
|
|
|
4
9
|
const VALID_PLATFORMS = CREATE_PLATFORMS;
|
|
5
10
|
const VALID_STRUCTURES = CREATE_STRUCTURES;
|
|
6
11
|
const VALID_PLUGINS = allPlugins.map((p) => p.name);
|
|
7
12
|
|
|
8
|
-
const VALUE_FLAGS = ['platform', 'structure', 'plugins', 'theme', 'app'];
|
|
13
|
+
const VALUE_FLAGS = ['platform', 'structure', 'plugins', 'theme', 'app', 'css'];
|
|
9
14
|
const BOOL_FLAGS = ['yes', 'help', 'dry-run'];
|
|
10
15
|
|
|
11
16
|
const SUBCOMMANDS = ['add-app', 'add-component'];
|
|
@@ -63,6 +68,17 @@ export const parseArgs = (argv) => {
|
|
|
63
68
|
if (name === 'structure' && !VALID_STRUCTURES.includes(value)) {
|
|
64
69
|
throw new Error(`--structure 는 ${VALID_STRUCTURES.join('/')} 중 하나여야 함 (받은 값: ${value})`);
|
|
65
70
|
}
|
|
71
|
+
if (name === 'css' && !CSS_FRAMEWORKS_SUPPORTED.includes(value)) {
|
|
72
|
+
// planned 값은 '곧 옵니다' 신호로 분기 — 사용자 의도가 더 명확히 전달.
|
|
73
|
+
if (CSS_FRAMEWORKS_PLANNED.includes(value)) {
|
|
74
|
+
throw new Error(
|
|
75
|
+
`--css='${value}' 는 곧 지원 예정. 현재는 ${CSS_FRAMEWORKS_SUPPORTED.join(', ')} 만 가능.`,
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
throw new Error(
|
|
79
|
+
`--css 는 ${CSS_FRAMEWORKS_SUPPORTED.join('/')} 중 하나여야 함 (받은 값: ${value})`,
|
|
80
|
+
);
|
|
81
|
+
}
|
|
66
82
|
flags[name] = value;
|
|
67
83
|
}
|
|
68
84
|
|
package/src/create/generator.js
CHANGED
|
@@ -32,9 +32,28 @@ import {
|
|
|
32
32
|
buildDartGradientsBlock,
|
|
33
33
|
} from './theme/inject.js';
|
|
34
34
|
import { getTemplatesRoot } from '../paths.mjs';
|
|
35
|
+
import {
|
|
36
|
+
CSS_FRAMEWORK_DEFAULT,
|
|
37
|
+
CSS_FRAMEWORKS_SUPPORTED,
|
|
38
|
+
CSS_FRAMEWORKS_PLANNED,
|
|
39
|
+
} from '../constants.js';
|
|
35
40
|
|
|
36
41
|
const TEMPLATES_DIR = getTemplatesRoot();
|
|
37
42
|
|
|
43
|
+
/**
|
|
44
|
+
* 템플릿 복사 직후 sh-ui.config.json 의 cssFramework 필드를 갱신.
|
|
45
|
+
* 템플릿엔 이미 기본값이 박혀 있지만, 사용자가 --css 로 다른 값을 지정한
|
|
46
|
+
* 경우 그 값으로 덮어쓴다. 1단계는 plain 만 지원하므로 사실상 idempotent
|
|
47
|
+
* 이지만 2단계 emitter 가 추가되면 이 한 호출만으로 곧바로 동작.
|
|
48
|
+
*/
|
|
49
|
+
async function patchShUiConfig(configPath, cssFramework) {
|
|
50
|
+
const fw = cssFramework ?? CSS_FRAMEWORK_DEFAULT;
|
|
51
|
+
if (!(await fs.pathExists(configPath))) return;
|
|
52
|
+
const config = await fs.readJson(configPath);
|
|
53
|
+
config.cssFramework = fw;
|
|
54
|
+
await fs.writeJson(configPath, config, { spaces: 2 });
|
|
55
|
+
}
|
|
56
|
+
|
|
38
57
|
// ─── Create new project ───
|
|
39
58
|
|
|
40
59
|
// 비대화형 환경(TTY 없음 — 에이전트, CI, 파이프) 에서는 prompt 가 멈추므로
|
|
@@ -71,6 +90,33 @@ export async function createProject(options = {}) {
|
|
|
71
90
|
],
|
|
72
91
|
});
|
|
73
92
|
|
|
93
|
+
// CSS 프레임워크 — 현재는 plain 만 지원하지만, 곧 추가될 옵션을 disabled 로
|
|
94
|
+
// 미리 노출해 사용자가 변종 시스템의 존재를 인지할 수 있게 한다.
|
|
95
|
+
// Flutter 는 CSS 프레임워크 개념이 무의미하므로 자동 plain.
|
|
96
|
+
let cssFramework = options.css ?? CSS_FRAMEWORK_DEFAULT;
|
|
97
|
+
if (
|
|
98
|
+
options.css == null &&
|
|
99
|
+
platform !== 'flutter' &&
|
|
100
|
+
process.stdin.isTTY &&
|
|
101
|
+
!options.yes
|
|
102
|
+
) {
|
|
103
|
+
cssFramework = await select({
|
|
104
|
+
message: 'CSS 프레임워크:',
|
|
105
|
+
default: CSS_FRAMEWORK_DEFAULT,
|
|
106
|
+
choices: [
|
|
107
|
+
...CSS_FRAMEWORKS_SUPPORTED.map((fw) => ({
|
|
108
|
+
name: `${fw} — 플레인 CSS + custom properties`,
|
|
109
|
+
value: fw,
|
|
110
|
+
})),
|
|
111
|
+
...CSS_FRAMEWORKS_PLANNED.map((fw) => ({
|
|
112
|
+
name: fw,
|
|
113
|
+
value: fw,
|
|
114
|
+
disabled: '곧 지원',
|
|
115
|
+
})),
|
|
116
|
+
],
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
74
120
|
let theme = null;
|
|
75
121
|
if (options.theme) {
|
|
76
122
|
theme = resolveTheme(options.theme);
|
|
@@ -114,7 +160,7 @@ export async function createProject(options = {}) {
|
|
|
114
160
|
}
|
|
115
161
|
|
|
116
162
|
if (platform === 'flutter') {
|
|
117
|
-
await generateFlutter(targetDir, projectName, theme);
|
|
163
|
+
await generateFlutter(targetDir, projectName, theme, cssFramework);
|
|
118
164
|
await finalizeProject(targetDir, { dryRun: options.dryRun });
|
|
119
165
|
console.log(`\n✅ ${projectName} Flutter 프로젝트가 생성되었습니다!`);
|
|
120
166
|
console.log(`\n cd ${projectName}`);
|
|
@@ -140,9 +186,9 @@ export async function createProject(options = {}) {
|
|
|
140
186
|
plugins.sort((a, b) => (a.priority ?? 0) - (b.priority ?? 0));
|
|
141
187
|
|
|
142
188
|
if (projectType === 'standalone') {
|
|
143
|
-
await generateStandalone(targetDir, projectName, plugins, theme);
|
|
189
|
+
await generateStandalone(targetDir, projectName, plugins, theme, cssFramework);
|
|
144
190
|
} else {
|
|
145
|
-
await generateMonorepo(targetDir, projectName, plugins, { yes: options.yes, theme });
|
|
191
|
+
await generateMonorepo(targetDir, projectName, plugins, { yes: options.yes, theme, css: cssFramework });
|
|
146
192
|
}
|
|
147
193
|
|
|
148
194
|
await finalizeProject(targetDir, { dryRun: options.dryRun });
|
|
@@ -302,13 +348,14 @@ export async function addComponent(componentName, appName) {
|
|
|
302
348
|
|
|
303
349
|
// ─── Generators ───
|
|
304
350
|
|
|
305
|
-
async function generateFlutter(targetDir, projectName, theme) {
|
|
351
|
+
async function generateFlutter(targetDir, projectName, theme, css) {
|
|
306
352
|
await fs.copy(path.join(TEMPLATES_DIR, 'flutter-standalone'), targetDir);
|
|
307
353
|
await replaceInAllFiles(targetDir, '{{project_name}}', projectName);
|
|
308
354
|
await injectDartTheme(targetDir, theme);
|
|
355
|
+
await patchShUiConfig(path.join(targetDir, 'sh-ui.config.json'), css);
|
|
309
356
|
}
|
|
310
357
|
|
|
311
|
-
async function generateStandalone(targetDir, projectName, plugins, theme) {
|
|
358
|
+
async function generateStandalone(targetDir, projectName, plugins, theme, css) {
|
|
312
359
|
await fs.copy(path.join(TEMPLATES_DIR, 'nextjs-standalone'), targetDir);
|
|
313
360
|
|
|
314
361
|
// Update package.json
|
|
@@ -331,9 +378,10 @@ async function generateStandalone(targetDir, projectName, plugins, theme) {
|
|
|
331
378
|
await composeProviders(targetDir, plugins);
|
|
332
379
|
await applyTransforms(targetDir, plugins);
|
|
333
380
|
await injectCssTheme(targetDir, theme);
|
|
381
|
+
await patchShUiConfig(path.join(targetDir, 'sh-ui.config.json'), css);
|
|
334
382
|
}
|
|
335
383
|
|
|
336
|
-
async function generateMonorepo(targetDir, projectName, plugins, { yes = false, theme } = {}) {
|
|
384
|
+
async function generateMonorepo(targetDir, projectName, plugins, { yes = false, theme, css } = {}) {
|
|
337
385
|
await fs.copy(path.join(TEMPLATES_DIR, 'monorepo'), targetDir);
|
|
338
386
|
|
|
339
387
|
// Update root package.json
|
|
@@ -367,6 +415,7 @@ async function generateMonorepo(targetDir, projectName, plugins, { yes = false,
|
|
|
367
415
|
await generateApp(appsDir, appName, port, plugins);
|
|
368
416
|
const uiAppDir = path.join(targetDir, 'packages', 'ui', 'ui-apps', `ui-${appName}`);
|
|
369
417
|
await injectCssTheme(uiAppDir, theme);
|
|
418
|
+
await patchShUiConfig(path.join(uiAppDir, 'sh-ui.config.json'), css);
|
|
370
419
|
}
|
|
371
420
|
|
|
372
421
|
async function generateApp(targetDir, appName, port, plugins) {
|
package/src/create/index.mjs
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import { parseArgs } from './cli-args.js';
|
|
4
4
|
import { createProject, addApp, addComponent } from './generator.js';
|
|
5
5
|
import { allPlugins } from './plugins/index.js';
|
|
6
|
-
import { CREATE_PLATFORMS, CREATE_STRUCTURES } from '../constants.js';
|
|
6
|
+
import { CREATE_PLATFORMS, CREATE_STRUCTURES, CSS_FRAMEWORKS_SUPPORTED } from '../constants.js';
|
|
7
7
|
import { THEME_PRESET_NAMES } from './theme/presets.js';
|
|
8
8
|
|
|
9
9
|
const PLUGIN_NAMES = allPlugins.map((p) => p.name);
|
|
@@ -22,6 +22,7 @@ export const HELP_TEXT = `sh-ui create — sh-ui 프로젝트 스캐폴드 (Next
|
|
|
22
22
|
--structure <${CREATE_STRUCTURES.join('|')}> Next.js 프로젝트 구조 (next 일 때)
|
|
23
23
|
--plugins <a,b> 플러그인 (${PLUGINS_LIST}). 미지정/"" → 없음
|
|
24
24
|
--theme <preset|base64> 프리셋 이름(${THEME_PRESETS_LIST}) 또는 playground base64. 선택
|
|
25
|
+
--css <${CSS_FRAMEWORKS_SUPPORTED.join('|')}> CSS 프레임워크 (현재 plain만 지원, 향후 tailwind 등 추가 예정)
|
|
25
26
|
--yes 디렉토리 덮어쓰기 + 모노레포 기본값 자동 채택
|
|
26
27
|
--dry-run 파일을 쓰지 않고 작성될 파일 목록만 출력
|
|
27
28
|
-h, --help 이 도움말
|
|
@@ -70,6 +71,7 @@ export async function runCreate(rest) {
|
|
|
70
71
|
structure: flags.structure,
|
|
71
72
|
plugins: flags.plugins,
|
|
72
73
|
theme: flags.theme,
|
|
74
|
+
css: flags.css,
|
|
73
75
|
yes: flags.yes,
|
|
74
76
|
dryRun: flags.dryRun,
|
|
75
77
|
});
|
package/src/init.mjs
CHANGED
|
@@ -9,6 +9,8 @@ import {
|
|
|
9
9
|
THEME_RADII,
|
|
10
10
|
THEME_MODES,
|
|
11
11
|
INIT_DEFAULTS,
|
|
12
|
+
CSS_FRAMEWORKS_SUPPORTED,
|
|
13
|
+
CSS_FRAMEWORKS_PLANNED,
|
|
12
14
|
} from "./constants.js";
|
|
13
15
|
|
|
14
16
|
const CHOICES = {
|
|
@@ -16,6 +18,7 @@ const CHOICES = {
|
|
|
16
18
|
base: THEME_BASES,
|
|
17
19
|
radius: THEME_RADII,
|
|
18
20
|
mode: THEME_MODES,
|
|
21
|
+
cssFramework: CSS_FRAMEWORKS_SUPPORTED,
|
|
19
22
|
};
|
|
20
23
|
|
|
21
24
|
const DEFAULTS = INIT_DEFAULTS;
|
|
@@ -80,11 +83,20 @@ async function prompt(rl, label, choices, def) {
|
|
|
80
83
|
}
|
|
81
84
|
|
|
82
85
|
function validateOrThrow(key, value) {
|
|
83
|
-
if (
|
|
86
|
+
if (CHOICES[key].includes(value)) return;
|
|
87
|
+
|
|
88
|
+
// cssFramework 는 향후 지원 예정 값에 대해 별도 안내. 사용자가 "tailwind"
|
|
89
|
+
// 같은 값을 미리 시도해 보고 싶을 때, 단순한 unknown 메시지보다 "곧 옵니다"
|
|
90
|
+
// 신호가 의도를 더 잘 전달함.
|
|
91
|
+
if (key === "cssFramework" && CSS_FRAMEWORKS_PLANNED.includes(value)) {
|
|
84
92
|
throw new Error(
|
|
85
|
-
|
|
93
|
+
`--cssFramework='${value}'는 곧 지원 예정입니다. 현재는 ${CHOICES[key].join(", ")} 만 가능합니다.`,
|
|
86
94
|
);
|
|
87
95
|
}
|
|
96
|
+
|
|
97
|
+
throw new Error(
|
|
98
|
+
`--${key}에 '${value}'는 허용되지 않습니다. 허용: ${CHOICES[key].join(", ")}`,
|
|
99
|
+
);
|
|
88
100
|
}
|
|
89
101
|
|
|
90
102
|
/** 플래그/TTY 상태에 따라 4개 축 값을 결정. 필요한 경우에만 프롬프트. */
|
|
@@ -97,7 +109,11 @@ async function resolveAnswers(flags) {
|
|
|
97
109
|
}
|
|
98
110
|
}
|
|
99
111
|
|
|
100
|
-
|
|
112
|
+
// 선택지가 1개뿐인 축은 프롬프트 스킵 — 기본값으로 자동 채움.
|
|
113
|
+
// (예: cssFramework 가 plain 만일 때 의미 없는 "[plain] (plain):" 질문 회피)
|
|
114
|
+
const missingKeys = Object.keys(CHOICES).filter(
|
|
115
|
+
(k) => flags[k] == null && CHOICES[k].length > 1,
|
|
116
|
+
);
|
|
101
117
|
if (flags.yes || missingKeys.length === 0) return answers;
|
|
102
118
|
|
|
103
119
|
if (!stdin.isTTY) {
|
|
@@ -125,14 +141,15 @@ function labelFor(key) {
|
|
|
125
141
|
base: "기본 색 스케일",
|
|
126
142
|
radius: "radius",
|
|
127
143
|
mode: "모드",
|
|
144
|
+
cssFramework: "CSS 프레임워크",
|
|
128
145
|
}[key];
|
|
129
146
|
}
|
|
130
147
|
|
|
131
|
-
function buildConfig({ platform, base, radius, mode }) {
|
|
148
|
+
function buildConfig({ platform, base, radius, mode, cssFramework }) {
|
|
132
149
|
return {
|
|
133
150
|
$schema: "https://your-ds.dev/sh-ui.schema.json",
|
|
134
151
|
platform,
|
|
135
|
-
|
|
152
|
+
cssFramework,
|
|
136
153
|
theme: { base, radius, mode },
|
|
137
154
|
paths: PATHS[platform],
|
|
138
155
|
...(ALIASES[platform] ? { aliases: ALIASES[platform] } : {}),
|
|
@@ -153,7 +170,8 @@ export async function init({ cwd, args }) {
|
|
|
153
170
|
await writeFile(configPath, JSON.stringify(config, null, 2) + "\n", "utf8");
|
|
154
171
|
|
|
155
172
|
console.log(`\n✓ sh-ui.config.json 생성 완료`);
|
|
156
|
-
console.log(` platform:
|
|
157
|
-
console.log(`
|
|
173
|
+
console.log(` platform: ${answers.platform}`);
|
|
174
|
+
console.log(` cssFramework: ${answers.cssFramework}`);
|
|
175
|
+
console.log(` theme: base=${answers.base}, radius=${answers.radius}, mode=${answers.mode}`);
|
|
158
176
|
console.log(`\n다음 단계: sh-ui add tokens button`);
|
|
159
177
|
}
|
package/src/mcp.mjs
CHANGED
|
@@ -36,6 +36,7 @@ import {
|
|
|
36
36
|
THEME_BASES,
|
|
37
37
|
THEME_RADII,
|
|
38
38
|
THEME_MODES,
|
|
39
|
+
CSS_FRAMEWORKS_SUPPORTED,
|
|
39
40
|
} from "./constants.js";
|
|
40
41
|
import { allPlugins } from "./create/plugins/index.js";
|
|
41
42
|
import { THEME_PRESET_NAMES } from "./create/theme/presets.js";
|
|
@@ -44,6 +45,7 @@ const PLATFORMS = INIT_PLATFORMS;
|
|
|
44
45
|
const BASES = THEME_BASES;
|
|
45
46
|
const RADII = THEME_RADII;
|
|
46
47
|
const MODES = THEME_MODES;
|
|
48
|
+
const CSS_FRAMEWORKS = CSS_FRAMEWORKS_SUPPORTED;
|
|
47
49
|
const PLUGIN_NAMES = allPlugins.map((p) => p.name);
|
|
48
50
|
const THEME_PRESETS_LIST = THEME_PRESET_NAMES.join(", ");
|
|
49
51
|
|
|
@@ -70,6 +72,10 @@ const INIT_DESCRIPTIONS = {
|
|
|
70
72
|
light: "라이트 전용",
|
|
71
73
|
dark: "다크 전용",
|
|
72
74
|
},
|
|
75
|
+
cssFramework: {
|
|
76
|
+
plain: "플레인 CSS — CSS custom properties + 일반 .css 파일 (모든 컴포넌트 지원)",
|
|
77
|
+
tailwind: "Tailwind v4 utility class — class-variance-authority 기반. 변종 미제공 컴포넌트는 plain 으로 자동 fallback",
|
|
78
|
+
},
|
|
73
79
|
};
|
|
74
80
|
|
|
75
81
|
/** stdout 으로 출력되는 console.* 호출을 버퍼에 캡처해 텍스트로 반환. */
|
|
@@ -146,7 +152,7 @@ const SERVER_INSTRUCTIONS = `sh-ui — Base UI 위에 빌드된 React/Flutter
|
|
|
146
152
|
|
|
147
153
|
export async function startMcpServer() {
|
|
148
154
|
const server = new McpServer(
|
|
149
|
-
{ name: "sh-ui", version: "0.
|
|
155
|
+
{ name: "sh-ui", version: "0.43.0" }, // sh-ui-cli 와 동기화
|
|
150
156
|
{
|
|
151
157
|
capabilities: { tools: {} },
|
|
152
158
|
instructions: SERVER_INSTRUCTIONS,
|
|
@@ -181,6 +187,8 @@ export async function startMcpServer() {
|
|
|
181
187
|
.describe(`Next.js 플러그인 (${PLUGIN_NAMES.join(', ')}). 미지정시 빈 배열`),
|
|
182
188
|
theme: z.string().optional()
|
|
183
189
|
.describe(`프리셋 이름 (${THEME_PRESETS_LIST}) 또는 playground 에서 생성한 base64 (선택)`),
|
|
190
|
+
cssFramework: z.enum(CSS_FRAMEWORKS).optional()
|
|
191
|
+
.describe(`CSS 프레임워크. 기본 plain. 현재 ${CSS_FRAMEWORKS.join('/')} 지원 (향후 tailwind 등 추가 예정)`),
|
|
184
192
|
cwd: z.string().optional()
|
|
185
193
|
.describe("부모 디렉토리. 기본 process.cwd()"),
|
|
186
194
|
force: z.boolean().optional()
|
|
@@ -222,6 +230,7 @@ export async function startMcpServer() {
|
|
|
222
230
|
structure: input.structure,
|
|
223
231
|
plugins: input.plugins,
|
|
224
232
|
theme: input.theme,
|
|
233
|
+
css: input.cssFramework,
|
|
225
234
|
yes: true, // 사전 검사를 마쳤으니 generator 의 confirm 프롬프트 우회
|
|
226
235
|
}),
|
|
227
236
|
);
|
|
@@ -249,6 +258,8 @@ export async function startMcpServer() {
|
|
|
249
258
|
.describe("기본 radius. 기본 md"),
|
|
250
259
|
mode: z.enum(MODES).optional()
|
|
251
260
|
.describe("색 모드. 기본 light-dark"),
|
|
261
|
+
cssFramework: z.enum(CSS_FRAMEWORKS).optional()
|
|
262
|
+
.describe(`CSS 프레임워크. 기본 plain. 현재 ${CSS_FRAMEWORKS.join('/')} 지원 (향후 tailwind 등 추가 예정)`),
|
|
252
263
|
cwd: z.string().optional()
|
|
253
264
|
.describe("작업 디렉토리. 기본 process.cwd()"),
|
|
254
265
|
force: z.boolean().optional()
|
|
@@ -257,7 +268,7 @@ export async function startMcpServer() {
|
|
|
257
268
|
},
|
|
258
269
|
async (input) => {
|
|
259
270
|
const args = ["--yes"];
|
|
260
|
-
for (const k of ["platform", "base", "radius", "mode"]) {
|
|
271
|
+
for (const k of ["platform", "base", "radius", "mode", "cssFramework"]) {
|
|
261
272
|
if (input[k]) args.push(`--${k}`, input[k]);
|
|
262
273
|
}
|
|
263
274
|
if (input.force) args.push("--force");
|
|
@@ -3,27 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
@custom-variant dark (&:is(.dark *));
|
|
5
5
|
|
|
6
|
-
@theme inline
|
|
7
|
-
--color-background: var(--background);
|
|
8
|
-
--color-background-subtle: var(--background-subtle);
|
|
9
|
-
--color-background-muted: var(--background-muted);
|
|
10
|
-
--color-background-inverse: var(--background-inverse);
|
|
11
|
-
--color-foreground: var(--foreground);
|
|
12
|
-
--color-foreground-muted: var(--foreground-muted);
|
|
13
|
-
--color-foreground-subtle: var(--foreground-subtle);
|
|
14
|
-
--color-foreground-inverse: var(--foreground-inverse);
|
|
15
|
-
--color-border: var(--border);
|
|
16
|
-
--color-border-strong: var(--border-strong);
|
|
17
|
-
--color-primary: var(--primary);
|
|
18
|
-
--color-primary-foreground: var(--primary-foreground);
|
|
19
|
-
--color-primary-hover: var(--primary-hover);
|
|
20
|
-
--color-danger: var(--danger);
|
|
21
|
-
--color-danger-foreground: var(--danger-foreground);
|
|
22
|
-
--radius-sm: calc(var(--radius) - 2px);
|
|
23
|
-
--radius-md: var(--radius);
|
|
24
|
-
--radius-lg: calc(var(--radius) + 2px);
|
|
25
|
-
--radius-xl: calc(var(--radius) + 4px);
|
|
26
|
-
}
|
|
6
|
+
/* Tailwind v4 @theme inline 블록은 tokens.css 가 자동으로 emit. */
|
|
27
7
|
|
|
28
8
|
@layer base {
|
|
29
9
|
body {
|
|
@@ -7,27 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
@custom-variant dark (&:is(.dark *));
|
|
9
9
|
|
|
10
|
-
@theme inline
|
|
11
|
-
--color-background: var(--background);
|
|
12
|
-
--color-background-subtle: var(--background-subtle);
|
|
13
|
-
--color-background-muted: var(--background-muted);
|
|
14
|
-
--color-background-inverse: var(--background-inverse);
|
|
15
|
-
--color-foreground: var(--foreground);
|
|
16
|
-
--color-foreground-muted: var(--foreground-muted);
|
|
17
|
-
--color-foreground-subtle: var(--foreground-subtle);
|
|
18
|
-
--color-foreground-inverse: var(--foreground-inverse);
|
|
19
|
-
--color-border: var(--border);
|
|
20
|
-
--color-border-strong: var(--border-strong);
|
|
21
|
-
--color-primary: var(--primary);
|
|
22
|
-
--color-primary-foreground: var(--primary-foreground);
|
|
23
|
-
--color-primary-hover: var(--primary-hover);
|
|
24
|
-
--color-danger: var(--danger);
|
|
25
|
-
--color-danger-foreground: var(--danger-foreground);
|
|
26
|
-
--radius-sm: calc(var(--radius) - 2px);
|
|
27
|
-
--radius-md: var(--radius);
|
|
28
|
-
--radius-lg: calc(var(--radius) + 2px);
|
|
29
|
-
--radius-xl: calc(var(--radius) + 4px);
|
|
30
|
-
}
|
|
10
|
+
/* Tailwind v4 @theme inline 블록은 tokens.css 가 자동으로 emit. */
|
|
31
11
|
|
|
32
12
|
@layer base {
|
|
33
13
|
body {
|