sh-ui-cli 0.22.2 → 0.23.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.
Files changed (111) hide show
  1. package/README.md +19 -2
  2. package/bin/sh-ui.mjs +7 -0
  3. package/data/changelog/versions.json +26 -0
  4. package/package.json +13 -2
  5. package/src/create/cli-args.js +63 -0
  6. package/src/create/generator.js +542 -0
  7. package/src/create/index.mjs +68 -0
  8. package/src/create/plugins/index.js +17 -0
  9. package/src/create/plugins/nextIntl.js +197 -0
  10. package/src/create/plugins/sentry.js +689 -0
  11. package/src/create/theme/decode.js +66 -0
  12. package/src/create/theme/inject.js +111 -0
  13. package/src/mcp.mjs +81 -27
  14. package/src/paths.mjs +5 -0
  15. package/templates/flutter-standalone/README.md +34 -0
  16. package/templates/flutter-standalone/analysis_options.yaml +1 -0
  17. package/templates/flutter-standalone/lib/main.dart +103 -0
  18. package/templates/flutter-standalone/lib/sh_ui/foundation/sh_ui_tokens.dart +389 -0
  19. package/templates/flutter-standalone/pubspec.yaml +20 -0
  20. package/templates/flutter-standalone/sh-ui.config.json +15 -0
  21. package/templates/monorepo/.dockerignore +7 -0
  22. package/templates/monorepo/.eslintrc.js +8 -0
  23. package/templates/monorepo/.prettierrc +17 -0
  24. package/templates/monorepo/README.md +103 -0
  25. package/templates/monorepo/package.json +24 -0
  26. package/templates/monorepo/packages/eslint-config/base.js +31 -0
  27. package/templates/monorepo/packages/eslint-config/fsd.js +119 -0
  28. package/templates/monorepo/packages/eslint-config/next.js +65 -0
  29. package/templates/monorepo/packages/eslint-config/package.json +31 -0
  30. package/templates/monorepo/packages/eslint-config/react-internal.js +36 -0
  31. package/templates/monorepo/packages/typescript-config/base.json +20 -0
  32. package/templates/monorepo/packages/typescript-config/nextjs.json +13 -0
  33. package/templates/monorepo/packages/typescript-config/package.json +5 -0
  34. package/templates/monorepo/packages/typescript-config/react-library.json +8 -0
  35. package/templates/monorepo/packages/ui/ui-apps/.gitkeep +0 -0
  36. package/templates/monorepo/packages/ui/ui-core/eslint.config.js +3 -0
  37. package/templates/monorepo/packages/ui/ui-core/package.json +23 -0
  38. package/templates/monorepo/packages/ui/ui-core/src/lib/utils.ts +6 -0
  39. package/templates/monorepo/packages/ui/ui-core/tsconfig.json +11 -0
  40. package/templates/monorepo/pnpm-workspace.yaml +5 -0
  41. package/templates/monorepo/tsconfig.json +3 -0
  42. package/templates/monorepo/turbo.json +26 -0
  43. package/templates/nextjs-app/.env.example +2 -0
  44. package/templates/nextjs-app/Dockerfile +11 -0
  45. package/templates/nextjs-app/README.md +64 -0
  46. package/templates/nextjs-app/app/layout.tsx +22 -0
  47. package/templates/nextjs-app/app/page.tsx +7 -0
  48. package/templates/nextjs-app/eslint.config.js +10 -0
  49. package/templates/nextjs-app/next.config.ts +12 -0
  50. package/templates/nextjs-app/package.json +45 -0
  51. package/templates/nextjs-app/postcss.config.mjs +1 -0
  52. package/templates/nextjs-app/src/app/layouts/.gitkeep +0 -0
  53. package/templates/nextjs-app/src/app/providers/GlobalProvider/index.tsx +23 -0
  54. package/templates/nextjs-app/src/app/providers/index.tsx +1 -0
  55. package/templates/nextjs-app/src/app/providers/tanstack/QueryClientProvider.tsx +27 -0
  56. package/templates/nextjs-app/src/app/providers/tanstack/TanstackDevtoolsProvider.tsx +13 -0
  57. package/templates/nextjs-app/src/app/providers/theme/ThemeProviders.tsx +12 -0
  58. package/templates/nextjs-app/src/entities/.gitkeep +0 -0
  59. package/templates/nextjs-app/src/features/.gitkeep +0 -0
  60. package/templates/nextjs-app/src/shared/api/.gitkeep +0 -0
  61. package/templates/nextjs-app/src/shared/config/.gitkeep +0 -0
  62. package/templates/nextjs-app/src/shared/hooks/.gitkeep +0 -0
  63. package/templates/nextjs-app/src/shared/lib/.gitkeep +0 -0
  64. package/templates/nextjs-app/src/shared/model/.gitkeep +0 -0
  65. package/templates/nextjs-app/src/shared/ui/.gitkeep +0 -0
  66. package/templates/nextjs-app/src/views/.gitkeep +0 -0
  67. package/templates/nextjs-app/src/widgets/.gitkeep +0 -0
  68. package/templates/nextjs-app/tsconfig.json +23 -0
  69. package/templates/nextjs-app/vitest.config.ts +15 -0
  70. package/templates/nextjs-app/vitest.setup.ts +1 -0
  71. package/templates/nextjs-standalone/.env.example +2 -0
  72. package/templates/nextjs-standalone/.prettierrc +17 -0
  73. package/templates/nextjs-standalone/README.md +77 -0
  74. package/templates/nextjs-standalone/app/globals.css +33 -0
  75. package/templates/nextjs-standalone/app/layout.tsx +22 -0
  76. package/templates/nextjs-standalone/app/page.tsx +7 -0
  77. package/templates/nextjs-standalone/eslint.config.js +162 -0
  78. package/templates/nextjs-standalone/next.config.ts +10 -0
  79. package/templates/nextjs-standalone/package.json +66 -0
  80. package/templates/nextjs-standalone/postcss.config.mjs +5 -0
  81. package/templates/nextjs-standalone/sh-ui.config.json +19 -0
  82. package/templates/nextjs-standalone/src/app/layouts/.gitkeep +0 -0
  83. package/templates/nextjs-standalone/src/app/providers/GlobalProvider/index.tsx +23 -0
  84. package/templates/nextjs-standalone/src/app/providers/index.tsx +1 -0
  85. package/templates/nextjs-standalone/src/app/providers/tanstack/QueryClientProvider.tsx +27 -0
  86. package/templates/nextjs-standalone/src/app/providers/tanstack/TanstackDevtoolsProvider.tsx +13 -0
  87. package/templates/nextjs-standalone/src/app/providers/theme/ThemeProviders.tsx +12 -0
  88. package/templates/nextjs-standalone/src/entities/.gitkeep +0 -0
  89. package/templates/nextjs-standalone/src/features/.gitkeep +0 -0
  90. package/templates/nextjs-standalone/src/shared/api/.gitkeep +0 -0
  91. package/templates/nextjs-standalone/src/shared/config/.gitkeep +0 -0
  92. package/templates/nextjs-standalone/src/shared/hooks/.gitkeep +0 -0
  93. package/templates/nextjs-standalone/src/shared/lib/utils.ts +6 -0
  94. package/templates/nextjs-standalone/src/shared/model/.gitkeep +0 -0
  95. package/templates/nextjs-standalone/src/shared/styles/tokens.css +95 -0
  96. package/templates/nextjs-standalone/src/shared/ui/.gitkeep +0 -0
  97. package/templates/nextjs-standalone/src/views/.gitkeep +0 -0
  98. package/templates/nextjs-standalone/src/widgets/.gitkeep +0 -0
  99. package/templates/nextjs-standalone/tsconfig.json +39 -0
  100. package/templates/nextjs-standalone/vitest.config.ts +15 -0
  101. package/templates/nextjs-standalone/vitest.setup.ts +1 -0
  102. package/templates/ui-app-template/eslint.config.js +3 -0
  103. package/templates/ui-app-template/package.json +38 -0
  104. package/templates/ui-app-template/postcss.config.mjs +5 -0
  105. package/templates/ui-app-template/sh-ui.config.json +14 -0
  106. package/templates/ui-app-template/src/components/.gitkeep +0 -0
  107. package/templates/ui-app-template/src/hooks/.gitkeep +0 -0
  108. package/templates/ui-app-template/src/lib/.gitkeep +0 -0
  109. package/templates/ui-app-template/src/styles/globals.css +37 -0
  110. package/templates/ui-app-template/src/styles/tokens.css +95 -0
  111. package/templates/ui-app-template/tsconfig.json +11 -0
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -0,0 +1,23 @@
1
+ {
2
+ "extends": "@workspace/typescript-config/nextjs.json",
3
+ "compilerOptions": {
4
+ "baseUrl": ".",
5
+ "paths": {
6
+ "@/*": ["./*"],
7
+ "@workspace/ui-app-name/*": ["../../packages/ui/ui-apps/ui-app-name/src/*"]
8
+ },
9
+ "plugins": [
10
+ {
11
+ "name": "next"
12
+ }
13
+ ]
14
+ },
15
+ "include": [
16
+ "next-env.d.ts",
17
+ "next.config.ts",
18
+ "**/*.ts",
19
+ "**/*.tsx",
20
+ ".next/types/**/*.ts"
21
+ ],
22
+ "exclude": ["node_modules"]
23
+ }
@@ -0,0 +1,15 @@
1
+ import path from 'node:path';
2
+ import { defineConfig } from 'vitest/config';
3
+
4
+ export default defineConfig({
5
+ resolve: {
6
+ alias: {
7
+ '@': path.resolve(__dirname, '.'),
8
+ },
9
+ },
10
+ test: {
11
+ globals: true,
12
+ environment: 'jsdom',
13
+ setupFiles: ['./vitest.setup.ts'],
14
+ },
15
+ });
@@ -0,0 +1 @@
1
+ import '@testing-library/jest-dom/vitest';
@@ -0,0 +1,2 @@
1
+ # API
2
+ API_URL=http://localhost:8080
@@ -0,0 +1,17 @@
1
+ {
2
+ "arrowParens": "always",
3
+ "bracketSpacing": true,
4
+ "endOfLine": "auto",
5
+ "htmlWhitespaceSensitivity": "css",
6
+ "bracketSameLine": true,
7
+ "jsxSingleQuote": true,
8
+ "printWidth": 80,
9
+ "proseWrap": "always",
10
+ "quoteProps": "as-needed",
11
+ "semi": true,
12
+ "singleQuote": true,
13
+ "tabWidth": 2,
14
+ "trailingComma": "all",
15
+ "useTabs": false,
16
+ "plugins": ["prettier-plugin-tailwindcss"]
17
+ }
@@ -0,0 +1,77 @@
1
+ # Next.js Standalone Template
2
+
3
+ 단독 실행 가능한 Next.js 프로젝트 템플릿 (sh-ui 기반).
4
+
5
+ ## 기술 스택
6
+
7
+ - **Next.js 16** (App Router, React Compiler)
8
+ - **React 19**
9
+ - **TypeScript 5.9**
10
+ - **Tailwind CSS 4** + **sh-ui** (neutral, radius=md, light-dark)
11
+ - **@base-ui-components/react** (sh-ui가 의존하는 언스타일드 primitive)
12
+ - **TanStack React Query** + **Axios** (데이터 페칭)
13
+ - **Zustand** (상태 관리)
14
+ - **next-themes** (다크 모드)
15
+ - **Sonner** (토스트)
16
+ - **Zod** (유효성 검증)
17
+ - **ESLint 9** (flat config, FSD 규칙 포함)
18
+ - **Prettier** (tailwind 플러그인)
19
+ - **Vitest** + **Testing Library**
20
+
21
+ ## 프로젝트 구조
22
+
23
+ ```
24
+ ├── app/ # Next.js App Router
25
+ │ ├── layout.tsx # 루트 레이아웃
26
+ │ ├── page.tsx # 홈 페이지
27
+ │ └── globals.css # Tailwind + sh-ui tokens import
28
+ ├── src/
29
+ │ ├── app/
30
+ │ │ └── providers/ # QueryClient, Theme, Toaster
31
+ │ ├── shared/ # FSD: 공유 유틸/설정/UI
32
+ │ │ ├── ui/ # ← sh-ui 컴포넌트가 여기로 복사됨
33
+ │ │ ├── lib/
34
+ │ │ │ └── utils.ts # cn() 유틸
35
+ │ │ ├── styles/
36
+ │ │ │ └── tokens.css # sh-ui 디자인 토큰
37
+ │ │ ├── hooks/
38
+ │ │ ├── api/
39
+ │ │ ├── config/
40
+ │ │ └── model/
41
+ │ ├── views/ # FSD: 페이지 단위 뷰
42
+ │ ├── widgets/ # FSD: 조합형 UI 블록
43
+ │ ├── features/ # FSD: 기능 단위
44
+ │ └── entities/ # FSD: 비즈니스 엔티티
45
+ ├── sh-ui.config.json # sh-ui 설정 (platform, theme, paths)
46
+ ├── eslint.config.js # ESLint flat config (FSD boundaries 포함)
47
+ ├── next.config.ts
48
+ ├── tsconfig.json
49
+ ├── vitest.config.ts
50
+ ├── .prettierrc
51
+ └── .env.example
52
+ ```
53
+
54
+ ## FSD (Feature-Sliced Design) 레이어 규칙
55
+
56
+ ```
57
+ app → view → widget → feature → entity → shared
58
+ ```
59
+
60
+ 상위 레이어는 하위 레이어만 import 가능. ESLint `boundaries` 플러그인으로 강제됨.
61
+
62
+ ## 시작하기
63
+
64
+ ```bash
65
+ pnpm install
66
+ pnpm dev
67
+ ```
68
+
69
+ ## sh-ui 컴포넌트 추가
70
+
71
+ ```bash
72
+ npx sh-ui add button
73
+ npx sh-ui add dialog
74
+ ```
75
+
76
+ `sh-ui.config.json` 의 `paths.components` 설정에 따라 `src/shared/ui/` 에 복사됩니다.
77
+ 토큰을 커스텀하려면 `sh-ui.config.json` 의 `theme` 값을 바꾸고 `npx sh-ui add tokens` 로 재생성.
@@ -0,0 +1,33 @@
1
+ @import 'tailwindcss';
2
+ @import '../src/shared/styles/tokens.css';
3
+
4
+ @custom-variant dark (&:is(.dark *));
5
+
6
+ @theme inline {
7
+ --color-background: var(--background);
8
+ --color-background-subtle: var(--background-subtle);
9
+ --color-background-muted: var(--background-muted);
10
+ --color-background-inverse: var(--background-inverse);
11
+ --color-foreground: var(--foreground);
12
+ --color-foreground-muted: var(--foreground-muted);
13
+ --color-foreground-subtle: var(--foreground-subtle);
14
+ --color-foreground-inverse: var(--foreground-inverse);
15
+ --color-border: var(--border);
16
+ --color-border-strong: var(--border-strong);
17
+ --color-primary: var(--primary);
18
+ --color-primary-foreground: var(--primary-foreground);
19
+ --color-primary-hover: var(--primary-hover);
20
+ --color-danger: var(--danger);
21
+ --color-danger-foreground: var(--danger-foreground);
22
+ --radius-sm: calc(var(--radius) - 2px);
23
+ --radius-md: var(--radius);
24
+ --radius-lg: calc(var(--radius) + 2px);
25
+ --radius-xl: calc(var(--radius) + 4px);
26
+ }
27
+
28
+ @layer base {
29
+ body {
30
+ background: var(--background);
31
+ color: var(--foreground);
32
+ }
33
+ }
@@ -0,0 +1,22 @@
1
+ import type { Metadata } from 'next';
2
+ import { GlobalProvider } from '@/src/app/providers';
3
+ import './globals.css';
4
+
5
+ export const metadata: Metadata = {
6
+ title: 'My App',
7
+ description: 'My App Description',
8
+ };
9
+
10
+ export default function RootLayout({
11
+ children,
12
+ }: {
13
+ children: React.ReactNode;
14
+ }) {
15
+ return (
16
+ <html lang='ko' suppressHydrationWarning>
17
+ <body>
18
+ <GlobalProvider>{children}</GlobalProvider>
19
+ </body>
20
+ </html>
21
+ );
22
+ }
@@ -0,0 +1,7 @@
1
+ export default function Home() {
2
+ return (
3
+ <main className='flex min-h-screen flex-col items-center justify-center'>
4
+ <h1 className='text-4xl font-bold'>Hello World</h1>
5
+ </main>
6
+ );
7
+ }
@@ -0,0 +1,162 @@
1
+ import js from "@eslint/js"
2
+ import pluginNext from "@next/eslint-plugin-next"
3
+ import eslintConfigPrettier from "eslint-config-prettier"
4
+ import boundaries from "eslint-plugin-boundaries"
5
+ import checkFile from "eslint-plugin-check-file"
6
+ import importX from "eslint-plugin-import-x"
7
+ import onlyWarn from "eslint-plugin-only-warn"
8
+ import pluginReact from "eslint-plugin-react"
9
+ import pluginReactHooks from "eslint-plugin-react-hooks"
10
+ import globals from "globals"
11
+ import tseslint from "typescript-eslint"
12
+ import { createTypeScriptImportResolver } from "eslint-import-resolver-typescript"
13
+
14
+ export default [
15
+ {
16
+ ignores: [".next/**", "dist/**", "node_modules/**"],
17
+ },
18
+
19
+ // ── Base ──
20
+ js.configs.recommended,
21
+ eslintConfigPrettier,
22
+ ...tseslint.configs.recommended,
23
+ {
24
+ plugins: { onlyWarn },
25
+ },
26
+ {
27
+ rules: {
28
+ "@typescript-eslint/no-unused-vars": [
29
+ "warn",
30
+ { argsIgnorePattern: "^_", varsIgnorePattern: "^_" },
31
+ ],
32
+ },
33
+ },
34
+
35
+ // ── React + Next.js ──
36
+ {
37
+ ...pluginReact.configs.flat.recommended,
38
+ languageOptions: {
39
+ ...pluginReact.configs.flat.recommended.languageOptions,
40
+ globals: {
41
+ ...globals.serviceworker,
42
+ },
43
+ },
44
+ },
45
+ {
46
+ plugins: {
47
+ "@next/next": pluginNext,
48
+ },
49
+ rules: {
50
+ ...pluginNext.configs.recommended.rules,
51
+ ...pluginNext.configs["core-web-vitals"].rules,
52
+ },
53
+ },
54
+ {
55
+ plugins: {
56
+ "react-hooks": pluginReactHooks,
57
+ },
58
+ settings: { react: { version: "detect" } },
59
+ rules: {
60
+ ...pluginReactHooks.configs.recommended.rules,
61
+ "react/react-in-jsx-scope": "off",
62
+ "react/prop-types": "off",
63
+ "react/function-component-definition": [
64
+ "warn",
65
+ {
66
+ namedComponents: "function-declaration",
67
+ unnamedComponents: "arrow-function",
68
+ },
69
+ ],
70
+ },
71
+ },
72
+
73
+ // ── FSD boundaries ──
74
+ {
75
+ plugins: { boundaries },
76
+ settings: {
77
+ "boundaries/elements": [
78
+ { type: "app", pattern: ["src/app"], mode: "folder" },
79
+ { type: "shared", pattern: ["src/shared/*"], mode: "folder" },
80
+ { type: "entity", pattern: ["src/entities/*"], mode: "folder" },
81
+ { type: "feature", pattern: ["src/features/*"], mode: "folder" },
82
+ { type: "widget", pattern: ["src/widgets/*"], mode: "folder" },
83
+ { type: "view", pattern: ["src/views/*"], mode: "folder" },
84
+ ],
85
+ "boundaries/ignore": ["**/*.test.*", "**/*.spec.*"],
86
+ },
87
+ rules: {
88
+ "boundaries/element-types": [
89
+ "warn",
90
+ {
91
+ default: "disallow",
92
+ rules: [
93
+ { from: "app", allow: ["view", "widget", "feature", "entity", "shared"] },
94
+ { from: "view", allow: ["widget", "feature", "entity", "shared"] },
95
+ { from: "widget", allow: ["feature", "entity", "shared"] },
96
+ { from: "feature", allow: ["entity", "shared"] },
97
+ { from: "entity", allow: ["shared"] },
98
+ { from: "shared", allow: ["shared"] },
99
+ ],
100
+ },
101
+ ],
102
+ "boundaries/entry-point": [
103
+ "warn",
104
+ {
105
+ default: "disallow",
106
+ rules: [
107
+ { target: "shared", allow: "**" },
108
+ { target: "app", allow: "**" },
109
+ {
110
+ target: ["entity", "widget", "view"],
111
+ allow: "index.{ts,tsx}",
112
+ },
113
+ {
114
+ target: ["feature"],
115
+ allow: ["index.{ts,tsx}", "*/index.{ts,tsx}"],
116
+ },
117
+ ],
118
+ },
119
+ ],
120
+ },
121
+ },
122
+
123
+ // ── File naming ──
124
+ {
125
+ plugins: { "check-file": checkFile },
126
+ rules: {
127
+ "check-file/filename-naming-convention": [
128
+ "error",
129
+ {
130
+ "**/src/**/*.tsx": "PASCAL_CASE",
131
+ "**/src/**/*.ts": "CAMEL_CASE",
132
+ },
133
+ { ignoreMiddleExtensions: true },
134
+ ],
135
+ },
136
+ },
137
+ {
138
+ files: [
139
+ "**/index.tsx", "**/index.ts",
140
+ "**/layout.tsx", "**/page.tsx",
141
+ "**/error.tsx", "**/not-found.tsx",
142
+ "**/routing.ts", "**/navigation.ts", "**/request.ts",
143
+ ],
144
+ rules: {
145
+ "check-file/filename-naming-convention": "off",
146
+ },
147
+ },
148
+
149
+ // ── import-x ──
150
+ {
151
+ plugins: { "import-x": importX },
152
+ settings: {
153
+ "import-x/resolver-next": [
154
+ createTypeScriptImportResolver({ alwaysTryTypes: true }),
155
+ ],
156
+ },
157
+ rules: {
158
+ "import-x/order": "off",
159
+ "import-x/no-unresolved": "off",
160
+ },
161
+ },
162
+ ]
@@ -0,0 +1,10 @@
1
+ import type { NextConfig } from 'next';
2
+
3
+ const nextConfig: NextConfig = {
4
+ reactCompiler: true,
5
+ images: {
6
+ unoptimized: process.env.NODE_ENV === 'development',
7
+ },
8
+ };
9
+
10
+ export default nextConfig;
@@ -0,0 +1,66 @@
1
+ {
2
+ "name": "my-app",
3
+ "version": "0.0.1",
4
+ "type": "module",
5
+ "private": true,
6
+ "scripts": {
7
+ "dev": "next dev --turbopack",
8
+ "build": "next build",
9
+ "start": "next start",
10
+ "lint": "eslint .",
11
+ "lint:fix": "eslint . --fix",
12
+ "format": "prettier --write \"**/*.{ts,tsx,md}\"",
13
+ "typecheck": "tsc --noEmit",
14
+ "test": "vitest run",
15
+ "test:watch": "vitest"
16
+ },
17
+ "dependencies": {
18
+ "@base-ui-components/react": "latest",
19
+ "@tanstack/react-query": "^5.90.21",
20
+ "axios": "^1.13.6",
21
+ "class-variance-authority": "^0.7.1",
22
+ "clsx": "^2.1.1",
23
+ "lucide-react": "^0.563.0",
24
+ "next": "16.1.6",
25
+ "next-themes": "^0.4.6",
26
+ "react": "^19.2.4",
27
+ "react-dom": "^19.2.4",
28
+ "sonner": "^2.0.7",
29
+ "tailwind-merge": "^3.5.0",
30
+ "zod": "^4.3.6",
31
+ "zustand": "^5.0.11"
32
+ },
33
+ "devDependencies": {
34
+ "@eslint/js": "^9.39.2",
35
+ "@next/eslint-plugin-next": "^16.1.6",
36
+ "@tailwindcss/postcss": "^4.1.18",
37
+ "@tanstack/react-query-devtools": "^5.91.3",
38
+ "@testing-library/jest-dom": "^6.9.1",
39
+ "@types/node": "^25.1.0",
40
+ "@types/react": "^19.2.10",
41
+ "@types/react-dom": "^19.2.3",
42
+ "@typescript-eslint/eslint-plugin": "^8.54.0",
43
+ "@typescript-eslint/parser": "^8.54.0",
44
+ "babel-plugin-react-compiler": "^1.0.0",
45
+ "eslint": "^9.39.2",
46
+ "eslint-config-prettier": "^10.1.8",
47
+ "eslint-import-resolver-typescript": "^4.4.4",
48
+ "eslint-plugin-boundaries": "^5.4.0",
49
+ "eslint-plugin-check-file": "^3.3.1",
50
+ "eslint-plugin-import-x": "^4.16.1",
51
+ "eslint-plugin-only-warn": "^1.1.0",
52
+ "eslint-plugin-react": "^7.37.5",
53
+ "eslint-plugin-react-hooks": "^7.0.1",
54
+ "jsdom": "^29.0.0",
55
+ "prettier": "^3.8.1",
56
+ "prettier-plugin-tailwindcss": "^0.6.13",
57
+ "tailwindcss": "^4.1.18",
58
+ "typescript": "^5.9.3",
59
+ "typescript-eslint": "^8.54.0",
60
+ "vitest": "^4.1.0"
61
+ },
62
+ "packageManager": "pnpm@10.4.1",
63
+ "engines": {
64
+ "node": ">=20"
65
+ }
66
+ }
@@ -0,0 +1,5 @@
1
+ const config = {
2
+ plugins: { "@tailwindcss/postcss": {} },
3
+ };
4
+
5
+ export default config;
@@ -0,0 +1,19 @@
1
+ {
2
+ "platform": "react",
3
+ "style": "default",
4
+ "theme": {
5
+ "base": "neutral",
6
+ "radius": "md",
7
+ "mode": "light-dark"
8
+ },
9
+ "paths": {
10
+ "tokens": "src/shared/styles/tokens.css",
11
+ "components": "src/shared/ui",
12
+ "utils": "src/shared/lib/utils.ts"
13
+ },
14
+ "aliases": {
15
+ "components": "@/src/shared/ui",
16
+ "utils": "@/src/shared/lib/utils",
17
+ "ui": "@/src/shared/ui"
18
+ }
19
+ }
@@ -0,0 +1,23 @@
1
+ import type { ReactNode } from 'react';
2
+ import { Toaster } from 'sonner';
3
+
4
+ import { QueryClientProvider } from '../tanstack/QueryClientProvider';
5
+ import { TanstackDevtoolsProvider } from '../tanstack/TanstackDevtoolsProvider';
6
+ import { ThemeProviders } from '../theme/ThemeProviders';
7
+
8
+ interface GlobalProviderProps {
9
+ children: ReactNode;
10
+ }
11
+
12
+ export function GlobalProvider({ children }: GlobalProviderProps) {
13
+ return (
14
+ <ThemeProviders>
15
+ <QueryClientProvider>
16
+ <TanstackDevtoolsProvider>
17
+ <Toaster />
18
+ {children}
19
+ </TanstackDevtoolsProvider>
20
+ </QueryClientProvider>
21
+ </ThemeProviders>
22
+ );
23
+ }
@@ -0,0 +1 @@
1
+ export { GlobalProvider } from './GlobalProvider';
@@ -0,0 +1,27 @@
1
+ 'use client';
2
+
3
+ import {
4
+ QueryClient,
5
+ QueryClientProvider as TanstackQueryClientProvider,
6
+ } from '@tanstack/react-query';
7
+ import { useState, type ReactNode } from 'react';
8
+
9
+ export function QueryClientProvider({ children }: { children: ReactNode }) {
10
+ const [queryClient] = useState(
11
+ () =>
12
+ new QueryClient({
13
+ defaultOptions: {
14
+ queries: {
15
+ staleTime: 60 * 1000,
16
+ retry: 1,
17
+ },
18
+ },
19
+ }),
20
+ );
21
+
22
+ return (
23
+ <TanstackQueryClientProvider client={queryClient}>
24
+ {children}
25
+ </TanstackQueryClientProvider>
26
+ );
27
+ }
@@ -0,0 +1,13 @@
1
+ 'use client';
2
+
3
+ import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
4
+ import type { ReactNode } from 'react';
5
+
6
+ export function TanstackDevtoolsProvider({ children }: { children: ReactNode }) {
7
+ return (
8
+ <>
9
+ {children}
10
+ <ReactQueryDevtools initialIsOpen={false} />
11
+ </>
12
+ );
13
+ }
@@ -0,0 +1,12 @@
1
+ 'use client';
2
+
3
+ import { ThemeProvider } from 'next-themes';
4
+ import type { ReactNode } from 'react';
5
+
6
+ export function ThemeProviders({ children }: { children: ReactNode }) {
7
+ return (
8
+ <ThemeProvider attribute='class' defaultTheme='system' enableSystem>
9
+ {children}
10
+ </ThemeProvider>
11
+ );
12
+ }
@@ -0,0 +1,6 @@
1
+ import { clsx, type ClassValue } from 'clsx';
2
+ import { twMerge } from 'tailwind-merge';
3
+
4
+ export function cn(...inputs: ClassValue[]) {
5
+ return twMerge(clsx(inputs));
6
+ }