W72 纯附件消息也有气泡外侧时间
时间原本挂在「这条消息**最后一个有正文的气泡**」外侧(W69),而纯附件消息(`documents` 或 `image` 非空、`content` 为空)解析出来的那个占位气泡 `body` 也是空,`Bubble` 见空 `body` 什么都不画 ⇒ 那条时间**整条没被画出来**(主人 2026-10-07 验收 W69 时发现的缺口)。 - 判据抽成纯函数 `messageTimeCarrier(message, bubbles)`:先找最后一个有正文的气泡(W69 原本那一档,一个字都不改);一个都没有时按「谁最后画」往回退(语音形态 → 图片 → 附件芯片排),落点用 `MessageTimeCarrier` 表示。真的空气泡(`isContentlessUserMessage`) 仍由 `ContentlessNote` 那条路自己画时间,与 W69 一致,未改。 - 附件芯片排 / 图片 / 语音形态三块改为经 `BubbleWithTime` 渲染:**不是落点时 `time = null`, 连 `Box` 都不建** ⇒ 与 W69 之前逐像素相同。贴哪一侧、宽度预扣(时间不参与 weight)、 底对齐全部复用 W69 那一个组件,没有第二份布局逻辑。 - 新增 `MessageTimeCarrierTest` 11 条:纯附件 / 纯图片 / 无转写语音 / 角色无关 / 多块时的优先序 / 有正文时 W69 口径不变 / 真空消息返回 null;`MessageTimeLabelTest` 16 条一条未动。 - 假后端补夹具:`/__control/messages` 的 `text=`(**空串** = 这条没有正文)与 `documents=` (`extracted` / `listed` / `unreadable`)。此前「纯附件消息」在本机**根本造不出来** ——工具不实现 upload 摄取链路,`PATCH` 又要求 `content` 非空——设备验收因此无从做起。 README 补 §8.3 与端点表。 验收:`assembleDebug` 通过;`testDebugUnitTest` **129 suites / 1636 tests / 0 failures / 0 errors**(基线 128 / 1625)。
This commit is contained in:
1 parent
f1e894e71f
commit
a94e310cd7
4 files changed
+436
-27
No files matched your search
@@ -223,6 +223,49 @@ def image_fixture(path: str) -> str:
|
||||
return "data:{};base64,{}".format(mime, base64.b64encode(raw).decode("ascii"))
|
||||
|
||||
|
||||
# —— 附件夹具(`documents` DTO 键)———————————————————————————————
|
||||
#
|
||||
# 为什么要造它:**纯附件消息**(`documents` 非空、`content` 为空)是「气泡外侧那枚时间」新补的那一档,
|
||||
# 而它在库里**没有别的造法** —— 本工具不实现 upload / 摄取链路(见 README §11),
|
||||
# `PATCH /api/v1/messages/{id}` 又要求 `content` 非空 ⇒ 一条这样的消息以前根本灌不出来。
|
||||
#
|
||||
# 形状按客户端解出来的写(`HistoryMessageDto.documents` → `DocumentRefDto.kt:67`)。与真后端的
|
||||
# 一致处:**空 / 缺失就整键省略**(后端 `omitempty`),客户端把那两种形状都归一成空列表。
|
||||
DOCUMENT_FIXTURE_NAME = "6c1f0a2b3d4e5f60718293a4b5c6d7e8.txt"
|
||||
|
||||
DOCUMENT_FIXTURE_REASON = "这个夹具不实现真正的摄取,所以没有可注入的正文"
|
||||
|
||||
|
||||
def documents_fixture(kind: str):
|
||||
"""按控制端点给的种类造一个 `documents` 键的值;`none` ⇒ 不打这个键(返回 None)。
|
||||
|
||||
`kind` 三档对应芯片上看得见的那三种样子(文案出处 `DocumentChips.statusLabel`):
|
||||
|
||||
| kind | `status` | 设备上那枚芯片 |
|
||||
|---|---|---|
|
||||
| `extracted` | `extracted` | 常规色 · `TXT · 12 B · 10 字 · 已读入` |
|
||||
| `listed` | `listed` | 常规色 · `… · 只看得到清单`(正文没进上下文,**不是**读不到) |
|
||||
| `unreadable` | `unreadable` | 灰态 · `… · 读不到`,`reason` 那句原话留给预览弹层 |
|
||||
|
||||
只给**一枚**芯片:造这一档要验的是「有附件、没有正文时那枚时间挂在哪一块」,不是芯片排的换行。
|
||||
"""
|
||||
if kind in (None, "", "none"):
|
||||
return None
|
||||
if kind not in ("extracted", "listed", "unreadable"):
|
||||
raise KeyError(kind)
|
||||
payload = {
|
||||
"name": DOCUMENT_FIXTURE_NAME,
|
||||
"mime": "text/plain",
|
||||
"size": 12,
|
||||
"status": kind,
|
||||
}
|
||||
if kind == "extracted":
|
||||
payload["chars"] = 10
|
||||
if kind == "unreadable":
|
||||
payload["reason"] = DOCUMENT_FIXTURE_REASON
|
||||
return [payload]
|
||||
|
||||
|
||||
def iso_now() -> str:
|
||||
return datetime.now().astimezone().isoformat(timespec="milliseconds")
|
||||
|
||||
@@ -1564,12 +1607,19 @@ class Handler(BaseHTTPRequestHandler):
|
||||
except (OSError, ValueError) as error:
|
||||
self._fail(400, "bad_request", "image_file unusable: {}".format(error))
|
||||
return
|
||||
try:
|
||||
documents = documents_fixture(self._param(query, "documents", None))
|
||||
except KeyError:
|
||||
self._fail(400, "bad_request", "documents must be one of extracted/listed/unreadable/none")
|
||||
return
|
||||
if session_id not in self.store.sessions:
|
||||
self._fail(404, "session_not_found", "unknown session {}".format(session_id))
|
||||
return
|
||||
first_id = last_id = None
|
||||
for index in range(1, count + 1):
|
||||
content = template.format(n=index) if template else "{} #{}".format(prefix, index)
|
||||
# `text=` **空串**与「不带 text」是两件事:前者是「就要一条没有正文的消息」
|
||||
# (纯附件 / 纯图 / 无转写的语音都靠它造),后者走原来的 `seed #N` 默认正文。
|
||||
content = template.format(n=index) if template is not None else "{} #{}".format(prefix, index)
|
||||
message = self.store.add_message(
|
||||
session_id,
|
||||
role,
|
||||
@@ -1577,6 +1627,7 @@ class Handler(BaseHTTPRequestHandler):
|
||||
platform=platform,
|
||||
voice=voice,
|
||||
image=image,
|
||||
documents=documents,
|
||||
)
|
||||
first_id = first_id or message["id"]
|
||||
last_id = message["id"]
|
||||
|
||||
Reference in new issue
Block a user