Fetching metadata from the HF Docker repository... 22 days ago
-
聊天室:消息里的 URL 自动变成可点击链接(新标签页打开)
xiaolongmr
- 改造 `renderContent`:原来只处理 @昵称 高亮,现在 @高亮 与 URL 链接化并存。
非 @ 片段再走一遍链接识别,两套 pattern 不互相干扰。
- ⚠️⚠️ **URL 正则必须把中文标点排除在字符类外**:
中文没有空格,`https://example.org。以及后面的话` 若不在 `。` 处停下,
会把整个句子吞进链接里 —— 真机实测踩到,第一版就是错的。
同时 ASCII 尾部标点(`)]}>.,;:!?`)在用 trimUrlTail 剥掉。
- ⚠️ 故意**不**匹配裸域名(`abc.com`):中文聊天里「图片.png」「3.5 折」极易误判。
- ⚠️ `target="_blank"` 必须配 `rel="noopener noreferrer"`,否则新页面能通过
`window.opener` 反向操作当前页。
- ⚠️ 链接 `onClick` 要 `stopPropagation()`:消息区外层有点击处理。
验证:解析逻辑 11 例单测 + 真机(Playwright)8 项断言全过,
含「中文句末标点未被包进链接」「裸域名不链接化」「点击真的开新标签页并导航到目标地址」。 -
聊天室:新增「回到底部」按钮,与画图页同款
xiaolongmr
- 离开底部(>80px)才出现,点一下平滑滚回最新;回到底部自动收起。
- ⚠️ 聊天室滚动在**内层消息容器**(listRef),不是 window —— 不能照抄画图页的
`window.scrollTo`,必须滚 `el.scrollTo`。
- ⚠️ 直接改 `el.scrollTop` 不一定触发 onScroll,所以自动滚到底后要**手动同步**
按钮状态,否则会出现「已经到底了按钮还挂着」。
- 按钮用 `absolute bottom-full` 贴在输入框上方(footer 是 relative,父级不裁剪);
⚠️ 外层容器必须 `pointer-events-none`、只给按钮本身 `pointer-events-auto`,
否则这块透明区域会挡住下面消息区的点击。
- 消息区加 `data-chat-room-list` 钩子:同页有 3 个 `hide-scrollbar` 容器,
按 class 选会选错(真机验证脚本实测踩到)。
验证:真机(Playwright + Chrome)5 项断言全过 —— 按钮渲染 44×44、底部时隐藏、
上滚后出现、点击后距底 0px、回底后再次隐藏。 -
聊天室:修复置顶 422,未读角标改挂到用户行
xiaolongmr
置顶报 422 的根因:
- 前端把 body 做了 JSON.stringify 再交给 httpRequest,而 httpRequest 是直接把它
当 axios 的 data 传下去的 —— **传字符串时 axios 不会自动补
Content-Type: application/json**,FastAPI 就把 body 当非 JSON,
报「Field required」→ 422。
- 修法:改成传**原始对象** `body: { pinned }`,与文件里其他 POST 调用一致。
(curl 复现:不带 Content-Type 的字符串 body → 422,带上 → 200。)
未读角标的位置与语义:
- ⚠️ 不再把「会话数」红色角标挂在「私聊」分组标题上 —— 那个数字分不清是谁发来的。
改成挂在**每个用户那一行**(对方昵称右侧),显示「这个人有几条我没看」。
- ⚠️ 原来 direct_conversations() 的 total 是「所有非我发的消息总数」,
没有已读概念 → 角标只增不减(读完也不消)。现在按已读位置过滤。
- 已读位置**不落库**:存 localStorage(`chatgpt2api_chat_room_direct_read`,
peer_id → 已读到的最大 id),接口用 `mark=peer:last_id,...` 把整张表带过去,
服务端只数 `id > last_read` 的对方消息。这样不必新建「每用户每会话已读表」。
- ⚠️ 私聊的已读位置**与公共大厅分开存**:两边消息 id 不同源,共用一个 key 会互相顶掉。
- 打开会话 / 正在看该会话时收到新消息 → 立刻写已读位置并广播事件,
收件箱监听该事件即时重拉,角标当场归零,不必等下一个轮询周期。
- `_parse_marks` 把前端输入一律当不可信:格式不对整段丢弃,宁多显示未读也不能 500。
另外顺手改:收件箱摘要里自己发的消息加「我:」前缀(否则看起来像对方说的),
已撤回的显示「[消息已撤回]」而不是空白。
验证:四套聊天室测试共 64 项全过(服务层 24 + HTTP 11 + 用户侧端到端 15 + 未读 14)。
tsc / eslint 全绿。已清… -
聊天室:管理员置顶公告 + 修复私聊收不到/回不了
xiaolongmr
置顶(仅管理员,服务端强制):
- 消息表新增 pinned_ts / pinned_by 两列,_row_to_message 下发 pinned / pinned_at。
- set_pinned 只放行 admin;已删除的消息不能置顶;撤回时一并清掉置顶。
- 新增 GET /api/chat-room/pinned 与 POST /api/chat-room/messages/{id}/pin。
- 置顶条**独立于翻页列表**:置顶消息可能早就不在当前页,仍要常驻顶部。
- 前端在 header 与消息区之间加置顶横幅,多条默认只露第一条(可展开),
点击跳转到原消息(不在当前页时给提示,不静默无反应)。
修复「管理员私聊用户,用户看不到消息,也无法回复」——两个独立成因:
1) 权限判定用错:_assert_can_direct(只允许 admin)被误用到读历史/回复路径,
导致收件人侧一律 403。拆出 _assert_can_access_conversation(会话双方可访问),
读历史与「回复已有会话」走它;只有「发起全新会话」才要求 admin。
⚠️ 判定新会话不能只看「me 在参与者里」——新会话的键就是 dm:me|peer,恒成立,
必须用 _has_history() 查是否有实际往来,否则普通用户能私聊任何人。
2) 收件人没有入口:原未读统计只看公共大厅,收件人根本不知道有人私聊自己。
新增 direct_conversations() + GET /api/chat-room/direct-conversations,
前端侧栏加「私聊」区块(头像/昵称/摘要/在线点 + 计数徽章)。
验证:服务层 24 项、HTTP 接口 11 项、用户侧真实鉴权端到端 15 项,共 50 项全过。
后端全量测试 948 passed,失败 26 项均为既有环境漂移(chrome 指纹版本、导航迁移),
与本次改动无交集(chat_room 相关 0 项失败)。 -
聊天室:图片改用灯箱查看,修复 @ 用户面板被裁掉
xiaolongmr
1. 聊天图片不再新标签页打开
原本用 <a target="_blank"> 包图片,点击会跳新标签页。改为复用站内已有的
ImageLightbox(Fancybox),点击原地放大;并把当前已加载消息里的图片一并
传入,灯箱内可左右翻看,不用退出去再点下一张。
2. 修复 @ 唤出面板被遮挡
@ 面板是 `absolute bottom-full`(贴在输入框上方),而 PromptComposerSurface
默认带 `overflow-hidden`,会把面板整块裁掉 —— 所以看起来像「没弹出来」。
按 chat-composer 的既有做法,在面板打开时给 Surface 加 `z-50 overflow-visible`。 -
聊天室:隐藏消息区与侧栏的滚动条
xiaolongmr
给消息区、侧栏在线列表、@ 唤出面板的 overflow-y-auto 容器加上站点现成的
hide-scrollbar 工具类(globals.css 已定义,画图页消息区同款)。
滚动功能不受影响,只是滚动条不再显示、也不再占位。 -
聊天室:头像回退微信头像、复用画图页输入框、撤回删除、布局收口
xiaolongmr
头像
- 根因是聊天室与 presence 只读 identity["avatar_url"],而微信登录用户的头像
存在 wechat_avatar 里,于是恒为空、界面只显示首字占位(签到页能显示是因为
它两个字段都读)。新增 presence_service.resolve_avatar_url 统一口径:
先 avatar_url,再回退 wechat_avatar,与顶栏 / 画图页 / 批量页 / 签到页一致。
- 在线列表、消息落库、summary.me 三处都改走这个函数。
输入框
- 底部输入框改用画图页同一套 @/components/prompt-composer 部件
(Surface / Textarea / Toolbar / SubmitButton),外形与画图页完全一致。
- @ 唤出面板通过 suggestions 传入,键盘拦截走 onKeyDownCapture
(返回 true 表示面板已消费该按键),粘贴图片仍走 onPaste。
撤回与删除
- 软删除:新增 deleted_ts / deleted_by 两列,不做物理删。消息 id 是前端翻页
游标,物理删会留下空洞、导致增量轮询的序列错乱。
- 已删消息的正文与图片在服务端就抹空(不能只靠前端不渲染,否则直接读接口
仍能拿到内容)。前端显示「你/某人撤回了一条消息」或「管理员删除了一条消息」。
- 权限在服务端强制:作者本人 2 分钟内可撤回(RECALL_WINDOW_SECS=120),
管理员可删任意消息。前端按钮只按同一规则决定是否渲染,不作为权限依据。
- 新增 DELETE /api/chat-room/messages/{id}。
- mergeMessages 支持就地更新:撤回后 id 不变而内容变了,若只做「去重后追加」,
轮询拉回的已删状态会被旧数据挡住,界面永远不会更新。
布局
- 输入框区去掉「所有登录用户可见,请勿发送隐私信息」。
- 左上角侧栏头部由「在线用户 + 计数 + 搜索框」改为单个「公告聊天室」标题。
- section 高度由 min-h 改为固定 h:AppShell 内层本身是 min-h-screen 加
pt-[5.25rem],section 再叠 min-h 会与之相… -
新增聊天室页面:登录用户群聊
xiaolongmr
- 后端:services/chat_room_service.py(SQLite 只追加存储,经镜像 runtime_file 持久化)+ api/chat_room.py(消息增删查 / 汇总接口,均需登录)
- 前端:/chat-room 页面,3s 轮询增量拉取、乐观发送、回车发送、500 字上限、自动滚底、日期分隔与角色徽章
- 导航:三套角色导航均插入「聊天室」(对话之后),含一次性迁移