sh-ui-cli 0.89.1 → 0.91.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.
|
@@ -2,6 +2,33 @@
|
|
|
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.91.0",
|
|
7
|
+
"date": "2026-05-14",
|
|
8
|
+
"title": "add-app 가 platform (next/vite) + tauri 인식 — vite monorepo 에 새 앱 정상 추가",
|
|
9
|
+
"type": "minor",
|
|
10
|
+
"highlights": [
|
|
11
|
+
"**`sh-ui-cli add-app` + MCP `sh_ui_add_app` 가 `platform: vite | next` 인식** — 이전엔 Next.js 하드코딩이라 vite monorepo (v0.87.0+) 에 `add-app` 하면 잘못된 Next.js 앱이 들어가던 빈틈 해소. `--platform` 미지정 시 기존 `apps/*` 의 `package.json` deps 스캔해서 platform 추론 (모든 앱이 같은 플랫폼이면 그 값으로). 혼재 or 빈 apps/ 면 TTY 면 prompt, 비대화형이면 'next' fallback.",
|
|
12
|
+
"**`--tauri` 옵션이 add-app 에도 동작** — `sh-ui-cli add-app admin --tauri` 또는 MCP `tauri: true` 로 `apps/{name}/src-tauri/` 까지 한 번에 emit. v0.90.0 의 monorepo+tauri 와 동일하게 dev port 가 `tauri.conf.json` 의 `devUrl` 과 `vite.config.ts` 의 `server.port` 양쪽에 자동 박힘. tauri + platform=next 조합은 CLI + MCP 양쪽에서 명시적 Korean 에러.",
|
|
13
|
+
"**Smoke V13 + V14 회귀 가드** — V13 은 vite monorepo create 후 `add-app admin --port 3001 --tauri` (platform 미지정) 가 추론으로 vite 선택 + apps/admin/src-tauri/ + devUrl=3001 + crate name 까지 단언. V14 는 next monorepo 에 `add-app --platform next --tauri` 가 에러 throw 확인. 기존 scenarios 4 + 4b 도 새 platform 추론 분기에 맞춰 prompt 모의 갱신.",
|
|
14
|
+
"**Backward-compat 확인** — Next.js 모노레포에서 기존 `add-app` 호출 흐름은 platform 추론 결과 'next' 가 나와 동일 동작. 깨지는 호출 없음."
|
|
15
|
+
],
|
|
16
|
+
"url": "https://github.com/sanghyeonKim0201/sh-ui/releases/tag/v0.91.0"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"version": "0.90.0",
|
|
20
|
+
"date": "2026-05-14",
|
|
21
|
+
"title": "vite + monorepo + tauri 지원 — Tauri 셸이 apps/{name}/src-tauri/ 안",
|
|
22
|
+
"type": "minor",
|
|
23
|
+
"highlights": [
|
|
24
|
+
"**`--platform vite --structure monorepo --tauri` 동작** — v0.88.0 에서 의도적으로 defer 했던 조합 풀림. Tauri 데스크탑 셸이 모노레포 안의 첫 vite 앱 디렉토리 (`apps/{appName}/src-tauri/`) 에 emit. `frontendDist: \"../dist\"` 관계가 그대로 유지되고, Tauri 의 `pnpm dev` / `pnpm build` beforeCommand 도 app 컨텍스트에서 정상 동작.",
|
|
25
|
+
"**Tauri devUrl 이 app port 와 자동 일치** — 모노레포 첫 app port (default 3000) 가 `tauri.conf.json` 의 `devUrl: http://localhost:{port}` + `vite.config.ts` 의 `server.port` 양쪽에 박힘. standalone (5173) 도 그대로 (placeholder 시스템으로 backward-compat). `{{tauri_dev_url}}` 플레이스홀더 신설, `emitTauri({ devPort })` + `patchViteForTauri({ port })` 옵션 시그니처.",
|
|
26
|
+
"**MCP + CLI 진입점에서 거부 가드 제거** — `mcp.mjs` 와 `generator.js` 의 `tauri + structure === 'monorepo'` throw 삭제. `sh_ui_create_project` 의 description 도 `Vite (standalone/monorepo)` 로 갱신. `tauri + platform !== 'vite'` 가드는 그대로 유지 (next/flutter 는 Tauri 통합 안 함).",
|
|
27
|
+
"**docs `/create` UI 도 풀림** — ProjectOptionsForm 의 `disabled={structure !== 'standalone'}` 제거 + composer 의 `--tauri` 가 monorepo 에서도 emit. `/api/template-content` 가 `apps/{name}/src-tauri/*` 경로를 tauri-shell 템플릿에서 resolve. describeTemplate 의 vite-monorepo 분기가 `tauri=true` 일 때 `apps/{appName}/src-tauri/` 그룹 emit.",
|
|
28
|
+
"**Smoke V12 회귀 가드** — vite + monorepo + tauri scaffold 가 (1) apps/web/src-tauri/ 8 파일 emit, (2) tauri.conf.json devUrl = http://localhost:3000, (3) vite.config.ts port = 3000, (4) Cargo.toml crate name = web/web_lib, (5) 루트엔 src-tauri/ 없음 — 모두 단언. v0.88.0 의 V7d (rejection guard) 는 제거 (조합 풀려 더 이상 거부 안 함)."
|
|
29
|
+
],
|
|
30
|
+
"url": "https://github.com/sanghyeonKim0201/sh-ui/releases/tag/v0.90.0"
|
|
31
|
+
},
|
|
5
32
|
{
|
|
6
33
|
"version": "0.89.1",
|
|
7
34
|
"date": "2026-05-14",
|
package/package.json
CHANGED
|
@@ -35,7 +35,7 @@ import { CSS_FRAMEWORK_DEFAULT } from '../constants.js';
|
|
|
35
35
|
* @property {'tailwind'|'plain'|'css-modules'} [cssFramework]
|
|
36
36
|
* @property {string} [projectName]
|
|
37
37
|
* @property {string} [appName] monorepo 첫 앱 이름. 기본 'web'
|
|
38
|
-
* @property {boolean} [tauri] platform=vite
|
|
38
|
+
* @property {boolean} [tauri] platform=vite (standalone/monorepo 둘 다) 일 때 Tauri 2.x 셸 같이 emit
|
|
39
39
|
*/
|
|
40
40
|
|
|
41
41
|
/**
|
|
@@ -140,6 +140,15 @@ export function describeTemplate(opts = {}) {
|
|
|
140
140
|
),
|
|
141
141
|
));
|
|
142
142
|
|
|
143
|
+
if (tauri) {
|
|
144
|
+
const tauriTpl = TEMPLATE_MANIFEST['tauri-shell'];
|
|
145
|
+
if (!tauriTpl) {
|
|
146
|
+
throw new Error("Template manifest missing entry for 'tauri-shell'.");
|
|
147
|
+
}
|
|
148
|
+
const tauriFiles = tauriTpl.base.map((p) => `apps/${appName}/src-tauri/${p}`);
|
|
149
|
+
groups.push(makeGroup('tauri', `Tauri 셸 (apps/${appName}/src-tauri/)`, tauriFiles));
|
|
150
|
+
}
|
|
151
|
+
|
|
143
152
|
return finalize(groups);
|
|
144
153
|
}
|
|
145
154
|
|
package/src/create/generator.js
CHANGED
|
@@ -222,12 +222,6 @@ export async function createProject(options = {}) {
|
|
|
222
222
|
`--tauri 옵션 제거 또는 --platform vite 사용.`,
|
|
223
223
|
);
|
|
224
224
|
}
|
|
225
|
-
if (options.structure === 'monorepo') {
|
|
226
|
-
throw new Error(
|
|
227
|
-
'platform=vite + structure=monorepo + tauri=true 는 아직 지원 안 함 (v0.89 후속). ' +
|
|
228
|
-
'standalone 으로 시도하거나 tauri 옵션 제거.',
|
|
229
|
-
);
|
|
230
|
-
}
|
|
231
225
|
}
|
|
232
226
|
|
|
233
227
|
// arch 결정 — platform 확정 후. 사용자가 --arch 미지정 시:
|
|
@@ -352,14 +346,7 @@ export async function createProject(options = {}) {
|
|
|
352
346
|
if (projectType === 'standalone') {
|
|
353
347
|
await generateViteStandalone(targetDir, projectName, theme, cssFramework, arch, themeBase, { tauri: !!options.tauri });
|
|
354
348
|
} else {
|
|
355
|
-
|
|
356
|
-
if (options.tauri) {
|
|
357
|
-
throw new Error(
|
|
358
|
-
'platform=vite + structure=monorepo + tauri=true 는 아직 지원 안 함 (v0.89 후속). ' +
|
|
359
|
-
'standalone 으로 시도하거나 tauri 옵션 제거.',
|
|
360
|
-
);
|
|
361
|
-
}
|
|
362
|
-
await generateMonorepo(targetDir, projectName, [], { yes: options.yes, theme, css: cssFramework, arch, themeBase, platform: 'vite' });
|
|
349
|
+
await generateMonorepo(targetDir, projectName, [], { yes: options.yes, theme, css: cssFramework, arch, themeBase, platform: 'vite', tauri: options.tauri });
|
|
363
350
|
}
|
|
364
351
|
|
|
365
352
|
await finalizeProject(targetDir, { dryRun: options.dryRun });
|
|
@@ -377,8 +364,11 @@ export async function createProject(options = {}) {
|
|
|
377
364
|
console.log(`\n cd ${projectName}`);
|
|
378
365
|
console.log(' pnpm install');
|
|
379
366
|
console.log(' pnpm dev\n');
|
|
380
|
-
if (options.tauri
|
|
367
|
+
if (options.tauri) {
|
|
381
368
|
console.log('Tauri 데스크탑 셸:');
|
|
369
|
+
if (projectType === 'monorepo') {
|
|
370
|
+
console.log(' cd apps/web # 또는 첫 앱 이름');
|
|
371
|
+
}
|
|
382
372
|
console.log(' pnpm tauri dev # Rust 처음 빌드는 5~10분 — 캐시 후 5~10초');
|
|
383
373
|
console.log(' (Rust 미설치 시 https://rustup.rs/ 참고)\n');
|
|
384
374
|
}
|
|
@@ -487,6 +477,37 @@ export async function addApp(options = {}) {
|
|
|
487
477
|
throw new Error('비대화형 환경(TTY 없음)에서는 name 이 필요합니다.');
|
|
488
478
|
}
|
|
489
479
|
|
|
480
|
+
// platform 결정 — 명시 시 그대로, 미지정 시 기존 apps/* 의 package.json 스캔으로 추론.
|
|
481
|
+
let platform = options.platform;
|
|
482
|
+
if (!platform) {
|
|
483
|
+
platform = await inferMonorepoPlatform(cwd);
|
|
484
|
+
if (!platform) {
|
|
485
|
+
// 모노레포에 아직 앱이 없거나 혼재 — 안전한 디폴트 (next 가 기존 동작).
|
|
486
|
+
// 첫 앱은 user 가 명시하는 게 권장 (interactive prompt).
|
|
487
|
+
if (process.stdin.isTTY) {
|
|
488
|
+
platform = await select({
|
|
489
|
+
message: '플랫폼:',
|
|
490
|
+
choices: [
|
|
491
|
+
{ name: 'Next.js', value: 'next' },
|
|
492
|
+
{ name: 'Vite (SPA)', value: 'vite' },
|
|
493
|
+
],
|
|
494
|
+
});
|
|
495
|
+
} else {
|
|
496
|
+
platform = 'next';
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
if (platform !== 'next' && platform !== 'vite') {
|
|
501
|
+
throw new Error(
|
|
502
|
+
`add-app 는 platform=next 또는 vite 만 지원 (받은 값: ${platform}). flutter 는 standalone 만 지원.`,
|
|
503
|
+
);
|
|
504
|
+
}
|
|
505
|
+
if (options.tauri && platform !== 'vite') {
|
|
506
|
+
throw new Error(
|
|
507
|
+
`tauri 는 platform=vite 일 때만 지원합니다 (현재 platform=${platform}). --platform vite 사용 또는 tauri 옵션 제거.`,
|
|
508
|
+
);
|
|
509
|
+
}
|
|
510
|
+
|
|
490
511
|
const appName = validateProjectName(
|
|
491
512
|
options.name ?? await input({
|
|
492
513
|
message: '앱 이름:',
|
|
@@ -533,9 +554,13 @@ export async function addApp(options = {}) {
|
|
|
533
554
|
// arch 는 모노레포가 처음 만들어질 때 정한 값과 같아야 한다. root config 에 별도
|
|
534
555
|
// 저장 안 해 두므로 일단 DEFAULT_ARCH (fsd) fallback. 향후 root config 에 arch
|
|
535
556
|
// 박아두고 여기서 읽어오는 흐름으로 개선 가능.
|
|
536
|
-
const arch = assertArchPlatformCompat(DEFAULT_ARCH,
|
|
557
|
+
const arch = assertArchPlatformCompat(DEFAULT_ARCH, platform);
|
|
537
558
|
|
|
538
|
-
|
|
559
|
+
if (platform === 'vite') {
|
|
560
|
+
await generateViteApp(appsDir, appName, port, arch, css, { tauri: !!options.tauri });
|
|
561
|
+
} else {
|
|
562
|
+
await generateApp(appsDir, appName, port, plugins, arch, css);
|
|
563
|
+
}
|
|
539
564
|
|
|
540
565
|
// monorepo 의 새 ui-app 패키지 — tokens-only role + theme 주입 + cssFramework 패치.
|
|
541
566
|
// generateMonorepo 의 흐름과 정확히 동일.
|
|
@@ -548,6 +573,41 @@ export async function addApp(options = {}) {
|
|
|
548
573
|
console.log(`\n✅ apps/${appName} 이 추가되었습니다!`);
|
|
549
574
|
console.log('\n pnpm install');
|
|
550
575
|
console.log(` pnpm --filter ${appName} dev\n`);
|
|
576
|
+
|
|
577
|
+
if (platform === 'vite' && options.tauri) {
|
|
578
|
+
console.log('Tauri 데스크탑 셸:');
|
|
579
|
+
console.log(` cd apps/${appName}`);
|
|
580
|
+
console.log(' pnpm tauri dev # Rust 처음 빌드는 5~10분 — 캐시 후 5~10초');
|
|
581
|
+
console.log(' (Rust 미설치 시 https://rustup.rs/ 참고)\n');
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
/**
|
|
586
|
+
* 모노레포의 기존 apps/* 들 package.json 을 스캔해 platform 추론.
|
|
587
|
+
* - 모든 앱이 vite — 'vite'
|
|
588
|
+
* - 모든 앱이 next — 'next'
|
|
589
|
+
* - 혼재 또는 다른 조합 — null (user 가 명시해야 함)
|
|
590
|
+
* - apps/ 가 없거나 비었으면 null
|
|
591
|
+
*/
|
|
592
|
+
async function inferMonorepoPlatform(cwd) {
|
|
593
|
+
const appsDir = path.resolve(cwd, 'apps');
|
|
594
|
+
if (!(await fs.pathExists(appsDir))) return null;
|
|
595
|
+
const entries = await fsp.readdir(appsDir, { withFileTypes: true });
|
|
596
|
+
const apps = entries.filter((e) => e.isDirectory());
|
|
597
|
+
if (apps.length === 0) return null;
|
|
598
|
+
|
|
599
|
+
const platforms = new Set();
|
|
600
|
+
for (const app of apps) {
|
|
601
|
+
const pkgPath = path.join(appsDir, app.name, 'package.json');
|
|
602
|
+
if (!(await fs.pathExists(pkgPath))) continue;
|
|
603
|
+
const pkg = await fs.readJson(pkgPath);
|
|
604
|
+
const deps = { ...(pkg.dependencies ?? {}), ...(pkg.devDependencies ?? {}) };
|
|
605
|
+
if (deps.next) platforms.add('next');
|
|
606
|
+
else if (deps.vite) platforms.add('vite');
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
if (platforms.size === 1) return [...platforms][0];
|
|
610
|
+
return null;
|
|
551
611
|
}
|
|
552
612
|
|
|
553
613
|
// ─── Add component to ui packages ───
|
|
@@ -798,8 +858,8 @@ async function generateViteStandalone(targetDir, projectName, theme, css, arch,
|
|
|
798
858
|
|
|
799
859
|
// Tauri 셸 emit (옵션) — vite SPA + native window. standalone 만 v1 지원.
|
|
800
860
|
if (tauri) {
|
|
801
|
-
await emitTauri(targetDir, projectName);
|
|
802
|
-
await patchViteForTauri(targetDir);
|
|
861
|
+
await emitTauri(targetDir, projectName, { devPort: 5173 });
|
|
862
|
+
await patchViteForTauri(targetDir, { port: 5173 });
|
|
803
863
|
}
|
|
804
864
|
}
|
|
805
865
|
|
|
@@ -812,7 +872,7 @@ async function generateViteStandalone(targetDir, projectName, theme, css, arch,
|
|
|
812
872
|
*
|
|
813
873
|
* generateViteStandalone 에서 tauri: true 인 경우 호출. monorepo+tauri 는 v0.89 후속.
|
|
814
874
|
*/
|
|
815
|
-
async function emitTauri(targetDir, projectName) {
|
|
875
|
+
async function emitTauri(targetDir, projectName, { devPort = 5173 } = {}) {
|
|
816
876
|
const srcTauriDir = path.join(targetDir, 'src-tauri');
|
|
817
877
|
await fs.copy(path.join(TEMPLATES_DIR, 'tauri-shell'), srcTauriDir);
|
|
818
878
|
|
|
@@ -824,6 +884,7 @@ async function emitTauri(targetDir, projectName) {
|
|
|
824
884
|
|
|
825
885
|
await replaceInAllFiles(srcTauriDir, '{{tauri_crate_name}}', tauriCrateName);
|
|
826
886
|
await replaceInAllFiles(srcTauriDir, '{{project_name}}', projectName);
|
|
887
|
+
await replaceInAllFiles(srcTauriDir, '{{tauri_dev_url}}', `http://localhost:${devPort}`);
|
|
827
888
|
}
|
|
828
889
|
|
|
829
890
|
/**
|
|
@@ -837,7 +898,7 @@ async function emitTauri(targetDir, projectName) {
|
|
|
837
898
|
* 이라 안전. 후속 task 에서 arch-specific vite.config.ts overlay 가 생기면 이 자리에서 머지 전략
|
|
838
899
|
* 필요 (현재는 단순 overwrite).
|
|
839
900
|
*/
|
|
840
|
-
async function patchViteForTauri(targetDir) {
|
|
901
|
+
async function patchViteForTauri(targetDir, { port = 5173 } = {}) {
|
|
841
902
|
const pkgPath = path.join(targetDir, 'package.json');
|
|
842
903
|
const pkg = await fs.readJson(pkgPath);
|
|
843
904
|
|
|
@@ -872,7 +933,7 @@ export default defineConfig({
|
|
|
872
933
|
plugins: [react(), tailwindcss(), tsconfigPaths()],
|
|
873
934
|
clearScreen: false,
|
|
874
935
|
server: {
|
|
875
|
-
port:
|
|
936
|
+
port: ${port},
|
|
876
937
|
strictPort: true,
|
|
877
938
|
host: false,
|
|
878
939
|
},
|
|
@@ -897,7 +958,7 @@ export default defineConfig({
|
|
|
897
958
|
}
|
|
898
959
|
}
|
|
899
960
|
|
|
900
|
-
async function generateMonorepo(targetDir, projectName, plugins, { yes = false, theme, css, arch, themeBase, platform = 'next' } = {}) {
|
|
961
|
+
async function generateMonorepo(targetDir, projectName, plugins, { yes = false, theme, css, arch, themeBase, platform = 'next', tauri = false } = {}) {
|
|
901
962
|
await fs.copy(path.join(TEMPLATES_DIR, 'monorepo'), targetDir);
|
|
902
963
|
|
|
903
964
|
// Update root package.json
|
|
@@ -929,7 +990,7 @@ async function generateMonorepo(targetDir, projectName, plugins, { yes = false,
|
|
|
929
990
|
|
|
930
991
|
const appsDir = path.join(targetDir, 'apps', appName);
|
|
931
992
|
if (platform === 'vite') {
|
|
932
|
-
await generateViteApp(appsDir, appName, port, arch, css);
|
|
993
|
+
await generateViteApp(appsDir, appName, port, arch, css, { tauri });
|
|
933
994
|
} else {
|
|
934
995
|
await generateApp(appsDir, appName, port, plugins, arch, css);
|
|
935
996
|
}
|
|
@@ -1030,7 +1091,7 @@ async function generateApp(targetDir, appName, port, plugins, arch, css = 'tailw
|
|
|
1030
1091
|
}
|
|
1031
1092
|
}
|
|
1032
1093
|
|
|
1033
|
-
async function generateViteApp(targetDir, appName, port, arch, css = 'tailwind') {
|
|
1094
|
+
async function generateViteApp(targetDir, appName, port, arch, css = 'tailwind', { tauri = false } = {}) {
|
|
1034
1095
|
// 베이스 (arch-neutral) + arch 오버레이 — generateApp 과 동일 패턴.
|
|
1035
1096
|
await fs.copy(path.join(TEMPLATES_DIR, 'vite-app'), targetDir, {
|
|
1036
1097
|
filter: (src) => !src.includes(`${path.sep}_arch${path.sep}`) && !src.endsWith(`${path.sep}_arch`),
|
|
@@ -1089,6 +1150,15 @@ async function generateViteApp(targetDir, appName, port, arch, css = 'tailwind')
|
|
|
1089
1150
|
if (await fs.pathExists(uiPkgDir)) {
|
|
1090
1151
|
await applyCssFrameworkVariant(uiPkgDir, css, { isMonorepo: true, plugins: [], arch, isUiPackage: true });
|
|
1091
1152
|
}
|
|
1153
|
+
|
|
1154
|
+
// tauri 가 켜져 있으면 이 app 안에 src-tauri/ shell 을 떨어뜨린다 (v0.90.0+).
|
|
1155
|
+
// standalone 과 달리 monorepo 에서는 app 단위로 별도 dev port (default 3000) 가 있고,
|
|
1156
|
+
// tauri 의 devUrl 이 그 port 와 일치해야 한다. frontendDist 는 src-tauri 기준 `../dist`.
|
|
1157
|
+
if (tauri) {
|
|
1158
|
+
const devPort = Number(port) || 3000;
|
|
1159
|
+
await emitTauri(targetDir, appName, { devPort });
|
|
1160
|
+
await patchViteForTauri(targetDir, { port: devPort });
|
|
1161
|
+
}
|
|
1092
1162
|
}
|
|
1093
1163
|
|
|
1094
1164
|
/**
|
package/src/create/index.mjs
CHANGED
|
@@ -18,7 +18,7 @@ export const HELP_TEXT = `sh-ui create — sh-ui 프로젝트 스캐폴드 (Next
|
|
|
18
18
|
|
|
19
19
|
사용법:
|
|
20
20
|
sh-ui create [name] [options]
|
|
21
|
-
sh-ui create add-app [name] [--port <n>] [--plugins ..] [--theme ..] [--css ..]
|
|
21
|
+
sh-ui create add-app [name] [--port <n>] [--platform <next|vite>] [--plugins ..] [--theme ..] [--css ..] [--tauri]
|
|
22
22
|
sh-ui create add-component <name> [--app <name>]
|
|
23
23
|
|
|
24
24
|
옵션:
|
|
@@ -73,6 +73,8 @@ export async function runCreate(rest) {
|
|
|
73
73
|
plugins: flags.plugins,
|
|
74
74
|
theme: flags.theme,
|
|
75
75
|
css: flags.css,
|
|
76
|
+
platform: flags.platform,
|
|
77
|
+
tauri: flags.tauri,
|
|
76
78
|
});
|
|
77
79
|
} else if (command === 'add-component') {
|
|
78
80
|
// 호환 별칭 — 신규 진입점은 `sh-ui add <name>` (bin/sh-ui.mjs 가 walk-up 으로 라우팅).
|
package/src/mcp.mjs
CHANGED
|
@@ -367,7 +367,7 @@ export async function startMcpServer() {
|
|
|
367
367
|
{
|
|
368
368
|
description:
|
|
369
369
|
"빈 폴더에 sh-ui 프로젝트 스캐폴드 — Next.js (standalone/monorepo) | Vite (standalone/monorepo) | Flutter. " +
|
|
370
|
-
"+ vite
|
|
370
|
+
"+ vite 인 경우 `tauri: true` 로 Tauri 2.x 데스크탑 셸 (`src-tauri/`) 까지 한 번에 emit (Rust toolchain 필요). " +
|
|
371
371
|
`FSD 폴더 구조 + 토큰 + sh-ui.config.json 일괄 생성. 사용자가 '새 프로젝트' / '빈 폴더' / '스캐폴드부터' 류 요청을 하면 이 툴 사용 (Bash 로 npx ${cliName} create 직접 호출보다 우선). ` +
|
|
372
372
|
"**단일 진입점** — theme/plugins/cssFramework/structure 모두 호출 시점에 정해서 한 번에 박는다. 호출 후 sh-ui.config.json/tokens.css 를 손으로 패치하지 말 것 (다음 재스캐폴드 시 유실). " +
|
|
373
373
|
"산출물: theme 인자가 프리셋이면 sh-ui.config.json 의 theme.base 가 그 이름, base64 면 'custom'. paths.styles · paths.tokens 도 자동 박혀서 sh_ui_add_component 가 사후 패치 없이 동작.",
|
|
@@ -397,9 +397,10 @@ export async function startMcpServer() {
|
|
|
397
397
|
.describe("기존 디렉토리 덮어쓰기. 기본 false (안전)"),
|
|
398
398
|
tauri: z.boolean().optional()
|
|
399
399
|
.describe(
|
|
400
|
-
"Tauri 2.x 데스크탑 셸 (`src-tauri/`) 함께 emit. platform=vite
|
|
400
|
+
"Tauri 2.x 데스크탑 셸 (`src-tauri/`) 함께 emit. platform=vite (standalone/monorepo 둘 다) 일 때만 지원. " +
|
|
401
|
+
"monorepo 의 경우 src-tauri/ 는 apps/{appName}/ 안에 emit, devUrl 은 해당 app 의 dev port 와 자동 동기화. " +
|
|
401
402
|
"Rust toolchain (`cargo`/`rustc`) 가 시스템에 설치되어 있어야 첫 `pnpm tauri dev` 가 동작. " +
|
|
402
|
-
"기본 false.
|
|
403
|
+
"기본 false.",
|
|
403
404
|
),
|
|
404
405
|
},
|
|
405
406
|
},
|
|
@@ -431,15 +432,6 @@ export async function startMcpServer() {
|
|
|
431
432
|
}],
|
|
432
433
|
};
|
|
433
434
|
}
|
|
434
|
-
if (input.tauri && input.structure === "monorepo") {
|
|
435
|
-
return {
|
|
436
|
-
isError: true,
|
|
437
|
-
content: [{
|
|
438
|
-
type: "text",
|
|
439
|
-
text: "platform=vite + structure=monorepo + tauri=true 는 아직 지원 안 함 (v0.89 후속). standalone 으로 시도하거나 tauri 옵션 제거.",
|
|
440
|
-
}],
|
|
441
|
-
};
|
|
442
|
-
}
|
|
443
435
|
const targetParent = resolveCwd(input);
|
|
444
436
|
const targetDir = resolve(targetParent, input.name);
|
|
445
437
|
if (existsSync(targetDir) && !input.force) {
|
|
@@ -480,10 +472,11 @@ export async function startMcpServer() {
|
|
|
480
472
|
"sh_ui_add_app",
|
|
481
473
|
{
|
|
482
474
|
description:
|
|
483
|
-
"기존 모노레포에 새 Next.js
|
|
475
|
+
"기존 모노레포에 새 앱 (Next.js 또는 Vite) 추가 — apps/{name}/ + packages/ui/ui-apps/ui-{name}/ 동시 생성. " +
|
|
484
476
|
"사용자가 '앱 추가' / 'monorepo 에 새 앱' / 'add admin app' 류 요청을 하면 이 툴 사용 (Bash 로 npx " + cliName + " add-app 직접 호출보다 우선). " +
|
|
485
477
|
"v0.65+ 레이아웃 준수 — ui-{name} 은 tokens-only role, 컴포넌트는 sibling ui-core 가 SoT. " +
|
|
486
|
-
"theme/css 는 새 ui-app 에만 적용 (다른 앱 영향 없음). monorepo 가 아니면 (pnpm-workspace.yaml 없음) 에러."
|
|
478
|
+
"theme/css 는 새 ui-app 에만 적용 (다른 앱 영향 없음). monorepo 가 아니면 (pnpm-workspace.yaml 없음) 에러. " +
|
|
479
|
+
"platform 미지정 시 기존 apps/* 스캔해 추론 (모든 앱이 같은 플랫폼이면 그것으로). vite + tauri:true 면 apps/{name}/src-tauri/ 도 함께 emit.",
|
|
487
480
|
inputSchema: {
|
|
488
481
|
name: z.string().min(1)
|
|
489
482
|
.describe("앱 이름 — apps/{name}/ + packages/ui/ui-apps/ui-{name}/ 디렉토리명. 영숫자 + 하이픈."),
|
|
@@ -495,6 +488,10 @@ export async function startMcpServer() {
|
|
|
495
488
|
.describe(`프리셋 이름 (${THEME_PRESETS_LIST}) 또는 base64 테마 코드. 새 ui-app 의 tokens.css 에만 주입.`),
|
|
496
489
|
cssFramework: z.enum(CSS_FRAMEWORKS).optional()
|
|
497
490
|
.describe("CSS 프레임워크. 기본 plain. 새 앱의 컴포넌트 변종 결정 — 같은 모노레포 내 다른 앱과 다른 값 가능."),
|
|
491
|
+
platform: z.enum(["next", "vite"]).optional()
|
|
492
|
+
.describe("플랫폼 — next | vite. 미지정 시 기존 apps/* 의 deps 로 추론 (모든 앱이 같은 플랫폼이면 그것으로, 혼재면 명시 필요)."),
|
|
493
|
+
tauri: z.boolean().optional()
|
|
494
|
+
.describe("Tauri 2.x 데스크탑 셸 — platform=vite 일 때만 의미. apps/{name}/src-tauri/ 에 emit. 기본 false."),
|
|
498
495
|
cwd: z.string().optional()
|
|
499
496
|
.describe("모노레포 루트 (pnpm-workspace.yaml 있는 곳). 기본 process.cwd()"),
|
|
500
497
|
},
|
|
@@ -505,6 +502,15 @@ export async function startMcpServer() {
|
|
|
505
502
|
} catch (e) {
|
|
506
503
|
return { isError: true, content: [{ type: "text", text: e.message }] };
|
|
507
504
|
}
|
|
505
|
+
if (input.tauri && input.platform === "next") {
|
|
506
|
+
return {
|
|
507
|
+
isError: true,
|
|
508
|
+
content: [{
|
|
509
|
+
type: "text",
|
|
510
|
+
text: "tauri 는 platform=vite 일 때만 지원합니다. --platform vite 사용 또는 tauri 옵션 제거.",
|
|
511
|
+
}],
|
|
512
|
+
};
|
|
513
|
+
}
|
|
508
514
|
const text = await captureConsole(() =>
|
|
509
515
|
addApp({
|
|
510
516
|
name: input.name,
|
|
@@ -512,6 +518,8 @@ export async function startMcpServer() {
|
|
|
512
518
|
plugins: input.plugins,
|
|
513
519
|
theme: input.theme,
|
|
514
520
|
css: input.cssFramework,
|
|
521
|
+
platform: input.platform,
|
|
522
|
+
tauri: input.tauri,
|
|
515
523
|
cwd: resolveCwd(input),
|
|
516
524
|
}),
|
|
517
525
|
);
|