@dsh-external/dsh-minigames
DSH Web UI 浮动小游戏窗口:18 款离线小游戏,可扩展游戏注册表。等待模型回复或修 bug 时的摸鱼神器。
安装
$
npx -p @deepseek-ai/dsh dsh plugin --profile web add github:omdsh-dev/dsh-minigames说明文档
阅读完整 README ↗可选:确认组合配置只出现一个插件行
dsh --profile web --dump-config | grep dsh-minigames
插件集合变更在**重启 `dsh web`** 后生效。卸载:
```sh
dsh plugin --profile web remove @dsh-external/dsh-minigames
本仓库同时携带
dsh.plugin.json(registry 通道清单)与cordis.patch.yml(官方 profile bundle 通道);二选一安装,不要同时启用。
使用
- 打开 DSH Web UI(
dsh web),页面右缘出现 🎮 小竖条。 - 点击竖条 → 面板展开为窗口右半部分。
- 点击游戏卡片开始;
P暂停、R(结算后)重开、点「选游戏」返回选择列表。 - 再次点击「收起」折叠为竖条(游戏自动暂停,进度保留)。
开发
pnpm install
pnpm run typecheck # tsc --noEmit
pnpm run test # vitest 纯逻辑单测(189 个:引擎/棋盘/世界模型/三消/华容道/贪吃蛇/2048/扫雷/翻牌/五子棋/跳一跳/打砖块/打地鼠/黑白棋/Flappy/数独/吃豆人/跟枪/注册表)
pnpm run build # tsc 声明 + tsdown:lib/index.js(node half)+ lib/client.js(浏览器 bundle)
产物结构(随源码提交,与 dsh-web-panel 一致):
lib/index.js—— node half(loader 行实现,仅日志;客户端插件扫描依赖该行存在)。lib/client.js—— 浏览器 bundle,window.__ModuleLoader__.load({ id: '@dsh-external/dsh-minigames', factory }),id 必须与package.json的name一致(client-modules 以包名作为 compose 键)。lib/types/—— 声明文件。
架构
src/
index.ts node half(loader 行实现)
invariant.ts 不变式伴生文件
client/
index.tsx 客户端插件:DOM portal 挂载面板(ctx.effect 管理生命周期)
app.css 面板样式(全部 dmg- 前缀,随 bundle 注入 )
panel/Panel.tsx 折叠条 / 展开面板 / 游戏选择器 / GameArea 生命周期
games/
types.ts 游戏扩展接口(MiniGameDefinition / MiniGameInstance)
registry.ts 注册表(registerGame / getGames / getGame)
focus.ts 键盘焦点门控(避免劫持聊天输入)
canvas-fit.ts 画布按宿主自适应缩放(devicePixelRatio,防截断)
index.ts 内置游戏注册点
dino/ 纯逻辑(引擎:物理/计分/昼夜/雨天/生成密度)+ 渲染 + 游戏定义
tetris/ 纯逻辑(棋盘:旋转/消行/暂存/等级)+ 渲染 + 游戏定义
tanks/ 纯逻辑(世界:网格/转向吸附/AI/波次/特效)+ 渲染 + 游戏定义
match3/ 纯逻辑(三消:四连通/落体/目标分/关卡)+ 渲染 + 游戏定义
huarong/ 纯逻辑(华容道:滑动/打乱/解状态)+ 渲染 + 游戏定义
snake/ 纯逻辑(贪吃蛇:环面穿越/自撞/成长)+ 渲染 + 游戏定义
game2048/ 纯逻辑(2048:滑动合并/生成/胜负判定)+ 渲染 + 游戏定义
minesweeper/ 纯逻辑(扫雷:布雷/洪水展开/标旗/双击展开)+ 渲染 + 游戏定义
memory/ 纯逻辑(翻牌:配对/步数)+ 渲染 + 游戏定义
gomoku/ 纯逻辑(五子棋:落子/胜负/启发式 AI)+ 渲染 + 游戏定义
hop/ 纯逻辑(跳一跳:蓄力/落地判定/计分)+ 渲染 + 游戏定义
breakout/ 纯逻辑(打砖块:挡板/碰撞/关卡/球速)+ 渲染 + 游戏定义
whack/ 纯逻辑(打地鼠:计时/冒头/得分)+ 渲染 + 游戏定义
othello/ 纯逻辑(黑白棋:翻转/让位/终局/启发式 AI)+ 渲染 + 游戏定义
flappy/ 纯逻辑(Flappy:重力/柱子/碰撞/计分)+ 渲染 + 游戏定义
sudoku/ 纯逻辑(数独:生成/挖洞/冲突/计时)+ 渲染 + 游戏定义
pacman/ 纯逻辑(吃豆人:迷宫/移动/幽灵 AI/力量豆)+ 渲染 + 游戏定义
aimtrack/ 纯逻辑(跟枪:视角转向/三维漂移/射击后坐/晃动/命中率)+ 渲染 + 游戏定义
tests/ 纯逻辑单元测试
面板不依赖任何宿主服务与布局槽(纯 document.body portal),因此:
- 与主框架版本解耦,
dsh.client.inject为空,客户端零@deepseek-ai依赖; - 无论 DSH 布局如何变化(左栏/右栏/折叠)都不会冲突。
添加新游戏(预留接口)
实现 MiniGameDefinition(src/client/games/types.ts):
import { registerGame, type MiniGameDefinition } from './index.ts'
const myGame: MiniGameDefinition = {
id: 'my-game', // 唯一 id(作为选中记忆与最高分 key)
title: '我的游戏',
icon: '🎯',
description: '一句话介绍',
controls: ['空格:跳跃', 'P:暂停'], // 画布下方的按键图例
create(host, options) {
// 在 host 内创建 ;返回实例:
return {
start() {}, // 启动/恢复主循环
pause() {}, // 暂停(面板折叠/切标签页/用户暂停时调用)
resume() {}, // 恢复
destroy() {}, // 释放 rAF/键盘监听等
}
},
}
registerGame(myGame)
create 返回的实例由面板驱动生命周期:切换游戏时先 destroy() 旧实例再
create() 新实例;面板折叠、标签页隐藏或用户点暂停时调用 pause()。
分数通过 options.onScore?.(score) 上报(仅在变化时调用),面板负责显示与
最高分持久化。建议把游戏逻辑拆成纯函数模块(不碰 DOM/定时器),与现有游戏
一样用单元测试覆盖。
已知限制与后续计划
- 键盘控制依赖游戏区域焦点;点击聊天输入框后需点回游戏区域才能继续操作(有意为之)。
- 坦克大战 AI 为贪心追踪 + 视线开火,无寻路;后续可加障碍绕行与不同装甲类型。
- 消消乐与方块/坦克的关卡进度不跨会话保存(仅最高分持久化)。
- 恐龙雨天/昼夜为纯视觉氛围,不影响判定。
使用
- 打开 DSH Web UI(
dsh web),页面右缘出现 🎮 小竖条。 - 点击竖条 → 面板展开为窗口右半部分。
- 点击游戏卡片开始;
P暂停、R(结算后)重开、点「选游戏」返回选择列表。 - 再次点击「收起」折叠为竖条(游戏自动暂停,进度保留)。