Skip to content

AtomCode WebUI SPA 前端架构分析

持续深度分析 — 嵌入的 React/Preact SPA、组件树、本地化、事件系统 2026-06-23


1. SPA 技术栈

技术详情
框架Preact (React 兼容,从 JS 代码中 $(...) 语法和 Hooks API 确认)
CSS单一 CSS 文件 assets/index-DQAxDfIC.css
JS单一 JS 文件 assets/index-CM83LMnS.js
Markdownmarked 库 (J.setOptions({gfm:true})) + DOMPurify (Mr.sanitize())
语法高亮无 Prism/highlight.js(使用服务器端 Markdown 渲染)
构建工具Vite 类构建工具(基于 asset hash 命名)

2. 组件树架构

App (root)

  ├── AppSidebar (gi)
  │   ├── SessionList (gi 内部)
  │   │   ├── SessionItem (各会话项)
  │   │   │   ├── session-name (会话名)
  │   │   │   ├── cwd-breadcrumb (工作目录面包屑)
  │   │   │   └── resume-button (恢复按钮)
  │   │   └── "No recent projects. Use /cd <path> to switch."
  │   │
  │   ├── DirBrowser (ka — 目录浏览器组件)
  │   │   ├── cwd-prefix / dir-breadcrumb (路径导航)
  │   │   ├── dir-item (目录项,显示 d: 前缀)
  │   │   └── file-item (文件项,显示 f: 前缀)
  │   │
  │   └── PermissionDecision (ti — 权限决策弹窗)
  │       ├── "action: allow" / "action: block"
  │       ├── tool_name 显示
  │       └── 确认/拒绝按钮

  ├── Header (头栏)
  │   └── HeaderRemoteBtn (远程连接按钮,ghost-btn)
  │       └── svg qr icon

  ├── ChatBody (Hr)
  │   ├── ChatLanding (Na — 欢迎页面)
  │   │   ├── "Welcome to AtomCode. Pick an option to get started:"
  │   │   └── 选项列表
  │   │
  │   ├── MessageList (消息列表)
  │   │   ├── assistant-message-content (.markdown-root)
  │   │   │   └── code-block-wrapper (pre > code + copy-button)
  │   │   └── user-message
  │   │
  │   └── InputArea (聊天输入)
  │       ├── SlashPopover (斜杠命令弹出)
  │       │   ├── slash-row (斜杠命令项)
  │       │   │   ├── slash-name (/命令名)
  │       │   │   └── slash-desc (命令描述)
  │       │
  │       ├── MessageInput (textarea)
  │       │   ├── onInput, onKeyDown, onPaste
  │       │   └── placeholder (本地化)
  │       │
  │       ├── AttachmentInsert (Zr)
  │       │   ├── onInsert, onPickFile, onAddImages
  │       │   └── 图片预览
  │       │
  │       ├── SyncToggle (btn-sync)
  │       │   ├── "Attach to live webui session (/sync off to detach)"
  │       │   └── aria-pressed 状态
  │       │
  │       └── InputFooter
  │           └── 状态指标

  ├── Panels (覆盖层)
  │   ├── ThemePanel (Sa)       — 主题选择
  │   ├── LanguagePanel (Ca)    — 语言选择
  │   ├── ModelPanel (wa)       — 模型/提供商选择
  │   ├── RemotePanel (Da)      — Tunnel QR + 远程访问
  │   └── ProviderConfigPanel   — 提供商配置
  │       ├── provider-edit-btn
  │       └── 配置表单 (API key, base_url, model, etc.)

  └── Dialogs (模态)
      ├── SessionRename (Ki)   — 会话重命名
      └── SessionDelete (qi)   — 会话删除确认

3. 本地化系统

3.1 支持的语言

语言代码语言
en英语
zh-CN简体中文
zh-TW繁体中文
zh-HK繁体中文(香港)
zh中文

3.2 本地化消息示例

从二进制提取的示例:

javascript
// 英语 (en)
{
  "header.menu": "...",    // 头部菜单
  "settings.menuRemote": "Remote",
  "chat.inputPlaceholder": "Start your conversation...",
  "chat.stop": "Stop",
  "chat.send": "Send",
  "chat.queue": "Queue message (sent after the current turn finishes)",
  "chat.queued": "Queued",
  "chat.removeQueued": "Remove",
  "chat.error": "[Error: {msg}]",
  "chat.connError": "[Connection error: {msg}]",
  "attach.menu": "Add",
  "attach.image": "Upload image",
  "attach.file": "Upload file",
  "attach.skill": "Choose skill",
  "attach.removeImage": "Remove image",
  // ...
}

4. Markdown 渲染

4.1 渲染器配置

javascript
J.setOptions({ gfm: true, breaks: false });

class CustomRenderer {
    code(code, language) {
        // Pre > Code block + copy button
        return `<div class="code-block-wrapper">
            <pre><code class="language-${lang}">${escapedCode}</code></pre>
            <button class="copy-button" data-copy="...">Copy</button>
        </div>`;
    }
}

4.2 支持的内容格式

格式支持
GFM Markdown✅ (gfm=true)
mermaid 图表✅ (从 CSS 检出)
SVG
HTML✅ (经过 DOMPurify 消毒)
XML
行内代码
代码块 + 复制

4.3 XSS 保护

javascript
// DOMPurify 配置
Mr.sanitize(html, {
    ADD_ATTR: ["data-copy"]  // 允许复制按钮属性
});

5. 状态管理

5.1 Hooks 使用模式

javascript
// Session state
const sessionId = useState(id)

// CWD state
const cwd = useState(path)

// Active session tracking
const activeSession = useState(session)

// Toggle states (sync, etc.)
const [syncEnabled, toggleSync] = useState(false)

// Autocomplete state
const [input, setInput] = useState("")
const [suggestions, setSuggestions] = useState([])
const [selectedIndex, setSelectedIndex] = useState(0)

5.2 设置面板切换

javascript
// 每个设置面板由全局状态 d 控制
d === `theme`    → 显示 ThemePanel
d === `language` → 显示 LanguagePanel
d === `model`    → 显示 ModelPanel
d === `remote`   → 显示 RemotePanel

// 会话重命名状态
w === `rename` → 显示 RenameDialog
w === `delete` → 显示 DeleteDialog

6. API 通信

6.1 认证头

javascript
function G() {
    let t = getAuthToken();  // 从存储/内存获取 token
    return t ? { "Authorization": `Bearer ${t}` } : {};
}
// 备用: Yi() 函数等效

6.2 API 调用模式

javascript
// GET 请求
async function Pe() {
    return (await fetch(`/sessions`, { headers: G() })).json();
}

// POST + 错误处理
async function Fe(e, t, n) {
    let r = await fetch(`/projects/${encodeURIComponent(e)}/sessions/${encodeURIComponent(t)}/rename`, {
        method: `PATCH`,
        headers: { "Content-Type": `application/json`, ...G() },
        body: JSON.stringify({ name: n })
    });
    if (!r.ok) throw Error(`rename failed: ${r.status}`);
}

7. 代码复制功能

javascript
// 复制按钮处理
onClick(e) {
    let button = e.target?.closest(`.copy-button`);
    if (button?.dataset.copy) {
        navigator.clipboard?.writeText(
            decodeURIComponent(button.dataset.copy)
        ).catch(() => {});
        // 按钮文字 原文本 → "Copied" → 1.2 秒后恢复
        let original = button.textContent;
        button.textContent = `Copied`;
        setTimeout(() => { button.textContent = original }, 1200);
    }
}

8. SPA 嵌入分析

SPA 文件:
  assets/index-CM83LMnS.js    → 嵌入在 atomcode.bin 二进制中
  assets/index-DQAxDfIC.css   → 嵌入在 atomcode.bin 二进制中

加载方式:
  - Daemon WebUI 服务在端口 13457
  - 从 URL /assets/ 提供嵌入的 JS/CSS
  - 所有前端逻辑通过单个 JS 包运行 (~100KB+ 压缩后)

9. 新覆盖的盲区

#之前未覆盖的领域状态发现
1前端技术栈Preact + GFM Markdown + DOMPurify
2完整组件树20+ 组件,4 层深度
3本地化系统5 种语言 + 本地化消息键
4Markdown 渲染流程自定义渲染器 + XSS 保护
5状态管理Preact hooks + 面板切换机制
6代码复制clipboard API + "Copied" 反馈
7API 通信模式Auth header + 错误处理模式
8嵌入方式2 个 asset 文件嵌入 binary

本报告由持续分析循环生成

逆向命令索引

bash
# 从 daemon 提取 SPA JS
curl -s http://localhost:13457/assets/index-*.js -o /tmp/spa.js 2>/dev/null
wc -c /tmp/spa.js 2>/dev/null

# 提取 API 端点
grep -oP '"[a-z]+/[a-z_-]+"' /tmp/spa.js 2>/dev/null | sort -u | head -15

基于 VitePress 构建 · AtomCode v4.25.3 逆向工程分析文档