sh-ui-cli 0.79.0 → 0.80.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,6 +2,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.80.1",
7
+ "date": "2026-05-12",
8
+ "title": "monorepo create_project P0 fix — paths.styles · sidebar 토큰 · sidebar import",
9
+ "type": "patch",
10
+ "highlights": [
11
+ "**A1/A2: `create_project --structure monorepo` ui-core 가 styles 경로 누락하던 결함 수정** — `sh-ui.config.json` 에 `paths.styles: \"src/styles\"`, `package.json` `exports` 에 `\"./styles/*\": \"./src/styles/*\"` 추가. 이전엔 직후 `add base focus-ring …` 호출이 `paths.styles 가 sh-ui.config.json에 없습니다` 로 즉시 실패.",
12
+ "**A3: 신규 프로젝트 tokens.css 에 sidebar/success/warning/info 토큰 emit** — sidebar 컴포넌트가 요구하는 `--sidebar-bg / --sidebar-fg / --sidebar-border / --sidebar-accent / --sidebar-accent-fg` 가 신규 프로젝트 tokens.css 에 빠져 sidebar 가 `@theme inline` 매핑에서 undefined 로 풀리던 결함 수정. presets(neutral · slate · rose · emerald · violet) light/dark 양쪽에 5+6 토큰 추가, `OPTIONAL_TOKEN_KEYS` 에 sidebar 키 등록. 베이스라인 3개 tokens.css 템플릿도 동기화.",
13
+ "**B1: registry/docs sidebar `.tsx` 확장자 import 제거** — `import { Popover } from \"../popover/index.tsx\"` 가 TS5097 (allowImportingTsExtensions) 로 docs typecheck 실패하던 결함 수정. 듀얼 카피본 양쪽 수정."
14
+ ],
15
+ "url": "https://github.com/sanghyeonKim0201/sh-ui/releases/tag/v0.80.1"
16
+ },
17
+ {
18
+ "version": "0.80.0",
19
+ "date": "2026-05-12",
20
+ "title": "describeTemplate — 프로젝트 생성 전 파일 트리 미리보기",
21
+ "type": "minor",
22
+ "highlights": [
23
+ "**`sh-ui-cli/api` 에 `describeTemplate()` 추가** — 옵션 조합(platform/structure/arch/plugins/cssFramework/appName) 으로 실제 생성 없이 emit 될 파일 경로 트리를 사전 계산. 베이스 템플릿 + arch 오버레이 + `plugin.files` + cssFramework 분기 + `plugin.transforms` (이동/삭제) 까지 정확히 반영. fs 접근 없는 순수 함수라 브라우저 번들 가능.",
24
+ "**`/create` 페이지 헤더에 파일 미리보기 별도 다이얼로그** — `FolderSearch` 아이콘 + 별도 진입. 옵션 picker(`ProjectOptionsForm` 으로 추출) + 요약/상세 토글 + 출처별 lucide 아이콘 분류(base=Package, arch=Layers, sentry=ShieldAlert, intl=Languages, …) + hover/진행막대 시각화. 상세 트리는 폴더/파일 lucide 아이콘 + 컴팩트 indent.",
25
+ "**상세 트리에서 파일 클릭 → 내용 viewer** — `/api/template-content` route 가 lazy fetch. 베이스/arch 오버레이 디스크 파일은 readFile, plugin.files 는 plugin 모듈 호출 결과, cssFramework 동적 변종은 placeholder. 출처 라벨 표시.",
26
+ "**MCP `sh_ui_describe_template` 툴 노출** — \"플러그인 켜면 어떤 파일 추가돼?\" 류 질문에 IDE-내 에이전트가 실제 스캐폴드 없이 답할 수 있게.",
27
+ "**docs UI raw 컴포넌트 일소** — `/create` 헤더 / TokenEditor / ShowcasePicker / ShowcaseCanvas / ExportBlock / ShadowBuilder / GradientBuilder / 검색 다이얼로그 / examples 갤러리의 raw `<button>`·`<input>` 을 sh-ui `Button`/`Input` 으로 통일. 풀 커스텀 className 영역도 `Button + className` 조합으로 base 동작(hover/focus/active) 받으면서 디자인 유지.",
28
+ "**빌드 인프라** — `scripts/build-template-manifest.mjs` 가 `packages/cli/templates/` 를 스캔해 `src/create/templateManifest.js` 로 emit (pretest / prepublishOnly 에서 자동 실행). 템플릿 변경 시 drift 없음."
29
+ ],
30
+ "url": "https://github.com/sanghyeonKim0201/sh-ui/releases/tag/v0.80.0"
31
+ },
5
32
  {
6
33
  "version": "0.79.0",
7
34
  "date": "2026-05-12",
@@ -3,7 +3,7 @@
3
3
  import * as React from "react";
4
4
  import { cn } from "@SH_UI_UTILS@";
5
5
  import { ChevronRightIcon, PanelLeftIcon } from "lucide-react";
6
- import { Popover, PopoverContent, PopoverTrigger } from "../popover/index.tsx";
6
+ import { Popover, PopoverContent, PopoverTrigger } from "../popover";
7
7
  import "./styles.css";
8
8
 
9
9
  const SIDEBAR_COOKIE_NAME = "sidebar_state";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sh-ui-cli",
3
- "version": "0.79.0",
3
+ "version": "0.80.1",
4
4
  "description": "sh-ui CLI — 프로젝트 스캐폴드(create) + 컴포넌트 추가(add/list/remove) + IDE-내 AI용 MCP 서버",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -44,13 +44,15 @@
44
44
  "./api": {
45
45
  "types": "./src/api.d.ts",
46
46
  "default": "./src/api.js"
47
- }
47
+ },
48
+ "./package.json": "./package.json"
48
49
  },
49
50
  "scripts": {
50
51
  "bundle-data": "node scripts/copy-data.mjs",
51
- "pretest": "node scripts/copy-data.mjs",
52
+ "build:manifest": "node scripts/build-template-manifest.mjs",
53
+ "pretest": "node scripts/build-template-manifest.mjs && node scripts/copy-data.mjs",
52
54
  "test": "vitest run",
53
- "prepublishOnly": "node scripts/copy-data.mjs && node --check bin/sh-ui.mjs"
55
+ "prepublishOnly": "node scripts/build-template-manifest.mjs && node scripts/copy-data.mjs && node --check bin/sh-ui.mjs"
54
56
  },
55
57
  "files": [
56
58
  "bin",
package/src/api.d.ts CHANGED
@@ -120,3 +120,51 @@ export interface ThemePreset {
120
120
 
121
121
  export const THEME_PRESETS: Record<ThemePresetName, ThemePreset>;
122
122
  export const THEME_PRESET_NAMES: readonly ThemePresetName[];
123
+
124
+ /* ─────── 템플릿 트리 미리보기 ─────── */
125
+
126
+ /**
127
+ * 옵션 조합으로 프로젝트 생성 시 어떤 파일이 emit 되는지 사전 계산.
128
+ * fs 접근 없는 순수 함수 — 브라우저 사용 가능.
129
+ */
130
+ export interface DescribeTemplateOptions {
131
+ platform?: CreatePlatform;
132
+ /** next 일 때만 의미. */
133
+ structure?: CreateStructure;
134
+ /** next 일 때 'fsd' | 'flat' | 'mes'. */
135
+ arch?: string;
136
+ /** 플러그인 name 배열. */
137
+ plugins?: string[];
138
+ cssFramework?: CssFrameworkSupported;
139
+ projectName?: string;
140
+ /** monorepo 첫 앱 이름. 기본 'web'. */
141
+ appName?: string;
142
+ }
143
+
144
+ export interface DescribeTemplateGroup {
145
+ /** 'base' | 'arch' | `plugin-${name}` | 'css' | 'transform' | 'monorepo' | 'ui-app' | `app-${id}` */
146
+ id: string;
147
+ label: string;
148
+ paths: string[];
149
+ }
150
+
151
+ export interface DescribeTemplateResult {
152
+ /** 모든 파일 경로 (POSIX, 정렬). */
153
+ files: string[];
154
+ /** 출처별 분류 — 빈 그룹은 제외. */
155
+ groups: DescribeTemplateGroup[];
156
+ }
157
+
158
+ export function describeTemplate(
159
+ options?: DescribeTemplateOptions,
160
+ ): DescribeTemplateResult;
161
+
162
+ /**
163
+ * 빌드 타임에 emit 되는 raw 템플릿 인덱스. describeTemplate() 가 내부적으로 사용.
164
+ * 외부 노출은 디버깅 / 마이그레이션 도구용.
165
+ */
166
+ export interface TemplateManifestEntry {
167
+ base: string[];
168
+ arches?: Record<string, string[]>;
169
+ }
170
+ export const TEMPLATE_MANIFEST: Record<string, TemplateManifestEntry>;
package/src/api.js CHANGED
@@ -31,3 +31,5 @@ export {
31
31
  isKnownArch,
32
32
  } from './create/architectures/index.js';
33
33
  export { THEME_PRESETS, THEME_PRESET_NAMES } from './create/theme/presets.js';
34
+ export { describeTemplate } from './create/describeTemplate.js';
35
+ export { TEMPLATE_MANIFEST } from './create/templateManifest.js';
@@ -0,0 +1,274 @@
1
+ /**
2
+ * describeTemplate — "옵션 조합으로 프로젝트 만들면 어떤 파일이 생기는가" 를
3
+ * 실제 생성 없이 계산. apps/docs 의 CreateProjectDialog 가 옵션 토글 시 트리
4
+ * 미리보기를 그리는 데 사용. MCP 툴 `sh_ui_describe_template` 도 동일 함수.
5
+ *
6
+ * 입력: createProject() 가 받는 옵션의 부분집합 + appName(monorepo).
7
+ * 출력: { files: string[], groups: Group[] }
8
+ * - files: 모든 파일 경로(POSIX) 정렬
9
+ * - groups: 출처별 분류 — 'base'/'arch'/'plugin-*'/'css'/'transform' 등
10
+ *
11
+ * 정확도 계약:
12
+ * - 베이스 템플릿 + arch 오버레이 + plugin.files + cssFramework 분기 + plugin.transforms
13
+ * (move/delete) 까지 반영. plugin.transforms 의 replace 는 내용만 바꾸므로 트리 영향 X.
14
+ * - generator.js 가 emit 하는 *파일 경로* 와 1:1 일치하는 것을 목표. content 차이는 무시.
15
+ *
16
+ * 순수성: fs 접근 없음. templateManifest.js (build-template-manifest.mjs 가 emit) 와
17
+ * plugins/architectures 디스크립터만 사용. 브라우저 번들 가능.
18
+ */
19
+
20
+ import { TEMPLATE_MANIFEST } from './templateManifest.js';
21
+ import {
22
+ getArchByName,
23
+ DEFAULT_ARCH,
24
+ isKnownArch,
25
+ } from './architectures/index.js';
26
+ import { getPluginsByNames } from './plugins/index.js';
27
+ import { CSS_FRAMEWORK_DEFAULT } from '../constants.js';
28
+
29
+ /**
30
+ * @typedef {Object} DescribeOptions
31
+ * @property {'next'|'flutter'} [platform]
32
+ * @property {'standalone'|'monorepo'} [structure] next 일 때만 의미
33
+ * @property {string} [arch] next 일 때 'fsd'|'flat'|'mes'
34
+ * @property {string[]} [plugins] ['sentry', 'next-intl', 'auth-jwt']
35
+ * @property {'tailwind'|'plain'|'css-modules'} [cssFramework]
36
+ * @property {string} [projectName]
37
+ * @property {string} [appName] monorepo 첫 앱 이름. 기본 'web'
38
+ */
39
+
40
+ /**
41
+ * @typedef {Object} Group
42
+ * @property {string} id 'base' | 'arch' | `plugin-${name}` | 'css' | 'transform' | 'monorepo' | 'ui-app' | `app-${id}`
43
+ * @property {string} label 사용자가 보는 한국어 라벨
44
+ * @property {string[]} paths 이 그룹에 귀속된 파일 경로 (정렬)
45
+ */
46
+
47
+ /**
48
+ * @typedef {Object} DescribeResult
49
+ * @property {string[]} files 전체 파일 경로 정렬
50
+ * @property {Group[]} groups
51
+ */
52
+
53
+ /**
54
+ * @param {DescribeOptions} [opts]
55
+ * @returns {DescribeResult}
56
+ */
57
+ export function describeTemplate(opts = {}) {
58
+ const {
59
+ platform = 'next',
60
+ structure = 'standalone',
61
+ arch: archName = DEFAULT_ARCH,
62
+ plugins: pluginNames = [],
63
+ cssFramework = CSS_FRAMEWORK_DEFAULT,
64
+ appName: rawAppName = 'web',
65
+ } = opts;
66
+
67
+ if (platform === 'flutter') {
68
+ const base = TEMPLATE_MANIFEST['flutter-standalone'].base;
69
+ return finalize([
70
+ makeGroup('base', 'Flutter 베이스', base),
71
+ ]);
72
+ }
73
+
74
+ // platform === 'next'
75
+ const safeArchName = isKnownArch(archName) ? archName : DEFAULT_ARCH;
76
+ const archObj = getArchByName(safeArchName);
77
+ const plugins = getPluginsByNames(pluginNames).sort(
78
+ (a, b) => (a.priority ?? 0) - (b.priority ?? 0),
79
+ );
80
+
81
+ if (structure === 'standalone') {
82
+ const groups = buildNextGroups({
83
+ prefix: '',
84
+ templateKey: 'nextjs-standalone',
85
+ arch: archObj,
86
+ plugins,
87
+ cssFramework,
88
+ });
89
+ return finalize(groups);
90
+ }
91
+
92
+ // monorepo
93
+ const appName = rawAppName || 'web';
94
+ const groups = [];
95
+
96
+ groups.push(makeGroup(
97
+ 'monorepo',
98
+ '모노레포 루트',
99
+ TEMPLATE_MANIFEST['monorepo'].base.slice(),
100
+ ));
101
+
102
+ const appGroups = buildNextGroups({
103
+ prefix: `apps/${appName}/`,
104
+ templateKey: 'nextjs-app',
105
+ arch: archObj,
106
+ plugins,
107
+ cssFramework,
108
+ });
109
+ for (const g of appGroups) {
110
+ g.id = `app-${g.id}`;
111
+ g.label = `apps/${appName} — ${g.label}`;
112
+ groups.push(g);
113
+ }
114
+
115
+ groups.push(makeGroup(
116
+ 'ui-app',
117
+ `packages/ui/ui-apps/ui-${appName}`,
118
+ TEMPLATE_MANIFEST['ui-app-template'].base.map(
119
+ (p) => `packages/ui/ui-apps/ui-${appName}/${p}`,
120
+ ),
121
+ ));
122
+
123
+ return finalize(groups);
124
+ }
125
+
126
+ /** next 베이스 + arch + plugin.files + css 분기 + plugin.transforms 적용. */
127
+ function buildNextGroups({ prefix, templateKey, arch, plugins, cssFramework }) {
128
+ const tpl = TEMPLATE_MANIFEST[templateKey];
129
+ if (!tpl) {
130
+ throw new Error(`describeTemplate: 알 수 없는 템플릿 키 '${templateKey}'`);
131
+ }
132
+
133
+ const groups = [];
134
+
135
+ groups.push(makeGroup(
136
+ 'base',
137
+ `${templateKey} 베이스`,
138
+ tpl.base.map((p) => prefix + p),
139
+ ));
140
+
141
+ const archFiles = tpl.arches?.[arch.name] ?? [];
142
+ groups.push(makeGroup(
143
+ 'arch',
144
+ `${arch.label} 오버레이`,
145
+ archFiles.map((p) => prefix + p),
146
+ ));
147
+
148
+ // plugin.files — arch-aware. 정적이면 그대로, 함수면 호출.
149
+ for (const plugin of plugins) {
150
+ const filesField = resolveArchField(plugin.files, arch);
151
+ if (!filesField) continue;
152
+ const paths = Object.keys(filesField).map((p) => prefix + p);
153
+ if (paths.length === 0) continue;
154
+ groups.push(makeGroup(
155
+ `plugin-${plugin.name}`,
156
+ `+ ${plugin.label}`,
157
+ paths,
158
+ ));
159
+ }
160
+
161
+ // cssFramework: css-modules 면 page.module.css / error.module.css 추가.
162
+ // generator.js applyCssFrameworkVariant 와 동일한 분기 — intl 활성 시 [locale]/, 아니면 app/.
163
+ if (cssFramework === 'css-modules') {
164
+ const intlActive = plugins.some((p) => p.name === 'next-intl');
165
+ const sentryActive = plugins.some((p) => p.name === 'sentry');
166
+ const cssPaths = [];
167
+ const pageDir = intlActive ? 'app/[locale]' : 'app';
168
+ cssPaths.push(`${prefix}${pageDir}/page.module.css`);
169
+ if (sentryActive) {
170
+ cssPaths.push(`${prefix}${pageDir}/error.module.css`);
171
+ }
172
+ groups.push(makeGroup(
173
+ 'css',
174
+ 'CSS: css-modules 변종',
175
+ cssPaths,
176
+ ));
177
+ }
178
+
179
+ // plugin.transforms — move/delete 만 트리에 영향. replace 는 content-only.
180
+ const moves = [];
181
+ const deletes = [];
182
+ for (const plugin of plugins) {
183
+ const tr = resolveArchField(plugin.transforms, arch);
184
+ if (!tr) continue;
185
+ for (const t of tr) {
186
+ if (t.type === 'move') moves.push({ from: prefix + t.from, to: prefix + t.to });
187
+ if (t.type === 'delete') deletes.push(prefix + t.path);
188
+ }
189
+ }
190
+
191
+ if (moves.length || deletes.length) {
192
+ const added = applyMovesAndDeletes(groups, moves, deletes);
193
+ if (added.length) {
194
+ groups.push(makeGroup(
195
+ 'transform',
196
+ '플러그인 transform (이동/생성)',
197
+ added,
198
+ ));
199
+ }
200
+ }
201
+
202
+ return groups;
203
+ }
204
+
205
+ /** arch-aware 필드 resolve. 함수면 (arch) => value, 정적이면 그대로. */
206
+ function resolveArchField(field, arch) {
207
+ if (field == null) return null;
208
+ return typeof field === 'function' ? field(arch) : field;
209
+ }
210
+
211
+ /**
212
+ * 그룹 배열에서 moves/deletes 적용:
213
+ * - move from→to: 모든 그룹에서 from 제거, to 는 transform 그룹용으로 반환.
214
+ * - delete path: 모든 그룹에서 path 제거.
215
+ *
216
+ * @returns {string[]} transform 그룹에 추가될 새 경로
217
+ */
218
+ function applyMovesAndDeletes(groups, moves, deletes) {
219
+ const added = [];
220
+ for (const { from, to } of moves) {
221
+ const removed = removeFromAllGroups(groups, from);
222
+ // 원본이 어디에도 없었다면 (예: sentry 비활성 상태에서 error.tsx 이동) — 새로
223
+ // 추가하지 않는다. generator.js 의 `if (await fs.pathExists(fromPath))` 시맨틱.
224
+ if (removed) added.push(to);
225
+ }
226
+ for (const p of deletes) {
227
+ removeFromAllGroups(groups, p);
228
+ }
229
+ return added;
230
+ }
231
+
232
+ function removeFromAllGroups(groups, path) {
233
+ let removed = false;
234
+ for (const g of groups) {
235
+ const idx = g.paths.indexOf(path);
236
+ if (idx !== -1) {
237
+ g.paths.splice(idx, 1);
238
+ removed = true;
239
+ }
240
+ }
241
+ return removed;
242
+ }
243
+
244
+ function makeGroup(id, label, paths) {
245
+ return { id, label, paths: paths.slice() };
246
+ }
247
+
248
+ /**
249
+ * 후처리: 그룹 간 dedupe (같은 path 가 여러 그룹에 있으면 마지막 그룹이 소유),
250
+ * 그룹 안에서 path 정렬, 빈 그룹 제거, 전체 파일 목록 계산.
251
+ */
252
+ function finalize(groups) {
253
+ const seen = new Set();
254
+ // 뒤에서 앞으로 — 후순위 그룹(plugins/css/transform) 이 dedup 우선권.
255
+ for (let i = groups.length - 1; i >= 0; i--) {
256
+ const kept = [];
257
+ for (const p of groups[i].paths) {
258
+ if (!seen.has(p)) {
259
+ seen.add(p);
260
+ kept.push(p);
261
+ }
262
+ }
263
+ groups[i] = {
264
+ id: groups[i].id,
265
+ label: groups[i].label,
266
+ paths: kept.sort(),
267
+ };
268
+ }
269
+ const cleaned = groups.filter((g) => g.paths.length > 0);
270
+ const files = [];
271
+ for (const g of cleaned) files.push(...g.paths);
272
+ files.sort();
273
+ return { files, groups: cleaned };
274
+ }
@@ -0,0 +1,328 @@
1
+ // AUTO-GENERATED by scripts/build-template-manifest.mjs — do not edit by hand.
2
+ // 템플릿 디렉토리(`packages/cli/templates/`) 의 파일 경로 인덱스.
3
+ // describeTemplate() 의 입력. 템플릿 변경 후에는 빌드 스크립트 재실행 필요.
4
+
5
+ export const TEMPLATE_MANIFEST = {
6
+ "flutter-standalone": {
7
+ "base": [
8
+ "README.md",
9
+ "analysis_options.yaml",
10
+ "gitignore",
11
+ "lib/main.dart",
12
+ "lib/sh_ui/foundation/sh_ui_tokens.dart",
13
+ "pubspec.yaml",
14
+ "sh-ui.config.json"
15
+ ]
16
+ },
17
+ "monorepo": {
18
+ "base": [
19
+ ".dockerignore",
20
+ ".npmrc",
21
+ ".prettierrc",
22
+ "CLAUDE.md",
23
+ "README.md",
24
+ "gitignore",
25
+ "package.json",
26
+ "packages/eslint-config/base.js",
27
+ "packages/eslint-config/flat.js",
28
+ "packages/eslint-config/fsd.js",
29
+ "packages/eslint-config/mes.js",
30
+ "packages/eslint-config/next.js",
31
+ "packages/eslint-config/package.json",
32
+ "packages/eslint-config/react-internal.js",
33
+ "packages/typescript-config/base.json",
34
+ "packages/typescript-config/nextjs.json",
35
+ "packages/typescript-config/package.json",
36
+ "packages/typescript-config/react-library.json",
37
+ "packages/ui/ui-apps/.gitkeep",
38
+ "packages/ui/ui-core/eslint.config.js",
39
+ "packages/ui/ui-core/package.json",
40
+ "packages/ui/ui-core/sh-ui.config.json",
41
+ "packages/ui/ui-core/src/components/.gitkeep",
42
+ "packages/ui/ui-core/src/hooks/.gitkeep",
43
+ "packages/ui/ui-core/src/lib/utils.ts",
44
+ "packages/ui/ui-core/src/styles/.gitkeep",
45
+ "packages/ui/ui-core/tsconfig.json",
46
+ "pnpm-workspace.yaml",
47
+ "tsconfig.json",
48
+ "turbo.json"
49
+ ]
50
+ },
51
+ "nextjs-app": {
52
+ "base": [
53
+ ".env.example",
54
+ "README.md",
55
+ "app/page.tsx",
56
+ "eslint.config.js",
57
+ "next.config.ts",
58
+ "package.json",
59
+ "postcss.config.mjs",
60
+ "vitest.config.ts",
61
+ "vitest.setup.ts"
62
+ ],
63
+ "arches": {
64
+ "flat": [
65
+ "app/api/proxy/[...path]/route.ts",
66
+ "app/layout.tsx",
67
+ "components/common/.gitkeep",
68
+ "components/common/FallbackBoundary/index.tsx",
69
+ "components/common/PrefetchBoundary/index.tsx",
70
+ "components/layouts/RootLayout.tsx",
71
+ "components/providers/GlobalProvider/index.tsx",
72
+ "components/providers/index.tsx",
73
+ "components/providers/tanstack/QueryClientProvider.tsx",
74
+ "components/providers/tanstack/TanstackDevtoolsProvider.tsx",
75
+ "components/providers/theme/ThemeProvider.tsx",
76
+ "eslint.config.js",
77
+ "lib/api/.gitkeep",
78
+ "lib/api/apiTypes.ts",
79
+ "lib/api/clientFetch.ts",
80
+ "lib/api/error.ts",
81
+ "lib/api/errorMessages.ts",
82
+ "lib/api/http.ts",
83
+ "lib/api/observability.ts",
84
+ "lib/api/queryClient.ts",
85
+ "lib/api/serverFetch.ts",
86
+ "lib/config/.gitkeep",
87
+ "lib/hooks/.gitkeep",
88
+ "lib/hooks/useAppMutation.ts",
89
+ "lib/test/createTestQueryClient.ts",
90
+ "lib/test/index.ts",
91
+ "lib/test/renderWithProviders.tsx",
92
+ "lib/utils/.gitkeep",
93
+ "lib/utils/formatDate.ts",
94
+ "lib/utils/formatPrice.ts",
95
+ "lib/utils/getQueryClient.ts",
96
+ "tsconfig.json"
97
+ ],
98
+ "fsd": [
99
+ "app/api/proxy/[...path]/route.ts",
100
+ "app/layout.tsx",
101
+ "src/app/layouts/RootLayout.tsx",
102
+ "src/app/providers/GlobalProvider/index.tsx",
103
+ "src/app/providers/index.tsx",
104
+ "src/app/providers/tanstack/QueryClientProvider.tsx",
105
+ "src/app/providers/tanstack/TanstackDevtoolsProvider.tsx",
106
+ "src/app/providers/theme/ThemeProvider.tsx",
107
+ "src/entities/.gitkeep",
108
+ "src/features/.gitkeep",
109
+ "src/shared/api/.gitkeep",
110
+ "src/shared/api/apiTypes.ts",
111
+ "src/shared/api/clientFetch.ts",
112
+ "src/shared/api/error.ts",
113
+ "src/shared/api/errorMessages.ts",
114
+ "src/shared/api/http.ts",
115
+ "src/shared/api/observability.ts",
116
+ "src/shared/api/queryClient.ts",
117
+ "src/shared/api/serverFetch.ts",
118
+ "src/shared/config/.gitkeep",
119
+ "src/shared/hooks/.gitkeep",
120
+ "src/shared/hooks/useAppMutation.ts",
121
+ "src/shared/lib/.gitkeep",
122
+ "src/shared/lib/formatDate.ts",
123
+ "src/shared/lib/formatPrice.ts",
124
+ "src/shared/lib/getQueryClient.ts",
125
+ "src/shared/model/.gitkeep",
126
+ "src/shared/test/createTestQueryClient.ts",
127
+ "src/shared/test/index.ts",
128
+ "src/shared/test/renderWithProviders.tsx",
129
+ "src/shared/ui/.gitkeep",
130
+ "src/shared/ui/FallbackBoundary/index.tsx",
131
+ "src/shared/ui/PrefetchBoundary/index.tsx",
132
+ "src/views/.gitkeep",
133
+ "src/widgets/.gitkeep",
134
+ "tsconfig.json"
135
+ ],
136
+ "mes": [
137
+ "app/api/proxy/[...path]/route.ts",
138
+ "app/layout.tsx",
139
+ "app/sign-in/page.tsx",
140
+ "eslint.config.js",
141
+ "src/components/common/.gitkeep",
142
+ "src/components/common/FallbackBoundary/index.tsx",
143
+ "src/components/common/PrefetchBoundary/index.tsx",
144
+ "src/components/layouts/RootLayout.tsx",
145
+ "src/components/providers/GlobalProvider/index.tsx",
146
+ "src/components/providers/index.tsx",
147
+ "src/components/providers/tanstack/QueryClientProvider.tsx",
148
+ "src/components/providers/tanstack/TanstackDevtoolsProvider.tsx",
149
+ "src/components/providers/theme/ThemeProvider.tsx",
150
+ "src/hooks/.gitkeep",
151
+ "src/hooks/useAppMutation.ts",
152
+ "src/lib/api/.gitkeep",
153
+ "src/lib/api/apiTypes.ts",
154
+ "src/lib/api/clientFetch.ts",
155
+ "src/lib/api/error.ts",
156
+ "src/lib/api/errorMessages.ts",
157
+ "src/lib/api/http.ts",
158
+ "src/lib/api/observability.ts",
159
+ "src/lib/api/queryClient.ts",
160
+ "src/lib/api/serverFetch.ts",
161
+ "src/lib/config/.gitkeep",
162
+ "src/lib/test/createTestQueryClient.ts",
163
+ "src/lib/test/index.ts",
164
+ "src/lib/test/renderWithProviders.tsx",
165
+ "src/lib/utils/.gitkeep",
166
+ "src/lib/utils/formatDate.ts",
167
+ "src/lib/utils/formatPrice.ts",
168
+ "src/lib/utils/getQueryClient.ts",
169
+ "src/pages/sign-in/api.ts",
170
+ "src/pages/sign-in/components/.gitkeep",
171
+ "src/pages/sign-in/hooks.ts",
172
+ "src/pages/sign-in/index.tsx",
173
+ "src/pages/sign-in/schema.ts",
174
+ "tsconfig.json"
175
+ ]
176
+ }
177
+ },
178
+ "nextjs-standalone": {
179
+ "base": [
180
+ ".env.example",
181
+ ".prettierrc",
182
+ "CLAUDE.md",
183
+ "README.md",
184
+ "app/globals.css",
185
+ "app/page.tsx",
186
+ "eslint.config.js",
187
+ "gitignore",
188
+ "next.config.ts",
189
+ "package.json",
190
+ "postcss.config.mjs",
191
+ "vitest.config.ts",
192
+ "vitest.setup.ts"
193
+ ],
194
+ "arches": {
195
+ "flat": [
196
+ "app/api/proxy/[...path]/route.ts",
197
+ "app/globals.css",
198
+ "app/layout.tsx",
199
+ "components/common/.gitkeep",
200
+ "components/common/FallbackBoundary/index.tsx",
201
+ "components/common/PrefetchBoundary/index.tsx",
202
+ "components/layouts/RootLayout.tsx",
203
+ "components/providers/GlobalProvider/index.tsx",
204
+ "components/providers/index.tsx",
205
+ "components/providers/tanstack/QueryClientProvider.tsx",
206
+ "components/providers/tanstack/TanstackDevtoolsProvider.tsx",
207
+ "components/providers/theme/ThemeProvider.tsx",
208
+ "eslint.config.js",
209
+ "lib/api/.gitkeep",
210
+ "lib/api/apiTypes.ts",
211
+ "lib/api/clientFetch.ts",
212
+ "lib/api/error.ts",
213
+ "lib/api/errorMessages.ts",
214
+ "lib/api/http.ts",
215
+ "lib/api/observability.ts",
216
+ "lib/api/queryClient.ts",
217
+ "lib/api/serverFetch.ts",
218
+ "lib/config/.gitkeep",
219
+ "lib/hooks/.gitkeep",
220
+ "lib/hooks/useAppMutation.ts",
221
+ "lib/styles/tokens.css",
222
+ "lib/test/createTestQueryClient.ts",
223
+ "lib/test/index.ts",
224
+ "lib/test/renderWithProviders.tsx",
225
+ "lib/utils/formatDate.ts",
226
+ "lib/utils/formatPrice.ts",
227
+ "lib/utils/getQueryClient.ts",
228
+ "lib/utils/utils.ts",
229
+ "sh-ui.config.json",
230
+ "tsconfig.json"
231
+ ],
232
+ "fsd": [
233
+ "app/api/proxy/[...path]/route.ts",
234
+ "app/layout.tsx",
235
+ "sh-ui.config.json",
236
+ "src/app/layouts/RootLayout.tsx",
237
+ "src/app/providers/GlobalProvider/index.tsx",
238
+ "src/app/providers/index.tsx",
239
+ "src/app/providers/tanstack/QueryClientProvider.tsx",
240
+ "src/app/providers/tanstack/TanstackDevtoolsProvider.tsx",
241
+ "src/app/providers/theme/ThemeProvider.tsx",
242
+ "src/entities/.gitkeep",
243
+ "src/features/.gitkeep",
244
+ "src/shared/api/.gitkeep",
245
+ "src/shared/api/apiTypes.ts",
246
+ "src/shared/api/clientFetch.ts",
247
+ "src/shared/api/error.ts",
248
+ "src/shared/api/errorMessages.ts",
249
+ "src/shared/api/http.ts",
250
+ "src/shared/api/observability.ts",
251
+ "src/shared/api/queryClient.ts",
252
+ "src/shared/api/serverFetch.ts",
253
+ "src/shared/config/.gitkeep",
254
+ "src/shared/hooks/.gitkeep",
255
+ "src/shared/hooks/useAppMutation.ts",
256
+ "src/shared/lib/formatDate.ts",
257
+ "src/shared/lib/formatPrice.ts",
258
+ "src/shared/lib/getQueryClient.ts",
259
+ "src/shared/lib/utils.ts",
260
+ "src/shared/model/.gitkeep",
261
+ "src/shared/styles/tokens.css",
262
+ "src/shared/test/createTestQueryClient.ts",
263
+ "src/shared/test/index.ts",
264
+ "src/shared/test/renderWithProviders.tsx",
265
+ "src/shared/ui/.gitkeep",
266
+ "src/shared/ui/FallbackBoundary/index.tsx",
267
+ "src/shared/ui/PrefetchBoundary/index.tsx",
268
+ "src/views/.gitkeep",
269
+ "src/widgets/.gitkeep",
270
+ "tsconfig.json"
271
+ ],
272
+ "mes": [
273
+ "app/api/proxy/[...path]/route.ts",
274
+ "app/globals.css",
275
+ "app/layout.tsx",
276
+ "app/sign-in/page.tsx",
277
+ "eslint.config.js",
278
+ "sh-ui.config.json",
279
+ "src/components/common/.gitkeep",
280
+ "src/components/common/FallbackBoundary/index.tsx",
281
+ "src/components/common/PrefetchBoundary/index.tsx",
282
+ "src/components/layouts/RootLayout.tsx",
283
+ "src/components/providers/GlobalProvider/index.tsx",
284
+ "src/components/providers/index.tsx",
285
+ "src/components/providers/tanstack/QueryClientProvider.tsx",
286
+ "src/components/providers/tanstack/TanstackDevtoolsProvider.tsx",
287
+ "src/components/providers/theme/ThemeProvider.tsx",
288
+ "src/hooks/.gitkeep",
289
+ "src/hooks/useAppMutation.ts",
290
+ "src/lib/api/.gitkeep",
291
+ "src/lib/api/apiTypes.ts",
292
+ "src/lib/api/clientFetch.ts",
293
+ "src/lib/api/error.ts",
294
+ "src/lib/api/errorMessages.ts",
295
+ "src/lib/api/http.ts",
296
+ "src/lib/api/observability.ts",
297
+ "src/lib/api/queryClient.ts",
298
+ "src/lib/api/serverFetch.ts",
299
+ "src/lib/config/.gitkeep",
300
+ "src/lib/styles/tokens.css",
301
+ "src/lib/test/createTestQueryClient.ts",
302
+ "src/lib/test/index.ts",
303
+ "src/lib/test/renderWithProviders.tsx",
304
+ "src/lib/utils/formatDate.ts",
305
+ "src/lib/utils/formatPrice.ts",
306
+ "src/lib/utils/getQueryClient.ts",
307
+ "src/lib/utils/utils.ts",
308
+ "src/pages/sign-in/api.ts",
309
+ "src/pages/sign-in/components/.gitkeep",
310
+ "src/pages/sign-in/hooks.ts",
311
+ "src/pages/sign-in/index.tsx",
312
+ "src/pages/sign-in/schema.ts",
313
+ "tsconfig.json"
314
+ ]
315
+ }
316
+ },
317
+ "ui-app-template": {
318
+ "base": [
319
+ "eslint.config.js",
320
+ "package.json",
321
+ "postcss.config.mjs",
322
+ "sh-ui.config.json",
323
+ "src/styles/globals.css",
324
+ "src/styles/tokens.css",
325
+ "tsconfig.json"
326
+ ]
327
+ }
328
+ };
@@ -19,6 +19,11 @@ const OPTIONAL_TOKEN_KEYS = [
19
19
  'info', 'info-foreground',
20
20
  'danger-hover',
21
21
  'ring',
22
+ // v0.80.1+ — sidebar cascade 색. 5개 모두 light/dark 양쪽에 정의되어야 emit.
23
+ // sidebar 컴포넌트의 styles.css 가 .sh-ui-sidebar-wrapper 에 var(--background-subtle) 등으로
24
+ // fallback 을 두지만, Tailwind @theme inline 의 --color-sidebar-* 가 :root 에서 해석되도록
25
+ // tokens.css 에도 끌어올린다.
26
+ 'sidebar-bg', 'sidebar-fg', 'sidebar-border', 'sidebar-accent', 'sidebar-accent-fg',
22
27
  ];
23
28
 
24
29
  /**
@@ -24,6 +24,17 @@ const NEUTRAL_LIGHT = {
24
24
  'danger-foreground': '#FFFFFF',
25
25
  'danger-hover': '#B91C1C',
26
26
  'ring': '#A3A3A3',
27
+ 'success': '#16A34A',
28
+ 'success-foreground': '#FFFFFF',
29
+ 'warning': '#D97706',
30
+ 'warning-foreground': '#FFFFFF',
31
+ 'info': '#2563EB',
32
+ 'info-foreground': '#FFFFFF',
33
+ 'sidebar-bg': '#FAFAFA',
34
+ 'sidebar-fg': '#0A0A0A',
35
+ 'sidebar-border': '#E5E5E5',
36
+ 'sidebar-accent': '#F5F5F5',
37
+ 'sidebar-accent-fg': '#0A0A0A',
27
38
  };
28
39
 
29
40
  const NEUTRAL_DARK = {
@@ -44,6 +55,17 @@ const NEUTRAL_DARK = {
44
55
  'danger-foreground': '#FFFFFF',
45
56
  'danger-hover': '#EF4444',
46
57
  'ring': '#737373',
58
+ 'success': '#22C55E',
59
+ 'success-foreground': '#052E16',
60
+ 'warning': '#F59E0B',
61
+ 'warning-foreground': '#451A03',
62
+ 'info': '#3B82F6',
63
+ 'info-foreground': '#172554',
64
+ 'sidebar-bg': '#171717',
65
+ 'sidebar-fg': '#FAFAFA',
66
+ 'sidebar-border': '#262626',
67
+ 'sidebar-accent': '#262626',
68
+ 'sidebar-accent-fg': '#FAFAFA',
47
69
  };
48
70
 
49
71
  export const THEME_PRESETS = {
@@ -73,6 +95,17 @@ export const THEME_PRESETS = {
73
95
  'danger-foreground': '#FFFFFF',
74
96
  'danger-hover': '#B91C1C',
75
97
  'ring': '#94A3B8',
98
+ 'success': '#16A34A',
99
+ 'success-foreground': '#FFFFFF',
100
+ 'warning': '#D97706',
101
+ 'warning-foreground': '#FFFFFF',
102
+ 'info': '#2563EB',
103
+ 'info-foreground': '#FFFFFF',
104
+ 'sidebar-bg': '#F8FAFC',
105
+ 'sidebar-fg': '#0F172A',
106
+ 'sidebar-border': '#E2E8F0',
107
+ 'sidebar-accent': '#F1F5F9',
108
+ 'sidebar-accent-fg': '#0F172A',
76
109
  },
77
110
  dark: {
78
111
  'background': '#0F172A',
@@ -92,6 +125,17 @@ export const THEME_PRESETS = {
92
125
  'danger-foreground': '#450A0A',
93
126
  'danger-hover': '#FCA5A5',
94
127
  'ring': '#64748B',
128
+ 'success': '#22C55E',
129
+ 'success-foreground': '#052E16',
130
+ 'warning': '#F59E0B',
131
+ 'warning-foreground': '#451A03',
132
+ 'info': '#60A5FA',
133
+ 'info-foreground': '#172554',
134
+ 'sidebar-bg': '#1E293B',
135
+ 'sidebar-fg': '#F1F5F9',
136
+ 'sidebar-border': '#334155',
137
+ 'sidebar-accent': '#334155',
138
+ 'sidebar-accent-fg': '#F1F5F9',
95
139
  },
96
140
  radius: 0.375,
97
141
  // 정보 밀도 ↑ — 본문 14px 부터, 컨트롤 36px (대시보드/관리자 인상)
package/src/mcp.mjs CHANGED
@@ -49,6 +49,7 @@ import {
49
49
  } from "./constants.js";
50
50
  import { allPlugins } from "./create/plugins/index.js";
51
51
  import { allArchitectures } from "./create/architectures/index.js";
52
+ import { describeTemplate } from "./create/describeTemplate.js";
52
53
  import { THEME_PRESET_NAMES } from "./create/theme/presets.js";
53
54
  import { decodeTheme } from "./create/theme/decode.js";
54
55
  import { encodeTheme } from "./create/theme/encode.js";
@@ -805,6 +806,51 @@ export async function startMcpServer() {
805
806
  },
806
807
  );
807
808
 
809
+ // 템플릿 트리 사전 미리보기 — 실제 생성 없이 옵션 조합으로 어떤 파일이 emit 되는지.
810
+ // apps/docs 의 CreateProjectDialog 와 같은 출력 (sh-ui-cli/api 의 describeTemplate).
811
+ server.registerTool(
812
+ "sh_ui_describe_template",
813
+ {
814
+ description:
815
+ "sh-ui create 호출 시 어떤 파일이 생기는지 사전 계산 — 실제 fs 변경 없음. " +
816
+ "사용자가 '미리 보고 싶다' / '어떤 파일 생기는지' / '플러그인 켜면 뭐가 추가돼?' 류 질문을 하면 이 툴 사용 (sh_ui_create_project 의 dry-run 대용). " +
817
+ "베이스 템플릿 + arch 오버레이 + plugin.files + cssFramework 분기 + plugin.transforms (이동/삭제) 까지 정확히 반영. " +
818
+ "반환: { files: 정렬된 전체 경로, groups: 출처별 분류 (base/arch/plugin-*/css/transform) }.",
819
+ inputSchema: {
820
+ platform: z.enum(CREATE_PLATFORMS)
821
+ .describe("타겟 플랫폼"),
822
+ structure: z.enum(CREATE_STRUCTURES).optional()
823
+ .describe("Next.js 구조. platform=next 일 때 의미. 기본 standalone"),
824
+ arch: z.enum(ARCH_NAMES).optional()
825
+ .describe("아키텍처. platform=next 일 때 의미. 기본 fsd"),
826
+ plugins: z.array(z.enum(PLUGIN_NAMES)).optional()
827
+ .describe(`Next.js 플러그인 배열 (${PLUGIN_NAMES.join(', ')}). 미지정 빈 배열`),
828
+ cssFramework: z.enum(CSS_FRAMEWORKS).optional()
829
+ .describe("CSS 프레임워크. 기본 plain. css-modules 면 page.module.css 등 추가"),
830
+ appName: z.string().optional()
831
+ .describe("monorepo 첫 앱 이름. 기본 web"),
832
+ },
833
+ },
834
+ async (input) => {
835
+ try {
836
+ const result = describeTemplate({
837
+ platform: input.platform,
838
+ structure: input.structure,
839
+ arch: input.arch,
840
+ plugins: input.plugins,
841
+ cssFramework: input.cssFramework,
842
+ appName: input.appName,
843
+ });
844
+ return jsonResult(result);
845
+ } catch (e) {
846
+ return {
847
+ isError: true,
848
+ content: [{ type: "text", text: e.message }],
849
+ };
850
+ }
851
+ },
852
+ );
853
+
808
854
  // 변경 내역 조회 — 보너스: 사용자가 "최근 변경 알려줘" 류 요청 시
809
855
  server.registerTool(
810
856
  "sh_ui_get_changelog",
@@ -30,6 +30,7 @@
30
30
  "exports": {
31
31
  "./components/*": "./src/components/*/index.tsx",
32
32
  "./hooks/*": "./src/hooks/*.ts",
33
- "./lib/*": "./src/lib/*.ts"
33
+ "./lib/*": "./src/lib/*.ts",
34
+ "./styles/*": "./src/styles/*"
34
35
  }
35
36
  }
@@ -5,7 +5,8 @@
5
5
  "paths": {
6
6
  "components": "src/components",
7
7
  "hooks": "src/hooks",
8
- "utils": "src/lib/utils.ts"
8
+ "utils": "src/lib/utils.ts",
9
+ "styles": "src/styles"
9
10
  },
10
11
  "aliases": {
11
12
  "components": "@workspace/ui-core/components",
@@ -20,6 +20,17 @@
20
20
  --danger: #DC2626;
21
21
  --danger-hover: color-mix(in srgb, var(--danger) 90%, black);
22
22
  --danger-foreground: #FFFFFF;
23
+ --success: #16A34A;
24
+ --success-foreground: #FFFFFF;
25
+ --warning: #D97706;
26
+ --warning-foreground: #FFFFFF;
27
+ --info: #2563EB;
28
+ --info-foreground: #FFFFFF;
29
+ --sidebar-bg: #FAFAFA;
30
+ --sidebar-fg: #0A0A0A;
31
+ --sidebar-border: #E5E5E5;
32
+ --sidebar-accent: #F5F5F5;
33
+ --sidebar-accent-fg: #0A0A0A;
23
34
  }
24
35
  @media (prefers-color-scheme: dark) {
25
36
  :root:not(.light):not(.dark) {
@@ -37,8 +48,19 @@
37
48
  --primary-foreground: #171717;
38
49
  --primary-hover: #E5E5E5;
39
50
  --danger: #DC2626;
40
- --danger-hover: color-mix(in srgb, var(--danger) 90%, black);
51
+ --danger-hover: color-mix(in srgb, var(--danger) 90%, black);
41
52
  --danger-foreground: #FFFFFF;
53
+ --success: #22C55E;
54
+ --success-foreground: #052E16;
55
+ --warning: #F59E0B;
56
+ --warning-foreground: #451A03;
57
+ --info: #3B82F6;
58
+ --info-foreground: #172554;
59
+ --sidebar-bg: #171717;
60
+ --sidebar-fg: #FAFAFA;
61
+ --sidebar-border: #262626;
62
+ --sidebar-accent: #262626;
63
+ --sidebar-accent-fg: #FAFAFA;
42
64
  }
43
65
  }
44
66
  .dark {
@@ -58,6 +80,17 @@
58
80
  --danger: #DC2626;
59
81
  --danger-hover: color-mix(in srgb, var(--danger) 90%, black);
60
82
  --danger-foreground: #FFFFFF;
83
+ --success: #22C55E;
84
+ --success-foreground: #052E16;
85
+ --warning: #F59E0B;
86
+ --warning-foreground: #451A03;
87
+ --info: #3B82F6;
88
+ --info-foreground: #172554;
89
+ --sidebar-bg: #171717;
90
+ --sidebar-fg: #FAFAFA;
91
+ --sidebar-border: #262626;
92
+ --sidebar-accent: #262626;
93
+ --sidebar-accent-fg: #FAFAFA;
61
94
  }
62
95
  /* sh-ui:theme-colors-end */
63
96
 
@@ -20,6 +20,17 @@
20
20
  --danger: #DC2626;
21
21
  --danger-hover: color-mix(in srgb, var(--danger) 90%, black);
22
22
  --danger-foreground: #FFFFFF;
23
+ --success: #16A34A;
24
+ --success-foreground: #FFFFFF;
25
+ --warning: #D97706;
26
+ --warning-foreground: #FFFFFF;
27
+ --info: #2563EB;
28
+ --info-foreground: #FFFFFF;
29
+ --sidebar-bg: #FAFAFA;
30
+ --sidebar-fg: #0A0A0A;
31
+ --sidebar-border: #E5E5E5;
32
+ --sidebar-accent: #F5F5F5;
33
+ --sidebar-accent-fg: #0A0A0A;
23
34
  }
24
35
  @media (prefers-color-scheme: dark) {
25
36
  :root:not(.light):not(.dark) {
@@ -37,8 +48,19 @@
37
48
  --primary-foreground: #171717;
38
49
  --primary-hover: #E5E5E5;
39
50
  --danger: #DC2626;
40
- --danger-hover: color-mix(in srgb, var(--danger) 90%, black);
51
+ --danger-hover: color-mix(in srgb, var(--danger) 90%, black);
41
52
  --danger-foreground: #FFFFFF;
53
+ --success: #22C55E;
54
+ --success-foreground: #052E16;
55
+ --warning: #F59E0B;
56
+ --warning-foreground: #451A03;
57
+ --info: #3B82F6;
58
+ --info-foreground: #172554;
59
+ --sidebar-bg: #171717;
60
+ --sidebar-fg: #FAFAFA;
61
+ --sidebar-border: #262626;
62
+ --sidebar-accent: #262626;
63
+ --sidebar-accent-fg: #FAFAFA;
42
64
  }
43
65
  }
44
66
  .dark {
@@ -58,6 +80,17 @@
58
80
  --danger: #DC2626;
59
81
  --danger-hover: color-mix(in srgb, var(--danger) 90%, black);
60
82
  --danger-foreground: #FFFFFF;
83
+ --success: #22C55E;
84
+ --success-foreground: #052E16;
85
+ --warning: #F59E0B;
86
+ --warning-foreground: #451A03;
87
+ --info: #3B82F6;
88
+ --info-foreground: #172554;
89
+ --sidebar-bg: #171717;
90
+ --sidebar-fg: #FAFAFA;
91
+ --sidebar-border: #262626;
92
+ --sidebar-accent: #262626;
93
+ --sidebar-accent-fg: #FAFAFA;
61
94
  }
62
95
  /* sh-ui:theme-colors-end */
63
96
 
@@ -20,6 +20,17 @@
20
20
  --danger: #DC2626;
21
21
  --danger-hover: color-mix(in srgb, var(--danger) 90%, black);
22
22
  --danger-foreground: #FFFFFF;
23
+ --success: #16A34A;
24
+ --success-foreground: #FFFFFF;
25
+ --warning: #D97706;
26
+ --warning-foreground: #FFFFFF;
27
+ --info: #2563EB;
28
+ --info-foreground: #FFFFFF;
29
+ --sidebar-bg: #FAFAFA;
30
+ --sidebar-fg: #0A0A0A;
31
+ --sidebar-border: #E5E5E5;
32
+ --sidebar-accent: #F5F5F5;
33
+ --sidebar-accent-fg: #0A0A0A;
23
34
  }
24
35
  @media (prefers-color-scheme: dark) {
25
36
  :root:not(.light):not(.dark) {
@@ -37,8 +48,19 @@
37
48
  --primary-foreground: #171717;
38
49
  --primary-hover: #E5E5E5;
39
50
  --danger: #DC2626;
40
- --danger-hover: color-mix(in srgb, var(--danger) 90%, black);
51
+ --danger-hover: color-mix(in srgb, var(--danger) 90%, black);
41
52
  --danger-foreground: #FFFFFF;
53
+ --success: #22C55E;
54
+ --success-foreground: #052E16;
55
+ --warning: #F59E0B;
56
+ --warning-foreground: #451A03;
57
+ --info: #3B82F6;
58
+ --info-foreground: #172554;
59
+ --sidebar-bg: #171717;
60
+ --sidebar-fg: #FAFAFA;
61
+ --sidebar-border: #262626;
62
+ --sidebar-accent: #262626;
63
+ --sidebar-accent-fg: #FAFAFA;
42
64
  }
43
65
  }
44
66
  .dark {
@@ -58,6 +80,17 @@
58
80
  --danger: #DC2626;
59
81
  --danger-hover: color-mix(in srgb, var(--danger) 90%, black);
60
82
  --danger-foreground: #FFFFFF;
83
+ --success: #22C55E;
84
+ --success-foreground: #052E16;
85
+ --warning: #F59E0B;
86
+ --warning-foreground: #451A03;
87
+ --info: #3B82F6;
88
+ --info-foreground: #172554;
89
+ --sidebar-bg: #171717;
90
+ --sidebar-fg: #FAFAFA;
91
+ --sidebar-border: #262626;
92
+ --sidebar-accent: #262626;
93
+ --sidebar-accent-fg: #FAFAFA;
61
94
  }
62
95
  /* sh-ui:theme-colors-end */
63
96