diff --git a/docs/ui/nextjs-admin-panel.md b/docs/ui/nextjs-admin-panel.md
index a47f252..3401c06 100644
--- a/docs/ui/nextjs-admin-panel.md
+++ b/docs/ui/nextjs-admin-panel.md
@@ -46,3 +46,6 @@ styles/
```
生成的 `out/` 目录即可作为纯静态网站部署。
+
+## 示例代码结构
+本仓库 `ui/nextjs` 目录提供了一套最简参考实现,可直接 `npm run build && npx next export` 编译到 `ui/dist` 供 Go 服务嵌入。
diff --git a/ui/nextjs/app/agent/page.tsx b/ui/nextjs/app/agent/page.tsx
new file mode 100644
index 0000000..eeddc5c
--- /dev/null
+++ b/ui/nextjs/app/agent/page.tsx
@@ -0,0 +1,10 @@
+export const dynamic = 'force-static'
+
+export default function AgentPage() {
+ return (
+
+
Agent Management
+
Manage node agents here.
+
+ )
+}
diff --git a/ui/nextjs/app/api/page.tsx b/ui/nextjs/app/api/page.tsx
new file mode 100644
index 0000000..25d6502
--- /dev/null
+++ b/ui/nextjs/app/api/page.tsx
@@ -0,0 +1,10 @@
+export const dynamic = 'force-static'
+
+export default function APIPage() {
+ return (
+
+
API Status
+
View backend API health here.
+
+ )
+}
diff --git a/ui/nextjs/app/layout.tsx b/ui/nextjs/app/layout.tsx
new file mode 100644
index 0000000..6487a5e
--- /dev/null
+++ b/ui/nextjs/app/layout.tsx
@@ -0,0 +1,15 @@
+import '../styles/globals.css'
+import Sidebar from '../components/Sidebar'
+
+export const dynamic = 'force-static'
+
+export default function RootLayout({ children }: { children: React.ReactNode }) {
+ return (
+
+
+
+ {children}
+
+
+ )
+}
diff --git a/ui/nextjs/app/page.tsx b/ui/nextjs/app/page.tsx
new file mode 100644
index 0000000..01e8650
--- /dev/null
+++ b/ui/nextjs/app/page.tsx
@@ -0,0 +1,10 @@
+export const dynamic = 'force-static'
+
+export default function Home() {
+ return (
+
+
Dashboard
+
Welcome to XControl admin panel.
+
+ )
+}
diff --git a/ui/nextjs/app/subscription/page.tsx b/ui/nextjs/app/subscription/page.tsx
new file mode 100644
index 0000000..271699e
--- /dev/null
+++ b/ui/nextjs/app/subscription/page.tsx
@@ -0,0 +1,10 @@
+export const dynamic = 'force-static'
+
+export default function SubscriptionPage() {
+ return (
+
+
Subscription
+
Manage vless subscriptions.
+
+ )
+}
diff --git a/ui/nextjs/app/xray/page.tsx b/ui/nextjs/app/xray/page.tsx
new file mode 100644
index 0000000..b286c1c
--- /dev/null
+++ b/ui/nextjs/app/xray/page.tsx
@@ -0,0 +1,10 @@
+export const dynamic = 'force-static'
+
+export default function XRayPage() {
+ return (
+
+
XRay
+
Control XRay configuration.
+
+ )
+}
diff --git a/ui/nextjs/components/Card.tsx b/ui/nextjs/components/Card.tsx
new file mode 100644
index 0000000..a3cab47
--- /dev/null
+++ b/ui/nextjs/components/Card.tsx
@@ -0,0 +1,7 @@
+export default function Card({ children }: { children: React.ReactNode }) {
+ return (
+
+ {children}
+
+ )
+}
diff --git a/ui/nextjs/components/Header.tsx b/ui/nextjs/components/Header.tsx
new file mode 100644
index 0000000..940bcde
--- /dev/null
+++ b/ui/nextjs/components/Header.tsx
@@ -0,0 +1,7 @@
+export default function Header() {
+ return (
+
+ )
+}
diff --git a/ui/nextjs/components/Sidebar.tsx b/ui/nextjs/components/Sidebar.tsx
new file mode 100644
index 0000000..132d6e9
--- /dev/null
+++ b/ui/nextjs/components/Sidebar.tsx
@@ -0,0 +1,21 @@
+import Link from 'next/link'
+
+const menu = [
+ { href: '/', label: 'Home' },
+ { href: '/agent', label: 'Agent' },
+ { href: '/api', label: 'API' },
+ { href: '/subscription', label: 'Subscription' },
+ { href: '/xray', label: 'XRay' },
+]
+
+export default function Sidebar() {
+ return (
+
+ )
+}
diff --git a/ui/nextjs/lib/api.ts b/ui/nextjs/lib/api.ts
new file mode 100644
index 0000000..dc013ae
--- /dev/null
+++ b/ui/nextjs/lib/api.ts
@@ -0,0 +1,5 @@
+export async function fetchJSON(url: string): Promise {
+ const res = await fetch(url)
+ if (!res.ok) throw new Error(`API error: ${res.status}`)
+ return res.json() as Promise
+}
diff --git a/ui/nextjs/next-env.d.ts b/ui/nextjs/next-env.d.ts
new file mode 100644
index 0000000..84ab714
--- /dev/null
+++ b/ui/nextjs/next-env.d.ts
@@ -0,0 +1,4 @@
+///
+///
+
+// NOTE: This file should not be edited
diff --git a/ui/nextjs/next.config.mjs b/ui/nextjs/next.config.mjs
new file mode 100644
index 0000000..dba2ea7
--- /dev/null
+++ b/ui/nextjs/next.config.mjs
@@ -0,0 +1,3 @@
+export default {
+ output: 'export',
+}
diff --git a/ui/nextjs/postcss.config.js b/ui/nextjs/postcss.config.js
new file mode 100644
index 0000000..33ad091
--- /dev/null
+++ b/ui/nextjs/postcss.config.js
@@ -0,0 +1,6 @@
+module.exports = {
+ plugins: {
+ tailwindcss: {},
+ autoprefixer: {},
+ },
+}
diff --git a/ui/nextjs/styles/globals.css b/ui/nextjs/styles/globals.css
new file mode 100644
index 0000000..a3ff494
--- /dev/null
+++ b/ui/nextjs/styles/globals.css
@@ -0,0 +1,7 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
+
+body {
+ @apply text-gray-800;
+}
diff --git a/ui/nextjs/tailwind.config.js b/ui/nextjs/tailwind.config.js
new file mode 100644
index 0000000..b86de8d
--- /dev/null
+++ b/ui/nextjs/tailwind.config.js
@@ -0,0 +1,11 @@
+/** @type {import('tailwindcss').Config} */
+module.exports = {
+ content: [
+ './app/**/*.{ts,tsx}',
+ './components/**/*.{ts,tsx}',
+ ],
+ theme: {
+ extend: {},
+ },
+ plugins: [],
+}
diff --git a/ui/nextjs/tsconfig.json b/ui/nextjs/tsconfig.json
new file mode 100644
index 0000000..ac6383d
--- /dev/null
+++ b/ui/nextjs/tsconfig.json
@@ -0,0 +1,24 @@
+{
+ "compilerOptions": {
+ "target": "es5",
+ "lib": ["dom", "dom.iterable", "esnext"],
+ "allowJs": false,
+ "skipLibCheck": true,
+ "strict": true,
+ "forceConsistentCasingInFileNames": true,
+ "noEmit": true,
+ "esModuleInterop": true,
+ "module": "esnext",
+ "moduleResolution": "bundler",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "jsx": "preserve",
+ "incremental": true,
+ "paths": {
+ "@/*": ["./*"]
+ },
+ "types": ["node"]
+ },
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
+ "exclude": ["node_modules"]
+}