omdsh-dev/dsh-minigames5

@dsh-external/dsh-minigames

DSH Web UI 浮动小游戏窗口:18 款离线小游戏,可扩展游戏注册表。等待模型回复或修 bug 时的摸鱼神器。

包名
@dsh-external/dsh-minigames
版本
0.3.3
许可证
BSD-3-Clause
最近更新
2026年8月18日

安装

$npx -p @deepseek-ai/dsh dsh plugin --profile web add github:omdsh-dev/dsh-minigames

可选:确认组合配置只出现一个插件行

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 通道);二选一安装,不要同时启用。

使用

  1. 打开 DSH Web UI(dsh web),页面右缘出现 🎮 小竖条。
  2. 点击竖条 → 面板展开为窗口右半部分。
  3. 点击游戏卡片开始;P 暂停、R(结算后)重开、点「选游戏」返回选择列表。
  4. 再次点击「收起」折叠为竖条(游戏自动暂停,进度保留)。

开发

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.jsonname 一致(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 布局如何变化(左栏/右栏/折叠)都不会冲突。

添加新游戏(预留接口)

实现 MiniGameDefinitionsrc/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 为贪心追踪 + 视线开火,无寻路;后续可加障碍绕行与不同装甲类型。
  • 消消乐与方块/坦克的关卡进度不跨会话保存(仅最高分持久化)。
  • 恐龙雨天/昼夜为纯视觉氛围,不影响判定。

使用

  1. 打开 DSH Web UI(dsh web),页面右缘出现 🎮 小竖条。
  2. 点击竖条 → 面板展开为窗口右半部分。
  3. 点击游戏卡片开始;P 暂停、R(结算后)重开、点「选游戏」返回选择列表。
  4. 再次点击「收起」折叠为竖条(游戏自动暂停,进度保留)。