Skip to content
dsh.fish
Bundle

sage-reminder

时间提醒(reminder)— DSH 会话头部的时间模块:现在时间 / 距上次发言 / 提醒 / 倒计时。定位是会话内的短期提醒(纯 client 半,数据存浏览器本地)

Source
gezi-wen
License
MIT
Updated
Updated 6 days ago

Readme

# sage-reminder(时间提醒)

DSH 会话头部的一个时间模块:**现在时间 / 距你上次发消息 / 提醒 / 倒计时**。

定位是**会话内的短期时间提醒** —— 管"此刻这台机器上的计时",不承担"关了也不会忘"的责任。
收起时只占一小条,点开向下拉出面板;到点时模块边框呼吸发亮 + 界面浮条。

> **DSH 版本兼容性(0.1.3 核对于官方桌面版 `0.2.0-rc.2` 与网页版 `0.2.0-rc.1`)**
> `conversation.session.header.actions` 槽位、`ctx.slots` API、以及「到点唤醒会话」走的
> `sessions.binding(id).session.prompt()` 链,在 0.1.6 → 0.1.7 → 0.2.0 三个版本线上**均未改变**。
> 0.1.3 起 `package.json` 带 `engines.dsh`,且 peer **不设上界** —— 任何未来的 DSH 版本都不会被这道
> 声明挡在门外。(反例:`dsh-skill-mcp-panel` 曾因 peer 上界 `<0.2.0-0` 让所有 0.2.x 用户的面板整体消失。)
> 0.1.1 加的槽位挂载失败留痕仍然有效:`ctx.slots` 缺失或注入抛异常会在控制台报警 ——
> 这两种情况下模块以前是**彻底无声**的,只表现为「这插件好像没装」。

纯前端插件(`dsh.client.platform = web`):

- 时间是浏览器本地时间
- 提醒与倒计时存在浏览器 `localStorage`
- 到点提示 = **模块边框呼吸灯 + 界面浮条**
- **host 半不参与任何事** —— 不发系统通知、不读文件、不建网络连接

## 两个模块,分工是「留不留」

| | 提醒 | 倒计时 |
|---|---|---|
| 是什么 | **一天内的一个钟点**(如 21:40) | **一段时长**(天 / 时 / 分 / 秒) |
| 寿命 | **长期保留**在列表里;响过之后回到初始状态,继续等下一个钟点 | **一次性**:跑完自动从列表清理 |
| 开关 | `▶` 启动、`⏸` 关闭(两个动作分开,当前生效的那个亮着) | `⏸` 暂停 / 继续、`×` 删除,带进度条 |
| 设定 | 时 / 分 **两个**滚轮 | 天 / 时 / 分 / 秒 **四个**滚轮 |

## 界面

收起态(会话头部,紧邻「客厅」按钮)—— 上行日期,下行星期与时刻,右侧胶囊是最近一个到期的计时:

![收起态](https://raw.githubusercontent.com/gezi-wen/sage-reminder/main/docs/clock-collapsed.png)

点模块向下拉出面板:

![展开面板](https://raw.githubusercontent.com/gezi-wen/sage-reminder/main/docs/clock-expanded.png)

面板自上而下四段:**现在时间**(秒级)→ **距你上次发消息** → **提醒** → **倒计时**,
最底部是「到点唤醒会话」开关(默认关)。

- 「还剩多久」**只在 10 分钟内**转成金色,其余时候是灰的
- 条目的操作按钮 hover 才浮出来
- 模块右边的小胶囊 = 最近一个要响的东西,不用展开就能看见
- **到点浮条只显示你设的内容**(名字),不加「到点了」这类旁白

## 用法

- **点模块**展开面板;`Esc`、点面板外面、或再点一次收起
- **提醒** → `+ 新建`:名称 + 时间(时 / 分滚轮)→ 保存即启动
  - 到点响一次,之后**照常留在列表里**等下一个钟点(长期保留、不消费)
  - 列表里 `▶` 启动、`⏸` 关闭、`⋯` 编辑(编辑页左下角是 `删除`)
- **倒计时** → `+ 新建`:名称 + 时长(天 / 时 / 分 / 秒滚轮)→ `开始`
  - 它是一次性的:**跑完自动从列表清理**
  - 列表里 `⏸` 暂停 / 继续、`×` 删除
- **到点唤醒会话**(面板最底部,**默认关**):提醒到点时往当前会话发一条署名消息,把空闲的会话叫醒
  - 提醒专属;倒计时不唤醒
  - 会话正在跑的时候不发 —— 不打断正在进行的回合
  - 发了没有、为什么没发,面板上看得到(`上次唤醒:已投递` / `跳过:会话正在跑` / …)
  - ⚠️ **每次唤醒 = 一轮完整的模型调用**(真花钱),所以默认关、一条条单独开

### 时间怎么选

- 一律**滚轮**,不接受文字输入 —— 避免有人敲进 `ahfkhas` 这类非时间字符
- 三种改法:点上下行、鼠标滚轮、`↑` / `↓` / `PageUp` / `PageDown`
- **24 / 60 进制**:时 0-23、分 0-59、秒 0-59、天 0-99。列之间**不做进位** ——
  要 90 分钟就写「1 时 30 分」,不写「90 分」
- **不做重复规则**(只一次 / 每天 / 每周):提醒天生就是"这个钟点",没什么可选的

## 安装

```jsonc
// profile 的 package.json
"dependencies": {
  "sage-reminder": "link:<你的路径>/sage-reminder"   // 本地目录用 link:,发布版写版本号
},
"dsh": { "profile": { "bundles": [ ..., "sage-reminder" ] } }
```

然后在该 profile 目录跑 `pnpm install`,重启(或刷新页面)。

## host 侧注册了什么

**什么都没注册。** host 半是故意空的(`lib/index.js` 只有一个 `apply()` 打一行启动标记):

- 不注册工具、不注册服务、不读文件、不起进程、不建网络连接
- 数据全在浏览器 `localStorage`,定时也全在浏览器
- host 半被加载之后不做任何事 —— profile 与它的关系只是"这个包在 `bundles` 里"

原则是**把复杂度压到最小**:会话头部的一个时间模块,不该为了"更稳"而拖出一条宿主侧的数据通道。

## 可选升级路线(当前故意不做)

下面几条都是讨论过、**明确决定先不做**的方向。记在这里是因为它们各有真实代价 —— 将来真要做时照这张表决策,别凭感觉加。

| 想解决 | 要动什么 | 代价 |
|---|---|---|
| **提醒活过 DSH 重启** | 数据从 `localStorage` 搬到宿主侧文件 | 需要一条 host ↔ client 的数据通道。注意 `localStorage` 的隔离键是 scheme + host + **端口**,而桌面壳每次启动都是 `--port 0`(OS 随机分配)→ **重启必丢**,刷新页面不受影响 |
| **让智能体自己设提醒** | 宿主侧注册一个工具(agent 只能调宿主工具) | 和上一条是**同一个改动** —— 一加工具,宿主就必须自己存数据,"重启不丢"会被一起带进来 |
| **页面关着也能到点提醒** | 定时也搬到宿主侧常驻 | 还要重新设计"提醒长什么样"(系统通知 / 托盘 / 等页面回来再补),并重新确认"不要系统通知"这条取舍 |

**别做半迁移。** 只把某一类提醒搬到宿主侧、另一类留在 `localStorage`,结果是两个真相源 + "为什么这条留下了那条没了"的困惑。要做就整库一起搬。

## 设计上值得一提的三处

**一、面板挂在模块自己的容器上,且右对齐。** 不是开一个全屏 overlay,而是
「模块 + 下拉面板」包在同一个 `position: relative` 的容器里,面板绝对定位到它 ——
跟着模块走,不需要算坐标。用 `right: 0` 而不是 `left: 0`:模块在会话头部右侧,
往右展开会顶出窗口(实测溢出 127px)。

**二、滚轮的对齐靠几何保证,不靠 margin 凑。** 列高 = 可见行数 × 行高(3 × 24px),
中间那一行天然落在列的正中,而高亮条也画在列的正中 —— 两者必然重合。
(早先用 `margin-top` 把首行往下推,本来就偏 10px;再叠上一条被上一行孤儿声明吃掉的
`.ck-picker` 规则,整块滚轮就退化成了上下两坨。)

**三、会话事件窗口要「订阅」而不是「读一次」。**
「距你上次发消息」取自 `sessions.binding(sessionId).eventSource`。
实测它第一次 `getSnapshot()` 拿到的 `entries` 是**空数组**,要 `subscribe()` 等事件填进来。
用户消息的事件名是 `user/message`。
拿不到就整行隐藏 —— 不编假数据。

## 已知限制

- **数据活不过一次 DSH 重启。** 提醒与倒计时存在浏览器 `localStorage`,
  而 `localStorage` 的隔离键是 scheme + host + **端口**;桌面壳每次启动都用 `--port 0`
  (OS 随机分配端口)→ 重启之后 origin 变了,这些数据就读不到。
  这是 2026-09-19 当面确认的定位:**它就是"会话内的短期提醒"**,不承担跨重启持久化 ——
  真要做,就不止是个 DSH 插件了(要 host 半存盘 + 定时 + 系统级提醒)。
  刷新页面不影响。
- 「距你上次发消息」依赖 DSH 客户端内部接口(`sessions` 服务 + 会话事件流)。
  接口若变,这一行会安静地消失,不影响其余功能。
- 不含系统通知。到点提醒只有界面内的呼吸灯与浮条 —— 页面不在前台时会错过。

## 许可

MIT

Install

dsh plugin --profile web add github:gezi-wen/sage-reminder

Profile: web

  • This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.
Source