diff --git a/src/app/globals.css b/src/app/globals.css index e3734be..311840f 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -1,42 +1,285 @@ +@import url("https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap"); + :root { - --background: #ffffff; - --foreground: #171717; -} + --bg-color: #0d1117; + --bg-gradient: radial-gradient(circle at top right, #1a2333, #0d1117); + --text-primary: #e6edf3; + --text-secondary: #8b949e; + --accent-color: #58a6ff; + --accent-hover: #3182ce; + --panel-bg: rgba(22, 27, 34, 0.65); + --panel-border: rgba(48, 54, 61, 0.5); + --input-bg: rgba(13, 17, 23, 0.7); + --input-border: #30363d; + --input-focus: #58a6ff; + --success-color: #238636; + --error-color: #f85149; -@media (prefers-color-scheme: dark) { - :root { - --background: #0a0a0a; - --foreground: #ededed; - } -} + --glass-blur: blur(12px); + --glass-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.37); -html, -body { - max-width: 100vw; - overflow-x: hidden; -} - -body { - color: var(--foreground); - background: var(--background); - font-family: Arial, Helvetica, sans-serif; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; + --transition-speed: 0.2s; } * { box-sizing: border-box; - padding: 0; margin: 0; + padding: 0; } -a { - color: inherit; - text-decoration: none; +body { + font-family: + "Inter", + -apple-system, + BlinkMacSystemFont, + "Segoe UI", + Roboto, + Helvetica, + Arial, + sans-serif; + background-color: var(--bg-color); + background-image: var(--bg-gradient); + color: var(--text-primary); + min-height: 100vh; + line-height: 1.5; + -webkit-font-smoothing: antialiased; } -@media (prefers-color-scheme: dark) { - html { - color-scheme: dark; +main { + max-width: 1000px; + margin: 0 auto; + padding: 3rem 1.5rem; +} + +h1 { + font-size: 2.5rem; + font-weight: 700; + margin-bottom: 0.5rem; + background: linear-gradient(135deg, #58a6ff 0%, #bc8cff 100%); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + text-align: center; +} + +.subtitle { + text-align: center; + color: var(--text-secondary); + font-size: 1.1rem; + margin-bottom: 3rem; + font-weight: 300; +} + +/* Glassmorphism Panel Container */ +.glass-panel { + background: var(--panel-bg); + backdrop-filter: var(--glass-blur); + -webkit-backdrop-filter: var(--glass-blur); + border: 1px solid var(--panel-border); + border-radius: 16px; + padding: 2.5rem; + box-shadow: var(--glass-shadow); + display: flex; + flex-direction: column; + gap: 2rem; + animation: fadeIn 0.6s ease-out; +} + +@media (min-width: 768px) { + .glass-panel { + flex-direction: row; + align-items: stretch; + } +} + +.form-section { + flex: 1; + display: flex; + flex-direction: column; + gap: 1.5rem; +} + +.preview-section { + flex: 1; + display: flex; + flex-direction: column; + background: rgba(0, 0, 0, 0.2); + border-radius: 12px; + border: 1px solid var(--panel-border); + overflow: hidden; +} + +/* Form Controls */ +.form-group { + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +.form-group label { + font-weight: 500; + font-size: 0.95rem; + color: var(--text-primary); +} + +.form-group input[type="text"], +.form-group input[type="number"], +.form-group select { + width: 100%; + padding: 0.75rem 1rem; + background: var(--input-bg); + border: 1px solid var(--input-border); + border-radius: 8px; + color: var(--text-primary); + font-size: 1rem; + font-family: inherit; + transition: all var(--transition-speed) ease; +} + +.form-group input:focus, +.form-group select:focus { + outline: none; + border-color: var(--input-focus); + box-shadow: 0 0 0 2px rgba(88, 166, 255, 0.2); +} + +.form-group select option { + background-color: var(--bg-color); + color: var(--text-primary); +} + +.checkbox-group { + display: flex; + align-items: center; + gap: 0.75rem; + cursor: pointer; +} + +.checkbox-group input[type="checkbox"] { + appearance: none; + width: 20px; + height: 20px; + border: 2px solid var(--input-border); + border-radius: 4px; + background: var(--input-bg); + cursor: pointer; + position: relative; + transition: all var(--transition-speed); +} + +.checkbox-group input[type="checkbox"]:checked { + background: var(--accent-color); + border-color: var(--accent-color); +} + +.checkbox-group input[type="checkbox"]:checked::after { + content: ""; + position: absolute; + top: 2px; + left: 6px; + width: 5px; + height: 10px; + border: solid white; + border-width: 0 2px 2px 0; + transform: rotate(45deg); +} + +.form-text { + font-size: 0.8rem; + color: var(--text-secondary); + margin-top: 0.25rem; +} + +/* Tabs */ +.tabs { + display: flex; + border-bottom: 1px solid var(--panel-border); + background: rgba(13, 17, 23, 0.5); +} + +.tab-btn { + flex: 1; + background: none; + border: none; + padding: 1rem; + color: var(--text-secondary); + font-size: 0.95rem; + font-weight: 500; + cursor: pointer; + transition: all var(--transition-speed); + border-bottom: 2px solid transparent; +} + +.tab-btn:hover { + color: var(--text-primary); + background: rgba(255, 255, 255, 0.05); +} + +.tab-btn.active { + color: var(--accent-color); + border-bottom-color: var(--accent-color); + background: rgba(88, 166, 255, 0.1); +} + +/* Code Preview */ +.code-container { + flex: 1; + position: relative; + display: flex; + flex-direction: column; +} + +.code-container pre { + margin: 0; + padding: 1.5rem; + overflow-x: auto; + font-family: + "ui-monospace", "SFMono-Regular", "Menlo", "Monaco", "Consolas", monospace; + font-size: 0.9rem; + color: #c9d1d9; + line-height: 1.6; + white-space: pre-wrap; + word-break: break-all; + height: 100%; +} + +.copy-btn { + position: absolute; + top: 1rem; + right: 1rem; + background: rgba(48, 54, 61, 0.8); + border: 1px solid var(--input-border); + color: var(--text-primary); + padding: 0.5rem 1rem; + border-radius: 6px; + font-size: 0.85rem; + font-weight: 500; + cursor: pointer; + transition: all var(--transition-speed); + display: flex; + align-items: center; + gap: 0.5rem; + backdrop-filter: blur(4px); +} + +.copy-btn:hover { + background: var(--input-border); + transform: translateY(-1px); +} + +.copy-btn.copied { + background: var(--success-color); + border-color: var(--success-color); + color: white; +} + +/* Animations */ +@keyframes fadeIn { + from { + opacity: 0; + transform: translateY(20px); + } + to { + opacity: 1; + transform: translateY(0); } } diff --git a/src/app/page.tsx b/src/app/page.tsx index 7b947a2..62136ae 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,66 +1,14 @@ -import Image from "next/image"; -import styles from "./page.module.css"; +import ScriptGenerator from '@/components/ScriptGenerator'; export default function Home() { return ( -
-
- Next.js logo -
-

To get started, edit the page.tsx file.

-

- Looking for a starting point or more instructions? Head over to{" "} - - Templates - {" "} - or the{" "} - - Learning - {" "} - center. -

-
-
- - Vercel logomark - Deploy Now - - - Documentation - -
-
-
+
+

OpenClaw 自动化脚本生成器

+

+ 为您的 OpenClaw 快速生成可跨平台的静默安装与配置脚本。 +

+ + +
); } diff --git a/src/components/ScriptGenerator.tsx b/src/components/ScriptGenerator.tsx new file mode 100644 index 0000000..8dcf663 --- /dev/null +++ b/src/components/ScriptGenerator.tsx @@ -0,0 +1,201 @@ +'use client'; + +import { useState } from 'react'; +import { generateScript, OpenClawConfig } from '../utils/generateScripts'; + +export default function ScriptGenerator() { + const [config, setConfig] = useState({ + workspace: '', + mode: 'local', + flow: 'quickstart', + nodeManager: 'npm', + installDaemon: true, + gatewayPort: '', + gatewayBind: '', + gatewayToken: '', + aiProvider: 'skip', + apiKey: '', + defaultModel: '' + }); + + const [activeTab, setActiveTab] = useState<'unix' | 'windows'>('unix'); + const [copied, setCopied] = useState(false); + + const handleChange = (e: React.ChangeEvent) => { + const { name, value, type } = e.target; + setConfig(prev => ({ + ...prev, + [name]: type === 'checkbox' ? (e.target as HTMLInputElement).checked : value + })); + }; + + const currentScript = generateScript(config, activeTab); + + const handleCopy = async () => { + try { + await navigator.clipboard.writeText(currentScript); + setCopied(true); + setTimeout(() => setCopied(false), 2000); + } catch (err) { + console.error('Failed to copy text: ', err); + } + }; + + return ( +
+ + {/* Configuration Form */} +
+

参数配置

+ +
+ + + OpenClaw 存储其节点数据的目录路径。 +
+ +
+
+ + +
+ +
+ + +
+
+ +
+ + +
+ +
+ +
+ +

AI 模型配置

+ +
+ + +
+ + {config.aiProvider !== 'skip' && ( +
+
+ + +
+
+ + +
+
+ )} + +

网关设置 (可选)

+ +
+
+ + +
+
+ + +
+
+ +
+ + +
+
+ + {/* Script Preview */} +
+
+ + +
+ +
+ +
+            {currentScript}
+          
+
+
+ +
+ ); +} diff --git a/src/utils/generateScripts.ts b/src/utils/generateScripts.ts new file mode 100644 index 0000000..3a6468e --- /dev/null +++ b/src/utils/generateScripts.ts @@ -0,0 +1,97 @@ +export interface OpenClawConfig { + workspace: string; + mode: 'local' | 'remote'; + flow: 'quickstart' | 'advanced' | 'manual'; + nodeManager: 'npm' | 'pnpm' | 'bun'; + installDaemon: boolean; + gatewayPort: string; + gatewayBind: string; + gatewayToken: string; + aiProvider: 'openai' | 'anthropic' | 'custom' | 'skip'; + apiKey: string; + defaultModel: string; +} + +export const generateScript = (config: OpenClawConfig, platform: 'unix' | 'windows') => { + const args: string[] = ['--non-interactive']; + + if (config.workspace) args.push(`--workspace "${config.workspace}"`); + if (config.mode) args.push(`--mode ${config.mode}`); + if (config.flow) args.push(`--flow ${config.flow}`); + if (config.nodeManager) args.push(`--node-manager ${config.nodeManager}`); + + if (config.installDaemon) { + args.push('--install-daemon'); + } else { + args.push('--skip-daemon'); + } + + if (config.gatewayPort) args.push(`--gateway-port ${config.gatewayPort}`); + if (config.gatewayBind) args.push(`--gateway-bind ${config.gatewayBind}`); + if (config.gatewayToken) args.push(`--gateway-token "${config.gatewayToken}"`); + + if (config.aiProvider === 'skip') { + args.push('--auth-choice skip'); + } else { + args.push(`--auth-choice ${config.aiProvider}`); + if (config.apiKey) { + if (config.aiProvider === 'openai') args.push(`--openai-api-key "${config.apiKey}"`); + else if (config.aiProvider === 'anthropic') args.push(`--anthropic-api-key "${config.apiKey}"`); + else if (config.aiProvider === 'custom') args.push(`--custom-api-key "${config.apiKey}"`); + } + if (config.defaultModel) { + args.push(`--default-model "${config.defaultModel}"`); + } + } + + const joinedArgs = args.join(' '); + + if (platform === 'unix') { + return `#!/bin/bash + +# 将代码包裹在函数中,避免直接复制粘贴运行失败时导致终端退出 +install_openclaw() { + # 检查当前系统环境是否安装了所选的包管理器 + if ! command -v ${config.nodeManager} &> /dev/null + then + echo "错误: 未在您的系统中检测到 \`${config.nodeManager}\` 环境。" + echo "请先安装 Node.js (https://nodejs.org) 或相应的包管理工具后再运行此脚本。" + return 1 + fi + + echo "正在使用 ${config.nodeManager} 全局安装 OpenClaw 最新版..." + ${config.nodeManager === 'npm' ? 'npm install -g' : config.nodeManager === 'pnpm' ? 'pnpm add -g' : 'bun install -g'} openclaw@latest + + echo "正在运行 OpenClaw 自动化配置向导..." + openclaw onboard ${joinedArgs} + + echo "安装与配置完成!" +} + +install_openclaw +`; + } else { + return `# Windows PowerShell 自动化脚本 (建议以管理员身份运行) + +# 将代码包裹在函数中,避免直接执行失败时导致终端异常 +function Install-OpenClaw { + # 检查当前系统环境是否安装了所选的包管理器 + if (!(Get-Command ${config.nodeManager} -ErrorAction SilentlyContinue)) { + Write-Host "错误: 未在您的系统中检测到 \`${config.nodeManager}\` 环境。" -ForegroundColor Red + Write-Host "请先下载并安装 Node.js (https://nodejs.org) 或相应的包管理工具后再运行此脚本。" -ForegroundColor Yellow + return + } + + Write-Host "正在使用 ${config.nodeManager} 全局安装 OpenClaw 最新版..." + ${config.nodeManager === 'npm' ? 'npm install -g' : config.nodeManager === 'pnpm' ? 'pnpm add -g' : 'bun install -g'} openclaw@latest + + Write-Host "正在运行 OpenClaw 自动化配置向导..." + openclaw onboard ${joinedArgs} + + Write-Host "安装与配置完成!" +} + +Install-OpenClaw +`; + } +};