Files
trade-message-center/.trellis/spec/chrome-extension/frontend/onetalk/page-controls.md
T

8.2 KiB
Raw Blame History

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.tssrc/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-flexjustify-content: center 保持同行及文字水平居中;文字为“复制会话 Id”,字体显式为 12px,不能依赖会话标题的继承字体。
  • 点击处理在用户手势中调用 navigator.clipboard.writeText(conversationId);成功显示“已复制”,不可用或 reject 显示“复制失败”,随后恢复按钮文案。不会以 execCommand、隐形文本框或其它方式降级复制。
  • 页面 DOM 尚未就绪或 SPA 重绘时可由 MutationObserver 重新尝试挂载;同一页面只能存在一个该属性的按钮。
  • 非观测页面 DOM 查询、控件创建和更新仅归 main-page/dom/ 所有;调用方只能使用其语义 API,不能重新取得同一 DOM 权限。
  • 动作提示以固定定位、pointer-events: none 的 extension-owned data-tmc-action-status-* DOM 节点呈现,不依赖 OneTalk 的业务 DOM,也不改变宿主布局。多个活动条目按照首次 start 的顺序纵向显示。
  • start 对同一 id 幂等,不覆盖已有行;updateclose 只作用于已存在的 id,找不到时返回 false,不得隐式创建行。颜色只接受 neutralinfowarningerror,任意 CSS 色值必须显式失败。
  • 状态 facade 必须保留身份,并在再次安装、startupdate 时重新挂载所有仍活动的行;宿主移除了 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.conversationfinalAttempt 仅是页面命令元数据。成功立即结算该会话;失败仅在此标记为 true 时结算,第一次可重试失败不得推进显示进度。
  • ConnectionStatusTooltip 只消费已经由 MAIN page bridge 校验的 disconnected: boolean,使用固定 id onetalk-connection-statustrue 幂等调用 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 或活动行被宿主移除 下次安装、startupdate 重新按原插入顺序挂载全部活动行
连接提示为 true / false 分别只启动 warning 行 / 关闭固定断线行,不影响历史行

5. Good / Base / Bad Cases

  • Good:用户选中单一会话,按钮和标题同行;点击后仅把该 DOM data-cid 写入剪贴板。
  • Goodtooltip.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、内容水平居中,并把唯一 selected data-cid 传给 Clipboard API。
  • 覆盖无唯一 selected 会话不安装按钮,以及 Clipboard reject 显示“复制失败”。
  • test/onetalk-action-status-tooltip.test.js 覆盖同 id 去重、多个 id 的纵向插入顺序、update / close 的 boolean 结果、预设颜色和原始 CSS 色拒绝。
  • 动作提示测试必须覆盖容器或行被外部移除后,通过 update 或重复安装恢复同一 facade、全部活动行和原始顺序。
  • 断线提示测试覆盖与 bootstrap 行并列、重复 truefalse 关闭及容器重挂载。
  • 修改控件后执行该定向测试、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");