地铁上也能写代码?这个开源项目让AI编程助手装进了你的手机
技术选型理由服务端渲染+SignalR实时通信,完美支持流式输出成熟的企业级UI组件库,开箱即用VS Code同款编辑器内核,专业级代码展示轻量级ORM,支持多数据库切换Docker一键部署,环境隔离你可能会问:为什么选Blazor而不是React/Vue?我觉得这是一个非常务实的选择。Blazor Server模式的最大优势是——所有代码都在服务端运行。前后端天然统一:不需要写两套代码、处理CO
当我第一次在拥挤的地铁里,用手机敲下"帮我写一个React登录页面",然后看着屏幕上Claude Code一行行输出代码时,我突然意识到:编程这件事,终于可以随身携带了。
引子:一个程序员的通勤噩梦
作为一个资深"996"程序员,我每天花在通勤上的时间大约两小时。这两小时里,我通常会刷刷技术文章、看看B站视频,或者干脆闭目养神。但心里总有个声音在说:要是能把这时间用来写代码多好?
可是——笔记本太重、iPad没有好用的IDE、手机屏幕又太小……各种借口让我一直没能实现"移动办公"的梦想。
直到我发现了 WebCode 这个项目。
它不是一个简单的在线代码编辑器,也不是又一个"可以在浏览器里写代码"的玩具。它是一个真正能在手机上远程驱动AI编程助手的平台——你只需要打开浏览器,输入你的想法,剩下的交给Claude Code或Codex来搞定。
听起来很魔幻?让我带你一探究竟。
一、项目背景:为什么需要一个Web版的AI编程助手?
1.1 AI编程工具的现状与痛点
2024-2025年,AI编程助手如雨后春笋般涌现:Claude Code、OpenAI Codex、GitHub Copilot、Qwen……这些工具正在彻底改变我们写代码的方式。
但问题来了:这些工具大多是命令行工具(CLI),需要在本地环境中运行。
这意味着什么呢?
-
你需要一台配置不错的电脑
-
你需要安装各种环境依赖
-
你需要配置API Key、代理等
-
最重要的——你必须坐在电脑前
作为一个经常在路上的人,我真的很想问一句:难道在咖啡馆、在地铁上、甚至在马桶上,我就不能让AI帮我写代码了吗?
1.2 WebCode的解决方案
WebCode的核心思路非常简单:
把CLI工具部署在服务器上,通过Web界面远程调用。
就像你用网页版的在线IDE一样,只不过这次敲代码的不是你,而是AI。你负责提需求,AI负责干活,网页负责展示——完美的三角关系。
更妙的是,它支持多种AI CLI工具,通过一套优雅的适配器模式,可以无缝切换Claude Code、Codex等不同的AI助手。今天用Claude,明天换Codex,后天说不定还能接入国产的Qwen——总有一款适合你。
二、技术架构解析:优雅设计的背后
2.1 整体架构概览
先来看看项目的目录结构,感受一下DDD(领域驱动设计)的气息:
WebCodeCli/
├── WebCodeCli/ # 主项目 (Blazor Server)
│ ├── Components/ # 可复用组件
│ ├── Pages/ # 页面组件
│ │ └── CodeAssistant.razor # 核心编程助手页面
│ └── wwwroot/ # 静态资源
├── WebCodeCli.Domain/ # 领域层
│ ├── Domain/
│ │ ├── Model/ # 领域模型
│ │ └── Service/ # 领域服务
│ │ └── Adapters/ # CLI适配器(核心!)
│ └── Repositories/ # 数据仓储
└── cli/ # CLI工具帮助文档
如果你熟悉企业级应用开发,这个结构一定让你感到亲切。清晰的分层、明确的职责划分——这不是一个周末写着玩的项目,而是一个经过认真设计的产品。
2.2 技术栈选型:为什么是Blazor?
项目采用的技术栈值得好好聊聊:
| 技术 | 选型理由 |
|---|---|
| Blazor Server | 服务端渲染+SignalR实时通信,完美支持流式输出 |
| Ant Design Blazor | 成熟的企业级UI组件库,开箱即用 |
| Monaco Editor | VS Code同款编辑器内核,专业级代码展示 |
| SqlSugar ORM | 轻量级ORM,支持多数据库切换 |
| Docker | 一键部署,环境隔离 |
你可能会问:为什么选Blazor而不是React/Vue?
我觉得这是一个非常务实的选择。Blazor Server模式的最大优势是——所有代码都在服务端运行。这意味着:
-
前后端天然统一:不需要写两套代码、处理CORS、维护API接口
-
实时通信开箱即用:SignalR自动管理WebSocket连接
-
流式输出天然支持:AI的打字机效果实现起来轻而易举
对于一个需要频繁与后端进程交互、需要实时展示输出的应用来说,Blazor Server简直是天作之合。
2.3 核心设计亮点:适配器模式的优雅运用
如果说整个项目有什么设计让我眼前一亮,那一定是CLI适配器系统。
不同的AI CLI工具有着完全不同的:
-
命令格式:Claude用
-p表示print模式,Codex用exec -
输出格式:Claude输出
stream-json,Codex输出JSONL -
会话恢复机制:Claude用
--resume,Codex用resume <session_id>
如果不做抽象,代码里会充斥着这样的判断:
if (toolId == "claude") {
// Claude的逻辑
} else if (toolId == "codex") {
// Codex的逻辑
} else if (toolId == "qwen") {
// Qwen的逻辑
}
// ... 噩梦开始
WebCode的解决方案是定义一个统一的适配器接口:
public interface ICliToolAdapter
{
// 支持哪些工具
string[] SupportedToolIds { get; }
// 是否支持流式输出解析
bool SupportsStreamParsing { get; }
// 构建命令行参数
string BuildArguments(CliToolConfig tool, string prompt, CliSessionContext context);
// 解析输出行
CliOutputEvent? ParseOutputLine(string line);
// 提取会话ID(用于恢复会话)
string? ExtractSessionId(CliOutputEvent outputEvent);
// ... 其他方法
}
然后为每个CLI工具实现自己的适配器。比如Claude Code适配器:
public class ClaudeCodeAdapter : ICliToolAdapter
{
public string[] SupportedToolIds => new[] { "claude-code", "claude" };
public bool SupportsStreamParsing => true;
public string BuildArguments(CliToolConfig tool, string prompt, CliSessionContext context)
{
var argsBuilder = new StringBuilder();
argsBuilder.Append("-p --verbose --output-format=stream-json ");
argsBuilder.Append("--dangerously-skip-permissions ");
// 如果是恢复会话,加上--resume参数
if (context.IsResume && !string.IsNullOrEmpty(context.CliThreadId))
{
argsBuilder.Append($"--resume {context.CliThreadId} ");
}
argsBuilder.Append($"\"{escapedPrompt}\"");
return argsBuilder.ToString();
}
// ... 其他实现
}
这种设计的好处是显而易见的:
-
开闭原则:添加新工具只需新增适配器,不修改现有代码
-
单一职责:每个适配器只关心自己的CLI工具
-
可测试性:适配器可以独立单元测试
-
可扩展性:支持运行时动态注册新适配器
// 在工厂里动态注册新适配器
public void RegisterAdapter(ICliToolAdapter adapter)
{
if (adapter != null && !_adapters.Contains(adapter))
{
_adapters.Add(adapter);
}
}
这意味着什么?你甚至可以开发一个插件系统,让用户自己上传适配器来支持新的CLI工具!
三、核心功能实现:流式输出的艺术
3.1 为什么流式输出很重要?
用过ChatGPT或Claude的朋友都知道,AI的回复是一个字一个字蹦出来的,像打字机一样。这不仅仅是为了好看——它大大提升了用户体验。
想象一下,你问AI一个问题,然后盯着空白的屏幕等30秒,最后"唰"地一下全部显示出来。这种体验简直让人焦虑。
但如果是逐字输出呢?你能看到AI在"思考",能看到代码一行行生成,能提前发现方向是否正确——这种实时感让交互变得更加自然。
3.2 流式输出的技术实现
WebCode的流式输出实现非常优雅。核心是使用了C#的IAsyncEnumerable:
public async IAsyncEnumerable<StreamOutputChunk> ExecuteStreamAsync(
string sessionId,
string toolId,
string userPrompt,
CancellationToken cancellationToken = default)
{
// 获取工具配置和适配器
var tool = GetTool(toolId);
var adapter = _adapterFactory.GetAdapter(tool);
// 启动进程
var process = StartCliProcess(tool, userPrompt, sessionContext);
// 流式读取输出
await foreach (var chunk in ReadProcessOutputAsync(process, cancellationToken))
{
yield return chunk;
}
}
IAsyncEnumerable是.NET中处理异步流的利器。它允许你在数据产生时就立即返回给调用者,而不需要等到所有数据都准备好。
在前端(Blazor组件)里,消费这个流也非常直观:
await foreach (var chunk in _cliExecutor.ExecuteStreamAsync(sessionId, toolId, prompt))
{
// 实时更新UI
_currentOutput += chunk.Content;
StateHasChanged(); // 触发Blazor重新渲染
}
每读到一个chunk,就更新一次UI——这就是打字机效果的秘密。
3.3 输出解析:把混乱变成有序
AI CLI工具的输出通常是JSON格式的,但格式五花八门。Claude Code输出的是这样的:
{"type":"assistant","message":{"role":"assistant","content":[{"type":"text","text":"让我帮你..."}]}}
而Codex输出的是这样的:
{"event":"item.created","item":{"type":"message","content":[{"type":"text","text":"..."}]}}
WebCode的适配器会把这些不同格式统一解析成CliOutputEvent对象:
public class CliOutputEvent
{
public string EventType { get; set; } // 事件类型:message/tool_use/error等
public string? SessionId { get; set; } // 会话ID
public string? Content { get; set; } // 消息内容
public bool IsError { get; set; } // 是否是错误
public CliCommandExecution? CommandExecution; // 如果是命令执行
public List<CliTodoItem>? TodoItems; // 如果是待办列表
}
这样,无论底层是Claude还是Codex,上层UI都可以用统一的方式来展示——这就是抽象的力量。
四、会话管理与工作区隔离
4.1 为什么需要会话隔离?
想象一下:小明在用你的WebCode写一个电商项目,小红在写一个博客系统。如果他们的文件都混在一起,那简直是灾难——小明的index.html可能会被小红的覆盖!
WebCode的解决方案是:每个会话拥有独立的工作区目录。
private string GetOrCreateSessionWorkspace(string sessionId)
{
lock (_workspaceLock)
{
// 如果已存在,直接返回
if (_sessionWorkspaces.TryGetValue(sessionId, out var existingWorkspace))
{
return existingWorkspace;
}
// 创建新的会话工作目录
var workspacePath = Path.Combine(workspaceRoot, sessionId);
Directory.CreateDirectory(workspacePath);
// 创建标记文件,记录创建时间
File.WriteAllText(
Path.Combine(workspacePath, ".workspace_info"),
$"Created: {DateTime.UtcNow}\nSessionId: {sessionId}"
);
return workspacePath;
}
}
每个会话都有一个以sessionId命名的目录,里面存放该会话的所有文件。AI在执行命令时,工作目录就设置为这个会话目录——完美隔离。
4.2 过期工作区自动清理
工作区多了怎么办?总不能无限增长吧?
WebCode实现了一个后台清理服务,定期清理过期的工作区:
public void CleanupExpiredWorkspaces()
{
var expirationTime = DateTime.UtcNow.AddHours(-_options.WorkspaceExpirationHours);
var directories = Directory.GetDirectories(workspaceRoot);
foreach (var dir in directories)
{
var markerFile = Path.Combine(dir, ".workspace_info");
var lastAccessTime = File.GetLastWriteTimeUtc(markerFile);
if (lastAccessTime < expirationTime)
{
Directory.Delete(dir, recursive: true);
_logger.LogInformation("已清理过期工作区: {Path}", dir);
}
}
}
默认配置是24小时,超过24小时没有活动的工作区会被自动清理。当然,这个时间是可配置的。
4.3 会话恢复:跨请求的上下文保持
Claude Code和Codex都支持会话恢复——你可以在一个会话里多次对话,AI会记住之前的上下文。
WebCode完整支持这个特性。当AI返回会话ID时,系统会自动保存:
// 从输出中解析会话ID
var parsedThreadId = ParseCliThreadId(output, adapter);
if (!string.IsNullOrEmpty(parsedThreadId))
{
SetCliThreadId(sessionId, parsedThreadId);
}
下次发送请求时,会话上下文会自动带上这个ID:
var sessionContext = new CliSessionContext
{
SessionId = sessionId,
CliThreadId = GetCliThreadId(sessionId), // 自动获取之前保存的ID
WorkingDirectory = sessionWorkspace
};
// 适配器会根据上下文构建恢复参数
// Claude: --resume <thread_id>
// Codex: resume <thread_id>
这意味着什么?你可以在手机上开始一个编程任务,晚上回家后在电脑上继续——只要会话没过期,AI就记得之前的对话!
五、移动端优化:真正的"随身编程"
5.1 响应式布局
WebCode在移动端的体验令人印象深刻。打开手机浏览器,你会发现界面自动适配成了上下布局:上面是聊天记录,下面是输入框。
这是通过Tailwind CSS的响应式类实现的:
<div class="flex flex-col lg:flex-row">
<div class="w-full lg:w-1/2"><!-- 聊天区域 --></div>
<div class="w-full lg:w-1/2"><!-- 预览区域 --></div>
</div>
lg:前缀表示只在大屏幕上生效,小屏幕上会使用默认的flex-col(垂直排列)。
5.2 触摸优化
移动端最恼人的体验是什么?按钮太小,总是点不中!
WebCode的所有可点击元素都遵循Apple的人机交互指南:最小44x44像素的触摸目标。
.touch-target {
min-height: 44px;
min-width: 44px;
padding: 12px 16px;
}
此外,还有按压反馈效果,让你知道自己确实点到了:
.touch-target:active {
transform: scale(0.98);
opacity: 0.9;
}
5.3 iOS Safari的100vh问题
如果你开发过移动端网页,一定被Safari的100vh问题折磨过——地址栏的出现和消失会导致布局跳动。
WebCode的解决方案:
.full-height {
height: 100vh;
height: 100dvh; /* dynamic viewport height */
}
100dvh是CSS新增的单位,表示"动态视口高度",会随着地址栏的显示/隐藏自动调整。对于不支持的老浏览器,会降级到100vh。
六、部署方案:Docker一键启动
6.1 30秒启动体验
WebCode的部署体验堪称完美。只需要三条命令:
git clone https://github.com/xuzeyu91/WebCode.git
cd WebCode
docker compose up -d
然后访问http://localhost:5000,完事。
首次访问时,系统会自动进入设置向导,引导你配置Claude/Codex的API Key等信息。不需要手动编辑任何配置文件——这才是现代软件应有的用户体验。
6.2 Docker镜像的设计
看看Dockerfile,你会发现作者的用心:
# 多阶段构建
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
# ... 构建阶段
FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS runtime
# 安装Node.js 20.x(Claude Code依赖)
RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \
&& apt-get install -y nodejs
# 安装Python 3(部分工具依赖)
RUN apt-get install -y python3 python3-pip
# 预装Claude Code CLI和Codex CLI
RUN npm install -g @anthropic-ai/claude-code @openai/codex-cli
# 非root用户运行(安全性)
RUN adduser --disabled-password --gecos "" webcode
USER webcode
多阶段构建、预装依赖、非root用户运行——这是一个生产级的Docker镜像。
6.3 数据持久化
docker-compose.yml配置了三个数据卷:
volumes:
- ./data:/app/data # 数据库和配置
- ./workspaces:/app/workspaces # 会话工作区
- ./logs:/app/logs # 日志文件
这意味着容器重启不会丢失任何数据——历史会话、用户设置、生成的代码文件都会保留。
七、安全特性:不只是能用,还要安全
7.1 命令白名单
虽然AI很智能,但我们不能完全信任它执行的命令。WebCode支持命令白名单:
{
"CliTools": {
"EnableCommandWhitelist": true,
"CommandWhitelist": ["git", "npm", "node", "python"]
}
}
只有白名单内的命令才能执行,有效防止AI"脑抽"执行rm -rf /这种危险操作。
7.2 并发限制
同时运行太多AI进程可能会拖垮服务器。WebCode使用信号量限制并发:
private readonly SemaphoreSlim _concurrencyLimiter;
public CliExecutorService(IOptions<CliToolsOption> options)
{
_concurrencyLimiter = new SemaphoreSlim(options.Value.MaxConcurrentExecutions);
}
public async IAsyncEnumerable<StreamOutputChunk> ExecuteStreamAsync(...)
{
await _concurrencyLimiter.WaitAsync(cancellationToken);
try
{
// 执行命令
}
finally
{
_concurrencyLimiter.Release();
}
}
默认最多同时执行3个AI请求,超出的会排队等待。
7.3 路径安全检查
文件操作时,系统会严格检查路径是否在工作区内,防止目录遍历攻击:
var normalizedWorkspace = Path.GetFullPath(workspacePath);
var normalizedFile = Path.GetFullPath(fullPath);
if (!normalizedFile.StartsWith(normalizedWorkspace))
{
_logger.LogWarning("尝试访问工作区外的文件: {File}", relativePath);
return null;
}
即使有人构造../../etc/passwd这样的恶意路径,也会被拦截。
八、实际应用场景
8.1 移动办公
这是WebCode最核心的使用场景。想象一下:
-
早上通勤时,在地铁上用手机告诉Claude:"帮我优化一下昨天那个SQL查询"
-
午休时间,在食堂用平板让Codex生成一组单元测试
-
晚上躺沙发,用手机review AI生成的代码
编程不再是被电脑"绑架"的活动。
8.2 远程开发
很多公司的代码只能在内网开发机上编辑。有了WebCode,你可以把它部署在开发机上,然后通过VPN在任何地方访问。
这比VS Code Remote或JetBrains Gateway更轻量——你只需要一个浏览器。
8.3 教学演示
给学生讲解编程时,你可以一边说需求,一边让AI实时生成代码。学生能看到整个"从需求到代码"的过程,比看PPT有趣100倍。
8.4 快速原型
客户突然来电:"能不能做个XXX功能?"
以前你可能会说"我回去看看",现在你可以当场掏出手机,让AI生成一个原型,3分钟后给客户演示——这就是AI时代的生产力。
九、未来展望:这只是开始
9.1 更多AI工具支持
目前WebCode完整支持Claude Code和Codex,GitHub Copilot CLI、Qwen、Gemini等都在适配中。
得益于优秀的适配器设计,添加新工具只需要实现一个接口——社区贡献者可以轻松参与进来。
9.2 协作功能
未来可能会加入:
-
多人共享同一个会话
-
实时看到其他人的输入和AI输出
-
会话快照分享
想象一下,远程面试时,面试官和候选人同时看着AI生成的代码进行讨论——这是不是比共享屏幕更优雅?
9.3 插件生态
如果能开放适配器的动态加载,让用户上传自己的适配器来支持新的CLI工具,那将会是一个真正的平台。
十、写在最后
WebCode并不是什么革命性的创新——它只是把几个已有的东西巧妙地组合在一起:
-
Blazor Server的实时通信能力
-
CLI工具的命令行接口
-
适配器模式的软件设计
-
Docker的容器化部署
但正是这种"把简单的事情做好"的态度,才让它成为一个真正好用的产品。
如果你也厌倦了只能坐在电脑前写代码,不妨试试WebCode。也许某天,你会和我一样,在拥挤的地铁里,对着手机屏幕上一行行生成的代码会心一笑。
编程,从此随身携带。
相关链接
-
项目地址:https://github.com/xuzeyu91/WebCode
-
在线体验:https://webcode.tree456.com/(账号:treechat,密码:treechat@123)
博主在参加2025博客之星活动
如果这篇文章对你有帮助,希望你能帮我投出宝贵的一票!
投票地址:https://www.csdn.net/blogstar2025/detail/070
每一票都是对我创作的巨大鼓励,感谢你的支持!
更多推荐





所有评论(0)