编程 Open Design 深度拆解:当 AI 代理遇上设计系统——从 12 种编程 CLI 到 72 套设计语言的全链路技术实战

2026-05-03 00:35:59 +0800 CST views 4

Open Design 深度拆解:当 AI 代理遇上设计系统——从 12 种编程 CLI 到 72 套设计语言的全链路技术实战

14.8K Stars,5 天破万,这个开源项目凭什么成为 Claude Design 的最强挑战者?

前言:设计工具的「代理化」时刻

2026 年 4 月 17 日,Anthropic 发布了 Claude Design——一个让 LLM 从「写文字」变成「产出设计物」的工具。它在社交媒体上疯传,因为这是第一次,AI 不再只是给你设计建议,而是直接输出可用的设计稿、PPT、原型页面。

但 Claude Design 有三个致命问题:闭源、付费、只支持 Anthropic 的模型。

五天后,一个名为 Open Design 的开源项目横空出世。它用一套完全不同的架构,实现了几乎相同的能力,还额外支持 12 种编程代理 CLI、72 套品牌设计系统、31 种组合式技能,以及完整的 BYOK(Bring Your Own Key)能力。

本文将从程序员视角,深入拆解 Open Design 的技术架构、设计哲学和实现细节。


一、项目概览:为什么它能在 5 天内引爆社区

1.1 核心定位

Open Design 的定位非常清晰:

Claude Design 的开源替代品,本地优先,BYOK,支持 12 种编程代理 CLI 自动检测。

它的核心洞察是:最强的编程代理已经装在你的笔记本上了——Claude Code、Codex、Cursor、Gemini CLI……为什么不直接复用它们,而不是重新造一个代理?

1.2 技术栈一览

├── TypeScript (主语言)
├── Next.js (Web 层)
├── SQLite (持久化)
├── React 18 + Babel (沙箱渲染)
├── Tailwind CSS (样式系统)
├── Node.js Daemon (本地守护进程)
└── 多种 AI CLI 适配器

1.3 核心数据

指标数值
Stars14,812
Forks1,673
Skills31
Design Systems72
支持的 CLI 代理12
媒体生成模板93

二、架构设计:四层分离的代理驱动模型

Open Design 的架构可以用一张图概括:

┌─────────────────────────────────────────────────────────────┐
│                      Web UI (Next.js)                        │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────────────┐   │
│  │  Skill Picker │  │  Direction  │  │  Live Preview       │   │
│  │  (31 Skills)  │  │  Picker (5) │  │  (Sandboxed Iframe) │   │
│  └─────────────┘  └─────────────┘  └─────────────────────┘   │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│                    API Layer (Next.js API)                   │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────────────┐   │
│  │  Agent Router │  │  Proxy API  │  │  Import Handler    │   │
│  │  (12 CLIs)   │  │  (BYOK)     │  │  (Claude Design)   │   │
│  └─────────────┘  └─────────────┘  └─────────────────────┘   │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│                 Local Daemon (Node.js Process)               │
│  ┌───────────────────────────────────────────────────────┐  │
│  │  - PATH 扫描,检测已安装的 CLI                          │  │
│  │  - 在项目文件夹中 spawn 子进程                         │  │
│  │  - 提供真实的 Read/Write/Bash/WebFetch 能力            │  │
│  │  - 阻止 SSRF / Internal-IP 访问                       │  │
│  └───────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│                   Project Workspace (文件系统)                │
│  ┌─────────────────┐  ┌─────────────┐  ┌────────────────┐   │
│  │  Seed Templates │  │  Layout Lib │  │  Self-check    │   │
│  │  (按 Skill 初始化)│  │  (设计系统) │  │  Checklist    │   │
│  └─────────────────┘  └─────────────┘  └────────────────┘   │
└─────────────────────────────────────────────────────────────┘

2.1 第一层:Web UI

Open Design 的前端是一个标准的 Next.js 应用,但有几个关键设计:

Skill Picker(技能选择器)

31 种技能被分为 9 个场景:

// apps/web/src/prompts/skills.ts
export const SKILL_GROUPS = {
  design: ['web-prototype', 'saas-landing', 'dashboard', 'mobile-app', 'gamified-app', 'social-carousel', 'magazine-poster', 'dating-web', 'sprite-animation', 'motion-frames'],
  marketing: ['pitch-deck', 'product-launch', 'case-study'],
  operation: ['weekly-update', 'team-okrs', 'kanban-board'],
  engineering: ['wireframe-sketch', 'pm-spec', 'eng-runbook', 'api-doc'],
  product: ['user-journey', 'feature-spec', 'roadmap'],
  finance: ['finance-report', 'invoice', 'budget-plan'],
  hr: ['onboarding-guide', 'job-description', 'performance-review'],
  sale: ['proposal', 'contract', 'pricing-page'],
  personal: ['resume', 'portfolio', 'blog-theme']
};

Direction Picker(视觉方向选择器)

五种设计风格,每种都有确定的配色和字体:

// apps/web/src/prompts/directions.ts
export const VISUAL_DIRECTIONS = {
  editorial_monocle: {
    name: 'Editorial Monocle',
    palette: [
      '#1a1a2e', '#16213e', '#0f3460', '#e94560', '#ffffff'
    ],
    fonts: {
      heading: 'Playfair Display',
      body: 'Source Serif Pro'
    }
  },
  modern_minimal: {
    name: 'Modern Minimal',
    palette: [
      '#ffffff', '#f8f9fa', '#e9ecef', '#212529', '#0d6efd'
    ],
    fonts: {
      heading: 'Inter',
      body: 'Inter'
    }
  },
  warm_soft: {
    name: 'Warm Soft',
    palette: [
      '#fefae0', '#faedcd', '#e9edc9', '#ccd5ae', '#d4a373'
    ],
    fonts: {
      heading: 'Nunito',
      body: 'Nunito'
    }
  },
  tech_utility: {
    name: 'Tech Utility',
    palette: [
      '#0d1117', '#161b22', '#21262d', '#30363d', '#c9d1d9'
    ],
    fonts: {
      heading: 'JetBrains Mono',
      body: 'Space Grotesk'
    }
  },
  brutalist_experimental: {
    name: 'Brutalist Experimental',
    palette: [
      '#ff0000', '#000000', '#ffffff', '#ffff00', '#00ff00'
    ],
    fonts: {
      heading: 'Space Mono',
      body: 'Courier New'
    }
  }
};

Live Preview(实时预览)

这是一个沙箱化的 iframe,使用 React 18 + Babel 在浏览器端实时编译 JSX:

// apps/web/src/components/preview/SandboxedPreview.tsx
export function SandboxedPreview({ artifact }: { artifact: string }) {
  const iframeRef = useRef<HTMLIFrameElement>(null);
  
  useEffect(() => {
    const iframe = iframeRef.current;
    if (!iframe) return;
    
    // 注入 React 18 + Babel 运行时
    const html = `
      <!DOCTYPE html>
      <html>
        <head>
          <script src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
          <script src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
          <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
          <script src="https://cdn.tailwindcss.com"></script>
        </head>
        <body>
          <div id="root"></div>
          <script type="text/babel">
            ${artifact}
          </script>
        </body>
      </html>
    `;
    
    iframe.srcdoc = html;
  }, [artifact]);
  
  return (
    <iframe
      ref={iframeRef}
      sandbox="allow-scripts allow-same-origin"
      className="w-full h-full border-0"
    />
  );
}

2.2 第二层:API Layer

这一层负责路由请求到不同的代理后端。

Agent Router(代理路由器)

// apps/web/src/lib/agent-router.ts
import { spawn } from 'child_process';
import which from 'which';

const AGENT_CLIS = [
  { name: 'claude', command: 'claude', protocol: 'claude-code' },
  { name: 'codex', command: 'codex', protocol: 'codex' },
  { name: 'devin', command: 'devin', protocol: 'devin-terminal' },
  { name: 'cursor', command: 'cursor-agent', protocol: 'cursor' },
  { name: 'gemini', command: 'gemini', protocol: 'gemini-cli' },
  { name: 'opencode', command: 'openode', protocol: 'openode' },
  { name: 'qwen', command: 'qwen-code', protocol: 'qwen' },
  { name: 'copilot', command: 'gh', args: ['copilot', 'chat'], protocol: 'copilot' },
  { name: 'hermes', command: 'hermes', protocol: 'acp' },
  { name: 'kimi', command: 'kimi', protocol: 'acp' },
  { name: 'pi', command: 'pi', protocol: 'rpc' },
  { name: 'kiro', command: 'kiro', protocol: 'acp' }
];

export async function detectAgents(): Promise<AgentInfo[]> {
  const detected: AgentInfo[] = [];
  
  for (const agent of AGENT_CLIS) {
    try {
      const path = await which(agent.command);
      detected.push({ ...agent, path });
    } catch {
      // CLI 未安装
    }
  }
  
  return detected;
}

BYOK Proxy

如果没有安装任何 CLI,用户可以直接提供 OpenAI 兼容的 API:

// apps/web/pages/api/proxy/stream.ts
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
  const { baseUrl, apiKey, model, messages } = req.body;
  
  // SSRF 防护:阻止内网 IP
  const url = new URL(baseUrl);
  if (isInternalIP(url.hostname)) {
    return res.status(400).json({ error: 'Internal IP blocked' });
  }
  
  // 转发请求到用户指定的端点
  const response = await fetch(`${baseUrl}/chat/completions`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${apiKey}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ model, messages, stream: true })
  });
  
  // 流式响应
  res.setHeader('Content-Type', 'text/event-stream');
  for await (const chunk of response.body) {
    res.write(chunk);
  }
  res.end();
}

2.3 第三层:Local Daemon

这是整个系统最关键的部分——一个本地守护进程,负责:

  1. 扫描 PATH,检测已安装的 CLI
  2. 在项目文件夹中 spawn 子进程
  3. 提供真实的文件系统访问能力
  4. 执行安全策略(阻止 SSRF)
// daemon/src/index.ts
import { spawn, ChildProcess } from 'child_process';
import { Server } from 'socket.io';
import fs from 'fs/promises';
import path from 'path';

interface AgentSession {
  process: ChildProcess;
  projectDir: string;
  agent: string;
}

const sessions = new Map<string, AgentSession>();

const io = new Server(3001, { cors: { origin: '*' } });

io.on('connection', (socket) => {
  socket.on('start-agent', async ({ agent, projectDir, taskId }) => {
    // 创建项目目录
    await fs.mkdir(projectDir, { recursive: true });
    
    // 初始化种子模板
    await initializeSeedTemplate(projectDir, taskId);
    
    // Spawn 代理进程
    const proc = spawn(agent, [], {
      cwd: projectDir,
      stdio: ['pipe', 'pipe', 'pipe'],
      env: { ...process.env, AGENT_MODE: 'design' }
    });
    
    sessions.set(socket.id, { process: proc, projectDir, agent });
    
    proc.stdout.on('data', (data) => {
      socket.emit('agent-stdout', data.toString());
    });
    
    proc.stderr.on('data', (data) => {
      socket.emit('agent-stderr', data.toString());
    });
    
    proc.on('close', (code) => {
      socket.emit('agent-close', { code });
      sessions.delete(socket.id);
    });
  });
  
  socket.on('send-input', ({ input }) => {
    const session = sessions.get(socket.id);
    if (session) {
      session.process.stdin.write(input + '\n');
    }
  });
  
  socket.on('disconnect', () => {
    const session = sessions.get(socket.id);
    if (session) {
      session.process.kill();
      sessions.delete(socket.id);
    }
  });
});

2.4 第四层:Project Workspace

每个设计任务都会创建一个独立的项目文件夹,包含:

project-workspace/
├── .od/
│   ├── app.sqlite          # SQLite 数据库
│   └── config.json         # 项目配置
├── seed/
│   ├── template.tsx        # 种子模板
│   ├── layout-lib.ts       # 布局库
│   └── checklist.md        # 自检清单
├── design-systems/
│   └── linear/             # 选用的设计系统
├── assets/
│   ├── frames/             # 设备框架
│   └── images/             # 生成的图片
└── output/
    ├── index.html          # 最终输出
    ├── deck.pdf            # PPT 导出
    └── preview.mp4         # 视频导出

三、Skill 系统:31 种组合式设计技能

Open Design 的 Skill 系统是其核心创新之一。每个 Skill 都是一个独立的「设计任务模板」,包含:

  1. 前置问卷:收集用户需求的表单
  2. 种子模板:初始化项目结构的代码
  3. 自检清单:AI 必须遵守的质量标准
  4. 输出格式:最终产物的结构定义

3.1 Skill 定义示例

saas-landing(SaaS 落地页)为例:

// skills/saas-landing/index.ts
export const skill: Skill = {
  id: 'saas-landing',
  name: 'SaaS Landing Page',
  category: 'design',
  description: 'Generate a conversion-optimized landing page for SaaS products',
  
  // 前置问卷
  questions: [
    { id: 'product_name', label: 'Product Name', type: 'text', required: true },
    { id: 'tagline', label: 'Tagline', type: 'text', required: true },
    { id: 'target_audience', label: 'Target Audience', type: 'textarea', required: true },
    { id: 'key_features', label: 'Key Features (comma-separated)', type: 'text', required: true },
    { id: 'pricing_tiers', label: 'Pricing Tiers (e.g., Free/Pro/Enterprise)', type: 'text', required: true },
    { id: 'cta_text', label: 'Primary CTA Text', type: 'text', default: 'Get Started Free' }
  ],
  
  // 种子模板
  seedTemplate: async (answers) => {
    return `
import React from 'react';
import { Hero } from './components/Hero';
import { Features } from './components/Features';
import { Pricing } from './components/Pricing';
import { CTA } from './components/CTA';

export default function LandingPage() {
  return (
    <main className="min-h-screen bg-background">
      <Hero 
        productName="${answers.product_name}"
        tagline="${answers.tagline}"
        ctaText="${answers.cta_text}"
      />
      <Features features={${JSON.stringify(answers.key_features.split(','))}} />
      <Pricing tiers={${JSON.stringify(answers.pricing_tiers.split('/'))}} />
      <CTA ctaText="${answers.cta_text}" />
    </main>
  );
}
    `;
  },
  
  // 自检清单
  checklist: [
    '✓ Hero section has clear value proposition',
    '✓ Features section is visually balanced',
    '✓ Pricing cards have consistent heights',
    '✓ CTA buttons are prominent and accessible',
    '✓ Mobile responsive layout'
  ],
  
  // 输出格式
  outputFormat: {
    type: 'react-component',
    entryFile: 'LandingPage.tsx',
    exportFormats: ['html', 'pdf', 'zip']
  }
};

3.2 五维自评系统

每个 Skill 都会触发一个「五维自评」流程,让 AI 对自己的输出进行质量检查:

// apps/web/src/prompts/critique.ts
export const CRITIQUE_DIMENSIONS = [
  {
    id: 'visual_hierarchy',
    name: 'Visual Hierarchy',
    description: 'Is the information architecture clear? Does the eye flow naturally?',
    weight: 0.25
  },
  {
    id: 'brand_consistency',
    name: 'Brand Consistency',
    description: 'Does it follow the selected design system? Colors, fonts, spacing?',
    weight: 0.20
  },
  {
    id: 'accessibility',
    name: 'Accessibility',
    description: 'WCAG 2.1 AA compliant? Color contrast, focus states, semantic HTML?',
    weight: 0.20
  },
  {
    id: 'responsiveness',
    name: 'Responsiveness',
    description: 'Does it work across 375px to 1440px? Mobile-first approach?',
    weight: 0.20
  },
  {
    id: 'conversion_optimization',
    name: 'Conversion Optimization',
    description: 'Clear CTAs? Trust signals? Social proof elements?',
    weight: 0.15
  }
];

export function generateCritiquePrompt(artifact: string): string {
  return `
You are a senior design reviewer. Evaluate the following artifact against these 5 dimensions:

${CRITIQUE_DIMENSIONS.map(d => `- ${d.name}: ${d.description}`).join('\n')}

For each dimension, give:
1. A score from 1-10
2. A brief justification
3. Specific improvement suggestions

Artifact:
\`\`\`tsx
${artifact}
\`\`\`

Output JSON format:
{
  "dimensions": [
    { "id": "visual_hierarchy", "score": 8, "justification": "...", "suggestions": ["...", "..."] },
    ...
  ],
  "overall_score": 7.5,
  "summary": "..."
}
  `;
}

四、设计系统:72 套品牌级视觉语言

Open Design 内置了 72 套设计系统,来源包括:

  1. 手写入门模板:2 套基础模板
  2. 产品级设计系统:70 套(Linear、Stripe、Vercel、Airbnb 等)
  3. 设计技能包:57 套来自 awesome-design-skills

4.1 设计系统结构

每个设计系统包含以下元素:

// design-systems/linear/index.ts
export const linearDesignSystem: DesignSystem = {
  name: 'Linear',
  source: 'https://linear.app',
  
  colors: {
    // 语义化颜色
    background: '#0a0a0b',
    foreground: '#f5f5f5',
    primary: '#5e6ad2',
    secondary: '#2c2d30',
    muted: '#8b8b8b',
    accent: '#5e6ad2',
    destructive: '#f44d4d',
    border: '#212121',
    card: '#141415',
    
    // 扩展色板
    palette: {
      gray: ['#fafafa', '#f5f5f5', '#e5e5e5', '#d4d4d4', '#a3a3a3', '#737373', '#525252', '#404040', '#262626', '#171717'],
      blue: ['#eff6ff', '#dbeafe', '#bfdbfe', '#93c5fd', '#60a5fa', '#3b82f6', '#2563eb', '#1d4ed8', '#1e40af', '#1e3a8a'],
      purple: ['#faf5ff', '#f3e8ff', '#e9d5ff', '#d8b4fe', '#c084fc', '#a855f7', '#9333ea', '#7e22ce', '#6b21a8', '#581c87']
    }
  },
  
  typography: {
    fontFamily: {
      sans: ['Inter', 'system-ui', 'sans-serif'],
      mono: ['JetBrains Mono', 'Consolas', 'monospace']
    },
    fontSize: {
      xs: '0.75rem',
      sm: '0.875rem',
      base: '1rem',
      lg: '1.125rem',
      xl: '1.25rem',
      '2xl': '1.5rem',
      '3xl': '1.875rem',
      '4xl': '2.25rem',
      '5xl': '3rem'
    },
    fontWeight: {
      normal: '400',
      medium: '500',
      semibold: '600',
      bold: '700'
    },
    lineHeight: {
      tight: '1.25',
      normal: '1.5',
      relaxed: '1.75'
    }
  },
  
  spacing: {
    0: '0',
    1: '0.25rem',
    2: '0.5rem',
    3: '0.75rem',
    4: '1rem',
    5: '1.25rem',
    6: '1.5rem',
    8: '2rem',
    10: '2.5rem',
    12: '3rem',
    16: '4rem',
    20: '5rem',
    24: '6rem'
  },
  
  borderRadius: {
    none: '0',
    sm: '0.125rem',
    DEFAULT: '0.25rem',
    md: '0.375rem',
    lg: '0.5rem',
    xl: '0.75rem',
    '2xl': '1rem',
    full: '9999px'
  },
  
  shadows: {
    sm: '0 1px 2px 0 rgb(0 0 0 / 0.05)',
    DEFAULT: '0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)',
    md: '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)',
    lg: '0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)',
    xl: '0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1)'
  },
  
  animations: {
    fadeIn: 'fadeIn 0.2s ease-out',
    slideUp: 'slideUp 0.3s ease-out',
    scaleIn: 'scaleIn 0.2s ease-out'
  },
  
  // 组件样式
  components: {
    button: {
      base: 'inline-flex items-center justify-center rounded-md font-medium transition-colors',
      variants: {
        primary: 'bg-primary text-white hover:bg-primary/90',
        secondary: 'bg-secondary text-foreground hover:bg-secondary/80',
        outline: 'border border-border bg-transparent hover:bg-secondary'
      },
      sizes: {
        sm: 'h-8 px-3 text-sm',
        md: 'h-10 px-4 text-base',
        lg: 'h-12 px-6 text-lg'
      }
    },
    card: {
      base: 'rounded-lg border border-border bg-card p-4',
      variants: {
        elevated: 'shadow-lg',
        interactive: 'hover:border-primary/50 cursor-pointer'
      }
    }
  }
};

4.2 设计系统集成方式

设计系统通过 Tailwind CSS 配置注入:

// tailwind.config.js
const designSystem = require('./design-systems/linear');

module.exports = {
  content: ['./src/**/*.{ts,tsx}'],
  theme: {
    extend: {
      colors: designSystem.colors,
      fontFamily: designSystem.typography.fontFamily,
      fontSize: designSystem.typography.fontSize,
      spacing: designSystem.spacing,
      borderRadius: designSystem.borderRadius,
      boxShadow: designSystem.shadows,
      animation: designSystem.animations
    }
  },
  plugins: []
};

五、多代理适配器:12 种 CLI 的统一接口

Open Design 支持的代理列表:

代理CLI 命令协议特点
Claude Codeclaudeclaude-codeAnthropic 官方
CodexcodexcodexOpenAI 官方
Devin Terminaldevindevin-terminalCognition AI
Cursor Agentcursor-agentcursorIDE 内嵌
Gemini CLIgeminigemini-cliGoogle 官方
OpenCodeopenodeopenode开源替代
Qwen Codeqwen-codeqwen阿里云
GitHub Copilot CLIgh copilot chatcopilotGitHub
HermeshermesacpACP 协议
Kimi CLIkimiacp月之暗面
PipirpcRPC 协议
Kiro CLIkiroacpAWS

5.1 适配器抽象

// daemon/src/adapters/base.ts
export interface AgentAdapter {
  name: string;
  command: string;
  protocol: 'claude-code' | 'codex' | 'acp' | 'rpc';
  
  // 启动代理
  start(projectDir: string, task: string): Promise<ChildProcess>;
  
  // 发送消息
  sendMessage(process: ChildProcess, message: string): Promise<void>;
  
  // 解析输出
  parseOutput(data: string): AgentMessage[];
  
  // 获取 artifact
  extractArtifact(output: string): string | null;
}

export interface AgentMessage {
  type: 'text' | 'tool_use' | 'artifact' | 'error';
  content: string;
  metadata?: Record<string, any>;
}

5.2 Claude Code 适配器示例

// daemon/src/adapters/claude-code.ts
import { spawn, ChildProcess } from 'child_process';
import { AgentAdapter, AgentMessage } from './base';

export class ClaudeCodeAdapter implements AgentAdapter {
  name = 'Claude Code';
  command = 'claude';
  protocol = 'claude-code' as const;
  
  async start(projectDir: string, task: string): Promise<ChildProcess> {
    const proc = spawn(this.command, [
      '--print',
      '--permission-mode', 'bypassPermissions',
      '--max-tokens', '8000',
      task
    ], {
      cwd: projectDir,
      stdio: ['pipe', 'pipe', 'pipe'],
      env: { ...process.env, CLAUDE_CODE_MODE: 'design' }
    });
    
    return proc;
  }
  
  async sendMessage(proc: ChildProcess, message: string): Promise<void> {
    proc.stdin?.write(message + '\n');
  }
  
  parseOutput(data: string): AgentMessage[] {
    const messages: AgentMessage[] = [];
    const lines = data.split('\n');
    
    for (const line of lines) {
      if (line.startsWith('[TOOL]')) {
        messages.push({
          type: 'tool_use',
          content: line.replace('[TOOL] ', '')
        });
      } else if (line.includes('<artifact')) {
        messages.push({
          type: 'artifact',
          content: this.extractArtifact(data) || ''
        });
      } else if (line.trim()) {
        messages.push({
          type: 'text',
          content: line
        });
      }
    }
    
    return messages;
  }
  
  extractArtifact(output: string): string | null {
    const match = output.match(/<artifact[^>]*>([\s\S]*?)<\/artifact>/);
    return match ? match[1].trim() : null;
  }
}

5.3 ACP 协议适配器

ACP (Agent Communication Protocol) 是一种标准化的代理通信协议:

// daemon/src/adapters/acp.ts
import { spawn, ChildProcess } from 'child_process';
import { AgentAdapter, AgentMessage } from './base';

export class ACPAdapter implements AgentAdapter {
  name: string;
  command: string;
  protocol = 'acp' as const;
  
  constructor(name: string, command: string) {
    this.name = name;
    this.command = command;
  }
  
  async start(projectDir: string, task: string): Promise<ChildProcess> {
    // ACP 协议通过 stdin/stdout 传递 JSON-RPC 消息
    const proc = spawn(this.command, ['--mode', 'acp'], {
      cwd: projectDir,
      stdio: ['pipe', 'pipe', 'pipe']
    });
    
    // 发送初始化请求
    const initRequest = {
      jsonrpc: '2.0',
      method: 'initialize',
      params: {
        capabilities: { artifacts: true, tools: true }
      },
      id: 1
    };
    
    proc.stdin?.write(JSON.stringify(initRequest) + '\n');
    
    // 发送任务
    const taskRequest = {
      jsonrpc: '2.0',
      method: 'task',
      params: { prompt: task },
      id: 2
    };
    
    proc.stdin?.write(JSON.stringify(taskRequest) + '\n');
    
    return proc;
  }
  
  parseOutput(data: string): AgentMessage[] {
    try {
      const response = JSON.parse(data);
      
      if (response.method === 'artifact') {
        return [{
          type: 'artifact',
          content: response.params.content
        }];
      }
      
      if (response.method === 'tool_use') {
        return [{
          type: 'tool_use',
          content: response.params
        }];
      }
      
      return [{
        type: 'text',
        content: response.result?.content || ''
      }];
    } catch {
      return [];
    }
  }
}

六、媒体生成:从图片到视频的完整能力链

Open Design 集成了三种媒体生成能力:

6.1 GPT-Image-2(图片生成)

用于海报、头像、信息图、地图插图:

// apps/web/src/lib/media/gpt-image.ts
import OpenAI from 'openai';

export async function generateImage(prompt: string, options?: ImageOptions) {
  const openai = new OpenAI({
    apiKey: process.env.OPENAI_API_KEY,
    baseURL: process.env.OPENAI_BASE_URL // 支持 Azure
  });
  
  const response = await openai.images.generate({
    model: 'gpt-image-2',
    prompt: `
      ${prompt}
      
      Style: Professional, clean, brand-appropriate
      Aspect ratio: ${options?.aspectRatio || '16:9'}
      Output format: PNG with transparency
    `,
    size: options?.size || '1920x1080',
    quality: 'hd',
    n: 1
  });
  
  return response.data[0].url;
}

// 使用示例
const heroImage = await generateImage(
  'A sleek SaaS dashboard showing real-time analytics with purple accents on dark background',
  { aspectRatio: '16:9' }
);

6.2 Seedance 2.0(视频生成)

用于 15 秒文本转视频、图转视频:

// apps/web/src/lib/media/seedance.ts
import { ByteDanceSeedance } from '@bytedance/seedance-sdk';

export async function generateVideo(prompt: string, image?: string) {
  const client = new ByteDanceSeedance({
    apiKey: process.env.BYTEDANCE_API_KEY
  });
  
  const params: VideoParams = {
    model: 'seedance-2.0',
    prompt,
    duration: 15, // 秒
    fps: 24,
    resolution: '1080p'
  };
  
  if (image) {
    params.image = image; // 图生视频
    params.mode = 'image-to-video';
  } else {
    params.mode = 'text-to-video';
  }
  
  const job = await client.createJob(params);
  
  // 轮询等待完成
  while (job.status !== 'completed' && job.status !== 'failed') {
    await new Promise(r => setTimeout(r, 5000));
    job = await client.getJob(job.id);
  }
  
  return job.outputUrl;
}

6.3 HyperFrames(HTML 转 MP4)

用于产品展示、动态排版、数据图表:

// apps/web/src/lib/media/hyperframes.ts
export async function htmlToVideo(html: string, options: VideoOptions) {
  const response = await fetch('https://api.hyperframes.dev/v1/render', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.HYPERFRAMES_API_KEY}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      html,
      duration: options.duration || 10,
      fps: options.fps || 30,
      width: options.width || 1920,
      height: options.height || 1080,
      animations: options.animations || []
    })
  });
  
  const { jobId } = await response.json();
  
  // 等待渲染完成
  while (true) {
    const status = await fetch(`https://api.hyperframes.dev/v1/jobs/${jobId}`);
    const { state, outputUrl } = await status.json();
    
    if (state === 'completed') return outputUrl;
    if (state === 'failed') throw new Error('Render failed');
    
    await new Promise(r => setTimeout(r, 2000));
  }
}

// 使用示例:产品展示动画
await htmlToVideo(`
  <div class="product-reveal">
    <div class="logo animate-fade-in">Product Name</div>
    <div class="screenshot animate-slide-up">
      <img src="dashboard.png" />
    </div>
    <div class="cta animate-pulse">
      Start Free Trial
    </div>
  </div>
`, {
  duration: 10,
  animations: [
    { target: '.logo', animation: 'fadeIn', start: 0, duration: 2 },
    { target: '.screenshot', animation: 'slideUp', start: 2, duration: 3 },
    { target: '.cta', animation: 'pulse', start: 6, duration: 4 }
  ]
});

七、安全设计:SSRF 防护与沙箱隔离

7.1 SSRF 防护

在 BYOK Proxy 中,Open Design 实现了严格的 SSRF 防护:

// apps/web/src/lib/security/ssrf.ts
import { isPrivate, isLocalhost } from 'ip-utils';

const BLOCKED_HOSTS = [
  'localhost',
  '127.0.0.1',
  '0.0.0.0',
  '169.254.169.254', // AWS 元数据
  'metadata.google.internal', // GCP 元数据
  '10.0.0.0/8',
  '172.16.0.0/12',
  '192.168.0.0/16'
];

export function isInternalIP(hostname: string): boolean {
  // 检查是否是内网 IP
  if (isPrivate(hostname)) return true;
  
  // 检查是否是 localhost
  if (isLocalhost(hostname)) return true;
  
  // 检查是否在黑名单中
  for (const blocked of BLOCKED_HOSTS) {
    if (blocked.includes('/')) {
      if (isIPInCIDR(hostname, blocked)) return true;
    } else if (hostname === blocked) {
      return true;
    }
  }
  
  return false;
}

export async function safeFetch(url: string): Promise<Response> {
  const parsed = new URL(url);
  
  // DNS 重绑定攻击防护
  const dns = await resolveDNS(parsed.hostname);
  for (const ip of dns) {
    if (isInternalIP(ip)) {
      throw new Error('SSRF attempt blocked: internal IP resolved');
    }
  }
  
  return fetch(url);
}

7.2 沙箱隔离

预览 iframe 使用严格的沙箱策略:

<iframe
  sandbox="allow-scripts allow-same-origin"
  // 注意:没有 allow-forms, allow-popups, allow-top-navigation
  // 防止表单提交、弹窗、跳转
/>

八、导入能力:从 Claude Design 无缝迁移

Open Design 支持直接导入 Claude Design 的导出文件:

// apps/web/pages/api/import/claude-design.ts
import { NextApiRequest, NextApiResponse } from 'next';
import { parse as parseZip } from 'jszip';

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
  const file = req.body; // ZIP 文件的 Buffer
  
  const zip = await parseZip(file);
  
  // Claude Design 导出结构
  // - project.json (项目元数据)
  // - messages.json (对话历史)
  // - artifacts/ (所有 artifact 文件)
  
  const projectMeta = JSON.parse(await zip.file('project.json')?.async('text') || '{}');
  const messages = JSON.parse(await zip.file('messages.json')?.async('text') || '[]');
  
  // 转换为 Open Design 的项目结构
  const odProject = {
    id: generateId(),
    name: projectMeta.name,
    createdAt: new Date().toISOString(),
    designSystem: projectMeta.design_system || 'linear',
    messages: messages.map(convertMessage),
    artifacts: []
  };
  
  // 提取 artifacts
  const artifactsFolder = zip.folder('artifacts');
  if (artifactsFolder) {
    for (const [path, file] of Object.entries(artifactsFolder.files)) {
      if (!file.dir) {
        const content = await file.async('text');
        odProject.artifacts.push({
          id: path.replace('.tsx', ''),
          content,
          createdAt: new Date().toISOString()
        });
      }
    }
  }
  
  // 保存到数据库
  await saveProject(odProject);
  
  res.json({ success: true, projectId: odProject.id });
}

function convertMessage(msg: any): Message {
  // Claude Design 的消息格式转换为 Open Design 格式
  return {
    role: msg.role,
    content: msg.content,
    timestamp: msg.timestamp,
    artifactId: msg.artifact_id
  };
}

九、性能优化:流式渲染与增量更新

9.1 流式 Artifact 渲染

Open Design 支持流式渲染 artifact,用户可以实时看到 AI 正在编写的内容:

// apps/web/src/components/preview/StreamingPreview.tsx
import { useEffect, useState, useRef } from 'react';

export function StreamingPreview({ stream }: { stream: ReadableStream<string> }) {
  const [code, setCode] = useState('');
  const [artifact, setArtifact] = useState<string | null>(null);
  const readerRef = useRef<ReadableStreamDefaultReader<string>>();
  
  useEffect(() => {
    const reader = stream.getReader();
    readerRef.current = reader;
    
    const process = async () => {
      while (true) {
        const { done, value } = await reader.read();
        if (done) break;
        
        setCode(prev => prev + value);
        
        // 尝试解析 artifact
        const parsed = tryParseArtifact(code);
        if (parsed) {
          setArtifact(parsed);
        }
      }
    };
    
    process();
    
    return () => {
      reader.cancel();
    };
  }, [stream]);
  
  return (
    <div className="grid grid-cols-2 h-full">
      <div className="border-r overflow-auto p-4">
        <pre className="text-sm">{code}</pre>
      </div>
      <div className="overflow-hidden">
        {artifact ? (
          <SandboxedPreview artifact={artifact} />
        ) : (
          <div className="flex items-center justify-center h-full text-muted">
            Waiting for artifact...
          </div>
        )}
      </div>
    </div>
  );
}

function tryParseArtifact(code: string): string | null {
  // 宽松的 artifact 提取
  const startMatch = code.match(/<artifact[^>]*>/);
  if (!startMatch) return null;
  
  const startIdx = startMatch.index! + startMatch[0].length;
  const endMatch = code.slice(startIdx).match(/<\/artifact>/);
  if (!endMatch) return null;
  
  return code.slice(startIdx, startIdx + endMatch.index!);
}

9.2 增量更新

对于大型 artifact,Open Design 只更新变化的部分:

// apps/web/src/lib/diff/patch.ts
import * as diff from 'diff';

export function patchArtifact(oldCode: string, newCode: string): string {
  const patches = diff.diffLines(oldCode, newCode);
  
  // 只返回有变化的部分
  return patches
    .filter(p => p.added)
    .map(p => p.value)
    .join('');
}

export function applyPatch(baseCode: string, patch: string): string {
  // 应用增量补丁
  const lines = baseCode.split('\n');
  const patchLines = patch.split('\n');
  
  // 找到插入位置(简化版)
  // 实际实现需要更复杂的定位逻辑
  return baseCode + '\n' + patch;
}

十、实战案例:从零构建一个 SaaS 落地页

让我们用 Open Design 实战演练一次。

10.1 启动项目

# 克隆仓库
git clone https://github.com/nexu-io/open-design.git
cd open-design

# 安装依赖
pnpm install

# 启动开发服务器
pnpm tools-dev

10.2 选择 Skill

在 Web UI 中选择 saas-landing Skill。

10.3 填写问卷

Product Name: DataPulse
Tagline: Real-time Analytics for Modern Teams
Target Audience: Product teams at B2B SaaS companies, 10-500 employees
Key Features: Real-time dashboards, Custom events, Team collaboration, API access
Pricing Tiers: Free/Pro/Enterprise
CTA Text: Start Free Trial

10.4 选择设计系统

选择 linear 设计系统(暗色科技风)。

10.5 AI 输出

代理会自动生成完整的 React 组件:

// 生成的内容示例
import React from 'react';
import { LineChart, BarChart, PieChart } from 'lucide-react';

export default function DataPulseLanding() {
  return (
    <main className="min-h-screen bg-[#0a0a0b] text-[#f5f5f5]">
      {/* Hero Section */}
      <section className="relative px-6 py-24 lg:px-8">
        <div className="mx-auto max-w-4xl text-center">
          <div className="inline-flex items-center gap-2 rounded-full bg-[#212121] px-4 py-2 text-sm text-[#8b8b8b]">
            <span className="h-2 w-2 rounded-full bg-[#5e6ad2]" />
            Now with AI-powered insights
          </div>
          <h1 className="mt-6 text-5xl font-bold tracking-tight lg:text-7xl">
            Real-time Analytics<br />
            <span className="text-[#5e6ad2]">for Modern Teams</span>
          </h1>
          <p className="mt-6 text-lg text-[#8b8b8b] lg:text-xl">
            Ship faster with instant visibility into user behavior.
            No more waiting for batch processing.
          </p>
          <div className="mt-10 flex items-center justify-center gap-4">
            <button className="rounded-lg bg-[#5e6ad2] px-6 py-3 font-medium hover:bg-[#5e6ad2]/90">
              Start Free Trial
            </button>
            <button className="rounded-lg border border-[#212121] px-6 py-3 font-medium hover:bg-[#141415]">
              Watch Demo
            </button>
          </div>
        </div>
        
        {/* Dashboard Preview */}
        <div className="mt-16 overflow-hidden rounded-xl border border-[#212121] bg-[#141415] p-4">
          <div className="aspect-[16/9] w-full overflow-hidden rounded-lg bg-[#0a0a0b]">
            <img 
              src="/dashboard-preview.png" 
              alt="DataPulse Dashboard"
              className="h-full w-full object-cover"
            />
          </div>
        </div>
      </section>
      
      {/* Features Section */}
      <section className="px-6 py-24 lg:px-8">
        <div className="mx-auto max-w-6xl">
          <h2 className="text-center text-3xl font-bold">
            Everything you need to understand your users
          </h2>
          <div className="mt-16 grid gap-8 lg:grid-cols-3">
            {/* Feature cards */}
          </div>
        </div>
      </section>
      
      {/* Pricing Section */}
      <section className="px-6 py-24 lg:px-8">
        <div className="mx-auto max-w-5xl">
          <div className="grid gap-8 lg:grid-cols-3">
            {/* Pricing cards */}
          </div>
        </div>
      </section>
      
      {/* CTA Section */}
      <section className="px-6 py-24 lg:px-8">
        <div className="mx-auto max-w-4xl rounded-2xl bg-gradient-to-r from-[#5e6ad2]/20 to-[#5e6ad2]/5 p-12 text-center">
          <h2 className="text-3xl font-bold">Ready to get started?</h2>
          <p className="mt-4 text-[#8b8b8b]">
            Join 1,000+ teams already using DataPulse
          </p>
          <button className="mt-8 rounded-lg bg-[#5e6ad2] px-8 py-4 font-medium">
            Start Free Trial
          </button>
        </div>
      </section>
    </main>
  );
}

10.6 五维自评

{
  "dimensions": [
    {
      "id": "visual_hierarchy",
      "score": 9,
      "justification": "Clear hero → features → pricing → CTA flow. Good use of section breaks.",
      "suggestions": ["Consider adding a navigation bar for longer pages"]
    },
    {
      "id": "brand_consistency",
      "score": 10,
      "justification": "Perfectly follows Linear design system. Colors, fonts, spacing all match.",
      "suggestions": []
    },
    {
      "id": "accessibility",
      "score": 8,
      "justification": "Good contrast ratios, semantic HTML. Missing some ARIA labels.",
      "suggestions": ["Add aria-label to buttons", "Ensure focus states are visible"]
    },
    {
      "id": "responsiveness",
      "score": 9,
      "justification": "Mobile-first responsive classes. Good breakpoint usage.",
      "suggestions": ["Test on 375px viewport for hero text sizing"]
    },
    {
      "id": "conversion_optimization",
      "score": 9,
      "justification": "Strong CTAs, social proof, clear value proposition.",
      "suggestions": ["Add trust badges or testimonials"]
    }
  ],
  "overall_score": 9.0,
  "summary": "Production-ready landing page with excellent brand alignment and clear conversion path."
}

十一、总结与展望

11.1 Open Design 的核心价值

Open Design 解决了三个关键问题:

  1. 闭源垄断:Claude Design 闭源,Open Design 开源
  2. 模型锁定:Claude Design 只支持 Anthropic 模型,Open Design 支持任何 OpenAI 兼容 API
  3. 代理重复建设:最强的编程代理已经在你的笔记本上,直接复用

11.2 技术亮点

特性实现方式
多代理支持PATH 扫描 + 协议适配器
设计系统Tailwind 配置 + 组件样式库
实时预览React 18 + Babel 沙箱
安全隔离SSRF 防护 + iframe sandbox
持久化SQLite + 文件系统
媒体生成GPT-Image-2 + Seedance + HyperFrames

11.3 未来展望

Open Design 仍有几个方向值得期待:

  1. 更多 Skill:目前 31 个,社区正在贡献更多
  2. 协作功能:多人实时编辑同一设计
  3. 版本控制:集成 Git,追踪设计变更
  4. AI 评估:自动 A/B 测试生成的变体

结语

Open Design 证明了:开源不等于低质量。它的 14.8K Stars、31 种 Skill、72 套设计系统,以及完整的多代理适配架构,使其成为目前最强的 Claude Design 开源替代品。

如果你是一名开发者,这个项目值得你 clone 下来跑一遍——不是为了「尝鲜」,而是为了理解「AI 时代的软件应该怎么设计」。

项目地址:https://github.com/nexu-io/open-design
文档:README.md
协议:Apache 2.0

复制全文 生成海报 AI Agent 设计系统 开源 TypeScript

推荐文章

对多个数组或多维数组进行排序
2024-11-17 05:10:28 +0800 CST
pin.gl是基于WebRTC的屏幕共享工具
2024-11-19 06:38:05 +0800 CST
最全面的 `history` 命令指南
2024-11-18 21:32:45 +0800 CST
PHP openssl 生成公私钥匙
2024-11-17 05:00:37 +0800 CST
软件定制开发流程
2024-11-19 05:52:28 +0800 CST
使用Vue 3实现无刷新数据加载
2024-11-18 17:48:20 +0800 CST
跟着 IP 地址,我能找到你家不?
2024-11-18 12:12:54 +0800 CST
Node.js中接入微信支付
2024-11-19 06:28:31 +0800 CST
HTML5的 input:file上传类型控制
2024-11-19 07:29:28 +0800 CST
Rust 高性能 XML 读写库
2024-11-19 07:50:32 +0800 CST
linux设置开机自启动
2024-11-17 05:09:12 +0800 CST
jQuery中向DOM添加元素的多种方法
2024-11-18 23:19:46 +0800 CST
Vue3中的自定义指令有哪些变化?
2024-11-18 07:48:06 +0800 CST
一些好玩且实用的开源AI工具
2024-11-19 09:31:57 +0800 CST
html夫妻约定
2024-11-19 01:24:21 +0800 CST
基于Flask实现后台权限管理系统
2024-11-19 09:53:09 +0800 CST
开源AI反混淆JS代码:HumanifyJS
2024-11-19 02:30:40 +0800 CST
程序员茄子在线接单