ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

OpenMausBot @提及高亮:隔离验证夹具、镜像输入框与 MAUS_COLORS 配色体系详解

OpenMausBot @提及高亮:隔离验证夹具、镜像输入框与 MAUS_COLORS 配色体系详解
  • 人工智能
  • AI Agent
  • AI 应用
  • 大模型
  • 交互助手
  • 工具调用
  • Agent 编排
  • 桌面应用

【免费下载链接】OpenMausBot

Open-source Grok Bot alternative with a virtual machine that bots can use

项目地址:https://gitcode.com/GitHub_Trending/op/OpenMausBot
点击查看免费下载

OpenMausBot 的频道与私聊支持@机器人提及:输入候选选择、多机器人配色高亮、Markdown 渲染和 RTL 多行方向对齐都围绕一套共享的提及识别算法实现。本文基于 提及高亮验证文档 展开,完整介绍如何启动隔离验证环境、逐项执行 QA 检查清单,并深入mentionRanges边界判定、MentionTextarea镜像布局与MAUS_COLORS色板注入等源码实现,帮助你在不接触任何真实账号的前提下复现整套验证流程。

一键启动隔离验证环境

整个验证的核心入口是一条命令:

node --experimental-strip-types scripts/verify-mentions.ts

该脚本在本地拉起一个一次性的 fake-engine 服务器(不依赖任何真实账号、模型提供商或用户数据),并挂载真实的StoreProvider、Composer、ChatView与GroupView组件。从 scripts/verify-mentions.ts 的源码可以看到完整流程:

  1. launchVerificationServer启动隔离服务器(来自 scripts/control-omb.ts 的 fixture 机制,fake CLI 为 server/testing/fake-claude-cli.ts);
  2. 依次调用runControlOmb(["new-bot", ...])创建Atlas、Juniper、調査担当三个机器人(含一个日语命名机器人);
  3. 通过/api/bots取出全部 bot id,用new-channel创建名为 "Design review" 的频道并拉入全部成员;
  4. mountPreview挂载预览入口entry: "/src/testing/mentions.tsx",路由__mentions.html;
  5. 打印 JSON 形式的previewUrl,随后parkUntilSignal()挂起等待;按Ctrl-C时finally块会关闭预览与 fixture 服务器,并删除临时数据目录(removeTempDir来自 server/testing/cleanup.ts)。

预览页面本身是一个极简测试壳:src/testing/mentions.tsx 提供 Channel / Direct chat / DM renderer preview 三个视图切换按钮和 Dark / Light 皮肤切换按钮(applySkin("midnight")/applySkin("atelier")),频道视图渲染<GroupView group={...} />,私聊视图渲染<ChatView bot={...} />——即真实生产组件,而非简化副本。

QA 验证清单(完整继承)

打开打印出的previewUrl后,按以下清单逐项手工验证。该清单直接继承自原文档,覆盖候选选择、键盘控制、多提及、Unicode 负例、长草稿对齐、私聊作用域、响应式与皮肤切换:

路径操作预期证据
频道输入框输入@Jun,用 Tab 选中 Juniper,再追加普通文本,按 Enter 发送已选中的名字保持高亮;频道以 Juniper 的 fake-engine 回复落定
候选键盘控制输入@,用 ArrowDown 移动,用 Enter 选中日语名字;Escape 关闭候选列表原生 textarea 保留已选名字与光标位置
多个提及输入@Juniper Please review this. @Atlas Check the layout.草稿中两个完整名字均高亮
日语与 @everyone输入@調査担当 確認して,Shift+Enter 换行后再输入@everyone 確認して频道内两个名字均高亮,换行保留
负向匹配输入me@Juniper.test @Ghost @Juniper2 @調査担当 確認して只有日语机器人被高亮
长草稿粘贴 12 行文本,在聊天框中选择 Display,滚动 textarea 并在末尾编辑镜像与 textarea 的宽度、折行高度、滚动偏移一致;12 个提及保留
直接对话切换到 Direct chat;用鼠标选中 Juniper 并发送用户气泡高亮提及,任务落定
私聊作用域在 Atlas 的私聊中输入@everyone @Atlas @Juniper只有 Juniper 被高亮
响应式草稿携带多提及草稿从桌面宽度缩到 390px输入框长到三行;镜像与 textarea 均测得 80px 高
皮肤切换Dark → Light → Dark输入框与已发送气泡中的名字均保持可读
机器人身份色对比 Juniper(红)与 Atlas(蓝)及其头像每个提及使用对应 bot 的 MAUS_COLORS 值;@everyone保持中性
Markdown运行 ChatMarkdown 测试散文/列表/表格中高亮已知名字;代码、链接与 HTML 安全性保持

其中"私聊作用域"一项对应源码中的everyone参数:@everyone是频道专属语义,mentionRanges(text, peers, everyone, person)只在传入everyone: true(即频道上下文)时才识别@everyone,私聊视图不传该参数,因此在 Atlas 的私聊里@everyone不产生高亮(见 src/lib/mentions.ts)。

核心实现一:边界与连续性判定

提及识别的关键难点是"何时算一个独立的@"。OpenMausBot 把这套规则放在 shared/mention-boundary.ts 中,注释明确说明其目标是"让渲染端与服务器路由端的提及识别保持完全一致":

// 只允许开启型标点/Markdown 标记作为 @ 前缀 const OPENING_MENTION_BOUNDARY = /[(*_~[{<'"\u2018\u201c\u3008\u300a\u300c\u300e\u3010\uff08]/u; export function isMentionBoundary(text: string, at: number): boolean { if (at === 0) return true; const before = text[at - 1]; return /\s/u.test(before) || OPENING_MENTION_BOUNDARY.test(before); } export function isMentionNameContinuation(value: string | undefined): boolean { return value !== undefined && /^[\p{L}\p{N}\p{M}_]/u.test(value); }

两条规则解释了 QA 表中"负向匹配"一行的行为:

  • 前缀规则:me@Juniper.test中@前面是字母e(既非空白也非开启标点),isMentionBoundary返回 false,因此Juniper不高亮——/@bot依然被视为 URL 路径,而(@bot)、【@bot】、**@bot**才是标签;
  • 连续性规则:匹配到的名字后面若紧跟 Unicode 字母、数字、组合符或下划线,说明它只是更长单词的前缀,不成立。这就是 QA 表中@Juniper2不高亮、而审查修复中@調査担当者、@everyone調査、补充平面字符与更长的同名前缀(roster 里存在精确更长的名字时)全部保持纯文本的原因。

@候选列表的过滤逻辑同样位于 src/lib/mentions.ts:mentionQueryAt从光标向前找最后一个@,要求前面是行首或空白,查询长度不超过 24 且不含@、换行;mentionChoicesForQuery做大小写不敏感的includes过滤,并在查询以空格结尾且恰好等于某个候选全名时清空列表(即输入完@Juniper后列表收起)。

核心实现二:mentionRanges 高亮算法

高亮的统一入口是mentionRanges(text, peers, everyone, person)(src/lib/mentions.ts),它返回一组{ start, end, color?, you?, person? }区间。算法要点:

  • 按名字长度降序排序(b.name.length - a.name.length),保证"先长后短"匹配,避免短名字抢先匹配长名字的前缀;
  • 逐字符扫描@,先过isMentionBoundary边界检查,再做大小写不敏感的全名匹配,最后过isMentionNameContinuation连续性检查;
  • 颜色只接受色板值:源码注释// Only palette values enter CSS明确——只有当peer.color是MAUS_COLORS的合法键时才写入区间,@everyone永远不带色,保持中性;
  • 除了机器人名字,还支持 person 标签(Team Cloud 队友)与 devs 标签两条旁路。

MAUS_COLORS色板定义在 src/lib/mascot.ts,共 12 色,与机器人头像共用同一套值——这正是 QA 表中"每个提及使用其 bot 的 MAUS_COLORS 值"这一检查项的落点:

export const MAUS_COLORS = { green: "#009957", blue: "#377FE6", red: "#D94B52", orange: "#E78531", purple: "#8057C8", cyan: "#0EA5C6", pink: "#D84F8B", yellow: "#D8A729", teal: "#01A492", coral: "#E5634E", silver: "#A8ADB4", chrome: "#B3BAC4", } satisfies Record<MausColor, string>;

色板名来自 shared/wire.ts 的MAUS_COLOR_NAMES,因此色名是线上协议的一部分,渲染端与服务器共享同一类型约束。

高亮的视觉样式在 src/styles.css 中,核心是.mention-highlight:

.mention-highlight { --mention-color: var(--color-ink-secondary); color: color-mix(in srgb, var(--mention-color) 30%, var(--color-ink)); background: color-mix(in srgb, var(--mention-color) 24%, var(--color-inset)); border-radius: 3px; text-decoration: underline; text-decoration-color: var(--mention-color); box-decoration-break: clone; -webkit-box-decoration-break: clone; }

机器人颜色通过 CSS 变量--mention-color注入(MentionText组件内联style: { "--mention-color": range.color }),前景用 30% 混入、背景用 24% 混入,从而在任何皮肤下保持可读。记录运行中用 CSS sRGB 混合比例核算了全部 70 种 bot 颜色/皮肤组合,最低文字对比度为 6.24:1;.mention-editor还有forced-colors: active下的回退:隐藏镜像、恢复原生文本色,保证 Windows 高对比度模式可用。

相关组件与测试:

  • src/components/MentionText.tsx:把mentionRanges区间切分成纯文本与<span class="mention-highlight">片段;
  • src/components/MentionText.test.ts 与 src/lib/mentions.test.ts:区间算法的单元测试;
  • src/components/MentionColors.test.ts:多机器人配色隔离回归,混合全部十种色板颜色并交叉重复/反转的名字与花名册顺序,同时覆盖MentionText与ChatMarkdown两条渲染路径(对应原文档"26 个聚焦测试通过"的回归描述);
  • src/lib/bot-identity.test.ts:验证每种颜色在styles.css中都有对应的--identity-*皮肤 token 与.bot-identity[data-bot-color]声明。

核心实现三:MentionTextarea 镜像布局

QA 表中"长草稿"与"响应式草稿"两项检查的难点在于:原生<textarea>无法给部分文本上色,而加一个可见的高亮层又必须与文本逐像素对齐。src/components/MentionTextarea.tsx 的解法是透明 textarea + 可见镜像:

<div ref={mirrorRef} dir={props.dir} aria-hidden="true" className="mention-editor-mirror pointer-events-none absolute inset-0 overflow-hidden"> <MentionText text={String(props.value ?? "")} peers={peers} everyone={everyone} />{"\n"} </div> <textarea {...props} ref={inputRef} onScroll={(event) => { sync(); props.onScroll?.(event); }} />
  • .mention-editor textarea设color: transparent(光标用caret-color: var(--color-ink)保持可见),实际看到的文字全部来自下面那层.mention-editor-mirror,由MentionText渲染带高亮的文本;
  • sync()把 textarea 的 12 项文本布局属性(font-family、letter-spacing、line-height、padding-*等,见 MentionTextarea.tsx 的TEXT_LAYOUT)逐条复制到镜像,并同步clientWidth与scrollLeft/scrollTop——这就是"镜像与 textarea 宽度、折行高度、滚动偏移一致"这一检查项的实现基础;
  • 高度自适应:input.style.height = min(scrollHeight, lineHeight * 6),即最多 6 行后开始滚动,并在改高度后恢复scrollTop防止跳动;
  • 窗口尺寸监听刻意只观察宽度(if (nextWidth !== width) resize(); else sync();),注释说明是为了避免"测量出的高度又触发 resize 形成循环";
  • 手机触摸设备(@media (pointer: coarse))强制 16px 字号,防止 Safari 在聚焦时缩放导致键盘收起后标题栏被状态栏遮挡。

样式注释还点明了纪律:.mention-highlight不能改变 padding、border 或字重等会移动光标的指标("No padding/border/font-weight changes that shift the caret")。

Markdown 渲染路径:remarkMentions 插件

已发送消息走 Markdown 管线。src/lib/mentions.ts 的remarkMentions是一个 remark 插件,遍历语法树时跳过link、linkReference、code、inlineCode节点——这保证了 QA 表中"代码、链接与 HTML 安全性保持"的结论:链接文字与代码块里的@Juniper不会被高亮,也不会把任意 HTML 注入高亮 span。插件把命中的文本节点拆分为普通文本与{ type: "mention" }节点,hName: "span",类名由mentionClassName决定(机器人 →mention-highlight,本人 →mention-highlight mention-you,队友 →mention-highlight mention-person,后者使用虚线下划线区分)。src/components/ChatMarkdown.test.ts 覆盖散文、列表、表格场景下已知名字的高亮。

RTL 混写草稿的 bidi 对齐(2026-09-09 集成记录)

原文档记录了一次针对当前主干的 RTL 集成:六个原始提交 rebase 到上游922715f0(0.1.69),保留了新版翻译后的输入框标签、BotAvatar 渲染、Markdown 代码块控制、线程控制与逐块/逐行 bidi 行为。

修复前的现象是:阿拉伯语优先的草稿里,镜像是 LTR/isolate 方向、原生 textarea 却是 RTL/plaintext 方向,两者错位。修复手段只有两条,且都能在仓库中直接看到:

  1. 把dir属性转发给镜像(见上文MentionTextarea中dir={props.dir});
  2. 镜像样式表声明unicode-bidi: plaintext(src/styles.css 的.mention-editor-mirror,注释为"逐段独立匹配原生 textarea 的方向")。

效果是每段文字跟随原生输入的方向:阿拉伯语与希伯来语行右对齐,夹在中间的英语行左对齐。记录中的实测数据:三行草稿下两个节点均为 540px 宽、80px 高;把首行从阿拉伯语改成英语后,两个节点的计算方向同步切换。复现步骤(注意端口与 channel ID 是当次运行的打印值,新运行必须用自己的):

node --experimental-strip-types scripts/verify-mentions.ts --bot-mentions # 打开打印出的 previewUrl,在真实 composer 中填入三行: # مرحبا @Atlas راجع هذا # English @Juniper review this # שלום @調査担当 תודה # 截取 before/after 后按 Enter 发送。 node --experimental-strip-types scripts/control-omb.ts channels --url http://127.0.0.1:20657 node --experimental-strip-types scripts/control-omb.ts wait --channel 18b5bfe7-deb0-4962-b3b4-414e9ab90492 --timeout 60 --url http://127.0.0.1:20657 node --experimental-strip-types scripts/control-omb.ts messages --channel 18b5bfe7-deb0-4962-b3b4-414e9ab90492 --limit 20 --url http://127.0.0.1:20657

证据文件:修复前、修复后、发送后(浅色)、DOM、wait、transcript。该次运行共 65 个聚焦测试(含方向转发、既有 bidi 控制、提及渲染、cloudflared 重试)、typecheck、生产渲染器构建与定向 oxlint 全部通过;两个 Windows symlink EPERM 测试在未修改的922715f0上复跑同样失败,与本次改动无关。

机器人自己发出的提及与 DM 预览

--bot-mentions标志是"机器人提及别人"场景的脚本化手段。从 scripts/verify-mentions.ts 可以看到其机制:

  • 启用后脚本创建独立 scratch 目录,设置FAKE_CLAUDE_REPLIES(一条包含 Juniper、調査担当、Atlas 提及的固定回复)与FAKE_CLAUDE_REPLY_STATE(共享计数器状态文件),launcher 会把FAKE_CLAUDE_*环境转发给 fake CLI;
  • 计数器保证只有第一条fake 回复携带提及,后续回复退化为默认文本,从而把频道内机器人之间的接力限制在有界范围内;
  • 启动失败时 catch 块先清理 scratch 再抛出,finally兜底关闭预览与服务器。

操作路径:在频道 composer 输入@Atlas Please ask the team to review.并发送。记录运行中,Atlas 的真实 bot 消息把 Juniper 渲染为红色、調査担当 为橙色、Atlas 为蓝色,重复提及在两种皮肤下都保留颜色,而@調査担当者等 Unicode 前缀保持纯文本、频道@everyone保持中性。

此外,预览页的DM renderer preview按钮以group.dm = true渲染同一份 fixture transcript,直接复用真实的GroupView/Composer组件而不创建 bot 到 bot 的服务器会话。该预览中 bot 回复与草稿里的@everyone均不高亮、输入@every也不出现候选,印证了"@everyone 仅限频道"的作用域规则(预览仅检查 DM 渲染,不实际发送消息)。该场景的证据:wait、transcript、DOM 配色、深色截图、浅色截图。

记录运行结果与测试矩阵

原文档保留了多轮完整记录,供追溯时参照:

0.1.61 首验(2026-09-07,Windows + Chromium,上游9c681f44):频道与私聊的wait结果均为settled,有界 transcript 存于 evidence/mentions/;长草稿检查测得 textarea 与镜像均为 584px 滚动高度、440px 滚动偏移,滚动中编辑不破坏对齐;390px 检查只覆盖 composer(既有频道头在该宽度下仍溢出);3,827 个 Vitest 测试通过、2 个失败(均为 WindowssymlinkSyncEPERM,在未修改基线上同样失败)。

bot 颜色后续(renderer-only):789 个渲染器测试、typecheck、构建、定向 lint 通过;共享头像色板同时提供每个提及的前景色调、背景色调与下划线颜色,颜色变化在纯文本与 Markdown 中同步体现;70 种组合最低对比度 6.24:1。

多机器人配色隔离:同一消息中 Atlas(蓝)、Juniper(红)、調査担当(橙)正序加反序重复,再跟中性的@everyone与无法识别的@Ghost;五种 DOM 快照各含 7 个提及且逐一匹配期望色板,浏览器无 console 错误。证据:深色、浅色、计算样式、wait、transcript。运行详情(隔离 APIhttp://127.0.0.1:21621,渲染器http://127.0.0.1:5173/__mentions.html):

node --experimental-strip-types scripts/verify-mentions.ts node --experimental-strip-types scripts/control-omb.ts channels --url http://127.0.0.1:21621 # 在真实频道 composer 填入三行并切到 Light,然后按 Enter。 # 发送后重填同一草稿用于截图对比。 # @Atlas first. @Juniper second. @調査担当 third. # @調査担当 reverse. @Juniper again. @Atlas last. # @everyone neutral. @Ghost plain. node --experimental-strip-types scripts/control-omb.ts wait --channel ec952c04-4e41-4a13-ba87-8c79b3137c05 --timeout 60 --url http://127.0.0.1:21621 node --experimental-strip-types scripts/control-omb.ts messages --channel ec952c04-4e41-4a13-ba87-8c79b3137c05 --limit 20 --url http://127.0.0.1:21621

control-omb的完整 verb 面(channels、wait、messages、new-bot、send-channel等,输出均为 JSON,wait默认 30 秒)见 scripts/control-omb.ts 的 HELP 文本,其无头 CLI 设计另见 docs/headless-cli.md。

皮肤前后对比

皮肤修改前修改后
Midnight
Atelier

验证范围与边界

原文档明确了这套 fixture 的证明边界,复现时务必注意:

  • 它验证的是浏览器渲染器行为与fake-engine 消息处理,不能建立打包版 Electron、Safari、操作系统 IME 候选窗、真实提供商委托行为的结论;
  • 提及高亮使用当前花名册——历史消息中已不在花名册里的名字保持纯文本,不会高亮;
  • 记录中的服务器日志落在%TEMP%/openmausbot-verification-evidence/server-*.log(Windows 路径);每次新运行必须使用自己打印出的 URL 与返回的 channel ID,不可复用记录中的端口或 ID。

小结

OpenMausBot 的提及高亮由三层组成:shared/mention-boundary.ts提供渲染端与服务器共享的边界/连续性规则;mentionRanges+remarkMentions分别服务输入框镜像与已发送 Markdown 两条渲染路径,颜色只从 12 色MAUS_COLORS色板注入;MentionTextarea用透明 textarea 加同布局镜像解决原生输入框无法局部上色的问题,并用unicode-bidi: plaintext与dir转发保证混写草稿逐段对齐。scripts/verify-mentions.ts+scripts/control-omb.ts构成的隔离夹具让你可以在无账号、无真实数据的环境里完整复现上述全部行为。

  • 人工智能
  • AI Agent
  • AI 应用
  • 大模型
  • 交互助手
  • 工具调用
  • Agent 编排
  • 桌面应用

【免费下载链接】OpenMausBot

Open-source Grok Bot alternative with a virtual machine that bots can use

项目地址:https://gitcode.com/GitHub_Trending/op/OpenMausBot
点击查看免费下载

相关推荐

上一篇:炉石传说HsMod插件免费装好指南,50多项功能3步跑通
下一篇:CodeQL Java 分析 1.23 版本变更深度解析:新增查询、既有查询增强与>

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

返回列表