create-lve 0.2.8 → 0.2.10

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/index.js CHANGED
@@ -24,62 +24,19 @@ export function App() {
24
24
  return (
25
25
  <div className="max-w-7xl mx-auto p-8 text-center font-sans antialiased text-[#213547] dark:text-zinc-200 min-h-dvh flex flex-col justify-center items-center">
26
26
  <div className="flex justify-center gap-12 mb-12">
27
- <a
28
- href="https://viteplus.dev"
29
- target="_blank"
30
- rel="noreferrer"
31
- className="transition-all duration-300 hover:drop-shadow-[0_0_2em_#646cffaa]"
32
- >
27
+ <a href="https://viteplus.dev" target="_blank" rel="noreferrer" className="transition-all duration-300 hover:drop-shadow-[0_0_2em_#646cffaa]">
33
28
  <img src="/favicon.svg" className="h-24 p-6" alt="VitePlus logo" />
34
29
  </a>
35
- <a
36
- href="https://react.dev"
37
- target="_blank"
38
- rel="noreferrer"
39
- className="transition-all duration-300 hover:drop-shadow-[0_0_2em_#61dafbaa]"
40
- >
41
- <img
42
- src={reactLogo}
43
- className="h-24 p-6 ${logoClass}"
44
- alt="React logo"
45
- />
30
+ <a href="https://react.dev" target="_blank" rel="noreferrer" className="transition-all duration-300 hover:drop-shadow-[0_0_2em_#61dafbaa]">
31
+ <img src={reactLogo} className="h-24 p-6 ${logoClass}" alt="React logo" />
46
32
  </a>
47
33
  </div>
48
-
49
34
  <h1 className="text-5xl font-bold leading-[1.1] mb-8">VitePlus + React</h1>
50
-
51
35
  <div className="p-8 space-y-4 flex flex-col items-center">
52
- <button
53
- onClick={() => setCount((count) => count + 1)}
54
- className="rounded-lg border border-transparent px-5 py-2.5 text-base font-medium bg-[#f9f9f9] dark:bg-zinc-800 cursor-pointer transition-colors hover:border-[#646cff] outline-none"
55
- >
36
+ <button onClick={() => setCount((count) => count + 1)} className="rounded-lg border border-transparent px-5 py-2.5 text-base font-medium bg-[#f9f9f9] dark:bg-zinc-800 cursor-pointer transition-colors hover:border-[#646cff] outline-none">
56
37
  count is {count}
57
38
  </button>
58
- <p className="text-zinc-500">
59
- Edit{' '}
60
- <code className="bg-[#f1f1f1] dark:bg-zinc-800 px-1.5 py-0.5 rounded font-mono">
61
- src/App.tsx
62
- </code>{' '}
63
- to test HMR
64
- </p>
65
39
  </div>
66
-
67
- <p className="text-[#888] mt-8">
68
- Check out{' '}
69
- <a
70
- href="https://github.com/voidzero-dev/vite-plus"
71
- target="_blank"
72
- rel="noreferrer"
73
- className="font-medium text-[#646cff] hover:text-[#535bf2]"
74
- >
75
- VitePlus
76
- </a>
77
- , the unified toolchain for the web.
78
- </p>
79
-
80
- <p className="text-[#888] mt-4 text-sm">
81
- Click on the VitePlus and React logos to learn more
82
- </p>
83
40
  </div>
84
41
  )
85
42
  }
@@ -93,87 +50,65 @@ async function main() {
93
50
  ${pc.cyan('█ █ █ █▀▀ ')}
94
51
  ${pc.cyan('█▄▄▄ ▀▄▀ █▄▄▄')} ${pc.gray('THE ULTRA-FAST FRONTEND STACK')}
95
52
  `
96
-
97
53
  console.log(logo)
98
54
  p.intro(`${pc.bgCyan(pc.black(' LVE-CLI '))}`)
99
55
 
100
- const project = await p.group(
101
- {
102
- path: () =>
103
- p.text({
104
- message: '项目名称',
105
- placeholder: 'react-app',
106
- defaultValue: 'react-app',
107
- validate: (value) => {
108
- if (!value || value.length === 0) return
109
- if (value.match(/[<>:"|?*]/)) return '路径包含非法字符'
110
- },
111
- }),
112
- shouldOverwrite: ({ results }) => {
113
- const targetDir = path.resolve(process.cwd(), results.path)
114
- if (fs.existsSync(targetDir) && fs.readdirSync(targetDir).length > 0) {
115
- return p.confirm({ message: `目录已存在,是否清空?`, initialValue: false })
116
- }
56
+ const project = await p.group({
57
+ path: () => p.text({
58
+ message: '项目名称',
59
+ placeholder: 'my-app',
60
+ defaultValue: 'my-app',
61
+ validate: (value) => {
62
+ if (!value || value.length === 0) return
63
+ if (value.match(/[<>:"|?*]/)) return '路径包含非法字符'
117
64
  },
118
- framework: () =>
119
- p.select({
120
- message: '选择框架',
121
- options: [
122
- { value: 'react', label: 'React 19', hint: 'VitePlus + Compiler' },
123
- { value: 'next', label: 'Next.js 16', hint: 'Oxc + Server Components (Fullstack)' },
124
- { value: 'vue', label: 'Vue 3', hint: 'VitePlus + Optimized' },
125
- ],
126
- }),
127
- cssEngine: ({ results }) =>
128
- results.framework === 'next' ?
129
- undefined :
130
- p.select(
131
- {
132
- message: '选择 CSS 引擎',
133
- options: [
134
- {
135
- value: 'tailwind',
136
- label: 'Tailwind v4',
137
- hint: '🛡️ 装甲级稳定:v4 引擎重构,完美适配所有主流 UI 库',
138
- },
139
- {
140
- value: 'unocss',
141
- label: 'UnoCSS',
142
- hint: '⚡️ 战机级性能:动态代码注入,实现源码级‘无感’混淆',
143
- },
144
- ],
145
- }),
146
- install: () =>
147
- p.confirm({
148
- message: '是否现在自动安装依赖?',
149
- initialValue: true,
150
- }),
65
+ }),
66
+ shouldOverwrite: ({ results }) => {
67
+ const targetDir = path.resolve(process.cwd(), results.path)
68
+ if (fs.existsSync(targetDir) && fs.readdirSync(targetDir).length > 0) {
69
+ return p.confirm({ message: `目录已存在,是否清空?`, initialValue: false })
70
+ }
151
71
  },
152
- {
153
- onCancel: () => {
154
- p.cancel('操作取消')
155
- process.exit(0)
156
- },
72
+ framework: () => p.select({
73
+ message: '选择框架',
74
+ options: [
75
+ { value: 'next', label: 'Next.js 16', hint: 'React 19 + Tailwind v4 + Base UI' },
76
+ { value: 'react', label: 'React 19', hint: 'VitePlus + Compiler' },
77
+ { value: 'vue', label: 'Vue 3', hint: 'VitePlus + Optimized' },
78
+ ],
79
+ }),
80
+ cssEngine: ({ results }) => {
81
+ if (results.framework === 'next') return;
82
+ return p.select({
83
+ message: '选择 CSS',
84
+ options: [
85
+ { value: 'unocss', label: 'UnoCSS', hint: '⚡️ 战机级性能' },
86
+ { value: 'tailwind', label: 'Tailwind v4', hint: '🛡️ 装甲级稳定' },
87
+ ],
88
+ })
157
89
  },
158
- )
159
-
90
+ install: () => p.confirm({ message: '是否现在自动安装依赖?', initialValue: true }),
91
+ }, {
92
+ onCancel: () => { p.cancel('操作取消'); process.exit(0) }
93
+ })
160
94
  if (!project) process.exit(0)
161
95
 
162
96
  const targetDir = path.resolve(process.cwd(), project.path)
163
97
  const templateDir = path.resolve(__dirname, `template-${project.framework}`)
98
+ const isNext = project.framework === 'next'
164
99
  const isUno = project.cssEngine === 'unocss'
165
100
  const s = p.spinner()
166
101
 
167
- const checkVp = () => {
102
+ if (!isNext) {
168
103
  try {
169
104
  execSync('vp --version', { stdio: 'ignore' })
170
105
  } catch {
171
106
  p.log.error(pc.red('未检测到 VitePlus (vp) 环境'))
172
107
  p.note(
173
108
  pc.white(
174
- `请先安装 vp 工具链:\n${pc.cyan('npm install -g @voidzero-dev/vite-plus')}`
109
+ `React/Vue 模板依赖 vp 工具链:\n${pc.cyan('https://viteplus.dev/guide')}`
175
110
  ),
176
- '环境缺失',
111
+ '环境缺失'
177
112
  )
178
113
  process.exit(1)
179
114
  }
@@ -184,12 +119,11 @@ async function main() {
184
119
  try {
185
120
  if (project.shouldOverwrite) await fs.emptyDir(targetDir)
186
121
  else await fs.ensureDir(targetDir)
187
-
188
122
  await fs.copy(templateDir, targetDir)
189
123
 
190
- try {
191
- execSync('git init', { cwd: targetDir, stdio: 'ignore' })
192
- } catch { }
124
+ await fs.remove(path.join(targetDir, 'pnpm-workspace.yaml'))
125
+
126
+ try { execSync('git init', { cwd: targetDir, stdio: 'ignore' }) } catch { }
193
127
 
194
128
  const oldGit = path.join(targetDir, '_gitignore')
195
129
  if (fs.existsSync(oldGit)) await fs.move(oldGit, path.join(targetDir, '.gitignore'))
@@ -198,73 +132,49 @@ async function main() {
198
132
  const pkg = await fs.readJson(pkgPath)
199
133
  pkg.name = path.basename(targetDir)
200
134
 
201
- // ================= Next.js 分支逻辑 =================
202
- if (project.framework === 'next') {
203
- await fs.writeJson(pkgPath, pkg, { spaces: 2 })
204
-
205
- s.message(pc.green('检测到全栈环境,正在执行 pnpm install'))
206
-
207
- const installCmd = 'pnpm'
208
- try {
209
- execSync(`${installCmd} install`, { cwd: targetDir, stdio: 'ignore' })
210
-
211
- s.message(pc.green('使用 oxfmt 优化全栈代码结构'))
212
- execSync(`${installCmd} fmt`, { cwd: targetDir, stdio: 'ignore' })
213
-
214
- s.stop(pc.green('Next.js 全栈环境装配就绪'))
215
- } catch {
216
- s.stop(pc.red('依赖安装失败,请进入目录后手动安装'))
217
- }
218
-
219
- p.note(pc.cyan(`cd ${project.path}\npnpm dev`), '快速开始')
220
- p.outro(pc.magenta('✨ 极致全栈环境已就绪!'))
221
- return
222
- }
223
-
224
- checkVp()
225
-
226
135
  if (isUno) {
227
136
  pkg.devDependencies['unocss'] = 'latest'
228
137
  } else {
229
138
  pkg.devDependencies['tailwindcss'] = 'latest'
230
- pkg.devDependencies['@tailwindcss/vite'] = 'latest'
139
+ if (!isNext) pkg.devDependencies['@tailwindcss/vite'] = 'latest'
231
140
  }
232
141
 
233
- pkg.pnpm = {
234
- ...pkg.pnpm,
235
- overrides: {
236
- ...pkg.pnpm?.overrides,
237
- vite: 'npm:@voidzero-dev/vite-plus-core@latest',
238
- vitest: 'npm:@voidzero-dev/vite-plus-test@latest',
239
- },
142
+ if (!isNext) {
143
+ pkg.pnpm = {
144
+ ...pkg.pnpm,
145
+ overrides: {
146
+ vite: 'npm:@voidzero-dev/vite-plus-core@latest',
147
+ vitest: 'npm:@voidzero-dev/vite-plus-test@latest',
148
+ },
149
+ }
240
150
  }
241
151
  await fs.writeJson(pkgPath, pkg, { spaces: 2 })
242
152
 
243
- const mainFile = project.framework === 'vue' ? 'src/main.ts' : 'src/main.tsx'
244
- const mainPath = path.join(targetDir, mainFile)
245
- const viteConfigPath = path.join(targetDir, 'vite.config.ts')
246
- const stylePath = path.join(targetDir, 'src/style.css')
247
-
248
- let mainContent = await fs.readFile(mainPath, 'utf-8')
249
- let viteContent = await fs.readFile(viteConfigPath, 'utf-8')
250
-
251
- const appFile = project.framework === 'vue' ? 'src/App.vue' : 'src/App.tsx'
252
- const appPath = path.join(targetDir, appFile)
253
-
254
- if (project.framework === 'react') {
255
- await fs.writeFile(appPath, getReactAppTemplate(isUno))
256
- }
153
+ if (isNext) {
154
+ s.message(pc.green('检测到 Next.js 架构,正在同步核心配置...'))
155
+ } else {
156
+ const mainFile = project.framework === 'vue' ? 'src/main.ts' : 'src/main.tsx'
157
+ const mainPath = path.join(targetDir, mainFile)
158
+ const viteConfigPath = path.join(targetDir, 'vite.config.ts')
159
+ const stylePath = path.join(targetDir, 'src/style.css')
160
+ const appFile = project.framework === 'vue' ? 'src/App.vue' : 'src/App.tsx'
161
+ const appPath = path.join(targetDir, appFile)
162
+
163
+ let mainContent = await fs.readFile(mainPath, 'utf-8')
164
+ let viteContent = await fs.readFile(viteConfigPath, 'utf-8')
165
+
166
+ if (project.framework === 'react') {
167
+ await fs.writeFile(appPath, getReactAppTemplate(isUno))
168
+ }
257
169
 
258
- const pluginCode = isUno ? 'UnoCSS()' : 'tailwindcss()'
259
- const pluginImport = isUno
260
- ? "import UnoCSS from 'unocss/vite'\n"
261
- : "import tailwindcss from '@tailwindcss/vite'\n"
170
+ const pluginCode = isUno ? 'UnoCSS()' : 'tailwindcss()'
171
+ const pluginImport = isUno ? "import UnoCSS from 'unocss/vite'\n" : "import tailwindcss from '@tailwindcss/vite'\n"
262
172
 
263
- viteContent = pluginImport + viteContent
264
- viteContent = viteContent.replace('/* VITE_PLUS_PLUGINS */', `${pluginCode}, `)
173
+ viteContent = pluginImport + viteContent
174
+ viteContent = viteContent.replace('/* VITE_PLUS_PLUGINS */', `${pluginCode}, `)
265
175
 
266
- if (isUno) {
267
- const unoConfig = `import { defineConfig, presetWind3, transformerCompileClass } from 'unocss'
176
+ if (isUno) {
177
+ const unoConfig = `import { defineConfig, presetWind3, transformerCompileClass } from 'unocss'
268
178
 
269
179
  export default defineConfig({
270
180
  presets: [presetWind3()],
@@ -290,59 +200,52 @@ export default defineConfig({
290
200
  }),
291
201
  ],
292
202
  })\n`
293
- await fs.writeFile(path.join(targetDir, 'uno.config.ts'), unoConfig)
294
203
 
295
- mainContent = `import 'virtual:uno.css'\n` + mainContent
296
- if (fs.existsSync(stylePath)) await fs.remove(stylePath)
297
- } else {
298
- await fs.writeFile(stylePath, `@import "tailwindcss";`)
299
- mainContent = `import './style.css'\n` + mainContent
300
- }
204
+ await fs.writeFile(path.join(targetDir, 'uno.config.ts'), unoConfig)
301
205
 
302
- await fs.writeFile(mainPath, mainContent)
303
- await fs.writeFile(viteConfigPath, viteContent)
206
+ mainContent = `import 'virtual:uno.css'\n` + mainContent
207
+ if (fs.existsSync(stylePath)) await fs.remove(stylePath)
208
+ } else {
209
+ await fs.writeFile(stylePath, `@import "tailwindcss";`)
210
+ mainContent = `import './style.css'\n` + mainContent
211
+ }
212
+ await fs.writeFile(mainPath, mainContent)
213
+ await fs.writeFile(viteConfigPath, viteContent)
214
+ }
304
215
 
305
216
  const toRemove = ['pnpm-lock.yaml', 'node_modules', 'dist']
306
217
  await Promise.all(toRemove.map((file) => fs.remove(path.join(targetDir, file))))
307
218
 
308
- s.message(pc.green('架构装配完成,正在执行 vp install'))
309
-
310
- try {
311
- await new Promise((resolve, reject) => {
312
- const child = spawn('vp', ['install'], {
313
- cwd: targetDir,
314
- stdio: 'pipe',
315
- shell: process.platform === 'win32',
316
- })
317
-
318
- child.on('close', (code) => {
319
- if (code === 0) resolve()
320
- else reject(new Error(`Exit code: ${code}`))
321
- })
322
- })
323
-
324
- s.message(pc.green('正在执行 vp fmt 优化代码结构'))
325
- await new Promise((resolve) => setTimeout(resolve, 500))
219
+ const pkgManager = isNext ? 'pnpm' : 'vp'
220
+ const devCmd = isNext ? 'pnpm dev' : 'vp dev'
221
+ const fmtCmd = isNext ? 'pnpm fmt' : 'vp fmt'
326
222
 
223
+ if (project.install) {
224
+ s.message(pc.green(`正在执行 ${pkgManager} install`))
327
225
  try {
328
- execSync('vp fmt', { cwd: targetDir, stdio: 'ignore' })
329
- } catch { }
226
+ await new Promise((resolve, reject) => {
227
+ const child = spawn(pkgManager, ['install'], {
228
+ cwd: targetDir,
229
+ stdio: 'ignore',
230
+ shell: process.platform === 'win32',
231
+ })
232
+ child.on('close', (code) => code === 0 ? resolve() : reject())
233
+ })
330
234
 
331
- s.stop(pc.green('全套环境装配就绪'))
332
- } catch {
333
- s.stop(pc.red('自动安装失败'))
334
- p.log.warn('请进入目录后手动尝试执行 vp install')
235
+ s.message(pc.green(`正在执行 ${fmtCmd} 优化代码结构`))
236
+ await new Promise((r) => setTimeout(r, 500))
237
+ try { execSync(fmtCmd, { cwd: targetDir, stdio: 'ignore' }) } catch { }
238
+ } catch (err) {
239
+ p.log.warn('自动安装失败,请手动执行安装')
240
+ }
335
241
  }
336
242
 
337
- const nextSteps = project.install
338
- ? `cd ${project.path}\nvp dev`
339
- : `cd ${project.path}\nvp install\nvp dev`
243
+ s.stop(pc.green('全套环境装配就绪'))
340
244
 
245
+ const nextSteps = project.install ? `cd ${project.path}\n${devCmd}` : `cd ${project.path}\n${pkgManager} install\n${devCmd}`
341
246
  p.note(pc.cyan(nextSteps), '快速开始')
342
- p.outro(
343
- `${pc.magenta('✨ 已经为你准备好了极致的开发环境!')}\n` +
344
- `${pc.gray(`==== ${project.framework} + ${project.cssEngine} ====`)}`,
345
- )
247
+ p.outro(pc.magenta('✨ 已经为你准备好了极致的开发环境!'))
248
+
346
249
  } catch (err) {
347
250
  s.stop(pc.red('手术失败'))
348
251
  console.error(err)
@@ -350,4 +253,4 @@ export default defineConfig({
350
253
  }
351
254
  }
352
255
 
353
- main()
256
+ main()
package/package.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "create-lve",
3
- "version": "0.2.8",
3
+ "version": "0.2.10",
4
4
  "bin": {
5
5
  "create-lve": "index.js"
6
6
  },
7
7
  "files": [
8
8
  "index.js",
9
9
  "template-react",
10
- "template-vue",
11
- "template-next"
10
+ "template-next",
11
+ "template-vue"
12
12
  ],
13
13
  "type": "module",
14
14
  "scripts": {
@@ -0,0 +1,6 @@
1
+ {
2
+ "$schema": "./node_modules/oxfmt/configuration_schema.json",
3
+ "ignorePatterns": [],
4
+ "semi": false,
5
+ "singleQuote": true
6
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "$schema": "./node_modules/oxlint/configuration_schema.json",
3
+ "plugins": ["typescript", "unicorn", "oxc"],
4
+ "categories": {
5
+ "correctness": "error"
6
+ },
7
+ "rules": {},
8
+ "env": {
9
+ "builtin": true
10
+ }
11
+ }
@@ -0,0 +1,73 @@
1
+ {
2
+ // ==================== Formatter (OXC) ====================
3
+ "editor.defaultFormatter": "oxc.oxc-vscode",
4
+ "editor.formatOnSave": true,
5
+ "editor.formatOnSaveMode": "file",
6
+ "oxc.enable.oxfmt": true,
7
+
8
+ // 语言特定 Formatter
9
+ "[javascript]": {
10
+ "editor.defaultFormatter": "oxc.oxc-vscode"
11
+ },
12
+ "[typescript]": {
13
+ "editor.defaultFormatter": "oxc.oxc-vscode"
14
+ },
15
+ "[typescriptreact]": {
16
+ "editor.defaultFormatter": "oxc.oxc-vscode"
17
+ },
18
+ "[javascriptreact]": {
19
+ "editor.defaultFormatter": "oxc.oxc-vscode"
20
+ },
21
+ "[css]": {
22
+ "editor.defaultFormatter": "oxc.oxc-vscode"
23
+ },
24
+ "[tailwindcss]": {
25
+ "editor.defaultFormatter": "oxc.oxc-vscode"
26
+ },
27
+ "[json]": {
28
+ "editor.defaultFormatter": "oxc.oxc-vscode"
29
+ },
30
+ "[jsonc]": {
31
+ "editor.defaultFormatter": "oxc.oxc-vscode"
32
+ },
33
+ "[html]": {
34
+ "editor.defaultFormatter": "oxc.oxc-vscode"
35
+ },
36
+ "[vue]": {
37
+ "editor.defaultFormatter": "oxc.oxc-vscode"
38
+ },
39
+
40
+ // ==================== Next.js 专用 ====================
41
+ "workbench.editor.customLabels.patterns": {
42
+ "**/app/**/layout.{js,jsx,ts,tsx}": "${dirname}/layout",
43
+ "**/app/**/page.{js,jsx,ts,tsx}": "${dirname}/page",
44
+ "**/app/**/route.{js,jsx,ts,tsx}": "${dirname}/route",
45
+ "**/app/**/loading.{js,jsx,ts,tsx}": "${dirname}/loading",
46
+ "**/app/**/template.{js,jsx,ts,tsx}": "${dirname}/template",
47
+ "**/app/**/default.{js,jsx,ts,tsx}": "${dirname}/default",
48
+ "**/app/**/error.{js,jsx,ts,tsx}": "${dirname}/error",
49
+ "**/app/**/not-found.{js,jsx,ts,tsx}": "${dirname}/not-found"
50
+ },
51
+
52
+ // ==================== TypeScript(解决 Next.js TS 问题)===================
53
+ "typescript.tsdk": "node_modules/typescript/lib",
54
+ "typescript.enablePromptUseWorkspaceTsdk": true,
55
+
56
+ // ==================== Tailwind + shadcn/ui(强烈推荐)===================
57
+ "tailwindCSS.experimental.classRegex": [["cn\\(([^)]*)\\)", "(?:'|\"|`)([^']*)(?:'|\"|`)"]],
58
+ "tailwindCSS.includeLanguages": {
59
+ "typescript": "html",
60
+ "typescriptreact": "html"
61
+ },
62
+ "editor.quickSuggestions": {
63
+ "strings": "on"
64
+ },
65
+
66
+ // ==================== 其他项目通用 ====================
67
+ "git.openRepositoryInParentFolders": "always",
68
+ "js/ts.updateImportsOnFileMove.enabled": "always",
69
+ "diffEditor.ignoreTrimWhitespace": true,
70
+ "diffEditor.hideUnchangedRegions.enabled": true,
71
+ "editor.inlayHints.enabled": "on",
72
+ "js/ts.tsdk.path": "node_modules/typescript/lib"
73
+ }
@@ -0,0 +1,48 @@
1
+ # ⚡️ ox-next-blank
2
+
3
+ 这是一个为 **2026 前端标准**打造的极致极简 Next.js 模板。拒绝冗余,拒绝手动优化,追求工具链的瞬时反馈。
4
+
5
+ ## 🚀 核心技术栈
6
+
7
+ - **Framework:** [Next.js 16.2](https://nextjs.org/) (App Router)
8
+ - **Runtime:** [React 19.2](https://react.dev/) + **React Compiler** (自动 Memoization)
9
+ - **Toolchain:** [Oxc](https://oxc.rs/) (Oxlint & Oxfmt) - **比 Prettier/ESLint 快 50~100 倍**
10
+ - **Styling:** [Tailwind CSS 4](https://tailwindcss.com/) + **OKLCH** 颜色系统
11
+ - **UI Components:** [Base UI](https://base-ui.com/) + [Shadcn UI](https://ui.shadcn.com/)
12
+ - **Icons:** [Hugeicons](https://hugeicons.com/) (Free Version) & Lucide
13
+
14
+ ## ✨ 特性
15
+
16
+ - **Zero-Manual-Memo:** 依靠 React Compiler,彻底告别 `useMemo` 和 `useCallback`,代码回归纯粹逻辑。
17
+ - **Instant DX:** 依托 Rust 驱动的 Oxc,保存即格式化,检查即瞬间,再无等待感。
18
+ - **Hybrid Dashboard:** 预设 RSC 示例,内置开发环境专用的 `DevBoundary` 高亮(生产环境自动剔除)。
19
+ - **Modern Aesthetic:** 极致的“陶瓷白”视觉风格,基于 CSS 变量的动态主题切换。
20
+
21
+ ## 🛠 常用命令
22
+
23
+ | 命令 | 说明 | 性能表现 (参考) |
24
+ | :----------- | :---------------------------- | :-------------- |
25
+ | `pnpm dev` | 启动 Next.js 开发服务器 | - |
26
+ | `pnpm fmt` | 使用 **oxfmt** 格式化全量代码 | **~80ms** |
27
+ | `pnpm lint` | 使用 **oxlint** 执行静态检查 | **~4ms** |
28
+ | `pnpm build` | 生产环境构建 | - |
29
+
30
+ ## 📁 目录结构
31
+
32
+ ```text
33
+ .
34
+ ├── app/ # Next.js App Router 核心
35
+ ├── components/ # UI 组件 (Shadcn + Base UI)
36
+ ├── lib/
37
+ │ └── utils.ts # 核心工具函数 (cn, twMerge)
38
+ ├── next.config.ts # 开启 React Compiler 配置
39
+ └── package.json # 基于 Oxc 的极致脚本定义
40
+ ```
41
+
42
+ ## 📝 开发备忘录
43
+
44
+ RSC 可视化: 开发模式下会自动显示 rsc-boundary 高亮,通过 components/DevBoundary 逻辑在 build 时自动安全剥离。
45
+
46
+ ## License: MIT
47
+
48
+ ## Created by: ieuforu (2026)
@@ -0,0 +1,41 @@
1
+ # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2
+
3
+ # dependencies
4
+ /node_modules
5
+ /.pnp
6
+ .pnp.*
7
+ .yarn/*
8
+ !.yarn/patches
9
+ !.yarn/plugins
10
+ !.yarn/releases
11
+ !.yarn/versions
12
+
13
+ # testing
14
+ /coverage
15
+
16
+ # next.js
17
+ /.next/
18
+ /out/
19
+
20
+ # production
21
+ /build
22
+
23
+ # misc
24
+ .DS_Store
25
+ *.pem
26
+
27
+ # debug
28
+ npm-debug.log*
29
+ yarn-debug.log*
30
+ yarn-error.log*
31
+ .pnpm-debug.log*
32
+
33
+ # env files (can opt-in for committing if needed)
34
+ .env*
35
+
36
+ # vercel
37
+ .vercel
38
+
39
+ # typescript
40
+ *.tsbuildinfo
41
+ next-env.d.ts
Binary file
@@ -0,0 +1,130 @@
1
+ @import 'tailwindcss';
2
+ @import 'tw-animate-css';
3
+ @import 'shadcn/tailwind.css';
4
+
5
+ @custom-variant dark (&:is(.dark *));
6
+
7
+ @theme inline {
8
+ --color-background: var(--background);
9
+ --color-foreground: var(--foreground);
10
+ --font-sans: var(--font-sans);
11
+ --font-mono: var(--font-geist-mono);
12
+ --font-heading: var(--font-sans);
13
+ --color-sidebar-ring: var(--sidebar-ring);
14
+ --color-sidebar-border: var(--sidebar-border);
15
+ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
16
+ --color-sidebar-accent: var(--sidebar-accent);
17
+ --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
18
+ --color-sidebar-primary: var(--sidebar-primary);
19
+ --color-sidebar-foreground: var(--sidebar-foreground);
20
+ --color-sidebar: var(--sidebar);
21
+ --color-chart-5: var(--chart-5);
22
+ --color-chart-4: var(--chart-4);
23
+ --color-chart-3: var(--chart-3);
24
+ --color-chart-2: var(--chart-2);
25
+ --color-chart-1: var(--chart-1);
26
+ --color-ring: var(--ring);
27
+ --color-input: var(--input);
28
+ --color-border: var(--border);
29
+ --color-destructive: var(--destructive);
30
+ --color-accent-foreground: var(--accent-foreground);
31
+ --color-accent: var(--accent);
32
+ --color-muted-foreground: var(--muted-foreground);
33
+ --color-muted: var(--muted);
34
+ --color-secondary-foreground: var(--secondary-foreground);
35
+ --color-secondary: var(--secondary);
36
+ --color-primary-foreground: var(--primary-foreground);
37
+ --color-primary: var(--primary);
38
+ --color-popover-foreground: var(--popover-foreground);
39
+ --color-popover: var(--popover);
40
+ --color-card-foreground: var(--card-foreground);
41
+ --color-card: var(--card);
42
+ --radius-sm: calc(var(--radius) * 0.6);
43
+ --radius-md: calc(var(--radius) * 0.8);
44
+ --radius-lg: var(--radius);
45
+ --radius-xl: calc(var(--radius) * 1.4);
46
+ --radius-2xl: calc(var(--radius) * 1.8);
47
+ --radius-3xl: calc(var(--radius) * 2.2);
48
+ --radius-4xl: calc(var(--radius) * 2.6);
49
+ }
50
+
51
+ :root {
52
+ --background: oklch(1 0 0);
53
+ --foreground: oklch(0.153 0.006 107.1);
54
+ --card: oklch(1 0 0);
55
+ --card-foreground: oklch(0.153 0.006 107.1);
56
+ --popover: oklch(1 0 0);
57
+ --popover-foreground: oklch(0.153 0.006 107.1);
58
+ --primary: oklch(0.228 0.013 107.4);
59
+ --primary-foreground: oklch(0.988 0.003 106.5);
60
+ --secondary: oklch(0.966 0.005 106.5);
61
+ --secondary-foreground: oklch(0.228 0.013 107.4);
62
+ --muted: oklch(0.966 0.005 106.5);
63
+ --muted-foreground: oklch(0.58 0.031 107.3);
64
+ --accent: oklch(0.966 0.005 106.5);
65
+ --accent-foreground: oklch(0.228 0.013 107.4);
66
+ --destructive: oklch(0.577 0.245 27.325);
67
+ --border: oklch(0.93 0.007 106.5);
68
+ --input: oklch(0.93 0.007 106.5);
69
+ --ring: oklch(0.737 0.021 106.9);
70
+ --chart-1: oklch(0.809 0.105 251.813);
71
+ --chart-2: oklch(0.623 0.214 259.815);
72
+ --chart-3: oklch(0.546 0.245 262.881);
73
+ --chart-4: oklch(0.488 0.243 264.376);
74
+ --chart-5: oklch(0.424 0.199 265.638);
75
+ --radius: 0.625rem;
76
+ --sidebar: oklch(0.988 0.003 106.5);
77
+ --sidebar-foreground: oklch(0.153 0.006 107.1);
78
+ --sidebar-primary: oklch(0.228 0.013 107.4);
79
+ --sidebar-primary-foreground: oklch(0.988 0.003 106.5);
80
+ --sidebar-accent: oklch(0.966 0.005 106.5);
81
+ --sidebar-accent-foreground: oklch(0.228 0.013 107.4);
82
+ --sidebar-border: oklch(0.93 0.007 106.5);
83
+ --sidebar-ring: oklch(0.737 0.021 106.9);
84
+ }
85
+
86
+ .dark {
87
+ --background: oklch(0.153 0.006 107.1);
88
+ --foreground: oklch(0.988 0.003 106.5);
89
+ --card: oklch(0.228 0.013 107.4);
90
+ --card-foreground: oklch(0.988 0.003 106.5);
91
+ --popover: oklch(0.228 0.013 107.4);
92
+ --popover-foreground: oklch(0.988 0.003 106.5);
93
+ --primary: oklch(0.93 0.007 106.5);
94
+ --primary-foreground: oklch(0.228 0.013 107.4);
95
+ --secondary: oklch(0.286 0.016 107.4);
96
+ --secondary-foreground: oklch(0.988 0.003 106.5);
97
+ --muted: oklch(0.286 0.016 107.4);
98
+ --muted-foreground: oklch(0.737 0.021 106.9);
99
+ --accent: oklch(0.286 0.016 107.4);
100
+ --accent-foreground: oklch(0.988 0.003 106.5);
101
+ --destructive: oklch(0.704 0.191 22.216);
102
+ --border: oklch(1 0 0 / 10%);
103
+ --input: oklch(1 0 0 / 15%);
104
+ --ring: oklch(0.58 0.031 107.3);
105
+ --chart-1: oklch(0.809 0.105 251.813);
106
+ --chart-2: oklch(0.623 0.214 259.815);
107
+ --chart-3: oklch(0.546 0.245 262.881);
108
+ --chart-4: oklch(0.488 0.243 264.376);
109
+ --chart-5: oklch(0.424 0.199 265.638);
110
+ --sidebar: oklch(0.228 0.013 107.4);
111
+ --sidebar-foreground: oklch(0.988 0.003 106.5);
112
+ --sidebar-primary: oklch(0.488 0.243 264.376);
113
+ --sidebar-primary-foreground: oklch(0.988 0.003 106.5);
114
+ --sidebar-accent: oklch(0.286 0.016 107.4);
115
+ --sidebar-accent-foreground: oklch(0.988 0.003 106.5);
116
+ --sidebar-border: oklch(1 0 0 / 10%);
117
+ --sidebar-ring: oklch(0.58 0.031 107.3);
118
+ }
119
+
120
+ @layer base {
121
+ * {
122
+ @apply border-border outline-ring/50;
123
+ }
124
+ body {
125
+ @apply bg-background text-foreground;
126
+ }
127
+ html {
128
+ @apply font-sans;
129
+ }
130
+ }
@@ -0,0 +1,35 @@
1
+ import type { Metadata } from 'next'
2
+ import { Roboto } from 'next/font/google'
3
+ import './globals.css'
4
+ import { cn } from '@/lib/utils'
5
+ import { RscBoundaryProvider } from 'rsc-boundary'
6
+
7
+ const roboto = Roboto({ subsets: ['latin'], variable: '--font-sans' })
8
+
9
+ export const metadata: Metadata = {
10
+ title: 'Next.js App',
11
+ description: 'Generated by create next app',
12
+ }
13
+
14
+ export default function RootLayout({
15
+ children,
16
+ }: Readonly<{
17
+ children: React.ReactNode
18
+ }>) {
19
+ const isDev = process.env.NODE_ENV === 'development'
20
+
21
+ if (!isDev) {
22
+ return (
23
+ <html lang="en" className={cn('h-full', 'antialiased', 'font-sans', roboto.variable)}>
24
+ <body className="min-h-full flex flex-col">{children}</body>
25
+ </html>
26
+ )
27
+ }
28
+ return (
29
+ <html lang="en" className={cn('h-full', 'antialiased', 'font-sans', roboto.variable)}>
30
+ <body className="min-h-full flex flex-col">
31
+ <RscBoundaryProvider>{children}</RscBoundaryProvider>
32
+ </body>
33
+ </html>
34
+ )
35
+ }
@@ -0,0 +1,32 @@
1
+ import { DevBoundary } from '@/components/DevBoundary'
2
+ import Counter from '../components/Counter'
3
+
4
+ export default function Home() {
5
+ const isDev = process.env.NODE_ENV === 'development'
6
+
7
+ const content = (
8
+ <div className="relative group max-w-lg w-full p-10 rounded-[2.5rem] bg-white border border-slate-100 shadow-[0_12px_40px_rgb(0,0,0,0.03)]">
9
+ <div className="relative z-10">
10
+ <header className="mb-8">
11
+ <span className="inline-flex items-center gap-1.5 px-3 py-1 text-[11px] font-medium bg-slate-100 text-slate-500 rounded-full border border-slate-200">
12
+ <span className="w-1.5 h-1.5 rounded-full bg-emerald-500"></span>
13
+ Server Side Rendered
14
+ </span>
15
+ <p className="mt-4 text-slate-600 leading-relaxed max-w-md">
16
+ Lorem ipsum dolor sit amet consectetur adipisicing elit. Consequuntur in inventore
17
+ veniam voluptatem repellat quisquam nisi quasi sequi hic officia, soluta quibusdam aut,
18
+ molestias unde recusandae, consectetur impedit doloremque aspernatur?
19
+ </p>
20
+ </header>
21
+
22
+ <Counter />
23
+ </div>
24
+ </div>
25
+ )
26
+
27
+ return (
28
+ <main className="min-h-screen bg-slate-50/50 flex items-center justify-center p-6 antialiased">
29
+ {isDev ? <DevBoundary label="Server Boundary Demo">{content}</DevBoundary> : content}
30
+ </main>
31
+ )
32
+ }
@@ -0,0 +1,37 @@
1
+ 'use client'
2
+
3
+ import { useState } from 'react'
4
+ import { Button } from '@/components/ui/button'
5
+
6
+ export default function Counter() {
7
+ const [count, setCount] = useState(0)
8
+
9
+ const onIncrement = () => setCount((prev) => prev + 1)
10
+ const onDecrement = () => setCount((prev) => prev - 1)
11
+ const onReset = () => setCount(0)
12
+
13
+ return (
14
+ <div className="p-6 bg-white border border-slate-100 rounded-3xl">
15
+ <div className="flex flex-col items-center gap-6">
16
+ <h3 className="text-6xl font-black tracking-tighter text-slate-900">{count}</h3>
17
+
18
+ <div className="flex gap-3">
19
+ <Button disabled={count === 0} onClick={onDecrement} variant="outline" size="lg">
20
+ Decrement
21
+ </Button>
22
+
23
+ <Button onClick={onIncrement} size="lg">
24
+ Increment
25
+ </Button>
26
+ </div>
27
+
28
+ <button
29
+ onClick={onReset}
30
+ className="text-xs font-bold text-slate-400 hover:text-slate-900 transition-colors uppercase tracking-widest"
31
+ >
32
+ Reset
33
+ </button>
34
+ </div>
35
+ </div>
36
+ )
37
+ }
@@ -0,0 +1,14 @@
1
+ import { RscServerBoundaryMarker } from 'rsc-boundary'
2
+
3
+ interface DevBoundaryProps {
4
+ children: React.ReactNode
5
+ label: string
6
+ }
7
+
8
+ export function DevBoundary({ children, label }: DevBoundaryProps) {
9
+ if (process.env.NODE_ENV !== 'development') {
10
+ return <>{children}</>
11
+ }
12
+
13
+ return <RscServerBoundaryMarker label={label}>{children}</RscServerBoundaryMarker>
14
+ }
@@ -0,0 +1,58 @@
1
+ import { Button as ButtonPrimitive } from '@base-ui/react/button'
2
+ import { cva, type VariantProps } from 'class-variance-authority'
3
+
4
+ import { cn } from '@/lib/utils'
5
+
6
+ const buttonVariants = cva(
7
+ "group/button inline-flex shrink-0 items-center justify-center rounded-lg border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
8
+ {
9
+ variants: {
10
+ variant: {
11
+ default: 'bg-primary text-primary-foreground [a]:hover:bg-primary/80',
12
+ outline:
13
+ 'border-border bg-background hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50',
14
+ secondary:
15
+ 'bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground',
16
+ ghost:
17
+ 'hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50',
18
+ destructive:
19
+ 'bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40',
20
+ link: 'text-primary underline-offset-4 hover:underline',
21
+ },
22
+ size: {
23
+ default:
24
+ 'h-8 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2',
25
+ xs: "h-6 gap-1 rounded-[min(var(--radius-md),10px)] px-2 text-xs in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3",
26
+ sm: "h-7 gap-1 rounded-[min(var(--radius-md),12px)] px-2.5 text-[0.8rem] in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5",
27
+ lg: 'h-9 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2',
28
+ icon: 'size-8',
29
+ 'icon-xs':
30
+ "size-6 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-lg [&_svg:not([class*='size-'])]:size-3",
31
+ 'icon-sm':
32
+ 'size-7 rounded-[min(var(--radius-md),12px)] in-data-[slot=button-group]:rounded-lg',
33
+ 'icon-lg': 'size-9',
34
+ },
35
+ },
36
+ defaultVariants: {
37
+ variant: 'default',
38
+ size: 'default',
39
+ },
40
+ },
41
+ )
42
+
43
+ function Button({
44
+ className,
45
+ variant = 'default',
46
+ size = 'default',
47
+ ...props
48
+ }: ButtonPrimitive.Props & VariantProps<typeof buttonVariants>) {
49
+ return (
50
+ <ButtonPrimitive
51
+ data-slot="button"
52
+ className={cn(buttonVariants({ variant, size, className }))}
53
+ {...props}
54
+ />
55
+ )
56
+ }
57
+
58
+ export { Button, buttonVariants }
@@ -0,0 +1,25 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema.json",
3
+ "style": "base-nova",
4
+ "rsc": true,
5
+ "tsx": true,
6
+ "tailwind": {
7
+ "config": "",
8
+ "css": "app/globals.css",
9
+ "baseColor": "olive",
10
+ "cssVariables": true,
11
+ "prefix": ""
12
+ },
13
+ "iconLibrary": "hugeicons",
14
+ "rtl": false,
15
+ "aliases": {
16
+ "components": "@/components",
17
+ "utils": "@/lib/utils",
18
+ "ui": "@/components/ui",
19
+ "lib": "@/lib",
20
+ "hooks": "@/hooks"
21
+ },
22
+ "menuColor": "default-translucent",
23
+ "menuAccent": "subtle",
24
+ "registries": {}
25
+ }
@@ -0,0 +1,18 @@
1
+ import { defineConfig, globalIgnores } from 'eslint/config'
2
+ import nextVitals from 'eslint-config-next/core-web-vitals'
3
+ import nextTs from 'eslint-config-next/typescript'
4
+
5
+ const eslintConfig = defineConfig([
6
+ ...nextVitals,
7
+ ...nextTs,
8
+ // Override default ignores of eslint-config-next.
9
+ globalIgnores([
10
+ // Default ignores of eslint-config-next:
11
+ '.next/**',
12
+ 'out/**',
13
+ 'build/**',
14
+ 'next-env.d.ts',
15
+ ]),
16
+ ])
17
+
18
+ export default eslintConfig
@@ -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
+ }
@@ -0,0 +1,6 @@
1
+ /// <reference types="next" />
2
+ /// <reference types="next/image-types/global" />
3
+ import "./.next/types/routes.d.ts";
4
+
5
+ // NOTE: This file should not be edited
6
+ // see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
@@ -0,0 +1,12 @@
1
+ import type { NextConfig } from 'next'
2
+
3
+ const isDev = process.env.NODE_ENV === 'development'
4
+
5
+ const nextConfig: NextConfig = {
6
+ reactCompiler: true,
7
+ env: {
8
+ ENABLE_RSC_VISUALIZER: isDev ? 'true' : 'false',
9
+ },
10
+ }
11
+
12
+ export default nextConfig
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "my-app",
3
+ "version": "0.0.0",
4
+ "private": true,
5
+ "scripts": {
6
+ "dev": "next dev",
7
+ "build": "next build",
8
+ "start": "next start",
9
+ "lint": "oxlint",
10
+ "lint:fix": "oxlint --fix",
11
+ "fmt": "oxfmt",
12
+ "fmt:check": "oxfmt --check"
13
+ },
14
+ "dependencies": {
15
+ "@base-ui/react": "^1.4.1",
16
+ "@hugeicons/core-free-icons": "^4.1.1",
17
+ "@hugeicons/react": "^1.1.6",
18
+ "class-variance-authority": "^0.7.1",
19
+ "clsx": "^2.1.1",
20
+ "next": "16.2.4",
21
+ "react": "19.2.4",
22
+ "react-dom": "19.2.4",
23
+ "rsc-boundary": "^0.2.0",
24
+ "shadcn": "^4.4.0",
25
+ "tailwind-merge": "^3.5.0",
26
+ "tw-animate-css": "^1.4.0"
27
+ },
28
+ "devDependencies": {
29
+ "@tailwindcss/postcss": "^4",
30
+ "@types/node": "^20",
31
+ "@types/react": "^19",
32
+ "@types/react-dom": "^19",
33
+ "babel-plugin-react-compiler": "^1.0.0",
34
+ "oxfmt": "^0.46.0",
35
+ "oxlint": "^1.61.0",
36
+ "tailwindcss": "^4",
37
+ "typescript": "^5"
38
+ }
39
+ }
@@ -0,0 +1,3 @@
1
+ ignoredBuiltDependencies:
2
+ - sharp
3
+ - unrs-resolver
@@ -0,0 +1,7 @@
1
+ const config = {
2
+ plugins: {
3
+ '@tailwindcss/postcss': {},
4
+ },
5
+ }
6
+
7
+ export default config
@@ -0,0 +1 @@
1
+ <svg fill="none" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M14.5 13.5V5.41a1 1 0 0 0-.3-.7L9.8.29A1 1 0 0 0 9.08 0H1.5v13.5A2.5 2.5 0 0 0 4 16h8a2.5 2.5 0 0 0 2.5-2.5m-1.5 0v-7H8v-5H3v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1M9.5 5V2.12L12.38 5zM5.13 5h-.62v1.25h2.12V5zm-.62 3h7.12v1.25H4.5zm.62 3h-.62v1.25h7.12V11z" clip-rule="evenodd" fill="#666" fill-rule="evenodd"/></svg>
@@ -0,0 +1 @@
1
+ <svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g clip-path="url(#a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.27 14.1a6.5 6.5 0 0 0 3.67-3.45q-1.24.21-2.7.34-.31 1.83-.97 3.1M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m.48-1.52a7 7 0 0 1-.96 0H7.5a4 4 0 0 1-.84-1.32q-.38-.89-.63-2.08a40 40 0 0 0 3.92 0q-.25 1.2-.63 2.08a4 4 0 0 1-.84 1.31zm2.94-4.76q1.66-.15 2.95-.43a7 7 0 0 0 0-2.58q-1.3-.27-2.95-.43a18 18 0 0 1 0 3.44m-1.27-3.54a17 17 0 0 1 0 3.64 39 39 0 0 1-4.3 0 17 17 0 0 1 0-3.64 39 39 0 0 1 4.3 0m1.1-1.17q1.45.13 2.69.34a6.5 6.5 0 0 0-3.67-3.44q.65 1.26.98 3.1M8.48 1.5l.01.02q.41.37.84 1.31.38.89.63 2.08a40 40 0 0 0-3.92 0q.25-1.2.63-2.08a4 4 0 0 1 .85-1.32 7 7 0 0 1 .96 0m-2.75.4a6.5 6.5 0 0 0-3.67 3.44 29 29 0 0 1 2.7-.34q.31-1.83.97-3.1M4.58 6.28q-1.66.16-2.95.43a7 7 0 0 0 0 2.58q1.3.27 2.95.43a18 18 0 0 1 0-3.44m.17 4.71q-1.45-.12-2.69-.34a6.5 6.5 0 0 0 3.67 3.44q-.65-1.27-.98-3.1" fill="#666"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>
@@ -0,0 +1 @@
1
+ <svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1155 1000"><path d="m577.3 0 577.4 1000H0z" fill="#fff"/></svg>
@@ -0,0 +1 @@
1
+ <svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 2.5h13v10a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1zM0 1h16v11.5a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5zm3.75 4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5M7 4.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0m1.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5" fill="#666"/></svg>
@@ -0,0 +1,34 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2017",
4
+ "lib": ["dom", "dom.iterable", "esnext"],
5
+ "allowJs": true,
6
+ "skipLibCheck": true,
7
+ "strict": true,
8
+ "noEmit": true,
9
+ "esModuleInterop": true,
10
+ "module": "esnext",
11
+ "moduleResolution": "bundler",
12
+ "resolveJsonModule": true,
13
+ "isolatedModules": true,
14
+ "jsx": "react-jsx",
15
+ "incremental": true,
16
+ "plugins": [
17
+ {
18
+ "name": "next"
19
+ }
20
+ ],
21
+ "paths": {
22
+ "@/*": ["./*"]
23
+ }
24
+ },
25
+ "include": [
26
+ "next-env.d.ts",
27
+ "**/*.ts",
28
+ "**/*.tsx",
29
+ ".next/types/**/*.ts",
30
+ ".next/dev/types/**/*.ts",
31
+ "**/*.mts"
32
+ ],
33
+ "exclude": ["node_modules"]
34
+ }
@@ -52,9 +52,5 @@
52
52
  "js/ts.updateImportsOnFileMove.enabled": "always",
53
53
  "diffEditor.ignoreTrimWhitespace": true,
54
54
  "diffEditor.hideUnchangedRegions.enabled": true,
55
-
56
- "editor.codeActionsOnSave": {
57
- "source.format.oxc": "always"
58
- },
59
55
  "editor.inlayHints.enabled": "on"
60
56
  }