mirror of
https://github.com/sinanyuntu/trade-message-center.git
synced 2026-09-17 13:22:11 +08:00
8.2 KiB
8.2 KiB
OneTalk 页面增强控件
1. Scope / Trigger
当 MAIN world 需要在 OneTalk SPA 页面内添加只读辅助控件(当前为复制会话 ID、进行中动作提示)时,遵循本文。控件只改善当前页面交互;不得自行创建 Page Bridge 消息、Service Worker 状态、IndexedDB 写入或 Bright 请求。
2. Signatures
const installOneTalkConversationIdCopyControl = (
pageWindow: OneTalkConversationIdCopyWindow,
): void => {};
type TooltipColor = "neutral" | "info" | "warning" | "error";
type OneTalkActionStatusTooltip = {
start(id: string, text: string, color?: TooltipColor): void;
update(id: string, text: string, color?: TooltipColor): boolean;
close(id: string): boolean;
};
const installOneTalkActionStatusTooltip = (pageWindow: Window): void => {};
class ConnectionStatusTooltip {
update(disconnected: boolean): void;
}
入口由 src/onetalk/main-page/page-script-entry.ts 安装;复制控件和动作提示的 DOM 实现分别位于 src/onetalk/main-page/dom/conversation-id-copy.ts 与 src/onetalk/main-page/dom/action-status-tooltip.ts。旧根路径仅保留兼容 re-export,不能再次拥有 DOM API。复制控件的 ID 读取复用 readCurrentConversationId(pageWindow);动作提示将 facade 安装到 window.__tradeMessageCenterOneTalk.tooltip。
3. Contracts
- 会话 ID 只接受
readCurrentConversationId返回值;它要求恰好一个.contact-item-container.selected[data-cid]。不得从 URL、会话标题、列表顺序、参与者或账号字段推断 ID。 - 仅当会话 ID 唯一且标题节点可用时创建一个
button[data-tmc-conversation-id-copy]。SPA 切换或无唯一选中会话时,更新控件的data-conversation-id或移除控件,不能保留旧 ID。 - 按钮直接追加到标题节点,使用
inline-flex和justify-content: center保持同行及文字水平居中;文字为“复制会话 Id”,字体显式为12px,不能依赖会话标题的继承字体。 - 点击处理在用户手势中调用
navigator.clipboard.writeText(conversationId);成功显示“已复制”,不可用或 reject 显示“复制失败”,随后恢复按钮文案。不会以execCommand、隐形文本框或其它方式降级复制。 - 页面 DOM 尚未就绪或 SPA 重绘时可由
MutationObserver重新尝试挂载;同一页面只能存在一个该属性的按钮。 - 非观测页面 DOM 查询、控件创建和更新仅归
main-page/dom/所有;调用方只能使用其语义 API,不能重新取得同一 DOM 权限。 - 动作提示以固定定位、
pointer-events: none的 extension-owneddata-tmc-action-status-*DOM 节点呈现,不依赖 OneTalk 的业务 DOM,也不改变宿主布局。多个活动条目按照首次start的顺序纵向显示。 start对同一id幂等,不覆盖已有行;update与close只作用于已存在的id,找不到时返回false,不得隐式创建行。颜色只接受neutral、info、warning、error,任意 CSS 色值必须显式失败。- 状态 facade 必须保留身份,并在再次安装、
start或update时重新挂载所有仍活动的行;宿主移除了 container 或某一行都不能改变状态顺序或丢失活动项。 - 当前 bootstrap 的会话发现与逐会话历史提示由
current-conversation-history/bootstrap-progress-tooltip.ts在 MAIN 页面本地拥有:它只保存本次发现的总数和已终态的唯一会话 ID,复用一条固定状态行;discovery command 可私有携带既有 marker 的terminalConversationIds,并且只与本次发现的 ID 求交后预结算。该元数据不进入 Page Bridge observation、IndexedDB、Bright 或诊断状态。 - tooltip facade 或页面 DOM 的异常必须在调用动作提示的本地通知边界内隔离,不能改变 discovery/history 命令结果、重试、消息观察、checkpoint 或持久化。
onetalk.sync.conversation的finalAttempt仅是页面命令元数据。成功立即结算该会话;失败仅在此标记为true时结算,第一次可重试失败不得推进显示进度。ConnectionStatusTooltip只消费已经由 MAIN page bridge 校验的disconnected: boolean,使用固定 idonetalk-connection-status。true幂等调用tooltip.start(..., "连接已断开,正在尝试重新连接", "warning"),false只关闭该 id;不得解释 Bright 原始状态或错误。- 断线行与历史 bootstrap 使用不同 id,保留首次插入顺序、关闭和宿主重挂载语义,彼此不能重排或结算。
4. Validation & Error Matrix
| 条件 | 行为 |
|---|---|
恰好一个 selected data-cid,标题已渲染 |
在标题内安装按钮,复制该 ID |
| 零个或多个 selected 会话 | 移除已有按钮,不复制、不猜测 ID |
| 标题节点暂未渲染 | 不创建按钮,等待后续 DOM 变化重新检查 |
Clipboard API 不可用或 writeText reject |
显示“复制失败”,不抛出到 OneTalk 页面 |
writeText resolve |
显示“已复制”,定时恢复“复制会话 Id” |
| SPA 重绘后旧按钮被移除 | 仅按新的唯一 selected data-cid 重建一个按钮 |
start(id, text, color?) 的 id 尚未活动 |
创建一行;默认使用 neutral,并以插入顺序显示 |
同一 id 再次 start |
不新增、不覆盖文案或颜色;同时重新挂载活动行 |
update / close 找不到 id |
返回 false,不创建或删除其它行 |
| 传入预设外颜色 | 抛出 onetalk_action_status_tooltip_color_invalid,不改变既有行 |
| container 或活动行被宿主移除 | 下次安装、start 或 update 重新按原插入顺序挂载全部活动行 |
连接提示为 true / false |
分别只启动 warning 行 / 关闭固定断线行,不影响历史行 |
5. Good / Base / Bad Cases
- Good:用户选中单一会话,按钮和标题同行;点击后仅把该 DOM
data-cid写入剪贴板。 - Good:
tooltip.start("collect-history", "正在收集所有对话历史", "info")与另一个id纵向共存,且不会改变页面桥或同步流程。 - Base:页面首次加载时标题未出现;控件暂不显示,DOM 稳定后再安装。
- Bad:读取地址栏
conversationId、复制标题文字或多个 selected 节点中的第一个值。 - Bad:因为复制失败而创建 bridge frame、写 IndexedDB、上报 Bright 或显示成功状态。
- Bad:让调用方传入
"#ff0000"等任意 CSS 色值,或在update找不到id时静默创建新状态行。
6. Tests Required
test/onetalk-conversation-id-copy.test.js断言按钮嵌入标题、文案为“复制会话 Id”、字体为12px、内容水平居中,并把唯一 selecteddata-cid传给 Clipboard API。- 覆盖无唯一 selected 会话不安装按钮,以及 Clipboard reject 显示“复制失败”。
test/onetalk-action-status-tooltip.test.js覆盖同id去重、多个id的纵向插入顺序、update/close的 boolean 结果、预设颜色和原始 CSS 色拒绝。- 动作提示测试必须覆盖容器或行被外部移除后,通过
update或重复安装恢复同一 facade、全部活动行和原始顺序。 - 断线提示测试覆盖与 bootstrap 行并列、重复
true、false关闭及容器重挂载。 - 修改控件后执行该定向测试、
pnpm --filter @trade-message-center/chrome-extension typecheck,以及经仓库根scripts/with-build-hash.mjs注入构建标识的扩展构建。
7. Wrong vs Correct
// Wrong: 标题和 URL 都不是会话 ID 的可信来源。
button.addEventListener("click", () => navigator.clipboard.writeText(title.textContent ?? ""));
// Correct: 只复制当前唯一 selected DOM 会话 ID。
const conversationId = readCurrentConversationId(pageWindow);
if (!conversationId) return;
void navigator.clipboard.writeText(conversationId);
// Wrong: 未知状态隐式创建且允许调用方接管主页面样式。
tooltip.update("collect-history", "正在收集", "#ff0000");
// Correct: 创建和更新有明确生命周期,颜色是受限语义值。
tooltip.start("collect-history", "正在收集所有对话历史", "info");
tooltip.update("collect-history", "已收集一半", "warning");
tooltip.close("collect-history");