sh-ui-cli 0.96.3 → 0.97.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/data/changelog/versions.json +13 -0
- package/package.json +1 -1
- package/src/api.d.ts +0 -2
- package/src/create/cli-args.js +1 -1
- package/src/create/describeTemplate.js +1 -20
- package/src/create/generator.js +7 -155
- package/src/create/index.mjs +1 -3
- package/src/create/templateManifest.js +0 -12
- package/src/mcp.mjs +1 -34
- package/templates/vite-standalone/_arch/flat/index.html +2 -2
- package/templates/vite-standalone/_arch/flat/src/lib/styles/globals.css +2 -2
- package/templates/vite-standalone/_arch/fsd/index.html +2 -2
- package/templates/vite-standalone/_arch/fsd/src/shared/styles/globals.css +2 -2
- package/templates/tauri-shell/Cargo.toml +0 -21
- package/templates/tauri-shell/README.md +0 -49
- package/templates/tauri-shell/build.rs +0 -3
- package/templates/tauri-shell/capabilities/default.json +0 -12
- package/templates/tauri-shell/src/lib.rs +0 -8
- package/templates/tauri-shell/src/main.rs +0 -6
- package/templates/tauri-shell/tauri.conf.json +0 -29
|
@@ -2,6 +2,19 @@
|
|
|
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.97.0",
|
|
7
|
+
"date": "2026-05-16",
|
|
8
|
+
"title": "Tauri 통합 제거 — 스캐폴더에서 src-tauri/ shell 및 --tauri 옵션 삭제 (breaking)",
|
|
9
|
+
"type": "minor",
|
|
10
|
+
"highlights": [
|
|
11
|
+
"**`--tauri` / MCP `tauri` 옵션 제거 (breaking)** — `sh-ui-cli create` / `add-app` 의 `--tauri` 플래그, `sh_ui_create_project` · `sh_ui_add_app` · `sh_ui_describe_template` 의 `tauri` 인자, `DescribeTemplateOptions.tauri` 타입을 모두 삭제. 이제 `--tauri` 는 '알 수 없는 플래그' 에러로 거부된다.",
|
|
12
|
+
"**`tauri-shell` 템플릿 + `emitTauri`/`patchViteForTauri` 삭제** — `src-tauri/` (Cargo.toml·tauri.conf.json·Rust 소스) 를 더 이상 emit 하지 않는다. `@tauri-apps/*` 의존성·`tauri:dev`/`tauri:build` scripts·`src-tauri/target/` gitignore 주입도 제거. Tauri 가 필요하면 공식 `@tauri-apps/cli init` 을 직접 쓰면 된다 (vite 의 `dist/` 표준 산출물과 그대로 호환).",
|
|
13
|
+
"**docs `/create` UI 정리** — Tauri 데스크탑 셸 토글 제거, `/api/template-content` 의 src-tauri resolve 분기 삭제. vite 의 mobile/PWA 친화 디폴트(`viewport-fit=cover`·`theme-color`·webview reset)는 그대로 유지 — Tauri 전용이 아니라 일반 모바일 하드닝이라 보존.",
|
|
14
|
+
"**smoke 정리** — tauri 전용 시나리오(V7a–f, V12–14) 삭제, all-options 가드(V20a)는 i18n+sentry 조합으로 축소 유지. 마이그레이션: `--tauri` 쓰던 사용자는 플래그를 빼고 생성 후 `@tauri-apps/cli init` 으로 desktop shell 을 별도 추가."
|
|
15
|
+
],
|
|
16
|
+
"url": "https://github.com/sanghyeonKim0201/sh-ui/releases/tag/v0.97.0"
|
|
17
|
+
},
|
|
5
18
|
{
|
|
6
19
|
"version": "0.96.3",
|
|
7
20
|
"date": "2026-05-16",
|
package/package.json
CHANGED
package/src/api.d.ts
CHANGED
|
@@ -139,8 +139,6 @@ export interface DescribeTemplateOptions {
|
|
|
139
139
|
projectName?: string;
|
|
140
140
|
/** monorepo 첫 앱 이름. 기본 'web'. */
|
|
141
141
|
appName?: string;
|
|
142
|
-
/** platform=vite + structure=standalone 일 때 Tauri 2.x 셸(`src-tauri/`) 동시 emit. */
|
|
143
|
-
tauri?: boolean;
|
|
144
142
|
/** vite 전용 — react-i18next opt-in. v0.92.0+ */
|
|
145
143
|
i18n?: 'none' | 'react-i18next';
|
|
146
144
|
/** i18n 활성화 시 생성할 locale 코드 (comma-separated). 기본 'ko,en' */
|
package/src/create/cli-args.js
CHANGED
|
@@ -15,7 +15,7 @@ const VALID_PLUGINS = allPlugins.map((p) => p.name);
|
|
|
15
15
|
const VALID_ARCHES = allArchitectures.map((a) => a.name);
|
|
16
16
|
|
|
17
17
|
const VALUE_FLAGS = ['platform', 'structure', 'plugins', 'theme', 'app', 'css', 'arch', 'port', 'i18n', 'locales', 'observability'];
|
|
18
|
-
const BOOL_FLAGS = ['yes', 'help', 'dry-run'
|
|
18
|
+
const BOOL_FLAGS = ['yes', 'help', 'dry-run'];
|
|
19
19
|
|
|
20
20
|
const SUBCOMMANDS = ['add-app', 'add-component'];
|
|
21
21
|
|
|
@@ -35,7 +35,6 @@ 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 (standalone/monorepo 둘 다) 일 때 Tauri 2.x 셸 같이 emit
|
|
39
38
|
* @property {'none'|'react-i18next'} [i18n] vite 전용 — react-i18next 셋업
|
|
40
39
|
* @property {string} [locales] i18n 활성화 시 생성할 locale 코드 (comma-separated, default 'ko,en')
|
|
41
40
|
* @property {'none'|'sentry'} [observability] vite 전용 — Sentry 셋업 (v0.93.0+)
|
|
@@ -66,7 +65,6 @@ export function describeTemplate(opts = {}) {
|
|
|
66
65
|
plugins: pluginNames = [],
|
|
67
66
|
cssFramework = CSS_FRAMEWORK_DEFAULT,
|
|
68
67
|
appName: rawAppName = 'web',
|
|
69
|
-
tauri = false,
|
|
70
68
|
i18n = 'none',
|
|
71
69
|
locales = 'ko,en',
|
|
72
70
|
observability = 'none',
|
|
@@ -97,14 +95,6 @@ export function describeTemplate(opts = {}) {
|
|
|
97
95
|
makeGroup('base', '베이스 (vite-standalone)', baseFiles),
|
|
98
96
|
makeGroup('arch', `Arch (${safeArchName})`, archFiles),
|
|
99
97
|
];
|
|
100
|
-
if (tauri) {
|
|
101
|
-
const tauriTpl = TEMPLATE_MANIFEST['tauri-shell'];
|
|
102
|
-
if (!tauriTpl) {
|
|
103
|
-
throw new Error("Template manifest missing entry for 'tauri-shell'.");
|
|
104
|
-
}
|
|
105
|
-
const tauriFiles = tauriTpl.base.map((p) => `src-tauri/${p}`);
|
|
106
|
-
groups.push(makeGroup('tauri', 'Tauri 셸 (src-tauri/)', tauriFiles));
|
|
107
|
-
}
|
|
108
98
|
if (i18n === 'react-i18next') {
|
|
109
99
|
const isFsd = safeArchName === 'fsd';
|
|
110
100
|
const i18nBase = isFsd ? 'src/shared/i18n' : 'src/lib/i18n';
|
|
@@ -170,15 +160,6 @@ export function describeTemplate(opts = {}) {
|
|
|
170
160
|
),
|
|
171
161
|
));
|
|
172
162
|
|
|
173
|
-
if (tauri) {
|
|
174
|
-
const tauriTpl = TEMPLATE_MANIFEST['tauri-shell'];
|
|
175
|
-
if (!tauriTpl) {
|
|
176
|
-
throw new Error("Template manifest missing entry for 'tauri-shell'.");
|
|
177
|
-
}
|
|
178
|
-
const tauriFiles = tauriTpl.base.map((p) => `apps/${appName}/src-tauri/${p}`);
|
|
179
|
-
groups.push(makeGroup('tauri', `Tauri 셸 (apps/${appName}/src-tauri/)`, tauriFiles));
|
|
180
|
-
}
|
|
181
|
-
|
|
182
163
|
if (i18n === 'react-i18next') {
|
|
183
164
|
const isFsd = safeArchName === 'fsd';
|
|
184
165
|
const i18nBase = isFsd ? 'src/shared/i18n' : 'src/lib/i18n';
|
|
@@ -426,7 +407,7 @@ function finalize(groups) {
|
|
|
426
407
|
* 현재 규칙:
|
|
427
408
|
* - basename 이 정확히 'gitignore' 인 경로 → '.gitignore' 로 prefix dot 추가.
|
|
428
409
|
* (npm publish 가 .gitignore 를 strip 하므로 템플릿엔 점 없이 두고 emit 후 dot-prefix.)
|
|
429
|
-
* - 이미 '.gitignore' 인
|
|
410
|
+
* - 이미 '.gitignore' 인 경로는 그대로.
|
|
430
411
|
*
|
|
431
412
|
* 미래에 다른 fs-level rename 이 추가되면 여기에 같이 등록 (describeTemplate 의
|
|
432
413
|
* file-plan ↔ create_project 의 실제 emit 1:1 정합성 유지).
|
package/src/create/generator.js
CHANGED
|
@@ -213,17 +213,6 @@ export async function createProject(options = {}) {
|
|
|
213
213
|
],
|
|
214
214
|
});
|
|
215
215
|
|
|
216
|
-
// tauri 옵션은 platform=vite + structure=standalone 일 때만 의미. 다른 조합은 명시적 에러.
|
|
217
|
-
// (MCP 진입점은 mcp.mjs 가 이미 동일 가드 — CLI 직접 호출에도 동일 안전망.)
|
|
218
|
-
if (options.tauri) {
|
|
219
|
-
if (platform !== 'vite') {
|
|
220
|
-
throw new Error(
|
|
221
|
-
`tauri: true 는 platform=vite 일 때만 지원합니다 (현재 platform=${platform}). ` +
|
|
222
|
-
`--tauri 옵션 제거 또는 --platform vite 사용.`,
|
|
223
|
-
);
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
|
|
227
216
|
// i18n 옵션도 vite preset 전용. v0.92.0+.
|
|
228
217
|
if (options.i18n && options.i18n !== 'none' && platform !== 'vite') {
|
|
229
218
|
throw new Error(
|
|
@@ -359,7 +348,6 @@ export async function createProject(options = {}) {
|
|
|
359
348
|
|
|
360
349
|
if (projectType === 'standalone') {
|
|
361
350
|
await generateViteStandalone(targetDir, projectName, theme, cssFramework, arch, themeBase, {
|
|
362
|
-
tauri: !!options.tauri,
|
|
363
351
|
i18n: options.i18n ?? 'none',
|
|
364
352
|
locales: options.locales ?? 'ko,en',
|
|
365
353
|
observability: options.observability ?? 'none',
|
|
@@ -368,7 +356,6 @@ export async function createProject(options = {}) {
|
|
|
368
356
|
await generateMonorepo(targetDir, projectName, [], {
|
|
369
357
|
yes: options.yes, theme, css: cssFramework, arch, themeBase,
|
|
370
358
|
platform: 'vite',
|
|
371
|
-
tauri: options.tauri,
|
|
372
359
|
i18n: options.i18n ?? 'none',
|
|
373
360
|
locales: options.locales ?? 'ko,en',
|
|
374
361
|
observability: options.observability ?? 'none',
|
|
@@ -392,14 +379,6 @@ export async function createProject(options = {}) {
|
|
|
392
379
|
console.log(`\n cd ${projectName}`);
|
|
393
380
|
console.log(' pnpm install');
|
|
394
381
|
console.log(' pnpm dev\n');
|
|
395
|
-
if (options.tauri) {
|
|
396
|
-
console.log('Tauri 데스크탑 셸:');
|
|
397
|
-
if (projectType === 'monorepo') {
|
|
398
|
-
console.log(' cd apps/web # 또는 첫 앱 이름');
|
|
399
|
-
}
|
|
400
|
-
console.log(' pnpm tauri dev # Rust 처음 빌드는 5~10분 — 캐시 후 5~10초');
|
|
401
|
-
console.log(' (Rust 미설치 시 https://rustup.rs/ 참고)\n');
|
|
402
|
-
}
|
|
403
382
|
console.log('다음 단계 — 베이스 컴포넌트 추가 (예시):');
|
|
404
383
|
console.log(' npx sh-ui-cli add button card input dialog\n');
|
|
405
384
|
return;
|
|
@@ -534,11 +513,6 @@ export async function addApp(options = {}) {
|
|
|
534
513
|
`add-app 는 platform=next 또는 vite 만 지원 (받은 값: ${platform}). flutter 는 standalone 만 지원.`,
|
|
535
514
|
);
|
|
536
515
|
}
|
|
537
|
-
if (options.tauri && platform !== 'vite') {
|
|
538
|
-
throw new Error(
|
|
539
|
-
`tauri 는 platform=vite 일 때만 지원합니다 (현재 platform=${platform}). --platform vite 사용 또는 tauri 옵션 제거.`,
|
|
540
|
-
);
|
|
541
|
-
}
|
|
542
516
|
if (options.i18n && options.i18n !== 'none' && platform !== 'vite') {
|
|
543
517
|
throw new Error(
|
|
544
518
|
`i18n='${options.i18n}' 은 platform=vite 일 때만 지원합니다 (현재 platform=${platform}).`,
|
|
@@ -600,7 +574,6 @@ export async function addApp(options = {}) {
|
|
|
600
574
|
|
|
601
575
|
if (platform === 'vite') {
|
|
602
576
|
await generateViteApp(appsDir, appName, port, arch, css, {
|
|
603
|
-
tauri: !!options.tauri,
|
|
604
577
|
i18n: options.i18n ?? 'none',
|
|
605
578
|
locales: options.locales ?? 'ko,en',
|
|
606
579
|
observability: options.observability ?? 'none',
|
|
@@ -620,13 +593,6 @@ export async function addApp(options = {}) {
|
|
|
620
593
|
console.log(`\n✅ apps/${appName} 이 추가되었습니다!`);
|
|
621
594
|
console.log('\n pnpm install');
|
|
622
595
|
console.log(` pnpm --filter ${appName} dev\n`);
|
|
623
|
-
|
|
624
|
-
if (platform === 'vite' && options.tauri) {
|
|
625
|
-
console.log('Tauri 데스크탑 셸:');
|
|
626
|
-
console.log(` cd apps/${appName}`);
|
|
627
|
-
console.log(' pnpm tauri dev # Rust 처음 빌드는 5~10분 — 캐시 후 5~10초');
|
|
628
|
-
console.log(' (Rust 미설치 시 https://rustup.rs/ 참고)\n');
|
|
629
|
-
}
|
|
630
596
|
}
|
|
631
597
|
|
|
632
598
|
/**
|
|
@@ -867,7 +833,7 @@ async function generateStandalone(targetDir, projectName, plugins, theme, css, a
|
|
|
867
833
|
await patchShUiConfig(path.join(targetDir, 'sh-ui.config.json'), css, themeBase);
|
|
868
834
|
}
|
|
869
835
|
|
|
870
|
-
async function generateViteStandalone(targetDir, projectName, theme, css, arch, themeBase, {
|
|
836
|
+
async function generateViteStandalone(targetDir, projectName, theme, css, arch, themeBase, { i18n = 'none', locales = 'ko,en', observability = 'none' } = {}) {
|
|
871
837
|
// 베이스 (arch-neutral) + arch 오버레이 — generateStandalone 과 같은 패턴.
|
|
872
838
|
await fs.copy(path.join(TEMPLATES_DIR, 'vite-standalone'), targetDir, {
|
|
873
839
|
filter: (src) => !src.includes(`${path.sep}_arch${path.sep}`) && !src.endsWith(`${path.sep}_arch`),
|
|
@@ -903,12 +869,6 @@ async function generateViteStandalone(targetDir, projectName, theme, css, arch,
|
|
|
903
869
|
await injectCssTheme(targetDir, theme);
|
|
904
870
|
await patchShUiConfig(path.join(targetDir, 'sh-ui.config.json'), css, themeBase);
|
|
905
871
|
|
|
906
|
-
// Tauri 셸 emit (옵션) — vite SPA + native window. standalone 만 v1 지원.
|
|
907
|
-
if (tauri) {
|
|
908
|
-
await emitTauri(targetDir, projectName, { devPort: 5173 });
|
|
909
|
-
await patchViteForTauri(targetDir, { port: 5173 });
|
|
910
|
-
}
|
|
911
|
-
|
|
912
872
|
if (i18n === 'react-i18next') {
|
|
913
873
|
const localesArr = parseLocales(locales);
|
|
914
874
|
await emitI18n(targetDir, { arch, locales: localesArr });
|
|
@@ -919,101 +879,6 @@ async function generateViteStandalone(targetDir, projectName, theme, css, arch,
|
|
|
919
879
|
}
|
|
920
880
|
}
|
|
921
881
|
|
|
922
|
-
/**
|
|
923
|
-
* tauri-shell 템플릿을 `<targetDir>/src-tauri/` 로 복사하고 placeholder 치환.
|
|
924
|
-
*
|
|
925
|
-
* - `{{project_name}}` → projectName (kebab-case 유지, npm 패키지명과 동일)
|
|
926
|
-
* - `{{tauri_crate_name}}` → snake_case 변환 (Rust crate name 규칙: 영숫자+언더스코어).
|
|
927
|
-
* 하이픈/점/대문자가 들어 있으면 모두 안전한 형태로 정규화.
|
|
928
|
-
*
|
|
929
|
-
* generateViteStandalone 에서 tauri: true 인 경우 호출. monorepo+tauri 는 v0.89 후속.
|
|
930
|
-
*/
|
|
931
|
-
async function emitTauri(targetDir, projectName, { devPort = 5173 } = {}) {
|
|
932
|
-
const srcTauriDir = path.join(targetDir, 'src-tauri');
|
|
933
|
-
await fs.copy(path.join(TEMPLATES_DIR, 'tauri-shell'), srcTauriDir);
|
|
934
|
-
|
|
935
|
-
// crate name: snake_case 강제 — Rust 식별자는 영숫자+'_' 만 허용
|
|
936
|
-
const tauriCrateName = projectName
|
|
937
|
-
.toLowerCase()
|
|
938
|
-
.replace(/[^a-z0-9]+/g, '_')
|
|
939
|
-
.replace(/^_+|_+$/g, '');
|
|
940
|
-
|
|
941
|
-
await replaceInAllFiles(srcTauriDir, '{{tauri_crate_name}}', tauriCrateName);
|
|
942
|
-
await replaceInAllFiles(srcTauriDir, '{{project_name}}', projectName);
|
|
943
|
-
await replaceInAllFiles(srcTauriDir, '{{tauri_dev_url}}', `http://localhost:${devPort}`);
|
|
944
|
-
}
|
|
945
|
-
|
|
946
|
-
/**
|
|
947
|
-
* vite 앱의 package.json + vite.config.ts 를 Tauri 친화적으로 패치.
|
|
948
|
-
*
|
|
949
|
-
* - package.json: `@tauri-apps/cli` (devDep), `@tauri-apps/api` (dep), `tauri`/`tauri:dev`/`tauri:build` scripts 추가
|
|
950
|
-
* - vite.config.ts: Tauri 공식 권장값 추가 — `clearScreen: false`, `server.strictPort: true`,
|
|
951
|
-
* `server.host: false`, `server.port: 5173`. 그래야 Tauri 가 dev server 를 안정적으로 wrap.
|
|
952
|
-
*
|
|
953
|
-
* NOTE: vite.config.ts 를 전부 재작성한다. 현재 base template 의 vite.config.ts 는 arch-neutral
|
|
954
|
-
* 이라 안전. 후속 task 에서 arch-specific vite.config.ts overlay 가 생기면 이 자리에서 머지 전략
|
|
955
|
-
* 필요 (현재는 단순 overwrite).
|
|
956
|
-
*/
|
|
957
|
-
async function patchViteForTauri(targetDir, { port = 5173 } = {}) {
|
|
958
|
-
const pkgPath = path.join(targetDir, 'package.json');
|
|
959
|
-
const pkg = await fs.readJson(pkgPath);
|
|
960
|
-
|
|
961
|
-
pkg.dependencies = pkg.dependencies ?? {};
|
|
962
|
-
pkg.devDependencies = pkg.devDependencies ?? {};
|
|
963
|
-
pkg.scripts = pkg.scripts ?? {};
|
|
964
|
-
|
|
965
|
-
pkg.dependencies['@tauri-apps/api'] = '^2.0.0';
|
|
966
|
-
pkg.dependencies['@tauri-apps/plugin-opener'] = '^2.0.0';
|
|
967
|
-
pkg.devDependencies['@tauri-apps/cli'] = '^2.0.0';
|
|
968
|
-
|
|
969
|
-
pkg.scripts.tauri = 'tauri';
|
|
970
|
-
pkg.scripts['tauri:dev'] = 'tauri dev';
|
|
971
|
-
pkg.scripts['tauri:build'] = 'tauri build';
|
|
972
|
-
|
|
973
|
-
pkg.dependencies = sortObjectKeys(pkg.dependencies);
|
|
974
|
-
pkg.devDependencies = sortObjectKeys(pkg.devDependencies);
|
|
975
|
-
await fs.writeJson(pkgPath, pkg, { spaces: 2 });
|
|
976
|
-
|
|
977
|
-
// vite.config.ts 재작성 — Tauri 공식 권장 설정 추가.
|
|
978
|
-
const viteCfgPath = path.join(targetDir, 'vite.config.ts');
|
|
979
|
-
const viteCfg = `import { defineConfig } from 'vite';
|
|
980
|
-
import react from '@vitejs/plugin-react';
|
|
981
|
-
import tailwindcss from '@tailwindcss/vite';
|
|
982
|
-
import tsconfigPaths from 'vite-tsconfig-paths';
|
|
983
|
-
|
|
984
|
-
// Tauri 권장 설정 (https://v2.tauri.app/start/frontend/vite/)
|
|
985
|
-
// - clearScreen: false — Rust 컴파일 에러가 터미널을 가리지 않게
|
|
986
|
-
// - server.strictPort — Tauri 가 사용할 포트를 고정 (충돌 시 에러)
|
|
987
|
-
// - server.host: false — Tauri dev 가 host network 안 열어도 됨
|
|
988
|
-
export default defineConfig({
|
|
989
|
-
plugins: [react(), tailwindcss(), tsconfigPaths()],
|
|
990
|
-
clearScreen: false,
|
|
991
|
-
server: {
|
|
992
|
-
port: ${port},
|
|
993
|
-
strictPort: true,
|
|
994
|
-
host: false,
|
|
995
|
-
},
|
|
996
|
-
});
|
|
997
|
-
`;
|
|
998
|
-
await fs.writeFile(viteCfgPath, viteCfg);
|
|
999
|
-
|
|
1000
|
-
// .gitignore 에 src-tauri/target 추가 — Rust 빌드 산출물 (수 GB 가능).
|
|
1001
|
-
// 스캐폴드 단계에서는 파일명이 `gitignore` (점 없음); finalizeProject 가 나중에 `.gitignore` 로 rename.
|
|
1002
|
-
// 양쪽 이름 모두 시도해서 호출 순서가 달라져도 안전하게 적용.
|
|
1003
|
-
const gitignoreCandidates = ['.gitignore', 'gitignore'];
|
|
1004
|
-
for (const name of gitignoreCandidates) {
|
|
1005
|
-
const p = path.join(targetDir, name);
|
|
1006
|
-
if (await fs.pathExists(p)) {
|
|
1007
|
-
let ignore = await fs.readFile(p, 'utf-8');
|
|
1008
|
-
if (!ignore.includes('src-tauri/target')) {
|
|
1009
|
-
ignore += `\n# Tauri build artifacts\nsrc-tauri/target/\n`;
|
|
1010
|
-
await fs.writeFile(p, ignore);
|
|
1011
|
-
}
|
|
1012
|
-
break;
|
|
1013
|
-
}
|
|
1014
|
-
}
|
|
1015
|
-
}
|
|
1016
|
-
|
|
1017
882
|
/**
|
|
1018
883
|
* react-i18next 셋업을 emit. arch 에 따라 경로가 달라짐:
|
|
1019
884
|
* - fsd: src/shared/i18n/* + src/app/providers/I18nProvider.tsx
|
|
@@ -1054,7 +919,6 @@ import { initReactI18next } from 'react-i18next';
|
|
|
1054
919
|
// 클라이언트 측 lazy-load — 빌드 산출물에서 public/locales/{lng}/{ns}.json 경로로 fetch.
|
|
1055
920
|
// 원본 locale 파일은 ${i18nDirRel}/locales/* 에 두고, vite-plugin-static-copy 가
|
|
1056
921
|
// dev/build 양쪽에서 public/locales/* 로 자동 미러링한다 (vite.config.ts 참고).
|
|
1057
|
-
// Tauri 빌드의 경우도 dist/locales 에 그대로 포함된다.
|
|
1058
922
|
|
|
1059
923
|
i18n
|
|
1060
924
|
.use(HttpBackend)
|
|
@@ -1532,7 +1396,7 @@ async function patchViteConfigForSentry(targetDir) {
|
|
|
1532
1396
|
await fs.writeFile(viteCfgPath, cfg);
|
|
1533
1397
|
}
|
|
1534
1398
|
|
|
1535
|
-
async function generateMonorepo(targetDir, projectName, plugins, { yes = false, theme, css, arch, themeBase, platform = 'next',
|
|
1399
|
+
async function generateMonorepo(targetDir, projectName, plugins, { yes = false, theme, css, arch, themeBase, platform = 'next', i18n = 'none', locales = 'ko,en', observability = 'none', appName: appNameOpt = null, port: portOpt = null } = {}) {
|
|
1536
1400
|
await fs.copy(path.join(TEMPLATES_DIR, 'monorepo'), targetDir);
|
|
1537
1401
|
|
|
1538
1402
|
// Update root package.json
|
|
@@ -1543,7 +1407,7 @@ async function generateMonorepo(targetDir, projectName, plugins, { yes = false,
|
|
|
1543
1407
|
|
|
1544
1408
|
// CLAUDE.md 의 platform 분기 placeholder 치환. AI 에이전트가 Next.js 가정으로
|
|
1545
1409
|
// 잘못된 컨벤션을 적용하지 않도록 (v0.94.0+).
|
|
1546
|
-
const platformAppDesc = describeAppPlatform(platform
|
|
1410
|
+
const platformAppDesc = describeAppPlatform(platform);
|
|
1547
1411
|
await replaceInAllFiles(targetDir, '{{PLATFORM_APP_DESCRIPTION}}', platformAppDesc);
|
|
1548
1412
|
|
|
1549
1413
|
// Update turbo.json
|
|
@@ -1584,7 +1448,7 @@ async function generateMonorepo(targetDir, projectName, plugins, { yes = false,
|
|
|
1584
1448
|
|
|
1585
1449
|
const appsDir = path.join(targetDir, 'apps', appName);
|
|
1586
1450
|
if (platform === 'vite') {
|
|
1587
|
-
await generateViteApp(appsDir, appName, port, arch, css, {
|
|
1451
|
+
await generateViteApp(appsDir, appName, port, arch, css, { i18n, locales, observability });
|
|
1588
1452
|
} else {
|
|
1589
1453
|
await generateApp(appsDir, appName, port, plugins, arch, css);
|
|
1590
1454
|
}
|
|
@@ -1685,7 +1549,7 @@ async function generateApp(targetDir, appName, port, plugins, arch, css = 'tailw
|
|
|
1685
1549
|
}
|
|
1686
1550
|
}
|
|
1687
1551
|
|
|
1688
|
-
async function generateViteApp(targetDir, appName, port, arch, css = 'tailwind', {
|
|
1552
|
+
async function generateViteApp(targetDir, appName, port, arch, css = 'tailwind', { i18n = 'none', locales = 'ko,en', observability = 'none' } = {}) {
|
|
1689
1553
|
// 베이스 (arch-neutral) + arch 오버레이 — generateApp 과 동일 패턴.
|
|
1690
1554
|
await fs.copy(path.join(TEMPLATES_DIR, 'vite-app'), targetDir, {
|
|
1691
1555
|
filter: (src) => !src.includes(`${path.sep}_arch${path.sep}`) && !src.endsWith(`${path.sep}_arch`),
|
|
@@ -1745,15 +1609,6 @@ async function generateViteApp(targetDir, appName, port, arch, css = 'tailwind',
|
|
|
1745
1609
|
await applyCssFrameworkVariant(uiPkgDir, css, { isMonorepo: true, plugins: [], arch, isUiPackage: true });
|
|
1746
1610
|
}
|
|
1747
1611
|
|
|
1748
|
-
// tauri 가 켜져 있으면 이 app 안에 src-tauri/ shell 을 떨어뜨린다 (v0.90.0+).
|
|
1749
|
-
// standalone 과 달리 monorepo 에서는 app 단위로 별도 dev port (default 3000) 가 있고,
|
|
1750
|
-
// tauri 의 devUrl 이 그 port 와 일치해야 한다. frontendDist 는 src-tauri 기준 `../dist`.
|
|
1751
|
-
if (tauri) {
|
|
1752
|
-
const devPort = Number(port) || 3000;
|
|
1753
|
-
await emitTauri(targetDir, appName, { devPort });
|
|
1754
|
-
await patchViteForTauri(targetDir, { port: devPort });
|
|
1755
|
-
}
|
|
1756
|
-
|
|
1757
1612
|
if (i18n === 'react-i18next') {
|
|
1758
1613
|
const localesArr = parseLocales(locales);
|
|
1759
1614
|
await emitI18n(targetDir, { arch, locales: localesArr });
|
|
@@ -2396,12 +2251,9 @@ async function finalizeProject(targetDir, { dryRun = false } = {}) {
|
|
|
2396
2251
|
* CLAUDE.md 의 `{{PLATFORM_APP_DESCRIPTION}}` 치환용 문장 — AI 에이전트에게 어떤 플랫폼인지
|
|
2397
2252
|
* 정확히 전달해서 잘못된 컨벤션 (예: vite 프로젝트에 App Router 가정) 적용 방지.
|
|
2398
2253
|
*/
|
|
2399
|
-
function describeAppPlatform(platform
|
|
2254
|
+
function describeAppPlatform(platform) {
|
|
2400
2255
|
if (platform === 'vite') {
|
|
2401
|
-
|
|
2402
|
-
? ' Tauri 데스크탑 셸이 동봉되어 있어 `src-tauri/` 가 native 진입점이다.'
|
|
2403
|
-
: '';
|
|
2404
|
-
return `Vite SPA (React + TypeScript). 라우트 + 비즈니스 로직. RSC / App Router 없음 — 모든 코드가 클라이언트 사이드 실행이다.${tauriSuffix}`;
|
|
2256
|
+
return `Vite SPA (React + TypeScript). 라우트 + 비즈니스 로직. RSC / App Router 없음 — 모든 코드가 클라이언트 사이드 실행이다.`;
|
|
2405
2257
|
}
|
|
2406
2258
|
// 디폴트: Next.js. 향후 platform 추가 시 분기 늘릴 것.
|
|
2407
2259
|
return 'Next.js 앱 (App Router + Server Components). 라우트 + 비즈니스 로직.';
|
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] [--observability <none|sentry>]
|
|
21
|
-
sh-ui create add-app [name] [--port <n>] [--platform <next|vite>] [--plugins ..] [--theme ..] [--css ..] [--
|
|
21
|
+
sh-ui create add-app [name] [--port <n>] [--platform <next|vite>] [--plugins ..] [--theme ..] [--css ..] [--i18n <react-i18next|none>] [--locales ko,en] [--observability <none|sentry>]
|
|
22
22
|
sh-ui create add-component <name> [--app <name>]
|
|
23
23
|
|
|
24
24
|
옵션:
|
|
@@ -77,7 +77,6 @@ export async function runCreate(rest) {
|
|
|
77
77
|
theme: flags.theme,
|
|
78
78
|
css: flags.css,
|
|
79
79
|
platform: flags.platform,
|
|
80
|
-
tauri: flags.tauri,
|
|
81
80
|
i18n: flags.i18n,
|
|
82
81
|
locales: flags.locales,
|
|
83
82
|
observability: flags.observability,
|
|
@@ -97,7 +96,6 @@ export async function runCreate(rest) {
|
|
|
97
96
|
arch: flags.arch,
|
|
98
97
|
theme: flags.theme,
|
|
99
98
|
css: flags.css,
|
|
100
|
-
tauri: flags.tauri,
|
|
101
99
|
i18n: flags.i18n,
|
|
102
100
|
locales: flags.locales,
|
|
103
101
|
observability: flags.observability,
|
|
@@ -314,18 +314,6 @@ export const TEMPLATE_MANIFEST = {
|
|
|
314
314
|
]
|
|
315
315
|
}
|
|
316
316
|
},
|
|
317
|
-
"tauri-shell": {
|
|
318
|
-
"base": [
|
|
319
|
-
".gitignore",
|
|
320
|
-
"Cargo.toml",
|
|
321
|
-
"README.md",
|
|
322
|
-
"build.rs",
|
|
323
|
-
"capabilities/default.json",
|
|
324
|
-
"src/lib.rs",
|
|
325
|
-
"src/main.rs",
|
|
326
|
-
"tauri.conf.json"
|
|
327
|
-
]
|
|
328
|
-
},
|
|
329
317
|
"ui-app-template": {
|
|
330
318
|
"base": [
|
|
331
319
|
"eslint.config.js",
|
package/src/mcp.mjs
CHANGED
|
@@ -432,7 +432,6 @@ export async function startMcpServer() {
|
|
|
432
432
|
{
|
|
433
433
|
description:
|
|
434
434
|
"빈 폴더에 sh-ui 프로젝트 스캐폴드 — Next.js (standalone/monorepo) | Vite (standalone/monorepo) | Flutter. " +
|
|
435
|
-
"+ vite 인 경우 `tauri: true` 로 Tauri 2.x 데스크탑 셸 (`src-tauri/`) 까지 한 번에 emit (Rust toolchain 필요). " +
|
|
436
435
|
`FSD 폴더 구조 + 토큰 + sh-ui.config.json 일괄 생성. 사용자가 '새 프로젝트' / '빈 폴더' / '스캐폴드부터' 류 요청을 하면 이 툴 사용 (Bash 로 npx ${cliName} create 직접 호출보다 우선). ` +
|
|
437
436
|
"**단일 진입점** — theme/plugins/cssFramework/structure 모두 호출 시점에 정해서 한 번에 박는다. 호출 후 sh-ui.config.json/tokens.css 를 손으로 패치하지 말 것 (다음 재스캐폴드 시 유실). " +
|
|
438
437
|
"산출물: theme 인자가 프리셋이면 sh-ui.config.json 의 theme.base 가 그 이름, base64 면 'custom'. paths.styles · paths.tokens 도 자동 박혀서 sh_ui_add_component 가 사후 패치 없이 동작.",
|
|
@@ -461,13 +460,6 @@ export async function startMcpServer() {
|
|
|
461
460
|
.describe("부모 디렉토리. 기본 process.cwd()"),
|
|
462
461
|
force: z.boolean().optional()
|
|
463
462
|
.describe("기존 디렉토리 덮어쓰기. 기본 false (안전)"),
|
|
464
|
-
tauri: z.boolean().optional()
|
|
465
|
-
.describe(
|
|
466
|
-
"Tauri 2.x 데스크탑 셸 (`src-tauri/`) 함께 emit. platform=vite (standalone/monorepo 둘 다) 일 때만 지원. " +
|
|
467
|
-
"monorepo 의 경우 src-tauri/ 는 apps/{appName}/ 안에 emit, devUrl 은 해당 app 의 dev port 와 자동 동기화. " +
|
|
468
|
-
"Rust toolchain (`cargo`/`rustc`) 가 시스템에 설치되어 있어야 첫 `pnpm tauri dev` 가 동작. " +
|
|
469
|
-
"기본 false.",
|
|
470
|
-
),
|
|
471
463
|
i18n: z.enum(I18N_LIBRARIES).optional()
|
|
472
464
|
.describe(
|
|
473
465
|
"i18n 라이브러리 — platform=vite 일 때만 의미. 'react-i18next' 로 설정 시 i18next + react-i18next + browser-languagedetector + http-backend 셋업 + " +
|
|
@@ -513,15 +505,6 @@ export async function startMcpServer() {
|
|
|
513
505
|
} catch (e) {
|
|
514
506
|
return { isError: true, content: [{ type: "text", text: e.message }] };
|
|
515
507
|
}
|
|
516
|
-
if (input.tauri && input.platform !== "vite") {
|
|
517
|
-
return {
|
|
518
|
-
isError: true,
|
|
519
|
-
content: [{
|
|
520
|
-
type: "text",
|
|
521
|
-
text: "tauri: true 는 platform=vite 일 때만 지원합니다 (현재 platform=" + input.platform + ").",
|
|
522
|
-
}],
|
|
523
|
-
};
|
|
524
|
-
}
|
|
525
508
|
if (input.i18n && input.i18n !== "none" && input.platform !== "vite") {
|
|
526
509
|
return {
|
|
527
510
|
isError: true,
|
|
@@ -565,7 +548,6 @@ export async function startMcpServer() {
|
|
|
565
548
|
arch: input.arch,
|
|
566
549
|
theme: input.theme,
|
|
567
550
|
css: input.cssFramework,
|
|
568
|
-
tauri: input.tauri,
|
|
569
551
|
i18n: input.i18n,
|
|
570
552
|
locales: input.locales,
|
|
571
553
|
observability: input.observability,
|
|
@@ -589,7 +571,7 @@ export async function startMcpServer() {
|
|
|
589
571
|
"사용자가 '앱 추가' / 'monorepo 에 새 앱' / 'add admin app' 류 요청을 하면 이 툴 사용 (Bash 로 npx " + cliName + " add-app 직접 호출보다 우선). " +
|
|
590
572
|
"v0.65+ 레이아웃 준수 — ui-{name} 은 tokens-only role, 컴포넌트는 sibling ui-core 가 SoT. " +
|
|
591
573
|
"theme/css 는 새 ui-app 에만 적용 (다른 앱 영향 없음). monorepo 가 아니면 (pnpm-workspace.yaml 없음) 에러. " +
|
|
592
|
-
"platform 미지정 시 기존 apps/* 스캔해 추론 (모든 앱이 같은 플랫폼이면 그것으로).
|
|
574
|
+
"platform 미지정 시 기존 apps/* 스캔해 추론 (모든 앱이 같은 플랫폼이면 그것으로).",
|
|
593
575
|
inputSchema: {
|
|
594
576
|
name: z.string().min(1)
|
|
595
577
|
.describe("앱 이름 — apps/{name}/ + packages/ui/ui-apps/ui-{name}/ 디렉토리명. 영숫자 + 하이픈."),
|
|
@@ -603,8 +585,6 @@ export async function startMcpServer() {
|
|
|
603
585
|
.describe("CSS 프레임워크. 기본 plain. 새 앱의 컴포넌트 변종 결정 — 같은 모노레포 내 다른 앱과 다른 값 가능."),
|
|
604
586
|
platform: z.enum(["next", "vite"]).optional()
|
|
605
587
|
.describe("플랫폼 — next | vite. 미지정 시 기존 apps/* 의 deps 로 추론 (모든 앱이 같은 플랫폼이면 그것으로, 혼재면 명시 필요)."),
|
|
606
|
-
tauri: z.boolean().optional()
|
|
607
|
-
.describe("Tauri 2.x 데스크탑 셸 — platform=vite 일 때만 의미. apps/{name}/src-tauri/ 에 emit. 기본 false."),
|
|
608
588
|
i18n: z.enum(I18N_LIBRARIES).optional()
|
|
609
589
|
.describe(
|
|
610
590
|
"i18n 라이브러리 — platform=vite 일 때만 의미. 'react-i18next' 로 설정 시 i18next + react-i18next + " +
|
|
@@ -629,15 +609,6 @@ export async function startMcpServer() {
|
|
|
629
609
|
} catch (e) {
|
|
630
610
|
return { isError: true, content: [{ type: "text", text: e.message }] };
|
|
631
611
|
}
|
|
632
|
-
if (input.tauri && input.platform === "next") {
|
|
633
|
-
return {
|
|
634
|
-
isError: true,
|
|
635
|
-
content: [{
|
|
636
|
-
type: "text",
|
|
637
|
-
text: "tauri 는 platform=vite 일 때만 지원합니다. --platform vite 사용 또는 tauri 옵션 제거.",
|
|
638
|
-
}],
|
|
639
|
-
};
|
|
640
|
-
}
|
|
641
612
|
if (input.i18n && input.i18n !== "none" && input.platform && input.platform !== "vite") {
|
|
642
613
|
return {
|
|
643
614
|
isError: true,
|
|
@@ -664,7 +635,6 @@ export async function startMcpServer() {
|
|
|
664
635
|
theme: input.theme,
|
|
665
636
|
css: input.cssFramework,
|
|
666
637
|
platform: input.platform,
|
|
667
|
-
tauri: input.tauri,
|
|
668
638
|
i18n: input.i18n,
|
|
669
639
|
locales: input.locales,
|
|
670
640
|
observability: input.observability,
|
|
@@ -1017,8 +987,6 @@ export async function startMcpServer() {
|
|
|
1017
987
|
.describe("monorepo 첫 앱 이름. 기본 web"),
|
|
1018
988
|
// vite 전용 — sh_ui_create_project 의 동일 옵션과 1:1 대응. describe ↔ create 가 같은
|
|
1019
989
|
// file-plan 을 보장하려면 여기서 받아 describeTemplate 에 전달해야 한다 (v0.95.0+).
|
|
1020
|
-
tauri: z.boolean().optional()
|
|
1021
|
-
.describe("Tauri 2.x 데스크탑 셸 emit (platform=vite 전용). 기본 false"),
|
|
1022
990
|
i18n: z.enum(I18N_LIBRARIES).optional()
|
|
1023
991
|
.describe(`i18n 라이브러리 (platform=vite 전용). 옵션: ${I18N_LIBRARIES.join(', ')}. 기본 none`),
|
|
1024
992
|
locales: z.string().optional()
|
|
@@ -1036,7 +1004,6 @@ export async function startMcpServer() {
|
|
|
1036
1004
|
plugins: input.plugins,
|
|
1037
1005
|
cssFramework: input.cssFramework,
|
|
1038
1006
|
appName: input.appName,
|
|
1039
|
-
tauri: input.tauri,
|
|
1040
1007
|
i18n: input.i18n,
|
|
1041
1008
|
locales: input.locales,
|
|
1042
1009
|
observability: input.observability,
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
|
6
|
-
<!-- viewport-fit=cover: iOS safe-area inset / Android edge-to-edge
|
|
6
|
+
<!-- viewport-fit=cover: iOS safe-area inset / Android edge-to-edge -->
|
|
7
7
|
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
|
|
8
|
-
<!-- theme-color: 모바일 브라우저 / PWA
|
|
8
|
+
<!-- theme-color: 모바일 브라우저 / PWA 의 status bar 톤. dark default 에 맞춰 어두운 톤. -->
|
|
9
9
|
<meta name="theme-color" content="#0A0A0A" media="(prefers-color-scheme: dark)" />
|
|
10
10
|
<meta name="theme-color" content="#FFFFFF" media="(prefers-color-scheme: light)" />
|
|
11
11
|
<title>sh-ui app</title>
|
|
@@ -34,8 +34,8 @@
|
|
|
34
34
|
--color-sidebar-bg: var(--sidebar-bg);
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
/* sh-ui:webview-base-start — 모바일 브라우저 /
|
|
38
|
-
*
|
|
37
|
+
/* sh-ui:webview-base-start — 모바일 브라우저 / PWA 공통 기본값.
|
|
38
|
+
* iOS/Android 친화. 사용자가 텍스트 셀렉트 필요한 곳은 input/textarea/
|
|
39
39
|
* contenteditable 에 명시적으로 user-select: text 로 복원. 토큰이 아니라 reset 성 규칙이라
|
|
40
40
|
* @theme inline 밖에 둠. */
|
|
41
41
|
@layer base {
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
|
6
|
-
<!-- viewport-fit=cover: iOS safe-area inset / Android edge-to-edge
|
|
6
|
+
<!-- viewport-fit=cover: iOS safe-area inset / Android edge-to-edge -->
|
|
7
7
|
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
|
|
8
|
-
<!-- theme-color: 모바일 브라우저 / PWA
|
|
8
|
+
<!-- theme-color: 모바일 브라우저 / PWA 의 status bar 톤. dark default 에 맞춰 어두운 톤. -->
|
|
9
9
|
<meta name="theme-color" content="#0A0A0A" media="(prefers-color-scheme: dark)" />
|
|
10
10
|
<meta name="theme-color" content="#FFFFFF" media="(prefers-color-scheme: light)" />
|
|
11
11
|
<title>sh-ui app</title>
|
|
@@ -34,8 +34,8 @@
|
|
|
34
34
|
--color-sidebar-bg: var(--sidebar-bg);
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
/* sh-ui:webview-base-start — 모바일 브라우저 /
|
|
38
|
-
*
|
|
37
|
+
/* sh-ui:webview-base-start — 모바일 브라우저 / PWA 공통 기본값.
|
|
38
|
+
* iOS/Android 친화. 사용자가 텍스트 셀렉트 필요한 곳은 input/textarea/
|
|
39
39
|
* contenteditable 에 명시적으로 user-select: text 로 복원. 토큰이 아니라 reset 성 규칙이라
|
|
40
40
|
* @theme inline 밖에 둠. */
|
|
41
41
|
@layer base {
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
[package]
|
|
2
|
-
name = "{{tauri_crate_name}}"
|
|
3
|
-
version = "0.1.0"
|
|
4
|
-
description = "{{project_name}} desktop shell"
|
|
5
|
-
authors = ["you"]
|
|
6
|
-
edition = "2021"
|
|
7
|
-
|
|
8
|
-
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
9
|
-
|
|
10
|
-
[lib]
|
|
11
|
-
name = "{{tauri_crate_name}}_lib"
|
|
12
|
-
crate-type = ["staticlib", "cdylib", "rlib"]
|
|
13
|
-
|
|
14
|
-
[build-dependencies]
|
|
15
|
-
tauri-build = { version = "2", features = [] }
|
|
16
|
-
|
|
17
|
-
[dependencies]
|
|
18
|
-
tauri = { version = "2", features = [] }
|
|
19
|
-
tauri-plugin-opener = "2"
|
|
20
|
-
serde = { version = "1", features = ["derive"] }
|
|
21
|
-
serde_json = "1"
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
# {{project_name}} — Tauri 데스크탑 셸
|
|
2
|
-
|
|
3
|
-
이 디렉토리는 Tauri 2.x 의 Rust 코드입니다. 부모 디렉토리의 vite SPA 가
|
|
4
|
-
프론트엔드, 이 디렉토리가 native 윈도우 셸.
|
|
5
|
-
|
|
6
|
-
## 첫 실행
|
|
7
|
-
|
|
8
|
-
```bash
|
|
9
|
-
# 부모 디렉토리에서 (vite 앱 루트)
|
|
10
|
-
pnpm install
|
|
11
|
-
pnpm tauri dev # Rust 처음 빌드는 5~10분 — 캐시되면 이후 5~10초
|
|
12
|
-
```
|
|
13
|
-
|
|
14
|
-
Rust toolchain (`cargo`, `rustc`) 이 시스템에 설치되어 있어야 합니다. 없으면
|
|
15
|
-
https://rustup.rs/ 참고.
|
|
16
|
-
|
|
17
|
-
## 프로덕션 빌드 전 체크리스트
|
|
18
|
-
|
|
19
|
-
1. **Bundle identifier** — `tauri.conf.json` 의 `identifier: "app.{{tauri_crate_name}}.dev"` 를
|
|
20
|
-
실제 도메인 기반 unique ID 로 교체 (예: `com.yourcompany.{{tauri_crate_name}}`).
|
|
21
|
-
동일 ID 로 publish 된 다른 앱과 충돌 시 OS install 이 깨질 수 있음.
|
|
22
|
-
2. **Icons** — `tauri.conf.json` 의 `bundle.icon: []` 가 비어 있습니다. 프로덕션 빌드 시:
|
|
23
|
-
- 1024x1024 PNG 준비 (square, 투명 배경 권장)
|
|
24
|
-
- 부모 디렉토리에서 `pnpm tauri icon path/to/source.png` 실행 — `src-tauri/icons/` 에
|
|
25
|
-
플랫폼별 variants 자동 emit + `bundle.icon` 자동 채워짐
|
|
26
|
-
3. **Capabilities** — `capabilities/default.json` 은 최소 권한. fs / dialog / shell 등
|
|
27
|
-
확장 API 가 필요하면 https://v2.tauri.app/security/ 참고.
|
|
28
|
-
4. **Window 옵션** — `tauri.conf.json` 의 `app.windows[0]` 에 `decorations`, `transparent`,
|
|
29
|
-
`alwaysOnTop` 등 추가 가능.
|
|
30
|
-
|
|
31
|
-
## Rust 코드 추가
|
|
32
|
-
|
|
33
|
-
`src/lib.rs` 의 `invoke_handler![]` 안에 새 command 등록:
|
|
34
|
-
|
|
35
|
-
```rust
|
|
36
|
-
#[tauri::command]
|
|
37
|
-
fn my_command(name: &str) -> String {
|
|
38
|
-
format!("Hello, {}!", name)
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// run() 안의 .invoke_handler 에:
|
|
42
|
-
.invoke_handler(tauri::generate_handler![my_command])
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
프론트엔드에서:
|
|
46
|
-
```ts
|
|
47
|
-
import { invoke } from '@tauri-apps/api/core';
|
|
48
|
-
const greeting = await invoke<string>('my_command', { name: 'World' });
|
|
49
|
-
```
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "../gen/schemas/desktop-schema.json",
|
|
3
|
-
"identifier": "default",
|
|
4
|
-
"description": "기본 capability — 핵심 Tauri API 만 허용. 추가 권한 (fs / dialog / shell 등) 은 https://v2.tauri.app/security/ 참고해서 명시적 추가.",
|
|
5
|
-
"windows": [
|
|
6
|
-
"main"
|
|
7
|
-
],
|
|
8
|
-
"permissions": [
|
|
9
|
-
"core:default",
|
|
10
|
-
"opener:default"
|
|
11
|
-
]
|
|
12
|
-
}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
|
2
|
-
pub fn run() {
|
|
3
|
-
tauri::Builder::default()
|
|
4
|
-
.plugin(tauri_plugin_opener::init())
|
|
5
|
-
.invoke_handler(tauri::generate_handler![])
|
|
6
|
-
.run(tauri::generate_context!())
|
|
7
|
-
.expect("error while running tauri application");
|
|
8
|
-
}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "https://schema.tauri.app/config/2",
|
|
3
|
-
"productName": "{{project_name}}",
|
|
4
|
-
"version": "0.1.0",
|
|
5
|
-
"identifier": "app.{{tauri_crate_name}}.dev",
|
|
6
|
-
"build": {
|
|
7
|
-
"beforeDevCommand": "pnpm dev",
|
|
8
|
-
"devUrl": "{{tauri_dev_url}}",
|
|
9
|
-
"beforeBuildCommand": "pnpm build",
|
|
10
|
-
"frontendDist": "../dist"
|
|
11
|
-
},
|
|
12
|
-
"app": {
|
|
13
|
-
"windows": [
|
|
14
|
-
{
|
|
15
|
-
"title": "{{project_name}}",
|
|
16
|
-
"width": 1200,
|
|
17
|
-
"height": 800
|
|
18
|
-
}
|
|
19
|
-
],
|
|
20
|
-
"security": {
|
|
21
|
-
"csp": null
|
|
22
|
-
}
|
|
23
|
-
},
|
|
24
|
-
"bundle": {
|
|
25
|
-
"active": true,
|
|
26
|
-
"targets": "all",
|
|
27
|
-
"icon": []
|
|
28
|
-
}
|
|
29
|
-
}
|