Bundle
dsh-heatmap
DeepSeek Harness 页面埋点与热力图分析插件:科学埋点采集(点击/聚焦/停留/滚动/曝光)、本地热力图与统计面板、预留 CLI 与外部上传接口、上传前用户授权弹窗。
- Source
- Olympianz
- License
- MIT
- Updated
- Updated 1 hour ago
Readme
# dsh-heatmap
DeepSeek Harness 页面埋点与热力图分析插件。为后续产品设计优化提供量化数据:科学采集页面交互行为,本地展示热力图与统计,预留 CLI 与外部上传接口,上传前强制用户授权。
## 功能一览(在原始需求上补充后的完整设计)
| # | 功能 | 说明 |
|---|------|------|
| 1 | **科学埋点设计** | 四类事件(生命周期 / 交互 / 滚动曝光 / 热力图坐标)+ 稳定元素身份 + schema 版本化,参考主流产品分析(Amplitude / Mixpanel / Heap / Clarity)的埋点原则 |
| 2 | **热力图模式** | 点击 + 鼠标悬停密度热力图,色带「蓝→青→绿→黄→红」;面板标注「本地 / 本人数据」并展示聚合统计 |
| 3 | **CLI / 外部访问接口** | host 侧 `analytics_export` 工具(Agent 可调用)+ `webServer` 上的 `/dsh-heatmap/*` HTTP 路由,预留统一分析平台对接 |
| 4 | **上传授权弹窗** | 数据上传到外部/其他软件前弹出授权弹窗,用户「同意并上传」后才发送 |
| 5 | **隐私最小化**(补充) | 只采集控件身份、坐标、时间、视口;**绝不采集对话正文与输入内容**(输入仅记录长度) |
| 6 | **数据本地化**(补充) | 采集数据默认只存本地 localStorage;host 收集器用 NDJSON 落盘,可随时导出/清空 |
| 7 | **可配置**(补充) | 采样率、事件开关、热力图开关、鼠标热力图、上传地址、授权要求、存储上限均可配置 |
## 埋点设计(事件分类学)
### 事件类型
| 类型 | 触发时机 | 采集字段 | 产品用途 |
|------|---------|---------|---------|
| `session_start` / `session_end` | 页面加载 / 卸载或隐藏 | 会话 id、时间 | 会话数、会话时长 |
| `page_view` | 初始加载 + hash 路由变化 | path、hash | 页面/视图访问量 |
| `click` | 任何可点击元素被点击 | 元素身份 + 视口坐标 | 按钮点击率、**点击热力图** |
| `focus` / `blur` | 元素获得/失去焦点 | 元素身份 | 是否 focus、交互路径 |
| `hover` | 鼠标悬停(250ms 节流,可选) | 视口坐标 + 元素身份 | **悬停/注意力热力图** |
| `scroll_depth` | 滚动停止后(500ms 防抖) | 滚动深度 0..1 | 内容消费深度 |
| `visibility` | 页签可见性变化 | visible | **停留时长**(活跃时段累计) |
| `input` | 输入框输入(1s 节流) | 元素身份 + 输入**长度** | 输入活跃度(不含内容) |
| `impression` | 元素首次进入视口 ≥50%(IntersectionObserver) | 元素身份 | **曝光分析**(哪些面板/控件被看到) |
### 元素身份(稳定指纹)
采集端按优先级生成稳定的 `elementId`,保证跨会话可聚合到同一控件:
```
data-testid > id > aria-label > role#tag > tag:文本摘要(≤40字)
```
同时记录 `tag / role / ariaLabel / title / text` 供分析侧做控件维度下钻。
### 数据模型(schema v1)
```ts
interface AnalyticsEvent {
v: number // schema 版本
id: string // 事件 uuid
ts: number // epoch ms
sessionId: string // 会话 uuid
type: EventType
page: { path: string; hash: string }
target?: ElementRef // 元素身份
position?: { x: number; y: number } // 热力图坐标
depth?: number // 滚动深度
visible?: boolean // 可见性
inputLength?: number // 输入长度(无内容)
}
```
## 架构
```
┌────────────────────────── browser 半部 ──────────────────────────┐
│ tracker.ts 全局埋点采集(document 级监听) │
│ storage.ts 本地环形缓冲 + 设置 + 授权(localStorage) │
│ HeatmapOverlay 热力图 + 统计面板 + 设置 + 上传入口(shell.overlay)│
│ ConsentModal 上传授权弹窗 │
└──────────────────────────────┬───────────────────────────────────┘
│ 授权后 fetch(POST)
┌──────────────────────────────▼────── host 半部 ──────────────────┐
│ /dsh-heatmap/ingest 批量上报(本地收集器) │
│ /dsh-heatmap/export 导出(JSON/NDJSON) │
│ /dsh-heatmap/stats 聚合统计 │
│ /dsh-heatmap/sessions 会话时间线(回放/复现) │
│ /dsh-heatmap/funnel 漏斗分析(有序步骤转化率) │
│ /dsh-heatmap/clear 清空 │
│ analytics_export 工具 stats/export/sessions/funnel/clear/upload │
│ AnalyticsStore NDJSON 落盘 + 内存环形缓冲 + 漏斗/会话聚合 │
└──────────────────────────────────────────────────────────────────┘
```
数据默认只在本机流动:采集→本地;上传→(授权后)host 收集器或配置的外部地址。
## 安装
```sh
dsh plugin --profile web add ./dsh-heatmap
# 或使用 dsh-master:dsh_master_install 工具
```
然后重启 dsh(`dsh web` 或 `dsh --profile web`)。重启后页面右下角会出现 🔥 按钮,打开面板即可查看统计、开启热力图。
## 开发
```sh
# 在插件目录内
pnpm install
pnpm run build # 构建 lib/client.js(browser 半部)
pnpm run typecheck
```
## 配置
### host 配置(cordis.yml,部署级默认值)
```yaml
- id: dsh-heatmap
config:
enabled: true # host 收集器与工具开关
dataDir: '' # 落盘目录;空则 $DSH_HOME/storages/dsh-heatmap
maxEvents: 20000 # 环形缓冲上限
uploadEndpoint: '' # 统一分析平台上传地址(analytics_export upload 目标)
consentRequired: true # 上传是否要求授权(文档化;实际弹窗在 browser 半部)
```
### 客户端设置(localStorage,运行时在面板中调整)
| 设置 | 默认 | 说明 |
|------|------|------|
| 热力图模式 | 关 | 开启全屏热力图覆盖层 |
| 采集埋点 | 开 | 总开关 |
| 鼠标热力图 | 关 | 是否采集悬停坐标(开启会增大数据量) |
| 上传需授权 | 开 | 上传前是否弹授权窗 |
| 上传地址 | 空 | 空 = 同源 host 收集器 `/dsh-heatmap/ingest` |
## CLI / 外部访问接口
### 1. Agent 工具(`analytics_export`)
在会话中让 Agent 调用:
```
动作:stats 聚合统计(事件总数/点击/聚焦/曝光/会话数/按类型分布)
动作:export 导出最近 5000 条事件
动作:sessions 会话时间线(最近 20 个会话的有序事件序列,用于回放/复现)
动作:funnel 漏斗分析(需 steps:有序事件类型数组,如 [session_start, click, input])
动作:clear 清空收集器数据
动作:upload 上传到 config.uploadEndpoint(统一分析平台)
```
### 2. HTTP 路由
```sh
curl http://127.0.0.1:PORT/dsh-heatmap/health
curl http://127.0.0.1:PORT/dsh-heatmap/stats
curl http://127.0.0.1:PORT/dsh-heatmap/export?format=json&limit=100
curl http://127.0.0.1:PORT/dsh-heatmap/sessions?limit=20
curl -X POST http://127.0.0.1:PORT/dsh-heatmap/funnel -H 'content-type: application/json' -d '{"steps":["session_start","click","input"]}'
curl -X POST http://127.0.0.1:PORT/dsh-heatmap/ingest -H 'content-type: application/json' -d '{"v":1,"sessionId":"s","sentAt":0,"events":[...]}'
curl -X DELETE http://127.0.0.1:PORT/dsh-heatmap/clear
```
> 端口即 DSH 网页服务的端口(客户端与 host 同源)。
## 统一分析平台对接
预留了三条对接路径,按需选择:
1. **Agent 工具 `upload`**:配置 `uploadEndpoint` 后,让 Agent 调用 `analytics_export`(action=upload)把收集器数据批量 POST 到平台。
2. **HTTP 路由**:`GET /dsh-heatmap/export` 拉取原始事件,由你的平台 SDK/脚本转发。
3. **示例适配脚本**:[`examples/push-to-platform.mjs`](examples/push-to-platform.mjs) —— 从 `/export` 拉取、按 `mapEvent` 适配成平台形状、POST 到 `PLATFORM_ENDPOINT`(仅需改这一个适配函数)。
```sh
DSH_BASE=http://127.0.0.1:3080 \
PLATFORM_ENDPOINT=https://analytics.example.com/ingest \
PLATFORM_API_KEY=sk-xxx \
node examples/push-to-platform.mjs
```
## 隐私与合规
- 采集默认**最小化**:不采集对话正文、不采集输入内容(仅长度)、不采集 PII。
- 数据默认只在用户本机(localStorage / 本地 NDJSON)。
- 上传动作前弹出授权弹窗,明示「上传什么、上传到哪里、不含对话内容」,用户确认后才发送。
- 可随时「清除本地」或在 host 侧 `clear` 删除收集器数据。
## 后续演进(预留)
- 可视化会话回放:在面板内把 `sessions` 时间线渲染成逐步重放。
- 曝光时长:为 `impression` 记录进入/离开视口时长(当前只记首次曝光)。
- 性能埋点:`navigation`/`resource` 时序。
> 生成自 DSH-Master(dsh-master)脚手架并扩展。
Install
dsh plugin --profile web add github:Olympianz/dsh-heatmap
Profile: web
With the hub plugin installed, ask your agent to install it by name — it resolves the same plan shown here.
dsh plugin --profile web add github:stvlynn/dsh.fish#path:packages/dsh-plugin-hub
install dsh-heatmap from the hub
- This package builds from source on install. pnpm will ask you to allow its build script — that is permission to run the package’s code on your machine, outside the agent sandbox. Only allow sources you trust.
- This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.