最近在使用 DeepSeek Harness 和 Codex 做 AI Coding 时,我一直有一个很简单的问题:

为什么 AI Coding 工具的界面一定只能是统一的灰黑色?

现在 Agent 已经可以帮我们读代码、修改文件、执行命令、跑测试。

与此同时,我们待在 AI Coding 界面里的时间也越来越长。

于是我做了一个开源项目:

BeautiCode

GitHub:

https://github.com/starsstreaming/beautiCode

它可以给 DeepSeek Harness 添加图片、视频和动态背景,同时也支持 Codex Desktop。

你可以把电脑里的:

  • 动漫 / 番剧
  • 电影
  • MV
  • 动态壁纸

直接放到 AI Coding 界面后面。
请添加图片描述
在这里插入图片描述

但这篇文章不只是介绍「怎么给 DSH 放个视频」。

我更想讲讲 BeautiCode 背后的几个设计问题:

如何在不破坏宿主应用的情况下加入动态背景?

视频如何加载,才能避免黑屏、闪屏和旧视频残留?

如果背景写入成功,但实际页面没显示,应该算成功吗?

本地视频如何提供给浏览器,又不把整个文件系统暴露出去?

DeepSeek Harness 和 Codex 两个完全不同的宿主,又该怎么共用一套核心逻辑?

这些才是 BeautiCode 真正比较有意思的部分。


一、首先,它不是「在窗口上盖一个播放器」

最简单的实现其实非常暴力:

直接创建一个永远置顶的视频窗口,然后调个透明度。

但这样很快就会遇到问题:

  • 鼠标点击被播放器截走
  • 输入框无法正常操作
  • 窗口尺寸和宿主不同步
  • Alt + Tab 多出一个窗口
  • 宿主滚动、弹窗、侧栏层级全部变得麻烦
  • 看起来也不像真正属于 Coding 界面的一部分

所以 BeautiCode 的设计方向从一开始就是:

背景属于宿主页面,而不是另一个播放器。

整体结构可以简单理解为:

┌───────────────────────────────┐
│       DeepSeek Harness        │
│                               │
│   ┌───────────────────────┐   │
│   │   原本的 UI / 对话区   │   │
│   │   输入框 / 按钮 / 代码 │   │
│   └───────────────────────┘   │
│                               │
│   ─────────────────────────   │
│      BeautiCode Background    │
│         Image / Video         │
└───────────────────────────────┘

动态背景处在内容层后方。

真正的 UI 依然属于 DeepSeek Harness。

这带来了 BeautiCode 的第一个设计原则:

Background First,Host UI First

BeautiCode 只负责「背景」。

不应该为了让背景更好看,就把宿主自己的 UI 大改一遍。

所以注入的背景层会:

pointer-events: none;

也就是说:

背景永远不能抢鼠标。

无论后面是一张图片还是正在播放的视频,都不会挡住:

  • 输入框
  • 按钮
  • Sidebar
  • 对话
  • 代码区域

这件事情看起来很小,但其实决定了 BeautiCode 到底是「皮肤」,还是一个盖在软件上面的播放器。


二、为什么首页很亮,真正工作时却会自动变暗?

如果只是为了展示效果,背景当然越清晰越好。

但真正 Coding 几分钟之后就会发现:

背景太亮是灾难。

尤其是视频。

人物、字幕、灯光不停变化,会持续干扰文字阅读。

所以 BeautiCode 没有简单地做一个固定的:

opacity: 0.3;

而是区分了两种状态。

首页 / 空闲状态

刚打开 DeepSeek Harness,还没有真正进入任务时:

Background
    ↓
保持原本亮度

这时候背景本身就是视觉主体。

可以完整看到壁纸、动漫或者视频。

项目 / 对话工作状态

真正进入任务后:

Background
    ↓
自动降低视觉权重
    ↓
Foreground UI
    ↓
重新成为视觉主体

也就是说,BeautiCode 的目标不是:

“让视频永远最显眼。”

而是:

需要工作时退到背景,需要休息时再回来。

我觉得这是整个项目里一个很重要的产品设计选择。

动态背景如果影响 Coding,那这个功能本身就失去了意义。


三、「摸鱼模式」其实不是重新创建一个播放器

BeautiCode 里还有一个很不正经的功能:

摸鱼模式

快捷键:

Ctrl + Shift + Space

Agent 正在执行一个比较长的任务时:

AI:正在修改 17 个文件……

我:Ctrl + Shift + Space

工作区域会暂时隐藏,只留下完整背景视频。

于是:

Coding Mode
┌─────────────────────┐
│ UI UI UI UI UI      │
│                     │
│    dimmed video     │
└─────────────────────┘

        ↓ hotkey

Fish Mode
┌─────────────────────┐
│                     │
│                     │
│     full video      │
│                     │
└─────────────────────┘

再按一次:

直接回到 Coding。

这里有一个实现上的细节:

BeautiCode 不会因为进入摸鱼模式就销毁并重建视频。

它做的只是切换页面状态。

类似:

data-bc-fish="true"

然后让宿主内容区域:

opacity → 0
visibility → hidden

背景舞台本身继续播放。

所以切换摸鱼模式时:

  • 视频不会重新加载
  • 不会重新解码
  • 不会从头播放
  • 不需要重新生成背景
  • 不会产生一次新的背景事务

这也是为什么它可以做到按一下立即隐藏,再按一下立即回来。

从实现上看,它更接近一个状态切换,而不是重新构造 UI。


四、真正麻烦的是视频,而不是图片

图片背景其实比较简单。

把图片验证、复制、加载、显示即可。

视频就完全不同了。

第一次做动态背景时,很容易碰到:

视频节点创建了
↓
浏览器还没解码第一帧
↓
背景突然黑一下
↓
视频出现

或者切换视频:

旧视频消失
↓
新视频加载中
↓
白屏 / 黑屏
↓
新视频出现

视觉效果会非常糟糕。

BeautiCode 因此采用了一个:

Poster → Video 的两阶段模型

每个视频背景并不只有视频本身。

而是:

Video Theme
├── poster
└── background.mp4

加载流程是:

显示 poster
      ↓
创建 video
      ↓
加载视频
      ↓
等待浏览器真正解码出第一帧
      ↓
video ready
      ↓
显示 video
      ↓
poster 退出

也就是说:

视频没有真正 ready 之前,用户看到的一直是 Poster,而不是一个空白的视频元素。

同时,新视频加载时,旧背景也不会立刻被干掉。

大致是:

旧背景正常显示

      ↓

新视频后台加载

      ↓

新视频 first frame ready

      ↓

完成切换

如果新视频失败:

new video
    ↓
decode failed
    ↓
保持 / 恢复旧背景

用户不会看到一个已经损坏的半成品状态。


五、Generation:解决异步视频最讨厌的竞态问题

视频加载是异步的。

这会产生一个非常经典的问题。

假设用户快速切换:

A.mp4
  ↓
B.mp4

理论上 B 是最新背景。

但 A 的某一个异步事件可能晚一点才回来:

A load()
B load()
B ready
A error   ← 现在才回来

如果不做任何保护:

A 的 error handler 可能会把已经正常显示的 B 错误地标记成失败。

所以 BeautiCode 给每次 Apply 都分配一个单调递增的:

generation

例如:

A → generation 31
B → generation 32

异步回调执行前先判断:

callbackGeneration === currentGeneration

如果:

31 !== 32

那么这个事件已经属于旧世界线。

直接忽略。

这套机制不仅用于视频。

它也是 BeautiCode 整个背景状态的一部分。

Manifest 大致可以理解为:

{
  "schema": "beauticode.background/v1",
  "generation": 32,
  "background": {
    "type": "video",
    "image": "poster.jpg",
    "video": "background.mp4"
  }
}

这样可以避免大量:

  • stale callback
  • 快速切换
  • 延迟 error
  • 页面重新注入
  • 旧媒体覆盖新媒体

导致的竞态问题。


六、写进磁盘,不等于应用成功

这是后来我越来越在意的一点。

假设用户点击:

设置 video.mp4 为背景

程序:

复制文件成功
写 JSON 成功

很多程序到这里可能就:

success!

但实际上页面可能:

  • 根本没有加载
  • video decode 失败
  • 插件连接断了
  • DOM 结构变化
  • 页面没有成功插入背景
  • 当前宿主已经退出

所以 BeautiCode 做了一个完整的:

Apply Transaction

核心原则是:

Disk success ≠ User-visible success

一次背景应用大致经历:

idle
 ↓
snapshot
 ↓
stage
 ↓
commit disk
 ↓
publish media
 ↓
apply host
 ↓
live verify
 ↓
┌──────────────┐
│              │
pass          fail
│              │
finalize      rollback

1. Snapshot

应用新背景之前,先记录当前状态。

包括:

  • 当前 Manifest
  • 当前 Poster
  • 当前 Video
  • 当前 Generation

2. Stage

新媒体不会直接覆盖当前文件。

而是先创建:

staging/

在 staging 中完成:

  • 文件复制
  • 类型验证
  • Manifest 构建
  • 完整性检查

3. Commit

只有 staging 完整后,才会切换成新的 active 状态。

因此不会出现:

manifest 是新的

但 video 还是旧的

这种一半新、一半旧的状态。


4. Apply Host

然后通知真正运行中的 DeepSeek Harness / Codex:

generation 42 已经准备好了。

宿主开始加载背景。


5. Live Verify

接下来不是直接返回成功。

而是检查:

背景舞台真的存在吗?

图片真的显示了吗?

视频真的 ready 了吗?

当前 generation 对吗?

背景层有没有造成页面横向 overflow?

浏览器有没有报告 videoFailed?

只有 Live Verify 通过:

transaction → finalize

否则:

transaction → rollback

恢复旧背景。

这套东西对于一个「动态壁纸工具」看起来可能有点重。

但我后来越来越觉得:

只要一个工具开始修改另一个应用的运行状态,就应该区分“我执行了操作”和“操作真的生效了”。


七、本地视频为什么还需要 Media Server?

浏览器里的视频不能简单理解成:

<video src="D:\Anime\xxx.mp4">

尤其是当你进入:

  • CSP
  • Chromium
  • Electron
  • 本地文件权限
  • Range Request

之后,会遇到非常多边界问题。

所以 BeautiCode 抽象了一层:

Media Server

结构类似:

Local MP4
   ↓
Validation
   ↓
beautiCode Data Root
   ↓
Local Media Server
   ↓
127.0.0.1:random_port
   ↓
DeepSeek Harness

这里我给自己定了一个很明确的安全边界:

永远只监听 Loopback

只允许:

127.0.0.1
localhost
::1

不允许:

0.0.0.0
LAN IP

因为 BeautiCode 根本没有理由把你正在看的本地视频暴露给局域网。


八、不是选到一个 MP4 就直接提供给浏览器

导入媒体时也会进行检查。

例如图片目前限定:

.jpg
.jpeg
.png
.webp

除了扩展名,还会检查:

  • regular file
  • 实际路径
  • 文件大小
  • Magic Bytes
  • Symlink / Reparse Point

视频目前主要支持:

.mp4

同样不仅仅检查:

filename.endsWith(".mp4")

还需要确认基本的 MP4 / ISO-BMFF ftyp 结构。

也就是说:

evil.exe
↓
改名
↓
evil.mp4

不会因为扩展名正确就直接被当成媒体。

导入的文件也不会继续直接引用用户原始路径。

而是复制到 BeautiCode 自己的数据目录。

这样 Core 只管理自己拥有的:

active/
staging/
saved/
snapshots/

而不是拿着任意用户路径到处传递。


九、本地服务也需要鉴权

有人可能会觉得:

都已经 127.0.0.1 了,还做什么鉴权?

但 localhost 并不意味着:

只有 BeautiCode 可以访问。

浏览器里的其他页面、本机其他程序也可能尝试访问本地端口。

所以 BeautiCode 的控制接口和媒体接口都增加了随机 Token。

可以理解为:

http://127.0.0.1:xxxxx/media/<random-token>

同时还会检查:

  • token
  • origin
  • 文件身份
  • 路径范围

也就是说,它不是:

GET /?file=C:\Users\xxx\anything

这种任意文件服务器。

Media Server 只知道:

我当前被允许提供的这一份媒体。

从设计上尽量把攻击面限制在一个非常小的范围内。


十、为什么不直接修改 DeepSeek Harness 源码?

早期做类似功能,一个非常自然的想法是:

找到前端文件
↓
修改 CSS
↓
Patch
↓
启动

但这种方案的问题也非常明显:

宿主升级
↓
文件变化
↓
Patch 失效

甚至更糟:

  • 修改官方安装目录
  • 修改签名
  • 修改打包后的文件
  • 每次更新重新 Patch

所以 BeautiCode 给自己定了一条安全边界:

不修改官方宿主安装。

对于 DeepSeek Harness,现在采用的是:

Cordis Plugin

大致结构:

DeepSeek Harness
       │
       │ Cordis Plugin
       ▼
@beauticode/dsh-plugin
       │
       │ local authenticated bridge
       ▼
BeautiCode Core
       │
       ├── Background Store
       ├── Apply Transaction
       ├── Media Server
       ├── Validation
       └── Theme

这样 BeautiCode 不需要 Fork DeepSeek Harness。

也不需要修改 DSH 源码。

更重要的是:

宿主负责宿主,插件负责插件。

这是我现在比较倾向的一种 Agent 工具扩展方式。


十一、甚至可以直接从 DSH 对话里换背景

既然已经是一个 DSH Plugin,就不一定非得通过托盘操作。

BeautiCode 目前已经可以向 DSH 注册对应的工具和命令。

例如:

/bg D:\Videos\rain.mp4

或者切换主题:

/bg-theme 雨夜写代码

清除:

/bg-clear

进一步还可以把操作暴露成 Tool。

于是理论上可以直接告诉 Agent:

把 D:\Videos\city.mp4 设置成背景。

插件负责:

Tool Call
   ↓
验证路径
   ↓
导入媒体
   ↓
Apply Transaction
   ↓
Host Apply
   ↓
Live Verify

这一点我其实觉得比「动态壁纸」本身更有意思。

因为它意味着 BeautiCode 正在从:

一个外部修改工具

逐渐变成:

AI Coding 宿主内部的一项能力。


十二、为什么 Codex 和 DSH 不共用同一套注入代码?

目前 BeautiCode 同时面对两个宿主:

DeepSeek Harness
Codex Desktop

它们的扩展能力不同。

因此项目没有强行写成一个巨型:

if (dsh) ...
else if (codex) ...

目前整体大致分成:

packages/
├── core
├── adapter-dsh
└── adapter-codex

Core

只负责与宿主无关的能力:

Background Store
Media Validation
Media Server
Apply Transaction
File Lock
Paths
Types

Adapter DSH

负责:

DSH Bridge
Cordis Plugin
Host Verify
DSH Page Integration

Adapter Codex

负责:

Codex Host Discovery
CDP Connection
Renderer Injection
Host Verify

于是:

             ┌── adapter-dsh ── DeepSeek Harness
             │
Core ────────┤
             │
             └── adapter-codex ─ Codex Desktop

媒体管理、安全规则、事务逻辑没有因为宿主变化而复制两份。

变化的只是:

怎么把最终状态送进 Host。

这也是这次重构之后我比较满意的地方。


十三、Codex 为什么会用到 CDP?

DeepSeek Harness 有插件接口。

Codex Desktop 并没有完全相同的扩展入口。

所以 Codex 适配走的是另一条路线:

Chrome DevTools Protocol

但 BeautiCode 并不会修改:

app.asar
官方 exe
签名
MSIX / AppX

而是尽量通过宿主已经暴露出来的调试能力连接 Renderer。

然后在运行时添加自己的 Background Stage。

例如:

#beauticode-bg-stage
├── img
└── video

整个 Stage:

position: fixed
pointer-events: none

再根据当前状态设置:

data-bc-active
data-bc-media
data-bc-video-ready
data-bc-generation
data-bc-working
data-bc-fish

然后由 CSS 和 Runtime 根据这些明确状态决定:

该显示谁
该隐藏谁
该降低多少视觉权重

而不是不停往页面上散落各种临时 Class。

这让运行时状态相对容易验证和回滚。


十四、保存主题不仅仅是保存一个文件路径

BeautiCode 还支持:

雨夜写代码
赛博城市
海边下午
番剧模式

这样的 Saved Theme。

视频主题还会记录:

videoPositionSec

也就是当前播放位置。

比如今天:

01:13:42

关掉。

明天重新选择这个主题:

seek → 01:13:42

继续播放。

这意味着 Theme 保存的不只是:

videoPath

而是一个小型的:

工作环境状态。

后续其实还可以继续扩展:

背景
+
亮度
+
音量
+
布局偏好
+
不同项目绑定

让不同项目真正拥有不同的 Coding Environment。


十五、BeautiCode 真正想做的,并不是「上班偷偷看番」

摸鱼模式当然很好玩。

但如果只把 BeautiCode 理解成:

AI 干活,我看番

其实少了一半。

我真正比较感兴趣的是另一个问题:

AI Coding 工具会不会逐渐变成开发者的新桌面?

过去我们主要待在:

IDE
Terminal
Browser

现在越来越多时间开始待在:

Claude Code
Codex
DeepSeek Harness
各种 Coding Agent

当 Agent 逐渐接管:

  • 搜代码
  • 改代码
  • 执行命令
  • 跑测试
  • Debug
  • 重构

开发者真正长时间注视的,可能不再只是编辑器。

而是:

Agent 的工作空间。

既然这个空间每天可能陪我们几个小时,那它是否也应该允许:

个性
氛围
审美
习惯
状态

进入其中?

有人喜欢纯黑。

有人喜欢 Cyberpunk。

有人喜欢雨夜。

有人喜欢海边。

也有人可能真的想让一部番剧慢慢在身后播放。

所以我最后给 BeautiCode 定下来的方向是:

把你喜欢的画面,放进 vibe coding 的每一分钟。

它首先必须是一个不打扰工作的工具。

然后才是一张动态皮肤。


十六、目前支持

目前 BeautiCode 主要支持:

Windows

DeepSeek Harness
Codex Desktop

JPG
JPEG
PNG
WebP

MP4

项目采用 Node.js / TypeScript 多包结构,核心逻辑与宿主 Adapter 分离。

同时提供 Windows 安装方式。

项目完全开源:

MIT License

GitHub:

https://github.com/starsstreaming/beautiCode


最后

BeautiCode 现在仍然是一个比较新的项目。

所以比起单纯增加更多背景格式,我目前更希望先把这些基础东西做好:

宿主适配稳定性
媒体生命周期
失败回滚
安全边界
DSH 插件化
安装体验
不同版本兼容

如果你愿意帮忙测试,欢迎直接下载使用。

遇到:

  • Bug
  • DeepSeek Harness 兼容问题
  • Codex 兼容问题
  • 视频播放异常
  • 安装问题
  • 页面显示问题

都可以直接提交 Issue。

有什么想要的功能,也欢迎提 Feature Request。

如果觉得这个项目有点意思,也欢迎顺手点一个:

⭐ Star

GitHub:

starsstreaming/beautiCode

AI Coding 工具正在变得越来越强。

我也希望它们在变强的同时,能够稍微变得更像一个属于自己的地方。

Logo

欢迎加入 MCP 技术社区!与志同道合者携手前行,一同解锁 MCP 技术的无限可能!

更多推荐