1、前言
程序员这个职业,真是个全能的职业,上的厨房下的机房。每天的"运动"就是手指从 A 键挪到 Z 键。每天的常态是不断 Ctrl+C、Ctrl+V ,越忙越粘键盘,效率反而掉成狗。
于是我把"摸鱼"两个字正经写进了 OKR,目标很单纯:把划水变成合法行为,定时休息,bug 减半。
但是一旦陷入头脑休息期间,就会发现脑袋空空,手指不知道干嘛。于是就用AI帮我写了个vscode插件,名副其实的摸鱼插件。
这里全程近乎没有自己手撸的代码,只是给到了AI创意想法,AI帮我实现。不得不感叹,危已~
2、项目到底干啥
Dev Fish 就是一款把"休息"包装成"养鱼"的插件。装完之后,VSCode 侧边栏会多一个鱼缸:
- 里头的鱼会围观你写代码,你敲得快它们就加速游,Debug 时集体装死。
- 番茄钟到点,鱼会冒泡提醒你"该起来倒水了"。
- 通过Ctrl+Shift+p,可以键入指令,添加鱼苗,喂养,或清洁鱼缸等互动。
3、演示下效果图(装个13)
首页图:

Ctrl+shift+P唤起指令输入框:

添加鱼:

智慧信息流:

4、技术栈一句话说完前端
TypeScript + VSCode Webview + Canvas
4.1、核心代码
4.1.1、MVC
scss
Model (数据层)
├── Fish.ts - 鱼的数据和行为
├── Tank.ts - 水缸的数据和管理
└── StorageService - 数据持久化
View (视图层)
├── AquariumProvider - Webview 提供者
├── HTML/CSS - UI 结构和样式
└── Canvas - 动画渲染
Controller (控制层)
├── fishCommands - 鱼的命令处理
├── breakCommands - 休息的命令处理
└── wisdomCommands - 智慧的命令处理
4.1.2、 鱼的运动算法
typescript
class Fish {
private baseVelocityX: number;
private baseVelocityY: number;
private speedMultiplier: number = 1.0;
update(deltaTime: number): void {
// 计算实际速度 = 基础速度 × 状态乘数
const actualVelocityX = this.baseVelocityX * this.speedMultiplier;
const actualVelocityY = this.baseVelocityY * this.speedMultiplier;
// 更新位置
this.x += actualVelocityX * deltaTime;
this.y += actualVelocityY * deltaTime;
// 边界检测和反弹
this.handleBoundaryCollision();
// 更新状态
this.updateHappiness(deltaTime);
this.updateHunger(deltaTime);
this.updateBubble();
}
private handleBoundaryCollision(): void {
const tankWidth = 800;
const tankHeight = 600;
if (this.x < 0 || this.x > tankWidth) {
this.velocityX *= -1;
this.x = Math.max(0, Math.min(tankWidth, this.x));
}
if (this.y < 0 || this.y > tankHeight) {
this.velocityY *= -1;
this.y = Math.max(0, Math.min(tankHeight, this.y));
}
}
}
4.1.3、泡泡显示算法
typescript
class BubbleStateMachine {
private state: 'showing' | 'hiding' = 'hiding';
private lastStateChangeTime: number = Date.now();
private showDuration: number = 3000; // 显示 3 秒
private hideDuration: number = 5000; // 隐藏 5 秒
update(): void {
const now = Date.now();
const elapsed = now - this.lastStateChangeTime;
if (this.state === 'showing') {
if (elapsed > this.showDuration) {
this.state = 'hiding';
this.lastStateChangeTime = now;
}
} else {
if (elapsed > this.hideDuration) {
this.state = 'showing';
this.lastStateChangeTime = now;
this.bubble = this.getRandomBubble();
}
}
}
isVisible(): boolean {
return this.state === 'showing';
}
}
4.1.4、事件节流
typescript
// 防止事件处理过于频繁
class EventThrottler {
private lastExecutionTime: number = 0;
private throttleInterval: number = 100; // 100ms
throttle(callback: () => void): void {
const now = Date.now();
if (now - this.lastExecutionTime >= this.throttleInterval) {
callback();
this.lastExecutionTime = now;
}
}
}
// 使用
const throttler = new EventThrottler();
vscode.workspace.onDidChangeTextDocument(() => {
throttler.throttle(() => {
tank.fish.forEach(fish => fish.onTyping());
});
});
5、开始养鱼吧
插件已经发布到vscode插件市场,目前支持最新版vscode 1.105版本以上。

5.1、下一步计划
- 把成就数据扔 GitHub Gist,换电脑也能同步。
- 给鱼写个最原始的 FSM,让它偶尔自己发呆、偶尔追鼠标。
- 欢迎提 PR,最好顺带给我写单元测试,我实在懒得动。
记住:Ctrl+S 之前,先喂鱼。