{"_id":"@blue-a11y/dsh-client-shortcuts","name":"@blue-a11y/dsh-client-shortcuts","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@blue-a11y/dsh-client-shortcuts","description":"Global keyboard shortcuts plugin for the DeepSeek Harness web GUI: ctx.shortcuts registry service, rebindable settings page, and default mod+l/mod+k/mod+shift+c bindings","version":"0.1.0","license":"MIT","publishConfig":{"access":"public"},"type":"module","main":"lib/index.js","types":"lib/types/index.d.ts","exports":{".":{"types":"./lib/types/index.d.ts","default":"./lib/index.js"},"./invariant":{"types":"./lib/types/invariant.d.ts","default":"./lib/invariant.js"},"./client":{"types":"./lib/types/client/index.d.ts","default":"./lib/client.js"},"./src/*":"./src/*","./package.json":"./package.json"},"dsh":{"client":{"inject":["@deepseek-ai/dsh-client-runtime","@deepseek-ai/dsh-client-connection"],"platform":"web"},"bundle":{"patch":"./cordis.patch.yml"}},"scripts":{"build":"tsc && tsdown","prepare":"npm run build","test":"vitest run","watch":"tsdown --watch"},"peerDependencies":{"@deepseek-ai/cordis":"^4.0.1","@deepseek-ai/dsh-invariants":"^0.1.0-rc.6"},"devDependencies":{"@deepseek-ai/cordis":"^4.0.1","@deepseek-ai/dsh-api-remotes":"0.1.0-rc.6","@deepseek-ai/dsh-client-runtime":"^0.1.0-rc.6","@deepseek-ai/dsh-client-ui-layout":"0.1.0-rc.6","@deepseek-ai/dsh-client-ui-primitives":"^0.1.0-rc.6","@deepseek-ai/dsh-client-ui-sidebar":"0.1.0-rc.6","@deepseek-ai/dsh-client-ui-workspace":"0.1.0-rc.6","@deepseek-ai/dsh-invariants":"^0.1.0-rc.6","@types/react":"^18.3.0","jsdom":"^26.1.0","react":"^18.3.1","tsdown":"^0.22.2","typescript":"^6.0.3","unrun":"^0.3.1","vitest":"^4.1.8"},"gitHead":"2ca67374a7551ab6581a0ad4d8a985d32be15cae","_id":"@blue-a11y/dsh-client-shortcuts@0.1.0","_nodeVersion":"24.12.0","_npmVersion":"11.6.2","dist":{"integrity":"sha512-/yiuUZnxpFbOKEpoZUt1fjSjer2lg187RFFqTgsJaR/yUFP70+gE+m63yphQ6fHiRh/u0s7FwNRiOxYALLVXWw==","shasum":"bd0f9afe11379ea3b80defc3b071a2c6b579a222","tarball":"https://registry.npmjs.org/@blue-a11y/dsh-client-shortcuts/-/dsh-client-shortcuts-0.1.0.tgz","fileCount":13,"unpackedSize":48512,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIDmJZR9wKmfzgOBgWDnkAZJ/q+73rnH5R6VcGdOQNV5hAiAb4mY/rXbqPkGJcZHcQtpt6QDueM0DAkUyEZCdW7EBbA=="}]},"_npmUser":{"name":"blue-a11y","email":"xuanzhang194@gmail.com"},"directories":{},"maintainers":[{"name":"blue-a11y","email":"xuanzhang194@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/dsh-client-shortcuts_0.1.0_1786702656474_0.6574281920844698"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-14T10:17:36.256Z","0.1.0":"2026-08-14T10:17:36.619Z","modified":"2026-08-14T10:17:36.842Z"},"maintainers":[{"name":"blue-a11y","email":"xuanzhang194@gmail.com"}],"description":"Global keyboard shortcuts plugin for the DeepSeek Harness web GUI: ctx.shortcuts registry service, rebindable settings page, and default mod+l/mod+k/mod+shift+c bindings","license":"MIT","readme":"# @blue-a11y/dsh-client-shortcuts\n\ndsh web GUI 的全局键盘快捷键插件。`ShortcutRegistry`（即 `ctx.shortcuts` 服务）在 `window` 上挂一个捕获阶段的 `keydown` 监听并分发已注册的组合键；焦点在文本输入框内时默认放行（除非绑定显式声明输入框内可用）。设置面板提供 **「快捷键」页**（注册于 `settings.section`，基于 `@deepseek-ai/dsh-client-ui-primitives` 构建），列出全部可绑定动作，支持录制/重置改键、冲突检测与保留组合键拦截。node half 刻意为空——整个功能都是浏览器 UI。\n\n`mod` 在 macOS 上匹配 Cmd、其他平台匹配 Ctrl（`e.metaKey || e.ctrlKey`），与 composer 的约定一致。UI 与本文档以 cmd 友好形式书写组合键（`cmd+l` ≡ `mod+l`）。\n\n---\n\n## 安装（给使用本插件的用户）\n\n### 前置\n\n- 已安装 `dsh` CLI（`npm i -g @deepseek-ai/dsh`）\n- Node ≥ 22.19 与 pnpm 就绪\n\n### 安装\n\n本包是一个**组合包**（声明了 `dsh.bundle`），安装即自动激活其配置层。三选一：\n\n```sh\n# 1) npm 上发布的版本\ndsh plugin --profile web add @blue-a11y/dsh-client-shortcuts\n\n# 2) 本地 checkout（开发期，改源码重建即热更新）\ndsh plugin --profile web add ./dsh-client-shortcuts\n\n# 3) 打包产物 tarball\ndsh plugin --profile web add ./dsh-client-shortcuts-0.1.0.tgz\n```\n\n### 激活与验证\n\n新增插件行后需**重启一次 `dsh web`**（插件行发现在每次启动时缓存）：\n\n```sh\ndsh --profile web --dump-config   # 应看到 id: shortcuts 行\ndsh web\n```\n\n浏览器打开后，在 设置 → 快捷键 应能看到设置页。之后每次源码重建，client HMR 链路会自动换新模块，无需重启。\n\n### 卸载 / 更新\n\n```sh\ndsh plugin --profile web remove @blue-a11y/dsh-client-shortcuts   # 同时移除依赖与配置层\ndsh plugin --profile web add @blue-a11y/dsh-client-shortcuts      # 更新 = 重新 add（锁定版本需指定版本号）\n```\n\n---\n\n## 使用\n\n### 可绑定动作\n\n| 动作 | 默认键位 | 输入框内可用 |\n| --- | --- | --- |\n| 聚焦输入框 | `cmd+l` | — |\n| 新建会话 | `cmd+shift+k` | ✅ |\n| 切换侧栏 | `cmd+b` | ✅ |\n| 切换详情面板 | `cmd+shift+e` | ✅ |\n| 上一个会话 | `cmd+[` | ✅ |\n| 下一个会话 | `cmd+]` | ✅ |\n| 切换浅色/深色主题 | 未绑定 | — |\n| 滚动到对话顶部 | 未绑定 | — |\n| 滚动到对话底部 | 未绑定 | — |\n| 分叉当前会话 | 未绑定 | — |\n\n会话切换镜像侧栏的完整显示顺序（工作区按注册表顺序、组内按最近活动排序、未分组桶垫底），跳过「新建任务」空白条目与归档会话，并在两端**循环**（到底再切回第一个）。\n\n### 改键与键位约束\n\n无安全默认值的动作默认不绑定；在设置页录制组合键即可启用，重置则清回未绑定。\n\n设置页会拒绝两类**永远无法触发**的组合键：\n\n- **浏览器保留组合键**（`cmd+n/t/w`、`cmd+shift+n/t/w/i/j/c`、`cmd+alt+i/j/u`）：它们在页面监听器 `preventDefault` 之前就被浏览器消费，web 插件绑不住（Codex 这类桌面应用可以，所以它的键位不能照搬）。\n- **macOS 的 Option/Alt 改写组合**：Option 会改写产出字符（`cmd+alt+c` 实际是 `ç`），导致 `event.key` 与注册键不再匹配。\n\n### 设置页\n\n打开 设置 → 快捷键。每行展示动作、当前组合键与触发计数。录制控件会暂停注册中心分发（按下的组合键被捕获而不是触发），校验与现有绑定的冲突并拒绝非法组合键。改键仅在插件 fiber 生命周期内有效——刷新页面恢复默认（持久化存储属于后续阶段）。\n\n![快捷键设置页](docs/images/settings-page.png)\n\n---\n\n## 开发（给贡献本插件的开发者）\n\n### 环境与克隆\n\n```sh\ngit clone https://github.com/blue-a11y/dsh-client-shortcuts.git\ncd dsh-client-shortcuts\npnpm install\n```\n\n### 目录结构\n\n```\nsrc/\n├── index.ts               # node half：空 apply（宿主 Loader 导入用）\n├── invariant.ts           # 包级 invariant 伴随件\n└── client/\n    ├── registry.ts        # ShortcutRegistry：捕获监听、combo 解析/匹配、disposer\n    ├── actions.tsx        # 动作集定义（id/默认键/run）+ ShortcutBindings（改键/重置/计数）\n    ├── settings.tsx       # 设置页组件（settings.section 注册、录制、样式注入）\n    └── index.ts           # 浏览器入口：provide shortcuts 服务 + 挂默认绑定 + 挂设置页\ntests/                     # vitest（jsdom）spec\ndocs/                      # 文档与截图\n```\n\n### 常用命令\n\n```sh\npnpm install\npnpm run build    # tsc 生成 lib/types → tsdown 打包 lib/index.js + lib/invariant.js + lib/client.js\npnpm test         # vitest\n```\n\n### 构建与加载链路\n\n- **node half**：`tsc` 先把 `src/` 转成 `lib/types/`，`tsdown` 再从那里产出 ESM 的 `lib/index.js`、`lib/invariant.js`（宿主 Loader 启动时 import）。\n- **client bundle**：`lib/client.js`（CJS）带 `window.__ModuleLoader__.load({ id, factory })` 的 banner/footer；`react` 与 `@deepseek-ai/dsh-client-ui-primitives` 被外部化，运行时从 shell 模块表解析。\n- **`prepare`** 跑同一构建，git 安装（`dsh plugin add github:...`）会从源码构建；pnpm ≥ 10 会要求 profile 在 `pnpm-workspace.yaml` 的 `allowBuilds` 中一次性授权。\n\n### 本地热更新\n\n1. `dsh plugin --profile web add ./dsh-client-shortcuts`（link 到本地目录）\n2. 改源码 → `pnpm run build` → host 的 client HMR 每 500ms 探测 `lib/client.js` 变化 → SSE 广播 → 浏览器不刷新换新模块\n3. 只有**新增/删除插件行**才需要重启 `dsh web`\n\n### 新增一个动作\n\n在 `src/client/actions.tsx` 的 `createActions()` 返回数组里追加一项：\n\n```tsx\n{\n  id: 'my-action',\n  defaultCombo: 'cmd+shift+m',   // '' 表示默认不绑定，用户录制启用\n  label: '我的动作',\n  allowInTextField: true,        // 输入框内是否也响应\n  icon: <IconSomeOutline16 size={16} />,\n  run: () => { /* 从 ctx.get('服务名') 读取目标服务并调用 */ },\n}\n```\n\n动作的 `run()` 在按键时读取目标服务，遵循「缺服务则静默 no-op」；组合键用 cmd 友好形式书写，`mod` 与 cmd/ctrl 的映射由 `toRegistryCombo` 统一处理。\n\n### 发布\n\n```sh\npnpm publish    # prepare 会自动构建 lib/，发布即预构建产物，安装方无需构建授权\n```\n\n---\n\n## Model Experience\n\n无。快捷键服务只把浏览器键盘手势转发为 UI 动作，不触及任何模型请求。\n\n## 已知限制与后续工作\n\n- **改键不持久化** —— 仅存活于插件 fiber，刷新即恢复默认；`settingsNamespace` schema 是持久绑定的规划归宿。\n- **聚焦输入框依赖 `[data-phase]` DOM 查询** —— 因为没有 composer 暴露聚焦服务；若将来有，应迁移到服务通道。\n","readmeFilename":"README.md","_rev":"1-f2e64aa65d045cc005c5456fda2b563a"}