<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>测试之家社区</title>
    <link>https://testerhome.com/</link>
    <description>测试之家社区最新发帖.</description>
    <language>en-us</language>
    <item>
      <title>使用 Browser-Use 测试用例执行太慢，有木有好方案</title>
      <description>&lt;p&gt;使用 Browser-Use 测试用例执行太慢，有木有好方案~&lt;/p&gt;

&lt;p&gt;刚开始用 Browser-Use，试了下确实能按照我给的步骤执行一些动作。但感觉它遇到一些超出想象的地方就比较慢（比如有些页面在测试环境需要登录两次，它就会卡在第二次登录的页面很久才动）。&lt;/p&gt;

&lt;p&gt;我在考虑加提示词来告诉它遇到这些行为要做的对应动作，但每次执行花的时间比较长，测试效果也比较久。&lt;/p&gt;

&lt;p&gt;就来问问大家有木有好的实践可以借鉴~~~~万分感谢&lt;/p&gt;</description>
      <author>spark813</author>
      <pubDate>Wed, 24 Jun 2026 13:49:07 +0800</pubDate>
      <link>https://testerhome.com/topics/44354</link>
      <guid>https://testerhome.com/topics/44354</guid>
    </item>
    <item>
      <title>2026 年无人系统与智能技术国际学术会议（USIT 2026）</title>
      <description>&lt;p&gt;&lt;img src="/uploads/photo/2026/2e4afff5-9f8e-4861-8525-ae34c810f12d.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;2026 年无人系统与智能技术国际学术会议&lt;/p&gt;

&lt;p&gt;2026 International Conference on Unmanned System and Intelligent Technology（USIT 2026）&lt;/p&gt;

&lt;p&gt;议题投稿入口：&lt;a href="https://ais.cn/u/myeMni" rel="nofollow" target="_blank"&gt;https://ais.cn/u/myeMni&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;大会时间：2026 年 7 月 31-8 月 2 日&lt;/p&gt;

&lt;p&gt;会议地点：中国·天津&lt;/p&gt;

&lt;p&gt;提交检索：IEEE Xplore、EI Compendex 和 Scopus 收录检索&lt;/p&gt;

&lt;p&gt;会议简介&lt;/p&gt;

&lt;p&gt;2026 年无人系统与智能技术国际学术会议（USIT 2026）将于 7 月 31 日-8 月 2 日在中国·天津隆重举行。大会立足理论前沿与产业实践，聚焦 “无人系统与智能技术” 主题范畴下的新理论、新算法、新应用、新平台及新趋势等重要方向，旨在为国内外专家、学者、工程师及从业者提供一个高水平的学术交流平台，推动无人系统与智能技术领域科研成果和前沿技术的分享与碰撞，激发创新思维，深化学术研究与合作，加快推进学术成果向产业应用转化。大会诚挚邀请国内外高校、科研院所、高新技术企业及相关领域的专家学者、技术人士与业界代表共聚一堂，携手促进无人系统与智能技术领域的国际交流与合作。 &lt;/p&gt;

&lt;p&gt;组织单位&lt;/p&gt;

&lt;p&gt;主办单位：天津工业大学、天津科技大学&lt;/p&gt;

&lt;p&gt;承办单位：天津工业大学控制科学与工程学院&lt;/p&gt;

&lt;p&gt;协办单位：华东交通大学电气与自动化工程学院&lt;/p&gt;

&lt;p&gt;支持单位：北京交通大学自主智能与无人系统研究中心&lt;/p&gt;

&lt;p&gt;征稿主题&lt;/p&gt;

&lt;p&gt;无人系统&lt;/p&gt;

&lt;p&gt;机器视觉、自动驾驶/无人驾驶、低空技术、水面/水下无人系统、机器人、传感器、环境感知、自主与协同控制、自主决策、导航与路径规划、模拟仿真、人机交互、智能信息融合处理、结构/能源/动力技术、反无人系统技术……&lt;/p&gt;

&lt;p&gt;智能技术&lt;/p&gt;

&lt;p&gt;人工智能、大模型、计算机视觉、智能制造、智能控制技术、机器学习、知识图谱、语义理解、自然语言处理、图像处理、自适应控制、多智能体系统控制、分布式智能处理、智能医疗、大数据分析……&lt;/p&gt;

&lt;p&gt;论文出版&lt;/p&gt;

&lt;p&gt;&lt;img src="/uploads/photo/2026/746f4c25-6986-417b-9959-f7ef83ac30e7.png!large" title="" alt=""&gt;&lt;/p&gt;</description>
      <author>leisigoule</author>
      <pubDate>Tue, 23 Jun 2026 15:46:14 +0800</pubDate>
      <link>https://testerhome.com/topics/44353</link>
      <guid>https://testerhome.com/topics/44353</guid>
    </item>
    <item>
      <title>8 月深圳举行！MTSC2026 中国互联网测试开发大会→总体议程出炉</title>
      <description>&lt;p&gt;&lt;strong&gt;随着大模型与智能体技术深度渗透软件研发全链路，测试行业正从 “工具提效” 迈入 “体系重构” 的全新阶段。质量团队如何完成 AGI 时代的组织转型？AI 原生应用的可信评测体系如何搭建？Agent 驱动的测试执行将走向何方？&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;这些行业核心命题，都将在2026年8月15日于深圳举办的 MTSC2026 第十五届中国互联网测试开发大会上集中呈现。 &lt;/p&gt;

&lt;p&gt;由 TesterHome 社区主办的年度软件质量技术盛会&lt;strong&gt;MTSC2026 中国互联网测试开发大会，即将于 8 月 15 日在深圳登喜路国际大酒店正式举行。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;本届大会以 “质效革新·智领未来” 为核心主题，设置 6 大平行技术专场，汇聚国内外一线科技企业的技术专家、行业领袖与企业高层，围绕 AI+ 软件测试领域的前沿技术、热点话题与落地实践展开深度研讨，为全行业搭建技术交流与生态共建的核心平台。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;大会面向&lt;/strong&gt;：AI 测试工程师、大模型质量保障工程师、RAG 应用质量保障工程师、测试工程师、研发工程师、测试经理、研发经理、测试总监、研发总监架构师、CTO 等行业人士，从 2015 年开始已成功举办了 14 届，“落地、务实、有深度” 的内容风格和良好口碑，得到测试开发者、互联网一线企业及全行业的认可和大力支持。&lt;/p&gt;
&lt;h2 id="六大专场全覆盖：锚定AI测试全维度变革"&gt;六大专场全覆盖：锚定 AI 测试全维度变革&lt;/h2&gt;
&lt;p&gt;MTSC2026 深圳大会聚焦 AI 测试领域前沿变革与产业落地，设置六大平行技术专场，全方位覆盖 AI 原生测试技术落地、质量治理可信评测、行业智能化实践、测试体系升级、组织数字化转型等核心赛道。各专场均汇聚头部企业一线实战成果，全方位展现 AI 时代软件测试与质量保障的技术迭代、范式革新与行业落地价值。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;AI 原生应用测试与行业落地专场：本专场聚焦 AI 原生测试技术在各垂直行业的落地实践，围绕 AI 产品、智能应用、工业机器人等场景的测试难题与解决方案展开深度分享，汇聚多家科技企业的前沿落地案例。完整呈现 AI 原生测试技术在文娱、工业智能、智能终端等领域的落地路径与质量建设经验。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;AI 时代的质量治理与可信评测专场：针对 AI 规模化应用下的质量管控新难题、可信性保障新诉求，聚焦多端一致性、内容质量、异常诊断、全链路性能等核心质量治理场景，输出体系化评测方案与实战经验。专场包含淘宝、岩山科技、蚂蚁集团、Shopee 落地实践，全方位解答 AI 时代多终端、多场景下的质量可信治理、智能评测与高效运维核心问题。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;TME 智能化专场：深度聚焦音视频与泛娱乐行业的研效升级与质量进化，围绕 AI 赋能研发全流程、质量全链路管控展开系列专题分享，完整展现泛娱乐赛道研测一体化、智能化的升级全貌。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Agent 驱动的测试执行体系专场：本专场是大会核心技术专场之一，主打测试执行体系的智能化、平台化、自动化范式升级，聚焦 AI 智能体重构测试全流程的落地实践，汇集高校与多家行业头部企业的标杆案例。核心内容包括香港科技大学、货拉拉、微众银行、湘财证券、蚂蚁集团的落地实践，集中展示 Agent 技术重塑测试执行环节的核心价值与落地成果。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;智能化游戏测试专场：深耕游戏行业 AI 测试赛道，围绕游戏性能压测、质量保障、测试组织升级等核心方向，汇聚腾讯、沐瞳科技、网易游戏等游戏行业头部企业的实战经验，全面覆盖游戏测试工具智能化、性能治理、组织模式升级全维度内容。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;AI 组织转型专场：聚焦 AI 浪潮下质量团队与产研体系的组织变革、架构升级与效能跃迁，从工具链、工作流、组织模式、研发体系多维度拆解 AI 转型路径，以蚂蚁集团、极智嘉等企业的重磅实践为核心内容，为企业质量团队、产研体系的智能化组织转型提供完整可落地的参考范式。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;以下是 MTSC2026 深圳大会六大专场具体议题&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="/uploads/photo/2026/4d51dc6a-86e5-47ca-a434-fd14d1e1de34.jpeg!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h2 id="MTSC2026深圳大会报名通道"&gt;MTSC2026 深圳大会报名通道&lt;/h2&gt;
&lt;p&gt;在 AI 驱动软件质量革命的行业浪潮下，MTSC2026 不仅是一场技术分享的盛会，更是测试行业从业者洞察趋势、交流实践、链接生态的核心平台。2026年8月15日，深圳，行业精英将共聚一堂，一同探索质效革新的发展路径，见证 AI 时代测试行业的生态之变。&lt;/p&gt;

&lt;p&gt;目前 MTSC2026 深圳大会已开启报名通道，现阶段购票可享 8 折优惠，5 人及以上团体购票可享每张再减 100 元的专属福利。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;火热报名中，时间越晚折扣越少~&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;&lt;a href="https://www.bagevent.com/event/9166439" rel="nofollow" target="_blank" title=""&gt;​&amp;gt;&amp;gt;&amp;gt;报名参会，点击购票&lt;/a&gt;&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;MTSC2026 中国互联网测试开发大会，8 月 15 日深圳见~&lt;/p&gt;
&lt;h2 id="联系方式"&gt;联系方式&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;商务合作&lt;/strong&gt;&lt;br&gt;
联系人：徐士钊&lt;br&gt;
邮箱：bd@testerhome.com&lt;br&gt;
微信：xiaozhao129540&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;票务联系&lt;/strong&gt;&lt;br&gt;
联系人：&lt;br&gt;
静静 微信：hahalovesj&lt;br&gt;
敏敏 微信：leejiaxiaopengyou&lt;/p&gt;

&lt;p&gt;&lt;img src="/uploads/photo/2026/4d0684fc-aaf4-4503-8445-fc9c57c30cfb.png!large" title="" alt=""&gt;&lt;/p&gt;</description>
      <author>TH_tester</author>
      <pubDate>Tue, 23 Jun 2026 13:39:51 +0800</pubDate>
      <link>https://testerhome.com/topics/44352</link>
      <guid>https://testerhome.com/topics/44352</guid>
    </item>
    <item>
      <title>全球首个！京东全栈开源 JoyAI-VL-Interaction，让大模型从 “一问一答” 走向 “边看边说”</title>
      <description>&lt;p&gt;一场火灾发生的瞬间，监控系统可以实时发出警报；独居老人在家摔倒，AI 可以马上提醒远方的亲人；视障人士外出，智能眼镜随时解读附近环境、指明方向……这些看似科幻的场景，在 AI 时代可能很快会成为现实。近日，京东开源实时视频视觉语言交互模型 JoyAI-VL-Interaction，这也是全球首个全栈开源的 interaction 模型和系统，并获得 vLLM-Omni 的 day-0 原生支持。它让大模型从 “一问一答” 走向 “边看边说”，开发者基于这套框架，可以快速搭建能持续观察、自主判断、即时响应的实景 AI 助手，有望推动 AI 在物理世界发挥巨大作用，为人类生产和生活带来全新变革。代码🔗&lt;a href="https://github.com/jd-opensource/JoyAI-VL-Interactionhttps://huggingface.co/jdopensource/JoyAI-VL-Interaction-Preview%E6%95%B0%E6%8D%AE%E9%9B%86%F0%9F%94%97https://huggingface.co/datasets/jdopensource/JoyAI-VL-Interaction%E4%B8%8D%E6%AD%A2%E7%9C%8B%E6%87%82%E8%BF%87%E5%8E%BB%EF%BC%8C%E6%9B%B4%E8%A6%81%E7%9C%8B%E6%87%82%E2%80%9C%E7%8E%B0%E5%9C%A8%E2%80%9D%E4%BB%8A%E5%A4%A9%E5%BE%88%E5%A4%9A%E5%A4%9A%E6%A8%A1%E6%80%81%E6%A8%A1%E5%9E%8B%EF%BC%8C%E9%87%8D%E5%9C%A8%E6%AF%94%E6%8B%BC%E5%8F%82%E6%95%B0%E3%80%81%E7%9F%A5%E8%AF%86%E5%92%8C%E6%8E%A8%E7%90%86%EF%BC%8C%E6%9C%AC%E8%B4%A8%E4%B8%8A%E4%BB%8D%E6%98%AF%E2%80%9C%E4%B8%80%E9%97%AE%E4%B8%80%E7%AD%94%E2%80%9D%EF%BC%8C%E5%8D%B3%E7%94%A8%E6%88%B7%E4%B8%8A%E4%BC%A0%E5%9B%BE%E7%89%87%E6%88%96%E8%A7%86%E9%A2%91%EF%BC%8C%E6%8F%90%E5%87%BA%E9%97%AE%E9%A2%98%EF%BC%8C%E6%A8%A1%E5%9E%8B%E5%86%8D%E7%BB%99%E5%87%BA%E5%9B%9E%E7%AD%94%E3%80%82%E8%BF%99%E7%A7%8D%E6%96%B9%E5%BC%8F%E5%9C%A8%E5%9B%BE%E6%96%87%E9%97%AE%E7%AD%94%E3%80%81%E8%A7%86%E9%A2%91%E5%A4%8D%E7%9B%98%E3%80%81%E5%86%85%E5%AE%B9%E5%88%86%E6%9E%90%E7%AD%89%E5%9C%BA%E6%99%AF%E4%B8%AD%E8%B6%B3%E5%A4%9F%E5%A5%BD%E7%94%A8%EF%BC%8C%E4%BD%86%E5%BD%93AI%E8%BF%9B%E5%85%A5%E7%9C%9F%E5%AE%9E%E4%B8%96%E7%95%8C%EF%BC%8C%E6%A8%A1%E5%9E%8B%E4%B8%8D%E5%8F%AA%E8%A6%81%E8%81%AA%E6%98%8E%EF%BC%8C%E6%9B%B4%E8%A6%81%E2%80%9C%E5%9C%A8%E5%9C%BA%E2%80%9D%E3%80%82%E6%AD%A3%E5%9C%A8%E5%8F%91%E7%94%9F%E7%9A%84%E7%9C%9F%E5%AE%9E%E4%B8%96%E7%95%8C%EF%BC%8C%E6%97%A0%E6%95%B0%E7%9E%AC%E6%81%AF%E4%B8%87%E5%8F%98%E7%9A%84%E6%97%B6%E5%88%BB%EF%BC%8C%E9%94%99%E8%BF%87%E5%B0%B1%E5%BE%88%E9%9A%BE%E8%A1%A5%E6%95%91%E3%80%82%E4%BA%AC%E4%B8%9C%E5%BC%80%E6%BA%90%E7%9A%84JoyAI-VL-Interaction%EF%BC%8C%E5%B0%B1%E6%98%AF%E8%AE%A9AI%E5%83%8F%E4%BA%BA%E4%B8%80%E6%A0%B7%E6%8C%81%E7%BB%AD%E2%80%9C%E5%9C%A8%E5%9C%BA%E2%80%9D%EF%BC%9A%E8%BE%B9%E7%9C%8B%E3%80%81%E8%BE%B9%E8%AE%B0%E3%80%81%E8%BE%B9%E5%88%A4%E6%96%AD%EF%BC%8C%E5%B9%B6%E5%9C%A8%E5%85%B3%E9%94%AE%E6%97%B6%E5%88%BB%E4%B8%BB%E5%8A%A8%E5%9B%9E%E5%BA%94%EF%BC%8C%E6%88%96%E9%80%89%E6%8B%A9%E6%80%A7%E5%9C%B0%E4%BA%A4%E6%8E%A5%E7%BB%99%E5%90%8E%E5%8F%B0Agent%E3%80%82%E7%9B%B8%E6%AF%94%E4%BC%A0%E7%BB%9F%E6%A8%A1%E5%9E%8B%EF%BC%8CJoyAI-VL-Interaction%E6%9C%89%E4%B8%89%E9%87%8D%E7%AA%81%E7%A0%B4%EF%BC%9A" rel="nofollow" target="_blank"&gt;https://github.com/jd-opensource/JoyAI-VL-Interactionhttps://huggingface.co/jdopensource/JoyAI-VL-Interaction-Preview 数据集&lt;img title=":link:" alt="🔗" src="https://twemoji.ruby-china.com/2/svg/1f517.svg" class="twemoji"&gt;https://huggingface.co/datasets/jdopensource/JoyAI-VL-Interaction 不止看懂过去，更要看懂 “现在” 今天很多多模态模型，重在比拼参数、知识和推理，本质上仍是 “一问一答”，即用户上传图片或视频，提出问题，模型再给出回答。这种方式在图文问答、视频复盘、内容分析等场景中足够好用，但当 AI 进入真实世界，模型不只要聪明，更要 “在场”。正在发生的真实世界，无数瞬息万变的时刻，错过就很难补救。京东开源的 JoyAI-VL-Interaction，就是让 AI 像人一样持续 “在场”：边看、边记、边判断，并在关键时刻主动回应，或选择性地交接给后台 Agent。相比传统模型，JoyAI-VL-Interaction 有三重突破：&lt;/a&gt;模型🔗&lt;/p&gt;

&lt;p&gt;1、主动判断，而非被动回答。传统模型通常要等用户发起问题，才开始处理当前画面，而 JoyAI-VL-Interaction 可以持续观察视频流，自主判断什么时候该说话，什么时候该沉默。比如用户设置 “裁判出示红牌时提醒我”，模型就会持续值守画面，并在事件发生时自动预警，而不是等用户再问一句 “刚才发生了什么”。 &lt;/p&gt;

&lt;p&gt;2、实时响应，而非事后总结。传统视频理解更多是上传完整视频后再分析，但在安防预警、实时翻译、直播解说、操作指导等场景里，晚几秒，体验和价值都会不同。而 JoyAI-VL-Interaction 面向正在发生的视频流，画面变化时就能响应。   &lt;/p&gt;

&lt;p&gt;3、适时智能体委托，同时保持观察和交互。JoyAI-VL-Interaction 还具备后台任务委派能力与相关机制。当模型遇到生成代码、调用工具、复杂推理等任务时，可以交给后台大模型或 Agent。前台模型继续观察现场，后台模型处理复杂任务，结果返回后再自然接回对话。它更像一套 “前台实时助手 + 后台智能大脑” 的协作系统：前台负责在场，后台负责干重活，有机会开启 AI 与人类协作的新范式。       &lt;/p&gt;

&lt;p&gt;开源一套系统，而不只是一个模型在实时视频流中，JoyAI-VL-Interaction 每秒都会做一次判断，比如，继续观察、保持沉默，发现关键事件、主动回应，遇到复杂任务，交给后台 Agent 处理。这意味着，“什么时候说话” 不再只靠外部规则或定时触发，而是成为模型自己学会的能力。对实时交互来说，会说话很重要，会沉默也同样重要。&lt;/p&gt;

&lt;p&gt;一个好的 AI 助手，不应该一直打扰用户，而应该知道什么时候该出现，什么时候该安静，以及什么时候自己解决，什么时候交由 agent 解决。很多开源模型只提供基础推理能力。开发者如果要真正用起来，还需要自己处理视频接入、语音交互、记忆模块、前后端协同等工程问题。JoyAI-VL-Interaction 开源的是完整技术栈，包括模型权重、交互数据集、训练方案和完整可部署系统，可以帮助开发者更快从模型研究走向真实场景落地。JoyAI-VL-Interaction 支持摄像头、直播流、监控流等多种视频输入，也支持语音输入输出、可视化界面、长期记忆、后台模型接口和 vLLM 部署方案。ASR、TTS、可视化界面、后台模型、外部工具和业务模块，都可以按需替换。开发者可以接入自己的语音服务、Agent、API、业务系统或前端界面。换句话说，JoyAI-VL-Interaction 不是封闭产品，而是一套开放框架。它既可以用于研究，也可以改造成安防监控、老人小孩看护、直播讲解、电商导购、操作指导、AI 眼镜、无障碍辅助等实时 AI 助手。&lt;/p&gt;

&lt;p&gt;在评测中，JoyAI-VL-Interaction 覆盖监控预警、实时计数、实时翻译、时间感知、直播导览解说等真实流式场景。在这些与视觉触发的主动响应、实时性高度相关的 58 个真人盲评案例中，JoyAI-VL-Interaction 对比豆包视频通话助手，总体胜率 77.6%；对比 Gemini 视频通话助手，总体胜率 87.9%。尤其在监控预警场景中，对两个基线均取得 100% 胜率。这源于交互模型相较传统 “一问一答” 的回合制模型的天然优势：自主交互性长在模型内部，而非依赖外部触发。从生成到交互，AI 走向物理世界今年以来，京东在模型基建方面取得多项重要进展。&lt;/p&gt;

&lt;p&gt;3 月，京东开源基础大模型 JoyAI-LLM Flash 的 Instruct 版本，打破了大模型参数内卷的困局；4 月，京东开源图像模型 JoyAI-Image-Edit，空间理解与编辑能力达到世界一流水平。6 月 3 日，京东又开源长视频生成模型 JoyAI-Echo，推动长视频生成 “所想即所得” 时代到来，标志着京东在上述领域进入全球第一梯队。从 “一问一答” 到 “边看边记边回应”，从离线视频理解到实时流式交互，从屏幕里的 AI 到物理世界里的 AI——此次 JoyAI-VL-Interaction 的全栈开源，是京东把 AI 从数字世界推向物理世界的又一步。深耕零售、物流、健康、工业等实体产业二十余年，京东拥有全球领先的物理世界运营网络，覆盖仓储、配送、门店、直播、客服、售后海量真实场景，每天都在发生人、货、场的实时互动。对 AI 而言，这些不是抽象数据，而是进入物理世界的天然训练场与应用场，为京东打造 “全球最大物理世界运营中心” 提供坚实的保障。未来京东将持续加大研发投入，开放技术能力，推动 AI 从千行百业走进千家万户。JoyAI-VL-Interaction 获得了 vLLM-Omni 的 day-0 支持🎉🎉🎉已经原生合入 vLLM-Omni 主线大家可以在 vLLM-Omni 上一键拉起服务体验也可以在我们的仓库下一键启动～&lt;/p&gt;

&lt;p&gt;代码🔗&lt;a href="https://github.com/jd-opensource/JoyAI-VL-Interactionhttps://huggingface.co/jdopensource/JoyAI-VL-Interaction-Preview%E6%95%B0%E6%8D%AE%E9%9B%86%F0%9F%94%97https://huggingface.co/datasets/jdopensource/JoyAI-VL-Interaction" rel="nofollow" target="_blank"&gt;https://github.com/jd-opensource/JoyAI-VL-Interactionhttps://huggingface.co/jdopensource/JoyAI-VL-Interaction-Preview 数据集&lt;img title=":link:" alt="🔗" src="https://twemoji.ruby-china.com/2/svg/1f517.svg" class="twemoji"&gt;https://huggingface.co/datasets/jdopensource/JoyAI-VL-Interaction&lt;/a&gt;模型🔗&lt;/p&gt;</description>
      <author>JCloud</author>
      <pubDate>Tue, 23 Jun 2026 10:44:50 +0800</pubDate>
      <link>https://testerhome.com/topics/44351</link>
      <guid>https://testerhome.com/topics/44351</guid>
    </item>
    <item>
      <title>正式上线！京东云 AI 智能渗透测试服务</title>
      <description>&lt;p&gt;近日，面向 AI 时代安全测试需求，京东云正式推出 AI 智能渗透测试服务，企业无需长时间排期、无需搭建复杂工具链，也无需完全依赖周期性的人工测试，通过 AI 模拟安全专家思维，即可在授权范围内自动完成目标分析、任务拆解、攻击验证和风险评估，帮助企业提前发现真实安全风险，提升系统安全性。&lt;/p&gt;

&lt;p&gt;当前，京东云 AI 智能渗透测试已正式上线，公测期 30 天内可享 10 次单目标测试任务权益，申请通过的企业用户登录京东云控制台搜索 “AI 智能渗透” 即可开通体验。&lt;br&gt;
&lt;a href="https://www.jdcloud.com/cn/products/security-attack-and-defense-drill-service" rel="nofollow" target="_blank"&gt;https://www.jdcloud.com/cn/products/security-attack-and-defense-drill-service&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One For All 一个平台服务不同安全角色&lt;br&gt;
企业安全建设投入越来越多，但很多企业最关心的问题其实很直接：如果今天真的遭遇攻击，系统能不能守得住？传统漏洞扫描能够发现风险，却往往停留在问题清单；人工渗透测试更接近真实攻击，但又高度依赖专家经验，成本高、周期长、覆盖有限。&lt;br&gt;
AI 智能渗透测试秉承 “One For All” 理念，希望让智能渗透测试不再只是少数安全专家的能力。一个平台，覆盖从新手到专家、从业务到管理的多元安全需求。&lt;br&gt;
对于业务团队，只需通过自然语言即可发起测试任务；对于安全运营人员，AI 可承担资产探测、漏洞验证、证据整理等重复工作；对于渗透专家和红队人员，AI 负责基础链路推进，让专家聚焦复杂场景与关键判断；对于管理者，则能够通过可视化攻击路径和风险报告快速掌握安全状况。&lt;br&gt;
四大核心能力让 AI 参与真实渗透全链条&lt;br&gt;
现实中的安全事件，很少由单一漏洞造成。一个暴露接口、一处敏感信息泄露、一次权限配置不当，往往会被攻击者串联利用，最终形成真正的安全威胁。京东云 AI 智能渗透测试可以输出一条完整的攻击路径、风险影响分析以及修复优先级建议，帮助企业真正看清风险所在。&lt;br&gt;
一句话发起任务：用户只需描述测试目标、授权范围和关注重点，京东云 AI 智能渗透测试即可自动理解需求，快速生成测试任务。AI 自动编排流程：京东云 AI 智能渗透测试自动完成任务拆解与能力调度，执行资产探测、接口验证、漏洞分析等测试工作，无需复杂工具配置。攻击路径持续推进：京东云 AI 智能渗透测试能够根据测试结果动态判断下一步行动，持续验证风险关联性，让测试过程更接近真实攻击行为。一键生成证据报告：测试完成后，自动沉淀攻击过程、风险证据、影响分析及修复建议，帮助安全团队高效推动整改和复盘。&lt;br&gt;
安全建设的价值，不仅在于发现漏洞，更在于验证风险是否真的会影响业务。京东云 AI 智能渗透测试让企业无需长时间排期、无需搭建复杂工具链，也无需完全依赖周期性的人工测试，即可在授权范围内随时发起一次智能渗透测试。在真实攻击发生之前，先让 AI 帮你验证一遍。&lt;br&gt;
面向全新的 AI 时代，京东云基于全栈大模型安全产品，包括大模型安全网关、AI 安全运营中心、智能渗透测试等产品和服务，为客户提供大模型训练与使用全链路安全、智能告警降噪、智能体安全风险验证、AI 渗透等全方位能力，实现模型应用端到端的安全防护，推动大模型在更多行业全面走向深度应用。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;End -&lt;/li&gt;
&lt;/ul&gt;</description>
      <author>JCloud</author>
      <pubDate>Tue, 23 Jun 2026 10:34:34 +0800</pubDate>
      <link>https://testerhome.com/topics/44350</link>
      <guid>https://testerhome.com/topics/44350</guid>
    </item>
    <item>
      <title>硬核产品塑造超强影响力 | 践行使命，为客户提供有价值的产品及服务</title>
      <description>&lt;p&gt;近日，小编注意到终端评测领域知名博主 “小白测评” 发布视频更新，题为 “为什么你的手机不流畅……”。&lt;br&gt;
&lt;img src="/uploads/photo/2026/df47ea7b-b5af-4ed6-a82c-9a6c2c83409d.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;其中以东舟终端性能测试系统作为底层测试能力支撑，对几大厂商的旗舰机进行了专业的流畅性测试，一时引起网友热议。&lt;br&gt;
&lt;img src="/uploads/photo/2026/48619b88-2304-4522-9a6f-aa4104d6def8.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;评论区有发表流畅性专业观点的，也有吐槽某某品牌厂商发热、掉帧、续航、卡顿等等问题的。见识到 “意见领袖” 营造的 “小天地” 影响力之余，也感慨东舟性能测试产品线的硬核影响力。&lt;br&gt;
&lt;img src="/uploads/photo/2026/e90fad0a-f858-489e-8da5-4aa92b015a9b.png!large" title="" alt=""&gt;&lt;br&gt;
&lt;img src="/uploads/photo/2026/9faab8ca-027e-4478-b453-7bf44d4c6da6.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;2023 年 3 月，由东舟作为主要起草单位的国家标准《移动终端人 - 系统交互工效学 触控界面感知流畅性》正式发布，为智能终端与人交互的流畅性提供了强有力的衡量标尺。从国家标准到行业标准、团体标准，从地位权威的行业实验室到广受网友喜爱的评测博主，从产品线丰富的终端厂商到技术密集的芯片厂商，从海内外的电信运营商到广为人知的 APP 应用公司，东舟凭借超硬核的产品力，朋友圈越来越大。&lt;br&gt;
&lt;img src="/uploads/photo/2026/458bfc6c-48e3-48dd-826a-f4910c829c40.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;此次为行业评测博主赋能专业测试能力，是东舟获得客户肯定的一个缩影，为客户提供有价值的测试产品及服务是我们赖以生存的生命线。未来每一位东舟人将继续秉持专业严谨的工匠精神，竭尽所能助力行业同仁提升测试能力、提高产品品质。&lt;/p&gt;

&lt;p&gt;如果你对手机的评测维度、测试标准感兴趣，强烈推荐这篇干货视频 ：「小白测评」2025 我们如何测评一部手机？数据库 5.0 更新解析！ 内容很硬核，看完会有收获！&lt;/p&gt;</description>
      <author>Dongzhou</author>
      <pubDate>Tue, 23 Jun 2026 09:17:14 +0800</pubDate>
      <link>https://testerhome.com/topics/44349</link>
      <guid>https://testerhome.com/topics/44349</guid>
    </item>
    <item>
      <title>claude-mem：让 Agent 上下文更可控</title>
      <description>&lt;p&gt;项目是长期演进的，AI 会话却是短期的。一个复杂项目里的架构决策、历史 Bug、失败方案、测试策略、兼容性约束，很难全部写进当前会话。没有长期记忆，Claude Code 每次新开会话，都像重新入职：它可能很聪明，但它不知道这个项目以前踩过哪些坑，也不知道哪些路已经被证明走不通。&lt;/p&gt;

&lt;p&gt;claude-mem 解决的正是这个问题。&lt;/p&gt;

&lt;p&gt;但如果只把 claude-mem 理解成给 Claude Code 加一个记忆插件，其实还是低估了它。&lt;/p&gt;

&lt;p&gt;更准确地说，claude-mem 背后真正重要的不是 Memory，而是 Context Engineering，也就是上下文工程。记忆只是手段，上下文质量才是目标。&lt;/p&gt;
&lt;h2 id="Prompt Engineering 解决不了长期任务"&gt;Prompt Engineering 解决不了长期任务&lt;/h2&gt;
&lt;p&gt;过去大家谈 AI 使用技巧，最常说的是 Prompt Engineering：怎么写提示词，怎么设定角色，怎么拆解任务，怎么让模型输出更稳定，怎么让模型按照指定格式回答。&lt;/p&gt;

&lt;p&gt;这些当然有用。&lt;/p&gt;

&lt;p&gt;在单轮问答场景里，Prompt Engineering 很重要。比如让模型写一段文案、解释一个概念、生成一段 SQL、总结一篇文章。任务边界清楚，输入基本完整，模型只需要根据当前指令完成输出。&lt;/p&gt;

&lt;p&gt;但 Claude Code 这类 AI Agent 面对的不是单轮任务。它要读文件、查调用链、运行命令、分析报错、修改代码、补测试，还要根据工具结果继续调整方向。任务越长，模型越不是在回答一个问题，而是在持续做工程判断。&lt;/p&gt;

&lt;p&gt;这时候，决定任务质量的就不只是提示词怎么写，而是模型在每一步推理时，到底看到了什么信息。&lt;/p&gt;

&lt;p&gt;Prompt Engineering 关注的是怎么说。&lt;/p&gt;

&lt;p&gt;Context Engineering 关注的是给什么信息。&lt;/p&gt;

&lt;p&gt;前者是表达问题，后者是信息系统问题。&lt;/p&gt;

&lt;p&gt;在 Agent 场景里，Prompt 只是 Context 的一部分。真正的上下文还包括项目规则、历史会话、工具描述、代码文件、运行结果、错误日志、外部文档、长期记忆、当前任务状态。模型不是凭一句提示词工作，而是基于这些信息组合来判断下一步该读什么、改哪里、怎么验证。&lt;/p&gt;

&lt;p&gt;所以，Claude Code 表现不好时，问题未必是 Prompt 写得不够好。更常见的问题是：上下文不完整、不相关、过期，或者噪声太多。&lt;/p&gt;

&lt;p&gt;比如你让 Claude Code 修一个登录 Bug，但不给它认证流程、session 逻辑、最近改动、历史缺陷和测试约束。哪怕 Prompt 写得很漂亮，它也只能从当前文件里猜。&lt;/p&gt;

&lt;p&gt;反过来，如果上下文足够精准，即使 Prompt 很简单，模型也可能做得很好。&lt;/p&gt;

&lt;p&gt;这就是 Agent 时代和普通聊天时代的关键区别：Prompt 决定模型怎么听指令，Context 决定模型基于什么做判断。&lt;/p&gt;
&lt;h2 id="上下文不是越多越好"&gt;上下文不是越多越好&lt;/h2&gt;
&lt;p&gt;很多人会自然认为，既然上下文很重要，那就把信息尽量多地塞给模型。项目背景塞进去，历史 Bug 塞进去，完整日志塞进去，接口文档塞进去，聊天记录也塞进去。&lt;/p&gt;

&lt;p&gt;看起来很合理，但实际很危险。&lt;/p&gt;

&lt;p&gt;因为上下文不是越长越好。上下文窗口再大，模型的注意力仍然是有限的。信息越多，噪声越多，关键内容越容易被稀释。&lt;/p&gt;

&lt;p&gt;这就是所谓的 Context Rot，可以理解为上下文腐烂。&lt;/p&gt;

&lt;p&gt;所谓上下文腐烂，不是信息消失了，而是信息虽然还在，但模型已经不再稳定使用它。&lt;/p&gt;

&lt;p&gt;比如你在会话前面明确告诉 Claude：订单取消接口必须保证幂等，不能重复释放库存。然后你继续让它读取大量代码、日志、工具输出、历史讨论。等到真正改代码时，这条约束可能仍然在上下文窗口里，但模型未必还会牢牢记住它。&lt;/p&gt;

&lt;p&gt;它可能补了正常取消流程，也可能补了异常参数测试，但就是漏掉重复请求和并发取消。&lt;/p&gt;

&lt;p&gt;这不是模型完全不知道那句话，而是那句话在大量噪声中失去了足够权重。&lt;/p&gt;

&lt;p&gt;这对 AI Agent 特别致命。&lt;/p&gt;

&lt;p&gt;因为 Agent 的任务通常很长。它会不断读取文件，不断运行工具，不断追加输出。如果不控制上下文质量，当前窗口很快会被低价值信息填满。最后模型看起来知道很多，但真正关键的约束反而被冲淡。&lt;/p&gt;

&lt;p&gt;所以，上下文工程的目标不是最大化输入量，而是最大化信噪比。&lt;/p&gt;

&lt;p&gt;不是把所有东西都给模型，而是把当前任务最需要的信息给模型。&lt;/p&gt;

&lt;p&gt;不是上下文越长越好，而是上下文越准越好。&lt;/p&gt;

&lt;p&gt;claude-mem 的价值就在这里。它不是把历史聊天记录全部塞回 Claude Code，而是把过去会话中的高价值经验压缩成可检索的记忆，并在相关任务中按需注入。&lt;/p&gt;

&lt;p&gt;这比简单扩大上下文窗口更重要。&lt;/p&gt;
&lt;h2 id="最小高信号上下文"&gt;最小高信号上下文&lt;/h2&gt;
&lt;p&gt;理解 claude-mem，需要抓住一个核心原则：找到最小但高信号的上下文，让模型完成任务的概率最大化。&lt;/p&gt;

&lt;p&gt;这里有两个关键词。&lt;/p&gt;

&lt;p&gt;第一个是最小。&lt;/p&gt;

&lt;p&gt;最小不是越短越好，而是不要塞无关信息。一个 Bug 修复任务不需要看到整个项目历史，一个接口测试任务也不需要看到所有架构讨论。上下文应该服务于当前任务，而不是展示系统知道多少东西。&lt;/p&gt;

&lt;p&gt;第二个是高信号。&lt;/p&gt;

&lt;p&gt;高信号意味着这段信息对模型的判断有直接帮助。它能减少猜测，缩小搜索空间，提醒历史风险，约束错误方向，或者提供关键决策依据。&lt;/p&gt;

&lt;p&gt;比如下面这类信息，信号就很高：&lt;/p&gt;

&lt;p&gt;订单取消接口历史上出现过库存重复释放问题。根因是取消操作缺少幂等保护。后续修改该逻辑时，必须覆盖重复请求、并发请求、已取消订单再次取消。&lt;/p&gt;

&lt;p&gt;它短，但价值很高。它告诉 Claude Code：这个模块的核心风险不是能不能取消订单，而是重复取消时会不会产生副作用。&lt;/p&gt;

&lt;p&gt;再看另一种记录：&lt;/p&gt;

&lt;p&gt;今天查看了 &lt;code&gt;order.ts&lt;/code&gt;、&lt;code&gt;inventory.ts&lt;/code&gt;、&lt;code&gt;cancel.test.ts&lt;/code&gt;。运行 &lt;code&gt;pnpm test&lt;/code&gt; 失败。后来修改了测试。&lt;/p&gt;

&lt;p&gt;这类内容虽然也是历史，但信号很低。它没有告诉模型真正的风险、根因、决策和后续约束。未来即使检索出来，也很难帮助 Claude Code 做出更好的判断。&lt;/p&gt;

&lt;p&gt;claude-mem 应该沉淀前一种信息，而不是后一种信息。&lt;/p&gt;

&lt;p&gt;这也是为什么说 claude-mem 不是历史存档，而是经验压缩。&lt;/p&gt;

&lt;p&gt;它应该把复杂开发过程压缩成未来可用的判断依据，而不是把所有过程原样保存下来。&lt;/p&gt;

&lt;p&gt;可以把 claude-mem 的理想工作方式理解成：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;过去的工具调用、排查过程、修复记录&lt;/li&gt;
&lt;li&gt;压缩成高信号记忆&lt;/li&gt;
&lt;li&gt;当前任务相关时被检索出来&lt;/li&gt;
&lt;li&gt;成为 Claude Code 的决策上下文&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;这就是上下文工程的核心路径。&lt;/p&gt;
&lt;h2 id="记忆的价值在于按需注入"&gt;记忆的价值在于按需注入&lt;/h2&gt;
&lt;p&gt;很多人使用记忆系统时，会犯一个错误：既然记了，就想让模型每次都看到。&lt;/p&gt;

&lt;p&gt;这会重新制造上下文污染。&lt;/p&gt;

&lt;p&gt;长期记忆不应该默认全部进入当前会话。因为绝大多数历史记忆对当前任务并不相关。即使它们都是真的，也会变成噪声。&lt;/p&gt;

&lt;p&gt;比如你现在让 Claude Code 修复登录超时问题，它不需要知道优惠券模块的历史缺陷，也不需要知道订单取消接口的幂等问题。即使这些记忆都很重要，也不该在这个任务里出现。&lt;/p&gt;

&lt;p&gt;真正好的记忆系统，应该是按需召回。&lt;/p&gt;

&lt;p&gt;当前任务和认证模块相关，就检索认证、session、token、cookie、登录超时相关记忆；当前任务和支付回调相关，就检索支付、回调、幂等、重复通知相关记忆；当前任务和测试补充相关，就检索历史缺陷、回归重点、Mock 约束相关记忆。&lt;/p&gt;

&lt;p&gt;这就是 claude-mem 和上下文工程结合的关键点：记忆不是为了常驻上下文，而是为了在合适时机补充上下文。&lt;/p&gt;

&lt;p&gt;这和人类工程师的工作方式很像。&lt;/p&gt;

&lt;p&gt;一个资深工程师不会在处理登录 Bug 时，把整个公司所有历史事故都回忆一遍。他只会想起和登录、认证、session 相关的经验。需要时再去查文档、看日志、翻记录。&lt;/p&gt;

&lt;p&gt;AI Agent 也应该如此。&lt;/p&gt;

&lt;p&gt;所以 claude-mem 最好的使用方式，不是让 Claude Code 背下所有历史，而是让它在任务开始时先检索相关记忆。&lt;/p&gt;

&lt;p&gt;比如：开始修改支付回调前，先检索历史记忆里和支付回调、幂等、重复通知相关的内容。&lt;/p&gt;

&lt;p&gt;或者：补充订单取消测试前，先查看记忆里是否有这个模块的历史缺陷和回归重点。&lt;/p&gt;

&lt;p&gt;这种用法能让 Claude Code 先获得历史线索，再决定读哪些文件、跑哪些测试、重点关注哪些风险。&lt;/p&gt;

&lt;p&gt;记忆在这里不是答案，而是导航。&lt;/p&gt;

&lt;p&gt;它帮助模型少走弯路，但最终仍然需要结合当前代码和测试结果验证。&lt;/p&gt;
&lt;h2 id="从 Memory 到 Context Engineering"&gt;从 Memory 到 Context Engineering&lt;/h2&gt;
&lt;p&gt;到这里可以看出，claude-mem 的核心并不是让 Claude 记住更多。&lt;/p&gt;

&lt;p&gt;它真正解决的是上下文工程问题。&lt;/p&gt;

&lt;p&gt;没有 claude-mem 时，Claude Code 的上下文主要来自当前会话、当前文件、当前工具调用和 &lt;code&gt;CLAUDE.md&lt;/code&gt;。这些信息足够完成短任务，但很难承载项目长期经验。&lt;/p&gt;

&lt;p&gt;有了 claude-mem 后，历史排查、架构决策、失败方案、测试策略、风险模式，都有机会被压缩成可检索记忆，在未来任务中重新进入上下文。&lt;/p&gt;

&lt;p&gt;这会让 Claude Code 从只理解当前任务逐步变成理解项目历史。&lt;/p&gt;

&lt;p&gt;但前提是，我们不能把 claude-mem 当成普通存档工具使用。它不是越记越好，也不是每次注入越多越好。真正关键的是三件事：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;记忆必须高信号。它要记录决策、根因、约束、风险，而不是流水账&lt;/li&gt;
&lt;li&gt;记忆必须按需检索。只有和当前任务相关的历史，才应该进入上下文&lt;/li&gt;
&lt;li&gt;记忆必须接受验证。历史经验只能提供方向，不能替代当前代码、当前测试和当前事实&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;所以，claude-mem 的最佳实践可以总结成一句话：&lt;/p&gt;

&lt;p&gt;用记忆保存经验，用检索控制相关性，用验证保证正确性。&lt;/p&gt;

&lt;p&gt;这就是它和 Context Engineering 的关系。Prompt Engineering 让你更会指挥模型。Context Engineering 让模型在更正确的信息环境里工作。claude-mem 则是把项目历史变成可复用上下文的一种方式。&lt;/p&gt;

&lt;p&gt;下一篇，我们继续聊一个更实操的问题：claude-mem 到底应该记什么，不应该记什么？&lt;/p&gt;
&lt;h5 id="FunTester 名片｜万粉千文，百无一用"&gt;FunTester 名片｜万粉千文，百无一用&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://mp.weixin.qq.com/s/o1dGKThvC64LvJDnKLybOw" rel="nofollow" target="_blank" title=""&gt;软件测试的道与术&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mp.weixin.qq.com/s/MFgZ3Btx0TKOd5sFGFDxcg" rel="nofollow" target="_blank" title=""&gt;测试开发成长史&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mp.weixin.qq.com/s/wyyn32HrwmH5uV8F0d2HDQ" rel="nofollow" target="_blank" title=""&gt;AI ，测试有点东西&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mp.weixin.qq.com/s/5ssJH4yOE3H1Vk4ziJ0Bdg" rel="nofollow" target="_blank" title=""&gt;《从 Java 开始性能测试》连载全集&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mp.weixin.qq.com/s/3ilD1fnJFo9Gt8puO7zFiQ" rel="nofollow" target="_blank" title=""&gt;性能测试修炼之道&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mp.weixin.qq.com/s/t2k6IITzaFicaEi77iZ5tA" rel="nofollow" target="_blank" title=""&gt;故障测试与混沌工程实战合集&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mp.weixin.qq.com/s/CSE2UmQNFrHaEnyyM4U41w" rel="nofollow" target="_blank" title=""&gt;我的语言迁徙：Java &amp;amp; Groovy &amp;amp; Go&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mp.weixin.qq.com/s/0F41EuPsziWwr25o7Cj3-A" rel="nofollow" target="_blank" title=""&gt;懂点 Web 前端&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mp.weixin.qq.com/s/LKEfCkx_iEr8JrA1lT3HnQ" rel="nofollow" target="_blank" title=""&gt;视频教程全收录&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <author>Fhaohaizi</author>
      <pubDate>Tue, 23 Jun 2026 08:31:05 +0800</pubDate>
      <link>https://testerhome.com/topics/44348</link>
      <guid>https://testerhome.com/topics/44348</guid>
    </item>
    <item>
      <title>基于 unittest 的 HTML 测试报告生成器，支持失败自动截图、用例重试覆盖、详情折叠/筛选</title>
      <description> &lt;pre class="highlight html"&gt;&lt;code&gt;# -*- coding: utf-8 -*-
"""
HTMLTestRunnerNew
=================
基于 unittest 的 HTML 测试报告生成器，支持失败自动截图、用例重试覆盖、详情折叠/筛选。

────────────────────────────────────────────────────────────────────────────
基础用法（保持向后兼容，无需任何额外参数）
────────────────────────────────────────────────────────────────────────────
    from HTMLTestRunnerNew import HTMLTestRunner

    with open('Report/Reports/Report.html', 'w', encoding='utf-8') as f:
        runner = HTMLTestRunner(
            stream=f,
            title='自动化测试报告',
            description='SIT 环境',
            verbosity=1,
        )
        runner.run(suite)

────────────────────────────────────────────────────────────────────────────
大规模执行优化（≥100 个失败用例时强烈建议）
────────────────────────────────────────────────────────────────────────────
默认 embed_screenshots=True 把每张截图 base64 内嵌进 HTML，单文件可独立分享，
但 100 个失败用例 × 5 张截图 ≈ 200MB，浏览器可能打不开。

切到外链模式 (HTML 仅几十 KB，截图按需懒加载)：

    runner = HTMLTestRunner(
        stream=f,
        title='...',
        embed_screenshots=False,                          # 不内嵌
        report_dir=os.path.dirname(reportsPath),          # HTML 所在目录的绝对路径
    )

外链模式下：
  - HTML 里的 &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; 会自动算成相对路径 (例如 "../Screenshots/xxx.png")
  - 分享报告时必须把整个 Report/ 目录一起给，单独发 HTML 看不到图
  - 浏览器会自动 lazy-load，滚动到的截图才解码

────────────────────────────────────────────────────────────────────────────
自动失败截图机制（无侵入测试代码）
────────────────────────────────────────────────────────────────────────────
当用例失败/错误时，会自动尝试调用 test 实例上的 webdriver 截图，
覆盖测试代码本身没主动截图（只 raise AssertionError）的场景。

工作前提（约定）：
  1. 测试用例 setUp 里赋值 driver 属性，例如：
        def setUp(self):
            self.driver = TestSuite.admin_driver

  2. driver 对象有以下方法之一：
        - saveScreenshot(path)       (项目自定义封装，优先调用)
        - save_screenshot(path)      (标准 selenium API，次选)

  3. 截图会保存到：
        Report/Screenshots/Failure_&lt;span class="nt"&gt;&amp;lt;test_name&amp;gt;&lt;/span&gt;_&lt;span class="nt"&gt;&amp;lt;timestamp_ms&amp;gt;&lt;/span&gt;.png

  4. 截图日志格式：
        [失败时刻] 屏幕截图成功, 存放位置 Report/Screenshots/Failure_xxx.png

  5. 报告里这张截图会用红色边框标记 "失败时刻"，置顶显示。

任何环节失败（无 driver / 浏览器已关闭 / 写文件失败）都会被静默吞掉，
不影响测试本身的执行。

────────────────────────────────────────────────────────────────────────────
其他特性
────────────────────────────────────────────────────────────────────────────
  - 用例重试覆盖：与 FrameWorkTestSuite.run() 的 failedReexec 配合，
    同一 test.id() 重复回调时覆盖之前的记录，报告里始终只显示最终结果。
  - 4 种筛选 tab：概览（仅 class 汇总）/ 仅失败 / 仅错误 / 全部。
  - 运行输出 / 错误堆栈一键复制按钮。
  - 截图按时间倒序，"失败时刻" 截图特别标记。
  - 滚动隔离：日志框内滚动不会带动整页滚动 (overscroll-behavior: contain)。
  - 内嵌运行环境信息：Python 版本、平台、主机名。
"""
import base64
import datetime
import io
import json
import logging
import os
import platform
import re
import sys
import time
import unittest
from xml.sax import saxutils

try:
    from logger import logger as Text
except ImportError:
    Text = None


class OutputRedirector(object):
    def __init__(self, fp):
        self.fp = fp

    def write(self, s):
        self.fp.write(s)

    def writelines(self, lines):
        self.fp.writelines(lines)

    def flush(self):
        self.fp.flush()


stdout_redirector = OutputRedirector(sys.stdout)
stderr_redirector = OutputRedirector(sys.stderr)


class Template_mixin(object):
    STATUS = {
        0: 'pass',
        1: 'fail',
        2: 'error',
        3: 'skip',
    }
    DEFAULT_TITLE = '自动化测试报告'
    DEFAULT_DESCRIPTION = ''

    HTML_TMPL = r"""&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"zh-CN"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"UTF-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1.0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;%(title)s&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://cdn.jsdelivr.net/npm/chart.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;box-sizing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;border-box&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;"Microsoft Yahei"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;"Helvetica Neue"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Arial&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;sans-serif&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#f0f2f5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#333&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;line-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.6&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.report-header&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#1e293b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;35px&lt;/span&gt; &lt;span class="m"&gt;32px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;text-align&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;box-shadow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;2px&lt;/span&gt; &lt;span class="m"&gt;8px&lt;/span&gt; &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;0.15&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.report-header&lt;/span&gt; &lt;span class="nt"&gt;h1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;margin-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;font-weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;700&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;letter-spacing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.report-header&lt;/span&gt; &lt;span class="nc"&gt;.sub&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;0.7&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.95rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.container-main&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;max-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1600px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;32px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.card-section&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;24px&lt;/span&gt; &lt;span class="m"&gt;32px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;margin-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;box-shadow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="m"&gt;4px&lt;/span&gt; &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.stat-cards&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;flex-wrap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;wrap&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="py"&gt;gap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;16px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;margin-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.stat-card&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;relative&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;min-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;180px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;12px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20px&lt;/span&gt; &lt;span class="m"&gt;22px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;overflow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;hidden&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;transform&lt;/span&gt; &lt;span class="m"&gt;0.2s&lt;/span&gt; &lt;span class="n"&gt;ease&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;box-shadow&lt;/span&gt; &lt;span class="m"&gt;0.2s&lt;/span&gt; &lt;span class="n"&gt;ease&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;align-items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="py"&gt;gap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;16px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.stat-card&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;translateY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;-3px&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="nl"&gt;box-shadow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;8px&lt;/span&gt; &lt;span class="m"&gt;24px&lt;/span&gt; &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;0.15&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.stat-card&lt;/span&gt; &lt;span class="nc"&gt;.sc-icon&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;48px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;48px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;flex-shrink&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;0.18&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;align-items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;justify-content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.4rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.stat-card&lt;/span&gt; &lt;span class="nc"&gt;.sc-body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;min-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.stat-card&lt;/span&gt; &lt;span class="nc"&gt;.sc-label&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.78rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.88&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;margin-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;text-transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;uppercase&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;letter-spacing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.05em&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.stat-card&lt;/span&gt; &lt;span class="nc"&gt;.sc-num&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;font-weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;700&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;line-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.stat-card&lt;/span&gt; &lt;span class="nc"&gt;.sc-pct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.78rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.85&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;margin-top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.stat-card&lt;/span&gt; &lt;span class="nc"&gt;.sc-watermark&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;absolute&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;-12px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;-16px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.08&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;line-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;pointer-events&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.stat-card.total&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;linear-gradient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;135deg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;#334155&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;#475569&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.stat-card.pass&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;linear-gradient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;135deg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;#059669&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;#10b981&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.stat-card.fail&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;linear-gradient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;135deg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;#dc2626&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;#ef4444&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.stat-card.error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;linear-gradient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;135deg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;#d97706&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;#f59e0b&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.stat-card.skip&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;linear-gradient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;135deg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;#6b7280&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;#9ca3af&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.progress-wrap&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;margin-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;16px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.progress&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#e5e7eb&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;overflow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;hidden&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.progress-bar&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="m"&gt;0.8s&lt;/span&gt; &lt;span class="n"&gt;ease&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.meta-grid&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;flex-wrap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;wrap&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="py"&gt;gap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;margin-top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;16px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.meta-item&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;align-items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="py"&gt;gap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#f8fafc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;#e5e7eb&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8px&lt;/span&gt; &lt;span class="m"&gt;14px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.82rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.meta-item&lt;/span&gt; &lt;span class="nt"&gt;i&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#64748b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.85rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;14px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;text-align&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.meta-item&lt;/span&gt; &lt;span class="nc"&gt;.mk&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#94a3b8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;margin-right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.meta-item&lt;/span&gt; &lt;span class="nc"&gt;.mv&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#1e293b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;font-weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;600&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.filter-bar&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="py"&gt;gap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;margin-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;16px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;flex-wrap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;wrap&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;align-items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.filter-btn&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;7px&lt;/span&gt; &lt;span class="m"&gt;18px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="nb"&gt;transparent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;pointer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;font-weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;500&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.9rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;all&lt;/span&gt; &lt;span class="m"&gt;0.2s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#f0f2f5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#555&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.filter-btn&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#e2e6ea&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.filter-btn.active&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;border-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;transparent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.filter-btn.active.all&lt;/span&gt;   &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#334155&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.filter-btn.active.fail&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#dc2626&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.filter-btn.active.error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#d97706&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.filter-btn.active.pass&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#059669&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.filter-btn.active.skip&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#6b7280&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.result-table&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;border-collapse&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;collapse&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.result-table&lt;/span&gt; &lt;span class="nt"&gt;th&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#343a40&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;12px&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;font-weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;600&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.9rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;text-align&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;left&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.result-table&lt;/span&gt; &lt;span class="nt"&gt;th&lt;/span&gt;&lt;span class="nd"&gt;:first-child&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.result-table&lt;/span&gt; &lt;span class="nt"&gt;th&lt;/span&gt;&lt;span class="nd"&gt;:last-child&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.result-table&lt;/span&gt; &lt;span class="nt"&gt;td&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;border-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;#eee&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.9rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;vertical-align&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;top&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.result-table&lt;/span&gt; &lt;span class="nt"&gt;tbody&lt;/span&gt; &lt;span class="nt"&gt;tr&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;background-color&lt;/span&gt; &lt;span class="m"&gt;0.15s&lt;/span&gt; &lt;span class="n"&gt;ease&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.result-table&lt;/span&gt; &lt;span class="nt"&gt;tbody&lt;/span&gt; &lt;span class="nt"&gt;tr&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#f8f9fa&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.result-table&lt;/span&gt; &lt;span class="nt"&gt;tbody&lt;/span&gt; &lt;span class="nt"&gt;tr&lt;/span&gt;&lt;span class="nc"&gt;.row-pass&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;border-left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;6px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;#059669&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#f0fdf4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.result-table&lt;/span&gt; &lt;span class="nt"&gt;tbody&lt;/span&gt; &lt;span class="nt"&gt;tr&lt;/span&gt;&lt;span class="nc"&gt;.row-pass&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#dcfce7&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.result-table&lt;/span&gt; &lt;span class="nt"&gt;tbody&lt;/span&gt; &lt;span class="nt"&gt;tr&lt;/span&gt;&lt;span class="nc"&gt;.row-fail&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;border-left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;6px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;#dc2626&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#fff5f5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.result-table&lt;/span&gt; &lt;span class="nt"&gt;tbody&lt;/span&gt; &lt;span class="nt"&gt;tr&lt;/span&gt;&lt;span class="nc"&gt;.row-fail&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#fee2e2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.result-table&lt;/span&gt; &lt;span class="nt"&gt;tbody&lt;/span&gt; &lt;span class="nt"&gt;tr&lt;/span&gt;&lt;span class="nc"&gt;.row-error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;border-left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;6px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;#d97706&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#fffbeb&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.result-table&lt;/span&gt; &lt;span class="nt"&gt;tbody&lt;/span&gt; &lt;span class="nt"&gt;tr&lt;/span&gt;&lt;span class="nc"&gt;.row-error&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#fef3c7&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.class-row&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#f8f9fa&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;font-weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;600&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;pointer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.class-row&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#e9ecef&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.status-tag&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;inline-block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3px&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;12px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.8rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;font-weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;600&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.status-tag.pass&lt;/span&gt;        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#d1fae5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#065f46&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.status-tag.pass-strong&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#059669&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.status-tag.fail&lt;/span&gt;        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#fee2e2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#991b1b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.status-tag.error&lt;/span&gt;       &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#fef3c7&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#92400e&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.status-tag.skip&lt;/span&gt;        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#f3f4f6&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#4b5563&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.detail-toggle&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;#dee2e6&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3px&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;pointer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.8rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#555&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;all&lt;/span&gt; &lt;span class="m"&gt;0.2s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;inline-flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;align-items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="py"&gt;gap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.detail-toggle&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#e9ecef&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;border-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#adb5bd&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#333&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.detail-toggle&lt;/span&gt; &lt;span class="nt"&gt;i&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;transform&lt;/span&gt; &lt;span class="m"&gt;0.25s&lt;/span&gt; &lt;span class="n"&gt;ease&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;inline-block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.7rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.detail-toggle.expanded&lt;/span&gt; &lt;span class="nt"&gt;i&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rotate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;180deg&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.inline-detail-row&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;all&lt;/span&gt; &lt;span class="m"&gt;0.2s&lt;/span&gt; &lt;span class="n"&gt;ease&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.inline-detail-row&lt;/span&gt; &lt;span class="nt"&gt;td&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="cp"&gt;!important&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;border-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;#dee2e6&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#fafbfc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.inline-detail-container&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;16px&lt;/span&gt; &lt;span class="m"&gt;22px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;border-top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;dashed&lt;/span&gt; &lt;span class="m"&gt;#ddd&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;border-left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;#667eea&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.inline-tab-bar&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="py"&gt;gap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;margin-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;12px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;border-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;#e9ecef&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.inline-tab-btn&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#666&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8px&lt;/span&gt; &lt;span class="m"&gt;16px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;pointer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.85rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;border-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="nb"&gt;transparent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;margin-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;-2px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;all&lt;/span&gt; &lt;span class="m"&gt;0.2s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.inline-tab-btn&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#333&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.inline-tab-btn.active&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#0f3460&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;border-bottom-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#0f3460&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;font-weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;600&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.inline-tab-content&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;animation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;fadeIn&lt;/span&gt; &lt;span class="m"&gt;0.2s&lt;/span&gt; &lt;span class="n"&gt;ease&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.inline-tab-content.active&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;@keyframes&lt;/span&gt; &lt;span class="n"&gt;fadeIn&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nt"&gt;from&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;translateY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;-5px&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;to&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;translateY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.inline-pre&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;white-space&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;pre-wrap&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;word-break&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;break-all&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Consolas&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Monaco&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;"Courier New"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;monospace&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;max-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;680px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;min-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;155px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;overflow-y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.9rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#1e1e1e&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#c75050&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;16px&lt;/span&gt; &lt;span class="m"&gt;20px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;6px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;line-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.7&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;#333&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="py"&gt;overscroll-behavior&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;contain&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.inline-pre&lt;/span&gt;&lt;span class="nd"&gt;::-webkit-scrollbar&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;6px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.inline-pre&lt;/span&gt;&lt;span class="nd"&gt;::-webkit-scrollbar-thumb&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#555&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.inline-pre&lt;/span&gt;&lt;span class="nd"&gt;::-webkit-scrollbar-track&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#2a2a2a&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.pre-wrap&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;relative&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.copy-btn&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;absolute&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;0.08&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#ccc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;0.15&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4px&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.78rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;pointer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;all&lt;/span&gt; &lt;span class="m"&gt;0.15s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;z-index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;inline-flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;align-items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="py"&gt;gap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;inherit&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.copy-btn&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;0.16&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#fff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.copy-btn.copied&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#16a34a&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#fff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;border-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#16a34a&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.copy-btn&lt;/span&gt; &lt;span class="nt"&gt;i&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.75rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.copy-toast&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;fixed&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;90px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;30px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#16a34a&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt; &lt;span class="m"&gt;18px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;6px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.85rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;box-shadow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;4px&lt;/span&gt; &lt;span class="m"&gt;12px&lt;/span&gt; &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;translateY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;20px&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="nl"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;all&lt;/span&gt; &lt;span class="m"&gt;0.25s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;z-index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;pointer-events&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.copy-toast.show&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;translateY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.screenshot-grid&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;flex-wrap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;wrap&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="py"&gt;gap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;15px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.screenshot-item&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;relative&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;#ddd&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;6px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;overflow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;hidden&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#fff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;box-shadow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;2px&lt;/span&gt; &lt;span class="m"&gt;8px&lt;/span&gt; &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;0.08&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nl"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;transform&lt;/span&gt; &lt;span class="m"&gt;0.2s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.screenshot-item.failure-shot&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;#dc2626&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;box-shadow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;2px&lt;/span&gt; &lt;span class="m"&gt;12px&lt;/span&gt; &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;220&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;38&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;38&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;0.25&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.screenshot-item&lt;/span&gt; &lt;span class="nc"&gt;.shot-badge&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;absolute&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;6px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;6px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;z-index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;0.6&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.7rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2px&lt;/span&gt; &lt;span class="m"&gt;6px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.screenshot-item.failure-shot&lt;/span&gt; &lt;span class="nc"&gt;.shot-badge&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#dc2626&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;font-weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;600&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.screenshot-item&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;translateY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;-2px&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="nl"&gt;box-shadow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;4px&lt;/span&gt; &lt;span class="m"&gt;12px&lt;/span&gt; &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;0.15&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.screenshot-thumb&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;220px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;160px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;object-fit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;cover&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;zoom-in&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.screenshot-name&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.75rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#666&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;6px&lt;/span&gt; &lt;span class="m"&gt;8px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#f8f9fa&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;border-top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;#eee&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;white-space&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;nowrap&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;overflow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;hidden&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;text-overflow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ellipsis&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.lightbox-overlay&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;fixed&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;0.92&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="nl"&gt;z-index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;9999&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;justify-content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;align-items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;zoom-out&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.lightbox-overlay.active&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.lightbox-overlay&lt;/span&gt; &lt;span class="nt"&gt;img&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;max-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;95%&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;max-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;95vh&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;object-fit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;contain&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.charts-row&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="py"&gt;gap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;24px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;flex-wrap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;wrap&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;align-items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;stretch&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.chart-panel&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#f8fafc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;#e5e7eb&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20px&lt;/span&gt; &lt;span class="m"&gt;24px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;flex-direction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;column&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.chart-panel.donut&lt;/span&gt;    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;min-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;240px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.chart-panel.bars&lt;/span&gt;     &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;min-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;240px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.chart-panel.modules&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;min-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;320px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.chart-panel-title&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.78rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;font-weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;600&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#64748b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;text-transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;uppercase&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;letter-spacing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.06em&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;margin-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;16px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.chart-box&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;min-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;240px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;relative&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.donut-wrap&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;relative&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;align-items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;justify-content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.donut-center&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;absolute&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;text-align&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;pointer-events&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.donut-center&lt;/span&gt; &lt;span class="nc"&gt;.dc-num&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;font-weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;700&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#1e293b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;line-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.donut-center&lt;/span&gt; &lt;span class="nc"&gt;.dc-lbl&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.72rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#94a3b8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;margin-top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.stat-bars&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;flex-direction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;column&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="py"&gt;gap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;12px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;justify-content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.stat-bar-item&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;align-items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="py"&gt;gap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.stat-bar-item&lt;/span&gt; &lt;span class="nc"&gt;.sb-label&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;36px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.78rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#64748b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;font-weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;500&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.stat-bar-item&lt;/span&gt; &lt;span class="nc"&gt;.sb-label.pass&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#059669&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.stat-bar-item&lt;/span&gt; &lt;span class="nc"&gt;.sb-label.fail&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#dc2626&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.stat-bar-item&lt;/span&gt; &lt;span class="nc"&gt;.sb-label.error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#d97706&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.stat-bar-item&lt;/span&gt; &lt;span class="nc"&gt;.sb-label.skip&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#9ca3af&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.stat-bar-item&lt;/span&gt; &lt;span class="nc"&gt;.sb-track&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#e5e7eb&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;overflow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;hidden&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.stat-bar-item&lt;/span&gt; &lt;span class="nc"&gt;.sb-fill&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="m"&gt;0.8s&lt;/span&gt; &lt;span class="n"&gt;ease&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.stat-bar-item&lt;/span&gt; &lt;span class="nc"&gt;.sb-fill.pass&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#059669&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.stat-bar-item&lt;/span&gt; &lt;span class="nc"&gt;.sb-fill.fail&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#dc2626&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.stat-bar-item&lt;/span&gt; &lt;span class="nc"&gt;.sb-fill.error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#d97706&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.stat-bar-item&lt;/span&gt; &lt;span class="nc"&gt;.sb-fill.skip&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#9ca3af&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.stat-bar-item&lt;/span&gt; &lt;span class="nc"&gt;.sb-num&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;28px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;text-align&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;right&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.82rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;font-weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;700&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#1e293b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.report-footer&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#1e293b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;0.6&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;16px&lt;/span&gt; &lt;span class="m"&gt;32px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;text-align&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.85rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.back-top&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;fixed&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;30px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;30px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;42px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;42px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;50%&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#0f3460&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;align-items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;justify-content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;pointer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;box-shadow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;4px&lt;/span&gt; &lt;span class="m"&gt;12px&lt;/span&gt; &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;0.3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;visibility&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;hidden&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;all&lt;/span&gt; &lt;span class="m"&gt;0.3s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.back-top.show&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;visibility&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;visible&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.back-top&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#16213e&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;translateY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;-3px&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;768px&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="nc"&gt;.container-main&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="nc"&gt;.stat-cards&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;flex-direction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;column&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="nc"&gt;.filter-bar&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;justify-content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="nc"&gt;.result-table&lt;/span&gt; &lt;span class="nt"&gt;th&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;.result-table&lt;/span&gt; &lt;span class="nt"&gt;td&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8px&lt;/span&gt; &lt;span class="m"&gt;6px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.8rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="nc"&gt;.screenshot-thumb&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;160px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;120px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="n"&gt;print&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nc"&gt;.back-top&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;.filter-bar&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt; &lt;span class="cp"&gt;!important&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"container-main"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;header&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"report-header"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;%(title)s&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sub"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;%(description)s&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/header&amp;gt;&lt;/span&gt;

        &lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"card-section"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"stat-cards"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"stat-card total"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sc-icon"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;i&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"fas fa-list-check"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/i&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sc-body"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sc-label"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;总用例数&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sc-num"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;%(count)s&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sc-pct"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;已执行&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;i&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"fas fa-list-check sc-watermark"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/i&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"stat-card pass"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sc-icon"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;i&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"fas fa-circle-check"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/i&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sc-body"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sc-label"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;通过&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sc-num"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;%(Pass)s&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sc-pct"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;占比 %(pass_pct)s%%&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;i&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"fas fa-circle-check sc-watermark"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/i&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"stat-card fail"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sc-icon"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;i&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"fas fa-circle-xmark"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/i&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sc-body"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sc-label"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;失败&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sc-num"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;%(fail)s&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sc-pct"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;占比 %(fail_pct)s%%&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;i&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"fas fa-circle-xmark sc-watermark"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/i&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"stat-card error"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sc-icon"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;i&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"fas fa-triangle-exclamation"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/i&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sc-body"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sc-label"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;错误&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sc-num"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;%(error)s&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sc-pct"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;占比 %(error_pct)s%%&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;i&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"fas fa-triangle-exclamation sc-watermark"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/i&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"stat-card skip"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sc-icon"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;i&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"fas fa-forward"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/i&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sc-body"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sc-label"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;跳过&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sc-num"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;%(skip)s&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sc-pct"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;占比 %(skip_pct)s%%&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;i&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"fas fa-forward sc-watermark"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/i&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"progress-wrap"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"display:flex;justify-content:space-between;margin-bottom:6px;align-items:center"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"font-size:0.82rem;color:#64748b;font-weight:500"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;i&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"fas fa-gauge-high"&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"margin-right:5px;color:#10b981"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/i&amp;gt;&lt;/span&gt;通过率
                    &lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"font-size:1rem;font-weight:700;color:#059669"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;%(pass_rate)s%%&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"progress"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"progress-bar"&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"width:%(pass_rate)s%%;background:#10b981;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"meta-grid"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;%(attributes)s&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;        &lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;

        &lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"card-section"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"charts-row"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"chart-panel donut"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"chart-panel-title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;i&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"fas fa-chart-pie"&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"margin-right:5px"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/i&amp;gt;&lt;/span&gt;结果分布&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"donut-wrap chart-box"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;canvas&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"pieChart"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/canvas&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"donut-center"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"dc-num"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;%(pass_rate)s%%&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"dc-lbl"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;通过率&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"chart-panel bars"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"chart-panel-title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;i&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"fas fa-bars"&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"margin-right:5px"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/i&amp;gt;&lt;/span&gt;分项统计&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"stat-bars"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"stat-bar-item"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sb-label pass"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;通过&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sb-track"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sb-fill pass"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"bar_pass"&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"width:0%%"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sb-num"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;%(Pass)s&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"stat-bar-item"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sb-label fail"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;失败&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sb-track"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sb-fill fail"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"bar_fail"&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"width:0%%"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sb-num"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;%(fail)s&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"stat-bar-item"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sb-label error"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;错误&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sb-track"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sb-fill error"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"bar_error"&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"width:0%%"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sb-num"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;%(error)s&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"stat-bar-item"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sb-label skip"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;跳过&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sb-track"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sb-fill skip"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"bar_skip"&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"width:0%%"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sb-num"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;%(skip)s&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"chart-panel modules"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"chart-panel-title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;i&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"fas fa-layer-group"&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"margin-right:5px"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/i&amp;gt;&lt;/span&gt;模块分布&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"chart-box"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;canvas&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"barChart"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/canvas&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;

        &lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"card-section"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"filter-bar"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"font-weight:600;margin-right:4px"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;i&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"fas fa-filter"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/i&amp;gt;&lt;/span&gt; 筛选：&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"filter-btn active all"&lt;/span&gt;  &lt;span class="na"&gt;onclick=&lt;/span&gt;&lt;span class="s"&gt;"showCase(0)"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;概览&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"filter-btn fail"&lt;/span&gt;        &lt;span class="na"&gt;onclick=&lt;/span&gt;&lt;span class="s"&gt;"showCase(1)"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;仅失败&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"filter-btn error"&lt;/span&gt;       &lt;span class="na"&gt;onclick=&lt;/span&gt;&lt;span class="s"&gt;"showCase(2)"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;仅错误&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"filter-btn pass"&lt;/span&gt;        &lt;span class="na"&gt;onclick=&lt;/span&gt;&lt;span class="s"&gt;"showCase(3)"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;全部&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"table-responsive"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;table&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"result-table"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;thead&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;tr&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;th&amp;gt;&lt;/span&gt;测试类 / 用例&lt;span class="nt"&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;th&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"width:80px"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;总数&lt;span class="nt"&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;th&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"width:80px"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;通过&lt;span class="nt"&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;th&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"width:80px"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;失败&lt;span class="nt"&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;th&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"width:80px"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;错误&lt;span class="nt"&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;th&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"width:90px"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;操作&lt;span class="nt"&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;/thead&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;tbody&amp;gt;&lt;/span&gt;
                        %(test_list)s
                    &lt;span class="nt"&gt;&amp;lt;/tbody&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;tfoot&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;tr&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"background:#343a40;color:white;font-weight:700"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;td&amp;gt;&lt;/span&gt;合计&lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;td&amp;gt;&lt;/span&gt;%(count)s&lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;td&amp;gt;&lt;/span&gt;%(Pass)s&lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;td&amp;gt;&lt;/span&gt;%(fail)s&lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;td&amp;gt;&lt;/span&gt;%(error)s&lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;td&amp;gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;/tfoot&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/table&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;

        &lt;span class="nt"&gt;&amp;lt;footer&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"report-footer"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&lt;span class="ni"&gt;&amp;amp;copy;&lt;/span&gt; %(generate_time)s 自动化测试团队&lt;span class="nt"&gt;&amp;lt;/footer&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"lightbox-overlay"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"lightboxOverlay"&lt;/span&gt; &lt;span class="na"&gt;onclick=&lt;/span&gt;&lt;span class="s"&gt;"closeLightbox()"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"lightboxImg"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"截图预览"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"copy-toast"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"copyToast"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;i&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"fas fa-check"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/i&amp;gt;&lt;/span&gt; 已复制到剪贴板&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"back-top"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"backTop"&lt;/span&gt; &lt;span class="na"&gt;onclick=&lt;/span&gt;&lt;span class="s"&gt;"window.scrollTo({top:0,behavior:'smooth'})"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;i&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"fas fa-arrow-up"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/i&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
        &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DOMContentLoaded&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="c1"&gt;// 分项统计条动画&lt;/span&gt;
            &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;bars&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;pass&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Pass&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;fail&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fail&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;skip&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;skip&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
            &lt;span class="nx"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bars&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;el&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bar_&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bars&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;total&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;%%&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="p"&gt;});&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="c1"&gt;// 结果分布（甜甜圈）&lt;/span&gt;
            &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Chart&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pieChart&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;getContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2d&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;doughnut&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;通过&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;失败&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;错误&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;跳过&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                    &lt;span class="na"&gt;datasets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Pass&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fail&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;skip&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                        &lt;span class="na"&gt;backgroundColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#059669&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#ef4444&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#f59e0b&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#9ca3af&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                        &lt;span class="na"&gt;borderWidth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;borderColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#f8fafc&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="na"&gt;hoverOffset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt; &lt;span class="p"&gt;}]&lt;/span&gt;
                &lt;span class="p"&gt;},&lt;/span&gt;
                &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="na"&gt;responsive&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;maintainAspectRatio&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;cutout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;68%%&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="na"&gt;legend&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
                        &lt;span class="na"&gt;tooltip&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;callbacks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                            &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;: &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parsed&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
                                       &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; (&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parsed&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;total&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;%%)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                            &lt;span class="p"&gt;}&lt;/span&gt;
                        &lt;span class="p"&gt;}}&lt;/span&gt;
                    &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;});&lt;/span&gt;

            &lt;span class="c1"&gt;// 模块分布（横向柱）&lt;/span&gt;
            &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Chart&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;barChart&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;getContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2d&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chart_labels&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="na"&gt;datasets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;用例数&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chart_data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="na"&gt;backgroundColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#334155&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;borderRadius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="na"&gt;barThickness&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;flex&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;maxBarThickness&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt; &lt;span class="p"&gt;}]&lt;/span&gt;
                &lt;span class="p"&gt;},&lt;/span&gt;
                &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="na"&gt;indexAxis&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;y&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="na"&gt;responsive&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;maintainAspectRatio&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="na"&gt;layout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
                    &lt;span class="na"&gt;scales&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;beginAtZero&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;ticks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;stepSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="na"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#f1f5f9&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
                        &lt;span class="na"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                            &lt;span class="na"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
                            &lt;span class="na"&gt;ticks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                                &lt;span class="na"&gt;autoSkip&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                &lt;span class="na"&gt;font&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
                                &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#475569&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                &lt;span class="c1"&gt;// 长名称按 _ 智能换行：每行最多 18 字符，超长的拆成多行（Chart.js 原生支持数组形式 label）&lt;/span&gt;
                                &lt;span class="na"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                                    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;lbl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getLabelForValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                                    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;lbl&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;lbl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;22&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;lbl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                                    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;parts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;lbl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;_&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                                    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;lines&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="nx"&gt;cur&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                                    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                                        &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;seg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cur&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;cur&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;_&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
                                        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;seg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;22&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                                            &lt;span class="nx"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                                            &lt;span class="nx"&gt;cur&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
                                        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                                            &lt;span class="nx"&gt;cur&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;seg&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                                        &lt;span class="p"&gt;}&lt;/span&gt;
                                    &lt;span class="p"&gt;}&lt;/span&gt;
                                    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                                    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                                &lt;span class="p"&gt;}&lt;/span&gt;
                            &lt;span class="p"&gt;}&lt;/span&gt;
                        &lt;span class="p"&gt;}&lt;/span&gt;
                    &lt;span class="p"&gt;},&lt;/span&gt;
                    &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="na"&gt;legend&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
                        &lt;span class="na"&gt;tooltip&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;callbacks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                            &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/,/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;_&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
                        &lt;span class="p"&gt;}}&lt;/span&gt;
                    &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;});&lt;/span&gt;

            &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;bt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;backTop&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;scroll&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;bt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toggle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;show&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pageYOffset&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
            &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;keydown&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Escape&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;closeLightbox&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

            &lt;span class="c1"&gt;// 应用初始筛选状态，与默认 active 的"概览"按钮保持一致&lt;/span&gt;
            &lt;span class="nx"&gt;showCase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;

        &lt;span class="c1"&gt;// 0=概览(折叠所有class，只看汇总) 1=仅失败 2=仅错误 3=全部&lt;/span&gt;
        &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;currentLevel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;showCase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;level&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;currentLevel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;level&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.filter-btn&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;active&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
            &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.filter-btn&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="nx"&gt;level&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;active&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="c1"&gt;// 先收起所有已展开的 detail 行，并重置 data-open 状态&lt;/span&gt;
            &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.result-table tbody tr&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;detail_&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nx"&gt;tr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;none&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                    &lt;span class="nx"&gt;tr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;open&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;mainRow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;detail_&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
                    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mainRow&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;btn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;mainRow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.detail-toggle&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;btn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;btn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;expanded&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                    &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;});&lt;/span&gt;

            &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;classRows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.result-table tbody tr.class-row&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;ci&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;ci&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;classRows&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;ci&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;classRow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;classRows&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;ci&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
                &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;cid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;classRow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;num&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;classRow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;td&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;getAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;onclick&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/,&lt;/span&gt;&lt;span class="se"&gt;(\d&lt;/span&gt;&lt;span class="sr"&gt;+&lt;/span&gt;&lt;span class="se"&gt;)\)&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

                &lt;span class="c1"&gt;// 概览：折叠所有用例行，只显示 class 汇总行&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;level&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nx"&gt;classRow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;ti&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;ti&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;num&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;ti&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;base0&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;t&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;cid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;substr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;ti&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                        &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;r0&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ff&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;base0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
                                 &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fe&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;base0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
                                 &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;p&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;  &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;base0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;r0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;none&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                    &lt;span class="p"&gt;}&lt;/span&gt;
                    &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;

                &lt;span class="c1"&gt;// 其他模式：按条件过滤用例行&lt;/span&gt;
                &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;hasVisible&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;ti&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;ti&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;num&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;ti&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;base&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;t&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;cid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;substr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;ti&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;testRow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ff&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;base&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
                                  &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fe&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;base&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
                                  &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;p&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;  &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;base&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;testRow&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

                    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;isFail&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;testRow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ff&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;isError&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;testRow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fe&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;show&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                    &lt;span class="k"&gt;if&lt;/span&gt;      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;level&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;show&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;isFail&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
                    &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;level&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;show&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;isError&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
                    &lt;span class="k"&gt;else&lt;/span&gt;                  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;show&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;  &lt;span class="c1"&gt;// level === 3 全部&lt;/span&gt;

                    &lt;span class="nx"&gt;testRow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;show&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;none&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;show&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;hasVisible&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;

                &lt;span class="c1"&gt;// 仅失败/仅错误时，若该 class 下无可见用例则隐藏 class 行&lt;/span&gt;
                &lt;span class="nx"&gt;classRow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;level&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;hasVisible&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;none&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;showClassDetail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;childRows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
            &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;anyVisible&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;base&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;t&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;cid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;substr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ff&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;base&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
                          &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fe&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;base&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
                          &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;p&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;  &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;base&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nx"&gt;childRows&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;none&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;anyVisible&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;childRows&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;childRows&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;anyVisible&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="c1"&gt;// 折叠：全部隐藏，收起 detail，重置 data-open&lt;/span&gt;
                    &lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;none&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;detailRow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;detail_&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;detailRow&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="nx"&gt;detailRow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;none&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                        &lt;span class="nx"&gt;detailRow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;open&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                        &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;btn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.detail-toggle&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;btn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;btn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;expanded&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                    &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="c1"&gt;// 展开：遵守当前筛选级别&lt;/span&gt;
                    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;isFail2&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ff&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;isError2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fe&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;show2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                    &lt;span class="k"&gt;if&lt;/span&gt;      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentLevel&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;show2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
                    &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentLevel&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;show2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;isFail2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
                    &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentLevel&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;show2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;isError2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
                    &lt;span class="k"&gt;else&lt;/span&gt;                          &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;show2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
                    &lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;show2&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;none&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;toggleDetail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;btn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;detailRow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;detail_&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;tid&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;detailRow&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

            &lt;span class="c1"&gt;// 用 dataset 标记展开状态，避免 display 空字符串歧义&lt;/span&gt;
            &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;isOpen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;detailRow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;open&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isOpen&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;detailRow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;none&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="nx"&gt;detailRow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;open&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="nx"&gt;btn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;expanded&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;detailRow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;table-row&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="nx"&gt;detailRow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;open&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="nx"&gt;btn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;expanded&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="nx"&gt;switchDetailTab&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;output&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;switchDetailTab&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tabName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;container&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;detail_&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;tid&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

            &lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.inline-tab-btn&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;active&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
            &lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.inline-tab-content&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;active&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

            &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;activeBtn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.inline-tab-btn.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;tabName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;activeContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.inline-tab-content.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;tabName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;activeBtn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;activeBtn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;active&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;activeContent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;activeContent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;active&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;openLightbox&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;imgSrc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;overlay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lightboxOverlay&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;img&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lightboxImg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;imgSrc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nx"&gt;overlay&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;active&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;closeLightbox&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;overlay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lightboxOverlay&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nx"&gt;overlay&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;active&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lightboxImg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;copyPre&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;btn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;pre&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;btn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parentNode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pre&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;pre&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pre&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;pre&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerText&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

            &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;done&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;span&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;btn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;span&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;icon&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;btn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;i&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;oldTxt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;span&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="nx"&gt;btn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;copied&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;icon&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;icon&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;className&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fas fa-check&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;span&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;已复制&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="nx"&gt;showCopyToast&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
                &lt;span class="nx"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nx"&gt;btn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;copied&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;icon&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;icon&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;className&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;far fa-copy&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;span&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;oldTxt&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;复制&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;1500&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;};&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clipboard&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isSecureContext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clipboard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;writeText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;done&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nx"&gt;fallbackCopy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;done&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="p"&gt;});&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;fallbackCopy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;done&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;fallbackCopy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cb&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;ta&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;textarea&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nx"&gt;ta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nx"&gt;ta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fixed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nx"&gt;ta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;top&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-9999px&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ta&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nx"&gt;ta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;select&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;execCommand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;copy&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="nx"&gt;cb&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;cb&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
            &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;removeChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ta&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;showCopyToast&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;copyToast&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;show&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nx"&gt;clearTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_timer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_timer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;show&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;1500&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;"""

    REPORT_CLASS_TMPL = r"""
&lt;span class="nt"&gt;&amp;lt;tr&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"class-row"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"%(cid)s"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;td&lt;/span&gt; &lt;span class="na"&gt;colspan=&lt;/span&gt;&lt;span class="s"&gt;"6"&lt;/span&gt; &lt;span class="na"&gt;onclick=&lt;/span&gt;&lt;span class="s"&gt;"showClassDetail('%(cid)s',%(count)s)"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;i&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"fas fa-folder-open"&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"margin-right:6px;color:#667eea"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/i&amp;gt;&lt;/span&gt;%(desc)s
        &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"font-weight:400;font-size:0.85rem;color:#888;margin-left:8px"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;%(count)s 个用例&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;"""

    REPORT_TEST_WITH_OUTPUT_TMPL = r"""
&lt;span class="nt"&gt;&amp;lt;tr&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"%(tid)s"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"%(row_class)s"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;td&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"padding-left:36px"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;%(desc)s&lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;td&lt;/span&gt; &lt;span class="na"&gt;colspan=&lt;/span&gt;&lt;span class="s"&gt;"5"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"status-tag %(tag_class)s"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;%(status_text)s&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"margin-left:8px;font-size:0.8rem;color:#888"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;i&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"fas fa-clock"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/i&amp;gt;&lt;/span&gt; %(elapsed)s&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"detail-toggle"&lt;/span&gt; &lt;span class="na"&gt;onclick=&lt;/span&gt;&lt;span class="s"&gt;"toggleDetail(this,'%(tid)s')"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;i&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"fas fa-chevron-down"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/i&amp;gt;&lt;/span&gt; 详情
        &lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;tr&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"detail_%(tid)s"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"inline-detail-row"&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"display:none"&lt;/span&gt; &lt;span class="na"&gt;data-open=&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;td&lt;/span&gt; &lt;span class="na"&gt;colspan=&lt;/span&gt;&lt;span class="s"&gt;"6"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"inline-detail-container"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"inline-tab-bar"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"inline-tab-btn output active"&lt;/span&gt; &lt;span class="na"&gt;onclick=&lt;/span&gt;&lt;span class="s"&gt;"switchDetailTab('%(tid)s','output')"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;运行输出&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"inline-tab-btn traceback"&lt;/span&gt; &lt;span class="na"&gt;onclick=&lt;/span&gt;&lt;span class="s"&gt;"switchDetailTab('%(tid)s','traceback')"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;错误堆栈&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"inline-tab-btn screenshot"&lt;/span&gt; &lt;span class="na"&gt;onclick=&lt;/span&gt;&lt;span class="s"&gt;"switchDetailTab('%(tid)s','screenshot')"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;截图&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"inline-tab-content output active"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"tab_output_%(tid)s"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"pre-wrap"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"copy-btn"&lt;/span&gt; &lt;span class="na"&gt;onclick=&lt;/span&gt;&lt;span class="s"&gt;"copyPre(this)"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;i&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"far fa-copy"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/i&amp;gt;&amp;lt;span&amp;gt;&lt;/span&gt;复制&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&amp;lt;pre&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"inline-pre"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;%(process)s&lt;span class="nt"&gt;&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"inline-tab-content traceback"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"tab_traceback_%(tid)s"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"pre-wrap"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"copy-btn"&lt;/span&gt; &lt;span class="na"&gt;onclick=&lt;/span&gt;&lt;span class="s"&gt;"copyPre(this)"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;i&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"far fa-copy"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/i&amp;gt;&amp;lt;span&amp;gt;&lt;/span&gt;复制&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&amp;lt;pre&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"inline-pre"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;%(script)s&lt;span class="nt"&gt;&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"inline-tab-content screenshot"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"tab_screenshot_%(tid)s"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;%(screenshots)s&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;"""

    REPORT_TEST_NO_OUTPUT_TMPL = r"""
&lt;span class="nt"&gt;&amp;lt;tr&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"%(tid)s"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"%(row_class)s"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;td&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"padding-left:36px"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;%(desc)s&lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;td&lt;/span&gt; &lt;span class="na"&gt;colspan=&lt;/span&gt;&lt;span class="s"&gt;"5"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"status-tag %(tag_class)s"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;%(status_text)s&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"margin-left:8px;font-size:0.8rem;color:#888"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;i&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"fas fa-clock"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/i&amp;gt;&lt;/span&gt; %(elapsed)s&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;"""

    ENDING_TMPL = ""


class _TestResult(unittest.TestResult):
    def __init__(self, verbosity=1):
        super().__init__(stream=None, descriptions=None, verbosity=verbosity)
        self.success_case = []
        self.failure_case = []
        self.error_case = []
        self.success_count = 0
        self.failure_count = 0
        self.error_count = 0
        self.skip_count = 0
        self.verbosity = verbosity
        self.result = []
        self.outputBuffer = None
        self.stdout0 = None
        self.stderr0 = None
        self._log_handler = None
        # 重试覆盖支持：同一 test.id() 重复回调时覆盖之前的记录
        self._test_id_to_idx = {}   # test.id() -&amp;gt; result 列表索引
        self._last_status = {}      # test.id() -&amp;gt; 上次状态码 (0/1/2/3)

    @staticmethod
    def _test_key(test):
        try:
            return test.id()
        except Exception:
            return str(id(test))

    def _record(self, status, test, output, script, elapsed):
        """统一记录入口：支持重试覆盖
        status: 0=pass, 1=fail, 2=error, 3=skip
        """
        key = self._test_key(test)
        # 已有记录：扣减旧计数
        if key in self._last_status:
            old = self._last_status[key]
            if   old == 0: self.success_count = max(0, self.success_count - 1)
            elif old == 1: self.failure_count = max(0, self.failure_count - 1)
            elif old == 2: self.error_count   = max(0, self.error_count   - 1)
            elif old == 3: self.skip_count    = max(0, self.skip_count    - 1)
            # 同步从案例列表里移除（仅一次）
            for case_list in (self.success_case, self.failure_case, self.error_case):
                if test in case_list:
                    try: case_list.remove(test)
                    except ValueError: pass

        # 累加新计数
        if   status == 0:
            self.success_count += 1
            self.success_case.append(test)
        elif status == 1:
            self.failure_count += 1
            self.failure_case.append(test)
        elif status == 2:
            self.error_count += 1
            self.error_case.append(test)
        elif status == 3:
            self.skip_count += 1

        self._last_status[key] = status

        entry = (status, test, output, script, elapsed)
        if key in self._test_id_to_idx:
            # 重试：覆盖旧记录
            self.result[self._test_id_to_idx[key]] = entry
        else:
            self._test_id_to_idx[key] = len(self.result)
            self.result.append(entry)

    def startTest(self, test):
        super().startTest(test)
        self._start_time = time.time()
        self.outputBuffer = io.StringIO()
        stdout_redirector.fp = self.outputBuffer
        stderr_redirector.fp = self.outputBuffer
        self.stdout0 = sys.stdout
        self.stderr0 = sys.stderr
        sys.stdout = stdout_redirector
        sys.stderr = stderr_redirector
        if Text:
            self._log_handler = logging.StreamHandler(self.outputBuffer)
            self._log_handler.setLevel(logging.DEBUG)
            fmt = logging.Formatter('[ %(asctime)s - %(levelname)-5s ] %(message)s')
            self._log_handler.setFormatter(fmt)
            Text.logger.addHandler(self._log_handler)

    def _get_output(self):
        if self.stdout0:
            sys.stdout = self.stdout0
            sys.stderr = self.stderr0
            self.stdout0 = None
            self.stderr0 = None
        if self._log_handler and Text:
            Text.logger.removeHandler(self._log_handler)
            self._log_handler = None
        if self.outputBuffer:
            return self.outputBuffer.getvalue()
        return ''

    def stopTest(self, test):
        if self.stdout0:
            sys.stdout = self.stdout0
            sys.stderr = self.stderr0
            self.stdout0 = None
            self.stderr0 = None
        if self._log_handler and Text:
            Text.logger.removeHandler(self._log_handler)
            self._log_handler = None
        self._elapsed = time.time() - getattr(self, '_start_time', time.time())
        super().stopTest(test)

    def _capture_failure_screenshot(self, test):
        """
        失败/错误时主动截图当前页面，无侵入测试代码。
        - 优先使用 test 实例的 driver/_driver/browser 属性
        - 优先调用 saveScreenshot（项目封装），其次 save_screenshot（标准 selenium）
        - 任何异常静默吞掉，不影响测试结果
        - 截图日志写入 outputBuffer，会被现有 _extract_screenshots 自动抓取
        """
        try:
            drv = (getattr(test, 'driver', None)
                   or getattr(test, '_driver', None)
                   or getattr(test, 'browser', None))
            if drv is None:
                return

            ts = time.strftime('%Y%m%d%H%M%S') + '_%03d' % int((time.time() % 1) * 1000)
            test_name = test.id().split('.')[-1] if hasattr(test, 'id') else 'failure'
            safe_name = re.sub(r'[^\w\-]', '_', test_name)[:40]
            rel_path = 'Report/Screenshots/Failure_%s_%s.png' % (safe_name, ts)

            # 确保目录存在（基于测试运行时的当前工作目录）
            try:
                os.makedirs(os.path.dirname(rel_path), exist_ok=True)
            except Exception:
                pass

            ok = False
            for fn_name in ('saveScreenshot', 'save_screenshot'):
                fn = getattr(drv, fn_name, None)
                if callable(fn):
                    try:
                        fn(rel_path)
                        ok = True
                        break
                    except Exception:
                        continue

            if ok:
                # 写入与现有日志一致的格式，能被 _extract_screenshots 自动识别
                msg = '[失败时刻] 屏幕截图成功, 存放位置 %s' % rel_path
                if Text:
                    Text.error(msg)
                else:
                    sys.stdout.write(msg + '\n')
        except Exception:
            # 任何异常都不能影响测试本身
            pass

    def addSuccess(self, test):
        super().addSuccess(test)
        output = self._get_output()
        elapsed = time.time() - getattr(self, '_start_time', time.time())
        self._record(0, test, output, '', elapsed)
        if self.verbosity &amp;gt; 1:
            sys.stderr.write('ok ')
            sys.stderr.write(str(test))
            sys.stderr.write('\n')
        else:
            if Text:
                Text.info('Result : Success.')

    def addError(self, test, err):
        super().addError(test, err)
        _, exc_str = self.errors[-1]
        if Text:
            Text.error('错误堆栈:\n%s' % exc_str)
        # 主动截取失败时刻的 UI 截图（无侵入）
        self._capture_failure_screenshot(test)
        output = self._get_output()
        elapsed = time.time() - getattr(self, '_start_time', time.time())
        self._record(2, test, output, exc_str, elapsed)
        if self.verbosity &amp;gt; 1:
            sys.stderr.write('E ')
            sys.stderr.write(str(test))
            sys.stderr.write('\n')
        else:
            if Text:
                Text.info('Result : Error.')

    def addFailure(self, test, err):
        super().addFailure(test, err)
        _, exc_str = self.failures[-1]
        if Text:
            Text.error('失败原因:\n%s' % exc_str)
        # 主动截取失败时刻的 UI 截图（无侵入）
        self._capture_failure_screenshot(test)
        output = self._get_output()
        elapsed = time.time() - getattr(self, '_start_time', time.time())
        self._record(1, test, output, exc_str, elapsed)
        if self.verbosity &amp;gt; 1:
            sys.stderr.write('F ')
            sys.stderr.write(str(test))
            sys.stderr.write('\n')
        else:
            if Text:
                Text.info('Result : Failure.')

    def addSkip(self, test, reason=''):
        super().addSkip(test, reason)
        output = self._get_output()
        elapsed = time.time() - getattr(self, '_start_time', time.time())
        self._record(3, test, output, '', elapsed)
        if self.verbosity &amp;gt; 1:
            sys.stderr.write('skiped ')
            sys.stderr.write(str(test))
            sys.stderr.write('\n')
        else:
            if Text:
                Text.info('Result : Skiped.')


class HTMLTestRunner(Template_mixin):
    def __init__(self, stream=sys.stdout, verbosity=1, title=None, description=None,
                 screenshot_base_path=None, embed_screenshots=True, report_dir=None):
        """
        :param embed_screenshots:
            True（默认）= 截图 base64 内嵌进 HTML，单文件可独立分享。
                          注意：100 个失败用例 × 5 张截图 ≈ 200MB，浏览器可能打不开。
            False        = 截图以相对链接引用，HTML 体积极小（KB 级），适合大规模执行。
                          报告 HTML 必须与 Report/Screenshots 目录一起分发。
        :param report_dir:
            外链模式下 HTML 文件所在目录的绝对路径，用于计算截图相对路径。
            默认 None 时假设 HTML 在 cwd（项目根）；如 Report/Reports/Report.html
            则需传 os.path.join(os.getcwd(), 'Report/Reports')。
        """
        self.stream = stream
        self.verbosity = verbosity
        self.title = title or self.DEFAULT_TITLE
        self.description = description or self.DEFAULT_DESCRIPTION
        self.screenshot_base_path = screenshot_base_path or os.getcwd()
        self.embed_screenshots = embed_screenshots
        self.report_dir = report_dir
        self.startTime = datetime.datetime.now()

    def run(self, test):
        result = _TestResult(self.verbosity)
        test(result)
        self.stopTime = datetime.datetime.now()
        self.generateReport(test, result)
        return result

    def sortResult(self, result_list):
        rmap = {}
        classes = []
        for n, t, o, e, elapsed in result_list:
            cls = t.__class__
            if cls not in rmap:
                rmap[cls] = []
                classes.append(cls)
            rmap[cls].append((n, t, o, e, elapsed))
        return [(cls, rmap[cls]) for cls in classes]

    def generateReport(self, test, result):
        total = result.success_count + result.failure_count + result.error_count + result.skip_count
        effective_total = total - result.skip_count
        pass_rate = round(result.success_count * 100.0 / effective_total, 1) if effective_total &amp;gt; 0 else 0

        # 各分项相对总用例数的占比
        def _pct(n):
            return ('%.1f' % (n * 100.0 / total)) if total &amp;gt; 0 else '0.0'
        pass_pct  = _pct(result.success_count)
        fail_pct  = _pct(result.failure_count)
        error_pct = _pct(result.error_count)
        skip_pct  = _pct(result.skip_count)

        duration = str(self.stopTime - self.startTime)
        attrs = [
            ('far fa-clock',       '开始时间', str(self.startTime.strftime('%Y-%m-%d %H:%M:%S'))),
            ('fas fa-stopwatch',   '耗时',     duration.split('.')[0]),
            ('fas fa-list-check',  '状态',     self._status_text(result)),
            ('fas fa-percent',     '通过率',   str(pass_rate) + '%'),
            ('fab fa-python',      'Python',   platform.python_version()),
            ('fas fa-desktop',     '平台',     '%s %s' % (platform.system(), platform.release())),
            ('fas fa-server',      '主机',     platform.node()),
        ]
        attr_html = ''
        for icon, key, value in attrs:
            attr_html += (
                '&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"meta-item"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;'
                '&lt;span class="nt"&gt;&amp;lt;i&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"%s"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/i&amp;gt;&lt;/span&gt;'
                '&lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mk"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;%s&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;'
                '&lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mv"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;%s&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;'
                '&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;'
            ) % (icon, saxutils.escape(key), saxutils.escape(value))

        chart_labels, chart_data = self._get_chart_data(result)
        report = self._generate_report(result)

        html = self.HTML_TMPL % dict(
            title=saxutils.escape(self.title),
            description=saxutils.escape(self.description),
            count=str(total),
            Pass=str(result.success_count),
            fail=str(result.failure_count),
            error=str(result.error_count),
            skip=str(result.skip_count),
            pass_pct=pass_pct,
            fail_pct=fail_pct,
            error_pct=error_pct,
            skip_pct=skip_pct,
            pass_rate=str(pass_rate),
            attributes=attr_html,
            chart_labels=chart_labels,
            chart_data=chart_data,
            generate_time=self.startTime.strftime('%Y-%m-%d %H:%M:%S'),
            test_list=report,
        )
        self.stream.write(html)

    def _status_text(self, result):
        parts = ['总计 %s' % (result.success_count + result.failure_count + result.error_count + result.skip_count)]
        if result.success_count:
            parts.append('通过 %s' % result.success_count)
        if result.failure_count:
            parts.append('失败 %s' % result.failure_count)
        if result.error_count:
            parts.append('错误 %s' % result.error_count)
        if result.skip_count:
            parts.append('跳过 %s' % result.skip_count)
        return ' '.join(parts)

    def _generate_report(self, result):
        rows = []
        sorted_result = self.sortResult(result.result)
        for cid, (cls, cls_results) in enumerate(sorted_result):
            np = nf = ne = ns = 0
            for n, t, o, e, elapsed in cls_results:
                if n == 0:
                    np += 1
                elif n == 1:
                    nf += 1
                elif n == 2:
                    ne += 1
                elif n == 3:
                    ns += 1

            if cls.__module__ == '__main__':
                name = cls.__name__
            else:
                name = cls.__module__
            doc = cls.__doc__ and cls.__doc__.split('\n')[0].strip() or ''
            desc = doc and '%s: %s' % (name, doc) or name

            rows.append(self.REPORT_CLASS_TMPL % dict(
                desc=saxutils.escape(desc),
                count=np + nf + ne + ns,
                cid='c%s' % (cid + 1),
            ))
            for tid, (n, t, o, e, elapsed) in enumerate(cls_results):
                self._generate_report_test(rows, cid, tid, n, t, o, e, elapsed)
        return ''.join(rows)

    def _generate_report_test(self, rows, cid, tid, n, t, o, e, elapsed=0):
        # ff=失败  fe=错误  p=通过/跳过
        prefix = {0: 'p', 1: 'ff', 2: 'fe', 3: 'p'}.get(n, 'p')
        tid_str = '%st%s.%s' % (prefix, cid + 1, tid + 1)
        name = t.id().split('.')[-1]

        doc = t.shortDescription() or ''
        desc = doc and '%s: %s' % (name, doc) or name

        elapsed_str = '%.2fs' % elapsed

        status_map = {
            0: ('pass-strong', '通过', 'row-pass'),
            1: ('fail',        '失败', 'row-fail'),
            2: ('error',       '错误', 'row-error'),
            3: ('skip',        '跳过', ''),
        }
        tag_class, status_text, row_class = status_map.get(n, ('fail', '未知', ''))

        # 值直接代入，不加 %% 转义（值不经过二次 % 格式化）
        script  = saxutils.escape(e) if e else '(无)'
        process = saxutils.escape(o) if o else '(无输出)'

        if n in (1, 2):
            screenshots_html = self._extract_screenshots(o)
        else:
            screenshots_html = '&lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"color:#999;font-size:0.85rem;padding:10px 0;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;(无截图)&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;'

        row = self.REPORT_TEST_WITH_OUTPUT_TMPL % dict(
            tid=tid_str,
            row_class=row_class,
            tag_class=tag_class,
            desc=saxutils.escape(desc),
            status_text=status_text,
            elapsed=elapsed_str,
            process=process,
            script=script,
            screenshots=screenshots_html,
        )
        rows.append(row)

    def _extract_screenshots(self, output):
        if not output:
            return '&lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"color:#999;font-size:0.85rem;padding:10px 0;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;(无截图)&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;'
        # 同时识别普通截图和失败时刻截图（带 [失败时刻] 标记的优先标红展示）
        pattern = r'(\[失败时刻\]\s*)?屏幕截图成功, 存放位置\s+(.+\.(?:png|jpg|jpeg|gif|bmp))'
        matches = re.findall(pattern, output)
        if not matches:
            return '&lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"color:#999;font-size:0.85rem;padding:10px 0;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;(无截图)&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;'

        # 按出现顺序去重（同一路径只保留首次出现，并合并失败标记）
        seen = {}
        ordered = []
        for tag, path in matches:
            path = path.strip()
            is_failure = bool(tag)
            if path not in seen:
                seen[path] = is_failure
                ordered.append(path)
            else:
                # 已存在的若被标记为失败时刻，保留这个标记
                if is_failure:
                    seen[path] = True

        # 倒序展示：最新的（含失败时刻）排在最前
        ordered_display = list(reversed(ordered))

        imgs = ['&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"screenshot-grid"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;']
        total = len(ordered_display)
        for idx, path in enumerate(ordered_display):
            is_failure = seen.get(path, False)
            # 倒序后第 1 张是最新的；编号按时间顺序（即原 ordered 里的位置 + 1）
            seq = total - idx
            badge_text = '失败时刻' if is_failure else ('#%d' % seq)
            cls_extra = ' failure-shot' if is_failure else ''

            resolved = path
            if not os.path.isabs(path):
                resolved = os.path.join(self.screenshot_base_path, path)
            if not os.path.isfile(resolved):
                imgs.append('&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"screenshot-item%s"&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"padding:10px;color:#dc2626;font-size:0.85rem"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;'
                            '&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"shot-badge"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;%s&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;'
                            '⚠ 截图文件不存在: %s&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;'
                            % (cls_extra, badge_text, saxutils.escape(path)))
                continue
            try:
                if self.embed_screenshots:
                    # base64 内嵌：单文件分享，但大量截图会让 HTML 巨大
                    with open(resolved, 'rb') as f:
                        data = base64.b64encode(f.read()).decode('ascii')
                    ext = os.path.splitext(path)[1].lstrip('.').lower()
                    mime = {'png': 'png', 'jpg': 'jpeg', 'jpeg': 'jpeg',
                            'gif': 'gif', 'bmp': 'bmp'}.get(ext, 'png')
                    img_src = 'data:image/%s;base64,%s' % (mime, data)
                else:
                    # 外链模式：从 HTML 所在目录计算到截图的相对路径
                    if self.report_dir:
                        try:
                            rel = os.path.relpath(resolved, self.report_dir)
                        except ValueError:
                            rel = path  # 跨盘符等异常情况兜底
                    else:
                        rel = path
                    img_src = saxutils.escape(rel.replace(os.sep, '/'))
                imgs.append(
                    '&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"screenshot-item%s"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;'
                    '&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"shot-badge"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;%s&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;'
                    '&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"screenshot-thumb"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"%s"&lt;/span&gt; &lt;span class="na"&gt;loading=&lt;/span&gt;&lt;span class="s"&gt;"lazy"&lt;/span&gt; &lt;span class="na"&gt;onclick=&lt;/span&gt;&lt;span class="s"&gt;"openLightbox(this.src)"&lt;/span&gt; &lt;span class="na"&gt;title=&lt;/span&gt;&lt;span class="s"&gt;"点击查看原图"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;'
                    '&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"screenshot-name"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;%s&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;'
                    '&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;' % (cls_extra, badge_text, img_src,
                                saxutils.escape(os.path.basename(path)))
                )
            except Exception as exc:
                imgs.append('&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"screenshot-item%s"&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"padding:10px;color:#dc2626;font-size:0.85rem"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;'
                            '&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"shot-badge"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;%s&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;'
                            '⚠ 读取截图失败 (%s): %s&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;'
                            % (cls_extra, badge_text,
                               saxutils.escape(path), saxutils.escape(str(exc))))
        imgs.append('&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;')
        return ''.join(imgs)

    def _get_chart_data(self, result):
        modules = {}
        for n, t, o, e, elapsed in result.result:
            mod = t.__class__.__module__
            modules[mod] = modules.get(mod, 0) + 1
        sorted_mods = sorted(modules.items(), key=lambda x: x[1], reverse=True)[:5]
        labels = [m[0].split('.')[-1] for m in sorted_mods] or ['默认']
        data = [m[1] for m in sorted_mods] or [0]
        return json.dumps(labels, ensure_ascii=False), json.dumps(data)


class TestProgram(unittest.TestProgram):
    def runTests(self):
        if self.testRunner is None:
            self.testRunner = HTMLTestRunner(verbosity=self.verbosity)
        unittest.TestProgram.runTests(self)


main = TestProgram

if __name__ == '__main__':
    main(module=None)

&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;&lt;img src="/uploads/photo/2026/6f010d97-af82-4cd2-912d-40ef22c01a21.png!large" title="" alt=""&gt;&lt;br&gt;
&lt;img src="/uploads/photo/2026/e09dcb52-d25e-463d-9e6e-dee0426a0fb0.png!large" title="" alt=""&gt;&lt;br&gt;
&lt;img src="/uploads/photo/2026/3aab3e98-eada-4d31-9336-21b371cbcf4b.png!large" title="" alt=""&gt;&lt;br&gt;
&lt;img src="/uploads/photo/2026/23b49a6b-9faa-40ff-81db-1118d5ce72af.png!large" title="" alt=""&gt;&lt;br&gt;
&lt;img src="/uploads/photo/2026/ba807ee4-709c-4dbd-8db0-1303ebb8e8ae.png!large" title="" alt=""&gt;&lt;br&gt;
&lt;img src="/uploads/photo/2026/9d152eb7-1560-4546-b672-0ccb1e6bacea.png!large" title="" alt=""&gt;&lt;br&gt;
&lt;img src="/uploads/photo/2026/7c063de8-68f8-4dc3-b38e-a39f59d134e4.png!large" title="" alt=""&gt;&lt;br&gt;
&lt;img src="/uploads/photo/2026/37c3233b-7c7e-405d-b71a-ae2c28bf70bc.png!large" title="" alt=""&gt;&lt;br&gt;
&lt;img src="/uploads/photo/2026/52ef3c4f-9e4b-4aac-8159-b4acb56b54ee.png!large" title="" alt=""&gt;&lt;br&gt;
&lt;img src="/uploads/photo/2026/a1531884-3965-49d7-ad1f-d9edc98757ae.png!large" title="" alt=""&gt;&lt;/p&gt;</description>
      <author>hello2014</author>
      <pubDate>Mon, 22 Jun 2026 12:41:12 +0800</pubDate>
      <link>https://testerhome.com/topics/44347</link>
      <guid>https://testerhome.com/topics/44347</guid>
    </item>
    <item>
      <title>拾光记发布 AI 亲子语音记录器；Midjourney 跨界推出水下 3D 超声仪，成像快 MRI 百倍丨日报</title>
      <description>&lt;p&gt;&lt;img src="https://717KLO2znpj6gvtH.public.blob.vercel-storage.com/uploads/RTE_Dev_Comm/slqwWQon1nP6_640" title="" alt="图片"&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;开发者朋友们大家好：&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;这里是&lt;strong&gt;「RTE 开发者日报」&lt;/strong&gt;，每天和大家一起看新闻、聊八卦。我们的社区编辑团队会整理分享&amp;nbsp;RTE（Real-Time Engagement）&amp;nbsp;领域内「有话题的&lt;strong&gt;技术&lt;/strong&gt;」、「有亮点的&lt;strong&gt;产品&lt;/strong&gt;」、「有思考的&lt;strong&gt;文章&lt;/strong&gt;」、「有态度的&lt;strong&gt;观点&lt;/strong&gt;」、「有看点的&lt;strong&gt;活动&lt;/strong&gt;」，但内容仅代表编辑的个人观点，欢迎大家留言、跟帖、讨论。&lt;/p&gt;

&lt;p&gt;&lt;em&gt;本期编辑：&lt;a href="/koki" class="user-mention" title="@koki"&gt;&lt;i&gt;@&lt;/i&gt;koki&lt;/a&gt;、@ 鲍勃&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="01 有话题的技术"&gt;&lt;strong&gt;01 有话题的技术&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;1、京东 JoyAI-VL 团队发布 JoyAI-VL-Interaction：开源 8B 级实时视觉 - 语言交互模型，端到端延迟低于 1 秒&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;京东 JoyAI-VL 团队开源了&lt;strong&gt;实时视觉 - 语言交互模型与系统 JoyAI-VL-Interaction&lt;/strong&gt;。该项目基于 8B 参数规模，打破了传统的单回合「问答式」多模态交互限制，通过每秒自主评估视频流并决策动作，实现了&lt;strong&gt;低于 1 秒的多模态流式交互&lt;/strong&gt;。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;主动性决策机制&lt;/strong&gt;：基于 JoyAI-VL-8B 视觉语言指令模型，将语音输入输出作为可插拔组件与核心模型解耦。&lt;strong&gt;模型每秒在「说话」、「保持沉默」或「委托任务」三者间进行自主决策&lt;/strong&gt;，从而&lt;strong&gt;无需等待&lt;/strong&gt;用户指令即可在突发场景（如火灾、跌倒）中主动预警。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;AdaCodec 预测性视频编解码&lt;/strong&gt;：系统集成 AdaCodec 算法，在处理实时长视频流时，对可预测的连续帧仅消耗极少 token，仅在画面发生显著变化时保留完整细节，有效&lt;strong&gt;控制了长会话部署中的 token 消耗增速与内存开销&lt;/strong&gt;。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;400 万时序对齐样本与强化学习&lt;/strong&gt;：使用超 400 万个包含秒级行为标注（何时发声、何时保持沉默、何时调用外部工具）的时序对齐视频剪辑进行微调，并引入强化学习，使 8B 模型在未进行特定应用界面训练的情况下，涌现出了引导手机 App 购物等操作能力。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;后台智能体协同委托&lt;/strong&gt;：系统支持在面临复杂数字化任务时，由模型将子任务异步委托给后台智能体（如 OpenClaw 或 Claude Code）或外部 API，在等待结果返回的同时，前端模型继续保持对视频流的实时监测与交互。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;时效性任务胜率优势&lt;/strong&gt;：在包含监控、翻译、计数等 58 个事件驱动的交互测试中，该系统&lt;strong&gt;对阵豆包和 Gemini 的人评综合胜率分别达到 77.6% 和 87.9%&lt;/strong&gt;，其中在监控告警、实时翻译和动态计数等高时效性场景中&lt;strong&gt;胜率达 100%&lt;/strong&gt;。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;https://huggingface.co/papers/2606.14777&lt;/p&gt;

&lt;p&gt;https://github.com/jd-opensource/JoyAI-VL-Interaction/&lt;/p&gt;

&lt;p&gt;( &lt;a href="/wjqdev" class="user-mention" title="@wjqdev"&gt;&lt;i&gt;@&lt;/i&gt;wjqdev&lt;/a&gt;\&lt;a href="/X" class="user-mention" title="@X"&gt;&lt;i&gt;@&lt;/i&gt;X&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2、Midjourney 跨界发布水下全身 3D 超声扫描仪，成像速度比传统 MRI 快了整整 100 倍&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;AI 图像巨头 Midjourney 正式宣布进军医疗硬件领域，发布其今年计划推出的 8 个探索性项目之首：Midjourney Scanner（水下全身超声层析成像扫描仪）。该设备利用 50 万个微型声学换能器采集海量数据，通过 AI 进行 3D 重建与分割，在 60 秒内即可提供亚毫米级的全身医学影像，成像速度比传统 MRI 快了整整 100 倍。&lt;/p&gt;

&lt;p&gt;&lt;img src="https://717KLO2znpj6gvtH.public.blob.vercel-storage.com/uploads/RTE_Dev_Comm/akqqf52uVeYF_640" title="" alt="图片"&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;核心特点&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;50 万通道传感器环与 TB/s 级数据流&lt;/strong&gt;：扫描仪的核心是一个由 50 万个沙粒大小、兼具收发功能的硅基超声换能器组成的传感器环。设备工作时，每秒会产生 TB 级别的原始声学数据，实时流式传输至数千个节点的计算集群中进行处理。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;60 秒全身 3D 逆向重构&lt;/strong&gt;：利用声波穿过水、皮肤、脂肪、肌肉和骨骼等不同密度介质时的传播形变，AI 算法进行亚毫米级 3D 地图的逆向重构。用户只需通过升降平台浸入温水，60 秒内即可完成扫描，并支持 AI 实时语义分割。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;1000 万美元极低研发成本&lt;/strong&gt;：该系统在没有任何外部投资、年研发预算仅 1000 万美元的「极简」架构下实现了该系统的开发。其资金效率较传统医疗器械巨头、政府及前沿实验室实现了 40 至 100 倍的跨越式提升。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;去医疗化的渐进式合规策略&lt;/strong&gt;：第一阶段避开严苛的 FDA 诊断审批限制，仅作为无创的「身体成分图谱」设备进行商业部署，后续通过持续积累的真实世界测试数据，逐步向 FDA 申请正式的医疗诊断认证。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;发布会参会者一手评价：&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;对标 iPhone 与 Tesla 的历史性首发&lt;/strong&gt;：曾现场见证初代 iPhone 和特斯拉发布会的科技圈意见领袖 Robert Scoble 评价，这场发布会的震撼程度与前两者处于同一量级。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;抗衰极客站台&lt;/strong&gt;：硅谷知名抗衰先锋 Bryan Johnson 亲临现场力挺 Midjourney 创始人 David Holz。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;硅基超声的「ChatGPT 时刻」&lt;/strong&gt;：现场技术博主 swyx 指出，该系统标志着芯片级超声技术（如 Butterfly Network 路线）在重建算法上的重大突破。更好的科学始于更好的成像数据，廉价、高精度超声成像的普及将带来医学研究的数据大爆炸。同时，他质疑为何传统巨头、政府实验室在拥有巨大预算的情况下，无法实现同等量级的研发创新。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;( &lt;a href="/swyx" class="user-mention" title="@swyx"&gt;&lt;i&gt;@&lt;/i&gt;swyx&lt;/a&gt; &lt;a href="/X" class="user-mention" title="@X"&gt;&lt;i&gt;@&lt;/i&gt;X&lt;/a&gt;、&lt;a href="/midjourney" class="user-mention" title="@midjourney"&gt;&lt;i&gt;@&lt;/i&gt;midjourney&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3、Catnip.ai 发布 MaineCoon：22B 参数音频 - 视频双流 DiT 模型，实现单卡 47.5 FPS 实时流式生成&lt;/strong&gt;&lt;/p&gt;
&lt;h5 id="***AI 创业公司 Catnip.ai 推出&amp;nbsp;22B 参数的实时自回归音视频基础模型 MaineCoon，该模型采用音视频双流联合生成的流式架构，在单张 H100 显卡上实现了 47.5 FPS 的推理速度（首帧延迟低于 1 秒）。通过引入智能体流式推理框架与自重采样训练，MaineCoon 解决了长视频流式生成的时序一致性与漂移问题，使高保真的人机音视频实时交互成为可能*。"&gt;*&lt;em&gt;**AI 创业公司 Catnip.ai 推出&amp;nbsp;&lt;/em&gt;&lt;em&gt;22B 参数的实时自回归音视频基础模型 MaineCoon&lt;/em&gt;&lt;em&gt;，该模型采用音视频双流联合生成的流式架构，在单张 H100 显卡上实现了 47.5 FPS 的推理速度（首帧延迟低于 1 秒）。通过引入智能体流式推理框架与自重采样训练，MaineCoon 解决了长视频流式生成的时序一致性与漂移问题，使&lt;/em&gt;&lt;em&gt;高保真的人机音视频实时交互成为可能&lt;/em&gt;*。&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;双流 DiT 联合流式架构&lt;/strong&gt;：采用 48 层、约 21B 参数的双流扩散 Transformer 骨干网络。视频流通道宽度为 4096，音频流通道宽度为 2048，两者通过双向交叉注意力机制实现底层对齐，单次生成块仅需 4-NFE 去噪，无需「先渲染视频后配音」的传统管线。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;极低延迟与多硬件兼容&lt;/strong&gt;：首帧渲染延迟控制在 1 秒以内，端到端生成速度较 LiveAvatar、LTX-2.3 等同类系统提升 6.7 至 7.7 倍；单张 H100 推理速度达 47.5 FPS，单张 RTX Pro 6000 显卡可达 30 FPS。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;智能体流式推理框架&lt;/strong&gt;：引入由语言模型充当的「导演」与智能 KV 缓存管理器组成的运行时环路，支持 10 分钟以上的超长视频无缝流式生成，通过「前向修复」机制在未来帧和缓存中动态纠正画面和声音漂移，无需硬重启视频。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;原生流式三阶段训练&lt;/strong&gt;：第一阶段引入自重采样（Self-resampling）模拟不完美历史缓存，提升模型自愈能力；第二阶段利用 V-JEPA 与 REPA 进行特征对齐，加速结构与运动特征学习；第三阶段通过领域感知优先权蒸馏（ROPD）将对话、远景、对口型等多领域专家策略融合成单一流式策略。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;SocialVideo-Bench 评测登顶&lt;/strong&gt;：在社交音视频评测集 SocialVideo-Bench 中，MaineCoon 获得 0.934 的综合平均分，在视觉质量（4.71）和音频质量（4.35）等多个关键维度上超越 SoulX-FlashTalk 及 LTX-2.3。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;( &lt;a href="/catnips" class="user-mention" title="@catnips"&gt;&lt;i&gt;@&lt;/i&gt;catnips&lt;/a&gt;_ai\&lt;a href="/X" class="user-mention" title="@X"&gt;&lt;i&gt;@&lt;/i&gt;X&lt;/a&gt;)&lt;/p&gt;
&lt;h2 id="02 有亮点的产品"&gt;&lt;strong&gt;02 有亮点的产品&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;1、拾光记发布 Always-on 亲子语音记录器：支持 AI 自动高光剪辑与育儿沟通分析&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="https://717KLO2znpj6gvtH.public.blob.vercel-storage.com/uploads/RTE_Dev_Comm/Uhzg-vVDWaEB_640" title="" alt="图片"&gt;&lt;/p&gt;

&lt;p&gt;拾光记团队推出面向 2-6 岁亲子家庭的 Always-on 语音记录硬件及配套 App。该产品支持在家长授权时段内进行持续语音采集，利用 AI 自动挖掘并剪辑儿童语言的高光片段。系统通过结合历史对话数据与科学育儿知识库，分析亲子沟通模式并提供客观的改进建议。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Always-on 硬件形态与无对话设计&lt;/strong&gt;：产品采用手环及挂件形态，专为 2-6 岁儿童日常场景设计；主动摒弃人机语音对话交互，专注于后台无感记录，避免 AI 设备干扰儿童的自然陪伴环境。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;AI 自动语言高光剪辑&lt;/strong&gt;：支持在授权时间段内进行持续音频采集，通过 AI 算法自动识别、提取并剪辑儿童的精彩语言瞬间，解决家长使用手机录制不及时、不完整的问题。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;基于历史上下文的亲子沟通分析&lt;/strong&gt;：AI 结合历史对话记录与科学育儿知识，评估家长的日常沟通方式，针对不合理的亲子互动提供客观的观察报告与话术改进建议。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;多端客户端支持&lt;/strong&gt;：配套软件已上线 iOS 平台，并面向安卓和华为用户提供官网 APK 下载，支持手机录音、AI 剪辑及个性化育儿分析功能。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;（&lt;a href="/AI" class="user-mention" title="@AI"&gt;&lt;i&gt;@&lt;/i&gt;AI&lt;/a&gt; 语音 AI 思考）&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2、乐奇 Rokid 联合 Pettichat 上线首个 AI 眼镜宠物翻译智能体：支持 20 余种意图识别，猫叫声学测试准确率达 94.6%&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;乐奇 Rokid 与 AI 宠物翻译品牌 Pettichat 达成合作，在&lt;strong&gt;&amp;nbsp;Rokid AI 眼镜端&lt;/strong&gt;正式上线「萌小译」AI 智能体。该系统通过&lt;strong&gt;&amp;nbsp;Pettichat 智能项圈采集猫狗叫声并完成云端 AI 转译&lt;/strong&gt;，将翻译结果实时以文字形式推送到眼镜的衍射光波导显示屏上，实现了&lt;strong&gt;免手机介入的人宠双向实时交互&lt;/strong&gt;。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;500 万条真实声纹数据集&lt;/strong&gt;：该智能体依托浙江大学动物科学学院提供的超 500 万条真实宠物声纹数据进行模型训练，支持识别「饥饿」、「害怕」、「想玩耍」等 20 余种常见宠物情绪与意图。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;高精度声学识别算法&lt;/strong&gt;：官方测试数据显示，该模型在&lt;strong&gt;猫&lt;/strong&gt;的情境声学模式测试中准确率达到&lt;strong&gt;&amp;nbsp;94.6%&lt;/strong&gt;，在狗的声纹识别中准确率达到 92.3%。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;多设备云端协同链路&lt;/strong&gt;：硬件层面采用 Pettichat 项圈进行音频采集与转译，通过云端实时同步至采用衍射光波导显示技术的乐奇 AI 眼镜，实现低延迟的前景文本视场角渲染。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;端侧 AI 智能体生态拓展&lt;/strong&gt;：该功能作为乐奇智能体商店的全新组件引入，后续版本规划接入语音播报、GPS 宠物定位及宠物健康档案等 API 接口，推进&lt;strong&gt;由单一翻译向动物行为世界模型的演进&lt;/strong&gt;。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;（&lt;a href="/Rokid" class="user-mention" title="@Rokid"&gt;&lt;i&gt;@&lt;/i&gt;Rokid&lt;/a&gt; 乐奇）&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3、YC 孵化项目 VoiceOS 发布语音智能体系统：集成多款第三方应用，实现语音指令跨软件一键执行&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;YC 孵化项目 VoiceOS 正式推出&lt;strong&gt;跨应用语音控制系统&lt;/strong&gt;。该系统通过「智能体模式」与「智能听写模式」，将多应用间的繁琐操作简化为单句语音指令，免去了频繁的手动界面切换。该产品旨在&lt;strong&gt;通过语音交互层重构办公软件的日常工作流，大幅提升多任务协同效率&lt;/strong&gt;。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;跨应用智能体驱动链路&lt;/strong&gt;：其「智能体模式」支持单条自然语言指令驱动跨应用协作。例如，指令「回复 Sam 的邮件并预约明天的会议」可自动触发 Gmail 检索及回复、读取日历数据、创建新日程并自动添加参会者，将原本需要 12 步的跨软件手动操作缩减至 1 步。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;应用生态原生级调用&lt;/strong&gt;：已打通&amp;nbsp;&lt;strong&gt;Notion、Cursor、Slack、Gmail、Linear、Figma&amp;nbsp;&lt;/strong&gt;等主流开发与办公软件，支持通过底层接口实现无缝的上下文切换与数据写入。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;自适应排版听写引擎&lt;/strong&gt;：其「听写模式」并非简单的语音转文字，而是&lt;strong&gt;支持根据语境自动对原始语音进行逻辑重组、语法修正与结构化排版&lt;/strong&gt;，输出符合邮件或文档规范的文本。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;本地优先的隐私安全策略&lt;/strong&gt;：默认本地化处理数据，音频不保存在服务器上。控制台提供「不保存云端音频」与「不使用个人数据训练 AI 模型」等安全开关；企业级版本提供零数据保留承诺，并合规对接 SOC 2 Type II、ISO 27001 等安全认证。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;试用链接：&lt;/p&gt;

&lt;p&gt;https://www.voiceos.com/&lt;/p&gt;

&lt;p&gt;( &lt;a href="/Voiceos" class="user-mention" title="@Voiceos"&gt;&lt;i&gt;@&lt;/i&gt;Voiceos&lt;/a&gt;、&lt;a href="/kai" class="user-mention" title="@kai"&gt;&lt;i&gt;@&lt;/i&gt;kai&lt;/a&gt;_brokering\&lt;a href="/X" class="user-mention" title="@X"&gt;&lt;i&gt;@&lt;/i&gt;X&lt;/a&gt;)&lt;/p&gt;
&lt;h2 id="03 有态度的观点"&gt;&lt;strong&gt;03 有态度的观点&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;1、罗剑岚：具身智能不能简单照搬大语言模型的发展路径&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="https://717KLO2znpj6gvtH.public.blob.vercel-storage.com/uploads/RTE_Dev_Comm/UPw7xA7ifp7h_640" title="" alt="图片"&gt;&lt;/p&gt;

&lt;p&gt;上海创智学院副教授、智元机器人首席科学家罗剑岚在接受采访时表示，&lt;strong&gt;具身智能不能简单照搬大语言模型的发展路径。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;罗剑岚强调真实训练数据的重要性。他指出，当前行业中很多所谓「机器人基础模型」更接近在开源底座上的任务适配、中训练或微调，还&lt;strong&gt;没有进入大规模、异构、真实交互数据驱动的预训练阶段&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;罗剑岚给出的核心判断是，机器人领域离线训练 Loss 下降，并不必然对应真实部署成功率提升。大语言模型的 Scaling Law 建立在预训练 Loss 与能力之间相对稳定的统计关系上；&lt;/p&gt;

&lt;p&gt;机器人面对开放物理世界，涉及接触、扰动、长尾场景、硬件差异和任务反馈，模型拟合静态数据不等于能驾驭现实。&lt;/p&gt;

&lt;p&gt;真正的机器人基础模型预训练，也应该像 LLM 一样，&lt;strong&gt;吸纳极其广泛、甚至包含噪声的数据&lt;/strong&gt;。机&lt;strong&gt;器人领域的数据来自真实世界中的交互、失败、纠错、恢复和长尾场景&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;( &lt;a href="/APPSO" class="user-mention" title="@APPSO"&gt;&lt;i&gt;@&lt;/i&gt;APPSO&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;img src="https://717KLO2znpj6gvtH.public.blob.vercel-storage.com/uploads/RTE_Dev_Comm/rybMdOkyjpmC_%E6%88%AA%E5%B1%8F2026-05-21" title="下午8.56.58.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="https://717KLO2znpj6gvtH.public.blob.vercel-storage.com/uploads/RTE_Dev_Comm/Sxx63qZ__dNG_640" title="" alt="图片"&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.rtecommunity.dev" rel="nofollow" target="_blank" title=""&gt;阅读更多 Voice Agent 学习笔记：了解最懂 AI 语音的头脑都在思考什么&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;写在最后：&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;我们欢迎更多的小伙伴参与&lt;strong&gt;「RTE 开发者日报」&lt;/strong&gt;内容的共创，感兴趣的朋友请通过开发者社区或公众号留言联系，记得报暗号「共创」。&lt;/p&gt;

&lt;p&gt;对于任何反馈（包括但不限于内容上、形式上）我们不胜感激、并有小惊喜回馈，例如你希望从日报中看到哪些内容；自己推荐的信源、项目、话题、活动等；或者列举几个你喜欢看、平时常看的内容渠道；内容排版或呈现形式上有哪些可以改进的地方等。&lt;/p&gt;

&lt;p&gt;&lt;img src="https://717KLO2znpj6gvtH.public.blob.vercel-storage.com/uploads/RTE_Dev_Comm/uv9tGDNrZqw4_640" title="" alt="图片"&gt;&lt;/p&gt;

&lt;p&gt;作者提示: 个人观点，仅供参考&lt;/p&gt;</description>
      <author>RTE</author>
      <pubDate>Fri, 19 Jun 2026 22:51:34 +0800</pubDate>
      <link>https://testerhome.com/topics/44346</link>
      <guid>https://testerhome.com/topics/44346</guid>
    </item>
    <item>
      <title>claude-mem：让 Claude Code 不再失忆</title>
      <description>&lt;p&gt;Claude Code 很强。它能读代码、改代码、查调用链、跑命令、补测试，甚至能在复杂项目里连续推进任务。很多开发者第一次认真使用 Claude Code，都会有一种感觉：这已经不太像传统代码生成工具，而更像一个能参与工程工作的 AI 开发助手。&lt;/p&gt;

&lt;p&gt;但用久之后，一个问题会越来越明显：Claude Code 每次新开会话，都像第一次接触你的项目。它能很快读懂眼前的代码，却很难自然延续上一次会话里形成的判断。&lt;/p&gt;

&lt;p&gt;你上次刚告诉它项目架构。你上次刚和它一起排查过一个 Bug。你上次刚解释过某个模块不能随便重构。你上次刚约定过测试命令、Mock 方式和发布注意事项。结果过几天新开一个会话，它又不知道了。&lt;/p&gt;

&lt;p&gt;你不得不重新解释：这个项目用什么技术栈，这个目录是干什么的，这个接口为什么不能改，这个测试为什么要这么写，这个 Bug 上次已经排查过，根因不是数据库，而是缓存刷新顺序。&lt;/p&gt;

&lt;p&gt;这时候你会意识到，Claude Code 最大的问题，可能不是不会写代码，而是记不住项目。它缺的不是一次性的聪明，而是长期项目里的历史感。&lt;/p&gt;
&lt;h2 id="会话是短期的，项目是长期的"&gt;会话是短期的，项目是长期的&lt;/h2&gt;
&lt;p&gt;大多数 AI 编程工具的基本工作单元是会话。你开一个会话，输入任务，模型读取当前上下文，调用工具，完成工作。这个过程看起来很自然，但它和真实软件项目的工作方式并不匹配。&lt;/p&gt;

&lt;p&gt;因为真实项目不是一次性任务。真实项目是长期演进的。&lt;/p&gt;

&lt;p&gt;一个项目里真正重要的信息，往往不是某个文件里的几行代码，而是长期积累下来的工程背景。比如为什么这个模块当初没有拆分，为什么这个接口必须兼容旧版本，为什么这个字段看起来没用但不能删，为什么这个测试必须使用 Mock，为什么某个方案之前试过但失败了，为什么某个线上问题每次发布前都要重点回归。&lt;/p&gt;

&lt;p&gt;这些信息不一定存在于代码里。即使存在，也可能散落在提交记录、需求文档、历史对话、排查过程和人的脑子里。&lt;/p&gt;

&lt;p&gt;对工程师来说，这些叫项目经验。对 AI Agent 来说，这些就是长期上下文。没有这层上下文，它只能反复从当前代码和当前提示里重新推断。&lt;/p&gt;

&lt;p&gt;没有长期上下文，Claude Code 每次都只能基于当前窗口工作。当前窗口里有，它就知道；当前窗口里没有，它就只能重新探索，甚至重新踩坑。&lt;/p&gt;

&lt;p&gt;所以问题不是 Claude Code 不够聪明，而是它缺少一层持续积累的项目记忆。&lt;/p&gt;

&lt;p&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt; 能解决一部分问题，但它不是全部。它适合保存稳定规则，比如技术栈、常用命令、代码规范、测试要求和禁止事项。它更像项目宪法。但项目经验是动态的：Bug 会被修复，方案会被否定，模块会被重构，测试策略会调整，某些坑只有在具体排查过程中才会出现。如果把这些内容全部塞进 &lt;code&gt;CLAUDE.md&lt;/code&gt;，这个文件很快会膨胀成一本项目百科，最后反而污染上下文。&lt;/p&gt;

&lt;p&gt;所以，&lt;code&gt;CLAUDE.md&lt;/code&gt; 管规则还不够。项目还需要一套机制，用来沉淀那些会变化、会过期、但在未来任务里仍然有价值的经验。&lt;/p&gt;

&lt;p&gt;这就是 claude-mem 的位置。&lt;/p&gt;
&lt;h2 id="claude-mem 解决的是记忆"&gt;claude-mem 解决的是记忆&lt;/h2&gt;
&lt;p&gt;很多人第一次听到 claude-mem，会把它理解成 Claude Code 的聊天记录存档。这个理解不准确。&lt;/p&gt;

&lt;p&gt;存档只是把过去发生的事情保存下来。记忆则是把过去发生的事情转化成未来可用的经验。这两者差别很大。&lt;/p&gt;

&lt;p&gt;如果只是保存聊天记录，那未来 Claude 仍然需要在大量历史内容中自己翻找。噪声很高，效率很低。更糟糕的是，大量无关历史还会挤占上下文窗口，让模型更难抓住重点。记录越多，不等于可用经验越多。&lt;/p&gt;

&lt;p&gt;claude-mem 真正有价值的地方在于，它不是简单把完整会话重新塞给 Claude，而是把开发过程中的关键信息提取出来，压缩成更适合未来检索和使用的记忆。&lt;/p&gt;

&lt;p&gt;可以把它理解成这样一条链路：&lt;/p&gt;
 &lt;pre class="highlight plaintext"&gt;&lt;code&gt;捕获开发过程
↓
提取关键信息
↓
压缩成语义记忆
↓
未来任务中按需检索
↓
注入相关上下文
&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;它的目标不是让 Claude Code 记住所有事情。它的目标是：让 Claude Code 在需要的时候，想起真正重要的事情。这里的关键不是全量回放，而是把历史压缩成能服务未来决策的信号。&lt;/p&gt;

&lt;p&gt;举个例子。假设你在做一个订单系统。有一次线上出现问题：用户重复点击取消订单按钮，导致库存被重复释放。你和 Claude Code 一起排查，最后发现根因是订单取消接口缺少幂等保护。你们修复了代码，也补了测试。&lt;/p&gt;

&lt;p&gt;如果没有长期记忆，这次经验就只存在于当前聊天里。过了两周，你又让 Claude Code 修改订单取消逻辑。新会话里没有相关上下文，它可能只会从当前代码出发分析。它也许会读接口文件，也许会看测试，但未必知道这个模块历史上出过重复释放库存的事故。于是它可能只补正常取消流程测试，而漏掉重复请求、并发请求、已取消订单再次取消等关键场景。&lt;/p&gt;

&lt;p&gt;如果使用 claude-mem，这次历史经验可以被沉淀成类似这样的记忆：&lt;/p&gt;

&lt;p&gt;订单取消接口历史上出现过库存重复释放问题。&lt;/p&gt;

&lt;p&gt;根因是取消操作缺少幂等保护。&lt;/p&gt;

&lt;p&gt;后续修改订单取消逻辑时，必须覆盖重复请求、并发请求、已取消订单再次取消等场景。&lt;/p&gt;

&lt;p&gt;下次 Claude Code 再处理订单取消相关任务时，它就有机会检索到这条记忆。此时它不会只看当前代码，而会知道这个模块的历史风险。它会更倾向于补幂等测试，也会提醒你注意并发和重复请求。这不是凭空增加测试用例，而是把历史事故转化成新的质量护栏。&lt;/p&gt;

&lt;p&gt;这就是从代码生成到风险感知的变化。&lt;/p&gt;
&lt;h2 id="AI Agent 要的是更好的上下文"&gt;AI Agent 要的是更好的上下文&lt;/h2&gt;
&lt;p&gt;这里要强调一点：claude-mem 的价值不是让上下文变得更长。&lt;/p&gt;

&lt;p&gt;很多人对 AI 工具有一个误解：只要上下文窗口足够大，把所有东西都塞进去，模型自然就会变强。实际不是这样。&lt;/p&gt;

&lt;p&gt;上下文越长，噪声越多。噪声越多，模型越容易忽略关键约束。信息虽然还在窗口里，但模型未必能稳定使用它。很多时候，问题不是模型没有看到，而是关键约束被淹没了。&lt;/p&gt;

&lt;p&gt;比如你在会话开头告诉 Claude：&lt;/p&gt;

&lt;p&gt;订单取消必须保证幂等，不能重复释放库存。&lt;/p&gt;

&lt;p&gt;但后面又塞进去大量日志、代码、工具输出、历史讨论。到了真正改代码时，模型可能已经不再稳定关注这条约束。&lt;/p&gt;

&lt;p&gt;这不是信息消失了，而是注意力被稀释了。&lt;/p&gt;

&lt;p&gt;所以 AI Agent 真正需要的不是更多上下文，而是更高信噪比的上下文。claude-mem 的核心价值就在这里：它不是粗暴地扩大输入，而是把历史经验压缩成更高信号密度的记忆，并在相关任务中按需使用。&lt;/p&gt;

&lt;p&gt;这也是为什么 claude-mem 更像一个上下文工程工具，而不是普通记事本。&lt;/p&gt;

&lt;p&gt;没有记忆的 Claude Code，更像一个很聪明的临时外包。你给它足够背景，它就能做得不错；但下一次再来，它又需要重新入场。它不知道之前发生过什么，也不知道这个项目踩过哪些坑。&lt;/p&gt;

&lt;p&gt;有了 claude-mem 之后，Claude Code 才有机会变成长期项目成员。它能记住这个模块过去为什么这样设计，这个 Bug 之前怎么排查过，哪些方案试过但失败了，哪些测试场景必须覆盖，哪些外部依赖容易出问题，哪些代码看起来可以改但实际上有兼容性约束。&lt;/p&gt;

&lt;p&gt;这对工程任务非常关键。因为软件开发不是从零开始写代码，而是在历史约束下做增量修改。AI Agent 如果没有历史感，就很容易给出看起来正确、实际危险的建议。有了长期记忆，它至少有机会知道：这里以前为什么不能这么做，以及如果一定要改，应该先验证哪些风险。&lt;/p&gt;
&lt;h2 id="对测试开发尤其重要"&gt;对测试开发尤其重要&lt;/h2&gt;
&lt;p&gt;claude-mem 对测试开发场景尤其有价值。&lt;/p&gt;

&lt;p&gt;因为测试工作本质上高度依赖历史风险。一个好的测试工程师，不只是看当前代码写用例。他会知道哪些模块经常出问题，哪些边界条件以前漏过，哪些外部依赖不稳定，哪些场景发布前必须回归。&lt;/p&gt;

&lt;p&gt;这些经验很难完全从代码里推导出来。&lt;/p&gt;

&lt;p&gt;比如支付回调历史上出现过重复通知。优惠券模块曾经多次出现叠加计算错误。权限接口曾经漏过跨租户访问。订单状态机曾经在并发场景下异常流转。缓存刷新曾经导致读写不一致。&lt;/p&gt;

&lt;p&gt;这些都是高价值测试记忆。&lt;/p&gt;

&lt;p&gt;如果 claude-mem 能把历史缺陷和测试策略沉淀下来，Claude Code 在后续生成测试用例时，就不会只输出正常流程、异常流程、边界值这种模板化内容，而是更接近真实项目风险。&lt;/p&gt;

&lt;p&gt;这对测试开发非常重要。真正有价值的测试，不是覆盖代码表面，而是覆盖历史上真实出过问题的地方。换句话说，这种记忆比模板更有价值，因为它指向的是项目真正疼过的地方。&lt;/p&gt;

&lt;p&gt;当然，长期记忆不是越多越好。如果什么都记，记忆会变成垃圾场。如果过时内容不清理，Claude 会被旧结论误导。如果敏感信息被记录，可能带来安全风险。如果错误判断进入记忆，后续任务可能持续放大这个错误。&lt;/p&gt;

&lt;p&gt;所以使用 claude-mem 的关键，不是打开插件就完事，而是建立一套记忆使用方法：哪些内容值得记，哪些内容不能记，什么时候应该检索历史，什么时候应该验证当前事实，什么时候应该清理过期记忆，&lt;code&gt;CLAUDE.md&lt;/code&gt; 和 claude-mem 应该怎么分工。&lt;/p&gt;

&lt;p&gt;这些才是 claude-mem 最佳实践的核心。记忆系统只有进入工程流程，才能真正变成上下文工程的一部分。&lt;/p&gt;
&lt;h2 id="claude-mem 的真正价值"&gt;claude-mem 的真正价值&lt;/h2&gt;
&lt;p&gt;Claude Code 已经让 AI 编程助手从代码生成器走向工程执行者。但如果没有长期记忆，它仍然很难真正参与一个长期项目。因为长期项目需要的不只是推理能力，还需要历史感。&lt;/p&gt;

&lt;p&gt;claude-mem 的价值，正是给 Claude Code 补上这层历史感。&lt;/p&gt;

&lt;p&gt;它不是为了让 Claude 记住一切，也不是为了把所有聊天记录塞回上下文。它真正要解决的是：如何把一次次开发、排查、测试、失败、修复中产生的经验，转化成未来任务可用的上下文资产。&lt;/p&gt;

&lt;p&gt;一句话总结：&lt;/p&gt;

&lt;p&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt; 让 Claude Code 知道项目规则，claude-mem 让 Claude Code 记住项目经验。&lt;/p&gt;

&lt;p&gt;从这个角度看，claude-mem 不是一个简单的记忆插件，而是 AI Agent 进入真实工程项目的一块关键拼图。&lt;/p&gt;

&lt;p&gt;下一篇，我们继续聊：为什么 claude-mem 的核心不是 Memory，而是 Context Engineering。&lt;/p&gt;
&lt;h5 id="FunTester 名片｜万粉千文，百无一用"&gt;FunTester 名片｜万粉千文，百无一用&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://mp.weixin.qq.com/s/o1dGKThvC64LvJDnKLybOw" rel="nofollow" target="_blank" title=""&gt;软件测试的道与术&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mp.weixin.qq.com/s/MFgZ3Btx0TKOd5sFGFDxcg" rel="nofollow" target="_blank" title=""&gt;测试开发成长史&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mp.weixin.qq.com/s/wyyn32HrwmH5uV8F0d2HDQ" rel="nofollow" target="_blank" title=""&gt;AI ，测试有点东西&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mp.weixin.qq.com/s/5ssJH4yOE3H1Vk4ziJ0Bdg" rel="nofollow" target="_blank" title=""&gt;《从 Java 开始性能测试》连载全集&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mp.weixin.qq.com/s/3ilD1fnJFo9Gt8puO7zFiQ" rel="nofollow" target="_blank" title=""&gt;性能测试修炼之道&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mp.weixin.qq.com/s/t2k6IITzaFicaEi77iZ5tA" rel="nofollow" target="_blank" title=""&gt;故障测试与混沌工程实战合集&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mp.weixin.qq.com/s/CSE2UmQNFrHaEnyyM4U41w" rel="nofollow" target="_blank" title=""&gt;我的语言迁徙：Java &amp;amp; Groovy &amp;amp; Go&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mp.weixin.qq.com/s/0F41EuPsziWwr25o7Cj3-A" rel="nofollow" target="_blank" title=""&gt;懂点 Web 前端&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mp.weixin.qq.com/s/LKEfCkx_iEr8JrA1lT3HnQ" rel="nofollow" target="_blank" title=""&gt;视频教程全收录&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <author>Fhaohaizi</author>
      <pubDate>Fri, 19 Jun 2026 12:02:20 +0800</pubDate>
      <link>https://testerhome.com/topics/44345</link>
      <guid>https://testerhome.com/topics/44345</guid>
    </item>
    <item>
      <title>随笔 - 技术力是很多岗位的基本门槛</title>
      <description>&lt;h2 id="前言"&gt;前言&lt;/h2&gt;
&lt;p&gt;今日端午，现在是早上 6 点半。本来可以睡懒觉的，但奈何家里的小母狗吐了，把我折腾了起来。所以趁着有点时间写一些东西。&lt;/p&gt;
&lt;h2 id="工作变动"&gt;工作变动&lt;/h2&gt;
&lt;p&gt;节前跟同事做好了工作交接，节后去 Agent Runtime 团队做测试。主要负责 Agent 沙箱产品，它也有开源版本。主要为各个 AI 产品提供 Agent 沙箱，用于运行浏览器，代码等功能。元宝，codebuddy，workbuddy 以及我之前负责的产品都是使用它来让 Agent 执行代码和浏览器的。所以整个工作更偏向了底层虚拟化上。后续需要跟文件系统，网络，linux 内核打交道了。也算是回归老本行，毕竟我当年是靠玩 k8s 起家的。而这个产品就相当于开发了一个小 k8s，只不过是用 microvm+containerd，而不是纯容器方案。&lt;/p&gt;

&lt;p&gt;新工作暂时只有我一个测试（之前都是研发自测），老板考虑到团队里只有我做过虚拟化的东西，所以就先派我一个人来了。要说压力肯定也是有的，开发团队那边担心我们是不是能够 hold 住这个产品，毕竟这里涉及到大量的 linux 内核知识，不是一般测试能搞的明白的东西。这也是为什么之前他们主要是研发自测来保证质量的。不过有压力也有动力，之前玩 k8s 的时候好歹是接触过这些的，所以也还好，起码研发同学说什么我还是听得懂的。&lt;/p&gt;
&lt;h2 id="技术，是很多高薪岗位的硬性门槛"&gt;技术，是很多高薪岗位的硬性门槛&lt;/h2&gt;
&lt;p&gt;最近在星球跟很多同学聊天，也是感慨颇多。很多同学正在找工作，行情不好，工作很不好找，而且基本上 AI 是面试必问的东西。企业对测试人员的要求越来越高。很多时候，大多数测试人员连去面试的门槛都没够上。而这个门槛， 往往就是技术力。&lt;/p&gt;

&lt;p&gt;一说到这里，肯定又会有很多人来说技术对测试人员没什么用，尤其是 AI 出现后技术更没用了，测试人员还是要深耕方法论和业务等等 balabala 的。但很多同学没意识到，一些产品里，技术就是它的业务。懂相关技术，是进入到这个产品的基本门槛（即便是产品经理也一样，不懂这门技术也做不好这个产品）。就比如我马上要去的 Agent runtime。没点虚拟化的底子，根本连开发同学再说什么都不知道。我们测试团队里有大几十个人，为什么让我去？因为我专门研究过虚拟化的东西，因为老板知道我已经跨过了这道门槛。&lt;/p&gt;

&lt;p&gt;在其他产品里，高职级可能是个稀罕物，但在这个产品里可能遍地的十一级和十二级，跟我沟通的开发同学里，我已经见到两个十二级大佬了，没准哪天还能见到个十三级。&lt;/p&gt;

&lt;p&gt;这里我想聊聊门槛的事情，它是一个硬性的指标。在跨过这道门槛前，不论你说你在过往工作中有多优秀，测试思维多强悍，业务理解多深刻都没有用。门槛就是门槛，它负责把一切门外汉死死的拦在外面。就好修车修的再优秀，别人也不会认为你能修飞机的。毕业就想进大厂，最低 985,211 的学历就是门槛。社招想进大厂，出类拔萃的技术能力和相关经验就是门槛。达不到门槛，那就连个面试资格都没有。而门槛本身，注定不是大多数人能跨过去的，否则它就不叫门槛了。&lt;/p&gt;
&lt;h2 id="如果不甘于现状，那就要努力的去追那道门槛"&gt;如果不甘于现状，那就要努力的去追那道门槛&lt;/h2&gt;
&lt;p&gt;不满足于现状的同学可以想想，我们现在做的工作，是不是大多数人都能做的？是不是招个新人来，培训培训就能立马投入到工作里的（别狡辩说他们测试思维和业务能力差，就说人家能不能过这个门槛，上手开始做）。如果是，那高薪和高职级基本和你无缘。有些同学可能不服，说现在有 AI 了，自动化代码和平台代码谁还不能写？但大家想想，谁说的技术力就是写平台和自动化的？你见过哪个年薪百万的测试人员是靠写自动化拿的这份钱？&lt;/p&gt;

&lt;p&gt;我在社区里一直说，不要只盯着自己那点业务，不要只盯着接口自动化，UI 自动化，他们的天花板没多高。想要破局的同学，需要多去接触那些在大多数岗位上可能用不到的东西。去接触那些只有一些专业领域才能用的上的东西。那些可能才是撬动大厂岗位，高薪岗位门槛的东西。就好像我在 2016 年开始搞 Dcoker 和 K8S，当时社区很多同学说学这些没用，这些是运维要干的，但它们正是我现在进入 agent runtime 团队做测试的关键，当年也因为 K8S 的能力拿到过阿里和百度的 offer（都是智能云团队）。 我 17 年开始硬学 AI（不是现在的 AI 提效，当年学 AI 是奔着测试 AI 去的，从模型训练原理开始硬学），当时很多同学也说对测试人员没什么用，但后来我就是靠着 AI 和 K8S 进的大厂，拿到了现在的职级。&lt;/p&gt;

&lt;p&gt;测试思维是很重要，业务理解能力这些软性能力都很重要。 但测试云原生产品，起码你得知道虚拟化的基本技术。测试大数据产品你得懂大数据技术基础。你说你想进大厂或者 AI 独角兽去测试 AI 产品，但你连超参是什么都不知道，人家根本懒得跟你聊。就好像相亲一样，你说你性格好，你说你情绪价值高。但对面姑娘说：我只找身高 175，月薪 5W 以上的。 再有趣的灵魂，也要先过了那道门槛，人家才有兴趣去了解，在那之前，只看硬性指标。&lt;/p&gt;
&lt;h2 id="结尾"&gt;结尾&lt;/h2&gt;
&lt;p&gt;最后也说一下，去追那些门槛有些时候也会失败的，不是每个人最后都会成功。我当年去学那些东西的时候，也不知道未来是不是真的用的上，是不是真的能进入那些专门的岗位。这个鸡汤我不能硬给大家灌进去。未来都是未知的，我现在瞎折腾的东西，我也不确定是不是以后用的上。努力去追那些门槛一定是有失败率的，就像不一定努力学习了就能进清华北大一样。但我仍然愿意去拼一拼那个机会。好了~ 今天就说这么多吧。&lt;/p&gt;

&lt;p&gt;最后再宣传一下我得知识星球：&lt;/p&gt;

&lt;p&gt;&lt;img src="/uploads/photo/2026/bd3d83d0-3098-481a-af69-e7c461932539.png!large" title="" alt=""&gt;&lt;/p&gt;</description>
      <author>ycwdaaaa</author>
      <pubDate>Fri, 19 Jun 2026 08:09:19 +0800</pubDate>
      <link>https://testerhome.com/topics/44344</link>
      <guid>https://testerhome.com/topics/44344</guid>
    </item>
    <item>
      <title>Gemini 音频总监：带摄像头 AirPods 将成最普及 AI 设备；Moss 推出实时网页检索语音智能体，依托无向量数据库检索架构丨日报</title>
      <description>&lt;p&gt;&lt;img src="https://717KLO2znpj6gvtH.public.blob.vercel-storage.com/uploads/RTE_Dev_Comm/yHKWIt7NBF9Y_640" title="" alt="图片"&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;开发者朋友们大家好：&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;这里是 &lt;strong&gt;「RTE 开发者日报」&lt;/strong&gt;，每天和大家一起看新闻、聊八卦。我们的社区编辑团队会整理分享&amp;nbsp;RTE（Real-Time Engagement）&amp;nbsp;领域内「有话题的&lt;strong&gt;技术&lt;/strong&gt;」、「有亮点的&lt;strong&gt;产品&lt;/strong&gt;」、「有思考的&lt;strong&gt;文章&lt;/strong&gt;」、「有态度的&lt;strong&gt;观点&lt;/strong&gt;」、「有看点的&lt;strong&gt;活动&lt;/strong&gt;」，但内容仅代表编辑的个人观点，欢迎大家留言、跟帖、讨论。&lt;/p&gt;

&lt;p&gt;&lt;em&gt;本期编辑：&lt;a href="/koki" class="user-mention" title="@koki"&gt;&lt;i&gt;@&lt;/i&gt;koki&lt;/a&gt;、@ 鲍勃&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="01 有话题的技术"&gt;&lt;strong&gt;01 有话题的技术&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;1、NVIDIA 发布免训练空间推理智能体 SpatialClaw：以 Python 代码为动作接口，20 项基准测试平均提升 11.2 分&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="https://717KLO2znpj6gvtH.public.blob.vercel-storage.com/uploads/RTE_Dev_Comm/MduMhsOnbsZF_640" title="" alt="图片"&gt;&lt;/p&gt;

&lt;p&gt;NVIDIA Research 推出免训练空间推理智能体 SpatialClaw，该智能体&lt;strong&gt;直接将 Python 代码作为处理复杂视觉任务的动作接口&lt;/strong&gt;。通过在持久化内核中动态编写代码、调用感知模块并复用感知变量，该智能体无需任何针对特定基准或模型的微调，即可在多样化的空间推理任务中显著提升性能。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;以 Python 代码替代预定义工具链&lt;/strong&gt;：智能体不再依赖调用固定的预定义工具集，而是在一个持久化内核中动态编写并执行 Python 代码，用于灵活组合感知模块并实时调整后续推理策略。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;感知输出变量化与科学计算库集成&lt;/strong&gt;：将视觉感知的输出直接转化为 Python 语言的标准变量，使其能与 NumPy、SciPy 等主流科学计算库无缝结合，直接进行复杂的空间几何与数学运算。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;持久化内核下的多步骤动态修正&lt;/strong&gt;：在执行长时序、多步骤任务时，智能体能够审查中间运行结果和内核状态，并根据报错或中间反馈实时修正代码逻辑与执行策略。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;免微调跨基准性能提升 11.2 分&lt;/strong&gt;：在不进行任何基准或模型专属微调的前提下，SpatialClaw 在 20 项空间推理基准测试中，相较于先前的前沿智能体平均成绩提升 11.2 分，且在 6 种不同的基座模型上均表现出高度的性能一致性。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;https://github.com/NVlabs/SpatialClaw&lt;/p&gt;

&lt;p&gt;论文链接：&lt;/p&gt;

&lt;p&gt;https://spatialclaw.github.io/static/pdfs/spatialclaw.pdf&lt;/p&gt;

&lt;p&gt;( &lt;a href="/NVIDIAAI" class="user-mention" title="@NVIDIAAI"&gt;&lt;i&gt;@&lt;/i&gt;NVIDIAAI&lt;/a&gt;\&lt;a href="/X" class="user-mention" title="@X"&gt;&lt;i&gt;@&lt;/i&gt;X&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2、Soniox 发布 v5 Real-Time 实时语音模型：原生支持 60+ 语言同传、流式说话人分离与自定义端点灵敏度&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="https://717KLO2znpj6gvtH.public.blob.vercel-storage.com/uploads/RTE_Dev_Comm/j8IXXkw9JqWC_640" title="" alt="图片"&gt;&lt;/p&gt;

&lt;p&gt;Soniox 推出流式语音 AI 模型 Soniox v5 Real-Time，将&lt;strong&gt;转录、同传及说话人分离等分步级联管线整合为单一的原生流式系统&lt;/strong&gt;。该模型专门针对重叠发言、嘈杂背景及多语种混合等复杂现实场景设计，能显著降低语音智能体、会议同传和客户服务系统的整体响应延迟。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;实时说话人分离&lt;/strong&gt;：&lt;strong&gt;无需等待音频结束进行后处理&lt;/strong&gt;。模型在流式推断过程中，结合声学特征与语义上下文实时识别发言人更替，在多人插话、重叠发言或背景噪声环境下输出带有发言人标签的文本。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;原生同传与 3600 个语言对识别&lt;/strong&gt;：支持&lt;strong&gt;超 60 种语言的原生语种识别&lt;/strong&gt;。在流式转录过程中直接完成翻译，消除先转录再翻译的多阶段 API 调用延迟，支持&amp;nbsp;&lt;strong&gt;3,600 个语言对的实时互译&lt;/strong&gt;，并优化了人名、专有名词和代词的&lt;strong&gt;翻译精度&lt;/strong&gt;。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;新增 endpoint_sensitivity 端点检测参数&lt;/strong&gt;：升级了基于语义、语调和上下文的端点检测算法。向开发者开放 endpoint_sensitivity 控制参数，调高该值可缩短检测延迟（适用于命令控制系统），调低该值可增加等待时间（适用于长文本听写）。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;原生上下文注入 API&lt;/strong&gt;：支持在建立 API 连接时传入会话特定的专有名词、产品 SKU、人名或首选翻译术语。该机制在模型内部生效而非后处理，可直接提升复杂声学环境下专有名词的识别和翻译准确率。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;向下兼容与平滑迁移&lt;/strong&gt;：新模型命名为 stt-rt-v5，完全兼容现有 API 接口。原 stt-rt-v4 模型将于2026年6月30日退役，届时未手动更改模型名称的请求将自动路由至新版。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;( &lt;a href="/soniox" class="user-mention" title="@soniox"&gt;&lt;i&gt;@&lt;/i&gt;soniox&lt;/a&gt;_ai\&lt;a href="/X" class="user-mention" title="@X"&gt;&lt;i&gt;@&lt;/i&gt;X&lt;/a&gt;)&lt;/p&gt;
&lt;h2 id="02 有亮点的产品"&gt;&lt;strong&gt;02 有亮点的产品&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;1、Snap 发布 SPECS 一体式 AR 眼镜：定价 2195 美元，主打高精度手势与语音纯空间交互&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="https://717KLO2znpj6gvtH.public.blob.vercel-storage.com/uploads/RTE_Dev_Comm/1UxyT9dFIU5Q_image.png" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;Snap 在 AWE 2026 开发者大会上正式发布首款消费级一体式 AR 眼镜 SPECS，并宣布完成对空间增强现实公司 Illumix 的收购。该设备采用独立式无线设计，通过双芯片架构与电致变色光学透视（OST）技术实现 7ms 的运动到光子延迟。SPECS*&lt;em&gt;&amp;nbsp;彻底摒弃了物理控制器，采用高精度手势追踪与语音协同的纯空间交互方案&lt;/em&gt;&lt;em&gt;。然而，&lt;/em&gt;&lt;em&gt;其 2195 美元的高昂售价引发了行业关于「轻量级交互硬件如何跑通商业化」的广泛争议。&lt;/em&gt;*&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;无控制器空间交互：基于手势追踪与语音控制&lt;/strong&gt;：SPECS 抛弃了传统手柄，完全依赖手部追踪与语音指令进行控制。依托低延迟算法，用户可通过自然手势在空中直接进行虚拟物体的抓取、缩放、点击及拖拽。配合 Snap OS 2.0，设备支持多窗口空间管理，交互逻辑向主流混合现实（MR）设备看齐。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;双处理器异构架构支撑 7ms 交互延迟&lt;/strong&gt;：搭载两颗骁龙处理器，其中一颗专门用于计算机视觉与空间定位（处理手势识别与空间锚定算法），另一颗专门负责运行 Snap OS 和渲染 Lenses。硬件层面的解耦确保了高频手势交互的响应速度，实现 7ms 的运动到光子延迟。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;空间地图技术与智能体化开发流&lt;/strong&gt;：整合新收购的 Illumix 空间地图技术，强化虚拟菜单和物体在真实物理空间中的锚定精度。同时，联合 Claude Code、Codex 和 Cursor 为开发者引入智能体化开发预览版。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;但不可忽视的是 Specs 仍存在几个难以回避的短板&lt;strong&gt;。续航、重量、性能这个「不可能三角」&lt;/strong&gt;依然制约着 Specs 的体验，再加上其搭载的 Snap OS 2.0 和高通双芯片方案带来的高能耗，使其&lt;strong&gt;续航只有 45 分钟左右&lt;/strong&gt;，这意味 Specs 无法成为重度的生产力工具，只能面向特定的短周期任务运行。同时，全彩光波导镜片在特定场景下会出现不可避免的颜色分离和彩虹伪影的物理缺陷，这也让 Specs 暂时&lt;strong&gt;难以进入高精度或极端环境场景&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;已在官网开放预售（需支付 200 美元可退还定金），售价为 2195 美元，预计今年秋季在美、英、法三地首批出货。&lt;/p&gt;

&lt;p&gt;（&lt;a href="/VR" class="user-mention" title="@VR"&gt;&lt;i&gt;@&lt;/i&gt;VR&lt;/a&gt; 陀螺、&lt;a href="/snap" class="user-mention" title="@snap"&gt;&lt;i&gt;@&lt;/i&gt;snap&lt;/a&gt;）&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2、Bland 获 5000 万美元融资：自研语音模型主打非线性交互，支持 45 分钟超长通话&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;语音智能体初创公司 Bland 宣布完成&amp;nbsp;&lt;strong&gt;5000 万美元 C 轮融资&lt;/strong&gt;，&lt;strong&gt;累计融资总额已超 1 亿美元&lt;/strong&gt;。该轮资金将用于扩展自研语音模型并扩充工程团队。Bland 避开了套壳第三方大模型的通用路线，通过全栈自研语音模型，直接在&lt;strong&gt;医疗、金融等强监管行业落地高风险、高复杂度的超长语音交互服务&lt;/strong&gt;。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;自研全栈语音模型&lt;/strong&gt;：Bland 拒绝使用第三方基础模型 API 进行套壳封装，其智能体完全运行在自研的语音底座模型上（且不支持客户替换底层模型），专门针对实时通话中的高延迟、易打断、多歧义等痛点进行底层调优。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;支持超长非线性对话&lt;/strong&gt;：突破传统语音机器人只能处理预约提醒、电话路由等简短脚本任务的局限，&lt;strong&gt;单次通话时长可达 30 至 45 分钟&lt;/strong&gt;，支持如引导老年患者测量血压并评估是否呼叫救护车等&lt;strong&gt;高风险、多分支的非线性复杂决策&lt;/strong&gt;。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;周通话处理量超 350 万次&lt;/strong&gt;：目前主要服务于医疗保健、金融服务等强监管行业，拥有超 250 家企业客户（包括 Samsara、Kin Insurance 等），周通话处理量超 350 万次，上年度累计处理通话量超 1.75 亿次。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;打破「电话消亡」的市场偏见&lt;/strong&gt;：在 Y Combinator 孵化阶段，曾有 180 位投资人因「电话业务日渐式微」的预判而拒绝投资。Bland 本轮获得&lt;strong&gt;戴尔科技资本、ElevenLabs 首席技术官及 Twilio 创始人的联合注资&lt;/strong&gt;，验证了实时语音在特定高门槛业务中的刚需属性。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;( &lt;a href="/SiliconANGLE" class="user-mention" title="@SiliconANGLE"&gt;&lt;i&gt;@&lt;/i&gt;SiliconANGLE&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3、Genesis AI 发布折叠式机器人 Eno：集成 22 自由度灵巧手，最大作业高度达 2.2 米&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Genesis AI 发布首款通用移动操作机器人 Eno，采用无头无腿的&lt;strong&gt;「轮式底盘 + 三段折叠躯干」去拟人化结构&lt;/strong&gt;，并搭载自研高自由度灵巧手。该机器人本体与 GENE 基础模型系统深度集成，旨在平整工业、仓储及实验室场景中，直接利用人类既有工具实现全栈软硬件协同的操作闭环。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;三段式折叠躯干机构&lt;/strong&gt;：采用非双足的轮式底盘，躯干支持前倾、后仰或下腰等动态姿态，可直接调节双臂基座高度，使最大作业高度达 2.2 米，非工作状态下可折叠收纳以降低空间占用。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;22 主动自由度自研灵巧手&lt;/strong&gt;：手指采用不等长设计以贴合人手比例，支持可反向驱动以确保人机协作安全；手部集成摄像头与触觉传感器，单臂负载能力 3 至 5 公斤，单次续航 4 至 6 小时。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;低成本数据采集方案&lt;/strong&gt;：避开传统高成本遥操作设备，计划部署单价约 300 美元的自研训练手套，通过大规模部署采集专家工人的高精度手部操作数据。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;无拟人外壳与认知交互界面&lt;/strong&gt;：机身无外露执行器、线缆、铰链或螺丝孔；去除了头部与脸部设计，&lt;strong&gt;可选配胸前认知交互屏幕，通过视觉窗口实时展示机器人的推理状态与操作意图&lt;/strong&gt;。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;端到端长时序任务控制&lt;/strong&gt;：由底层控制频率达 200Hz 级别的 GENE-26.5 基础模型系统驱动，支持双手协同进行线束整理、缠绕粘性胶带、移液和试管加盖等非结构化任务，具备失误后自主重试与容错能力。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;（@ 极客公园）&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4、Moss 发布实时网页检索语音智能体 Founding Agent：依托 &amp;lt;10ms 无向量数据库检索架构&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="https://717KLO2znpj6gvtH.public.blob.vercel-storage.com/uploads/RTE_Dev_Comm/899lFT-UuBdc_640" title="" alt="图片"&gt;&lt;/p&gt;

&lt;p&gt;实时检索基础设施服务商 Moss 宣布推出网页原生语音智能体「Founding Agent」并开启早期访问。该智能体直接运行在 Moss 自研的超低延迟检索架构上，跳过传统外部向量数据库，将端到端检索延迟压缩至 10 毫秒以内，旨在为企业网站提供即时、无延迟的语音问答、客户意向筛选与会议预约服务。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;无向量数据库的底层检索架构&lt;/strong&gt;：Founding Agent 摒弃了传统的外部向量数据库设计，消除因网络跳数带来的延迟瓶颈，支持 100% 本地、边缘、浏览器、设备或云端部署，直接在 AI 运行的同侧执行语义检索。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;端到端检索延迟低于 10ms&lt;/strong&gt;：在 10 万个文档的基准测试中，Moss 的检索延迟 P50 仅为 3.1ms，P99 为 5.4ms，相较于 ChromaDB（351.8ms）或 Pinecone（432.6ms）等传统向量数据库，检索速度提升近 100 倍，解决了实时语音交互中因检索卡顿导致的对话中断问题。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;网页端多功能语音交互&lt;/strong&gt;：该智能体可直接嵌入企业官网，基于企业自身的文档、产品手册、常见问题解答及内部知识库进行本地索引，支持用户通过实时语音直接询问定价、技术规格等复杂问题，并支持自动评估潜在客户意向及调用日程接口预约会议。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;主流语音与大模型生态原生兼容&lt;/strong&gt;：支持通过数行代码接入现有大模型技术栈，底层深度集成语音 AI 基础设施，并与 LangChain、DSPy 及 Vercel AI SDK 等主流大模型开发工具链无缝互通。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;( &lt;a href="/moss.dev" class="user-mention" title="@moss.dev"&gt;&lt;i&gt;@&lt;/i&gt;moss.dev&lt;/a&gt;)&lt;/p&gt;
&lt;h2 id="03 有态度的观点"&gt;&lt;strong&gt;03 有态度的观点&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;1、Gemini 音频总监预言：配备摄像头的 AirPods 将成为最普及的 AI 可穿戴设备&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="https://717KLO2znpj6gvtH.public.blob.vercel-storage.com/uploads/RTE_Dev_Comm/sKS0XCe63l_9_640" title="" alt="图片"&gt;&lt;/p&gt;

&lt;p&gt;根据最新行业爆料，苹果计划于明年推出配备摄像头的全新 AI 版 AirPods，届时将与 20 周年纪念版 iPhone Pro 以及第二代折叠屏 iPhone 一同发布。&lt;/p&gt;

&lt;p&gt;对此，Google DeepMind Gemini 音频总监、语音模型 Hume AI 创始人做出预言：配备摄像头的 AirPods 将成为最普及的 AI 可穿戴设备。&lt;/p&gt;

&lt;p&gt;他给出了以下三点理由：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;更出色的音频体验：相比智能眼镜（其声音容易被环境噪音盖过），AirPods 能提供更好的听觉反馈。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;无需承担「时尚选择」的压力：眼镜往往带有强烈的个人审美和时尚标签，而 AirPods 则是几乎人手一个、大众早已普遍接受的日常用品。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;无缝叠加实用功能：在普通 AirPods 原有功能之上，它能直接叠加实时翻译、烹饪步骤指导、博物馆导览等丰富的 AI 场景。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;( &lt;a href="/AlanCowen" class="user-mention" title="@AlanCowen"&gt;&lt;i&gt;@&lt;/i&gt;AlanCowen&lt;/a&gt;\&lt;a href="/X" class="user-mention" title="@X"&gt;&lt;i&gt;@&lt;/i&gt;X&lt;/a&gt;)&lt;/p&gt;
&lt;h2 id="04 Real-Time Demo"&gt;&lt;strong&gt;04 Real-Time Demo&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;开发者&lt;a href="/Ryan" class="user-mention" title="@Ryan"&gt;&lt;i&gt;@&lt;/i&gt;Ryan&lt;/a&gt;__Stephen：&lt;/p&gt;

&lt;p&gt;playing with realtime diffusion ui&lt;/p&gt;

&lt;p&gt;正在测试实时扩散效果的用户界面&lt;/p&gt;

&lt;p&gt;( &lt;a href="/Ryan" class="user-mention" title="@Ryan"&gt;&lt;i&gt;@&lt;/i&gt;Ryan&lt;/a&gt;__Stephen\&lt;a href="/X" class="user-mention" title="@X"&gt;&lt;i&gt;@&lt;/i&gt;X&lt;/a&gt;)&lt;/p&gt;
&lt;h2 id="05 社区黑板报"&gt;&lt;strong&gt;05 社区黑板报&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;招聘、项目分享、求助……任何你想和社区分享的信息，请联系我们投稿。（加微信 creators2022，备注「社区黑板报」）&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1、&lt;/strong&gt;🚀** TRAE AI 创造力大赛正式开启！超百万现金等你来造 **&lt;/p&gt;

&lt;p&gt;&lt;img src="https://717KLO2znpj6gvtH.public.blob.vercel-storage.com/uploads/RTE_Dev_Comm/mZ978NV8iZeF_640" title="" alt="图片"&gt;&lt;/p&gt;

&lt;p&gt;🙋 正在做对话式 AI、Voice Agent、多模态 AI 或硬件产品的朋友看过来！&lt;/p&gt;

&lt;p&gt;如果你最近有一个 AI 产品想法，或者正在开发自己的项目，我们推荐关注一下这次 TRAE AI 创造力大赛。&lt;/p&gt;

&lt;p&gt;RTE 开发者社区也将组织开发者参赛、交流和作品分享。如果你想寻找队友、早起测试用户，或者和更多 Builder 一起边做边聊，欢迎加入「创造力大赛 RTE 小分队」交流群。&lt;/p&gt;

&lt;p&gt;💬 如何加群？添加微信 Creators2022，备注：&lt;/p&gt;

&lt;p&gt;【创造力大赛 + 一句话介绍 + 项目方向】&lt;/p&gt;

&lt;p&gt;👇 以下为赛事详情：&lt;/p&gt;

&lt;p&gt;TRAE 是字节跳动旗下 AI 产品，可以帮你完成产品开发与各类工作任务。&lt;/p&gt;

&lt;p&gt;大赛提供高额奖金池、字节系超高曝光权益、官方辅导支持、行业投资机会，让你的好产品真正被看见！&lt;/p&gt;

&lt;p&gt;💡有想法就能参赛——用 TRAE 把「想想而已」变成能体验的产品&lt;/p&gt;

&lt;p&gt;1⃣报名：社区发一篇「创意提案」帖，3 分钟搞定，审核通过即参赛&lt;/p&gt;

&lt;p&gt;2⃣领奖：报名成功即得中国版速通 Pro 月卡（¥99）+ 决赛门票&lt;/p&gt;

&lt;p&gt;3⃣冲大奖：总奖池 113 万，单作品最高 35 万现金&lt;/p&gt;

&lt;p&gt;⏰ 报名 + 初赛同步进行至 7/15，越早开造越早出作品！&lt;/p&gt;

&lt;p&gt;点击报名：&lt;/p&gt;

&lt;p&gt;https://www.trae.cn/ai-creativity?utm_source=RTE&lt;/p&gt;

&lt;p&gt;&lt;img src="/uploads/photo/2026/916c4fdc-ed33-421b-889e-b41a5706e8a1.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="https://717KLO2znpj6gvtH.public.blob.vercel-storage.com/uploads/RTE_Dev_Comm/Sxx63qZ__dNG_640" title="" alt="图片"&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.rtecommunity.dev" rel="nofollow" target="_blank" title=""&gt;阅读更多 Voice Agent 学习笔记：了解最懂 AI 语音的头脑都在思考什么&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;写在最后：&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;我们欢迎更多的小伙伴参与&lt;strong&gt;「RTE 开发者日报」&lt;/strong&gt;内容的共创，感兴趣的朋友请通过开发者社区或公众号留言联系，记得报暗号「共创」。&lt;/p&gt;

&lt;p&gt;对于任何反馈（包括但不限于内容上、形式上）我们不胜感激、并有小惊喜回馈，例如你希望从日报中看到哪些内容；自己推荐的信源、项目、话题、活动等；或者列举几个你喜欢看、平时常看的内容渠道；内容排版或呈现形式上有哪些可以改进的地方等。&lt;/p&gt;

&lt;p&gt;&lt;img src="https://717KLO2znpj6gvtH.public.blob.vercel-storage.com/uploads/RTE_Dev_Comm/uv9tGDNrZqw4_640" title="" alt="图片"&gt;&lt;/p&gt;

&lt;p&gt;作者提示: 个人观点，仅供参考&lt;/p&gt;</description>
      <author>RTE</author>
      <pubDate>Fri, 19 Jun 2026 02:03:12 +0800</pubDate>
      <link>https://testerhome.com/topics/44343</link>
      <guid>https://testerhome.com/topics/44343</guid>
    </item>
    <item>
      <title>基于 LangChain + LangGraph 的智能化、零代码 Web UI 自动化测试平台。</title>
      <description>&lt;p&gt;由于 2026 年前离职到 6 月仍然没有找到工作，在此期间猛补 Agent 开发相关的技能，虽说之前做过 LLM +RAG 的本地知识库问答，但随着小龙虾横空出世，将 agent 场景使用具象化。趁着这段时间学习折腾，做了个 UI 自动化测试平台 Agent&lt;/p&gt;

&lt;p&gt;基于 &lt;strong&gt;LangChain + LangGraph&lt;/strong&gt; 的智能化、零代码 Web UI 自动化测试平台。&lt;/p&gt;
&lt;h2 id="项目简介"&gt;项目简介&lt;/h2&gt;
&lt;p&gt;自动化 UI 测试 Agent 是一款基于 &lt;strong&gt;FastAPI + Vue3 + LangChain/LangGraph&lt;/strong&gt; 的企业级 Web UI 自动化测试平台，具有以下特点：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;零代码测试&lt;/strong&gt;：只需输入 URL 即可自动生成测试用例&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI 驱动&lt;/strong&gt;：基于大语言模型智能分析页面元素和需求文档&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Agent 架构&lt;/strong&gt;：标准的「大模型 + 思考决策逻辑 + 外部工具集」架构&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;技能扩展&lt;/strong&gt;：遵循企业级标准化技能目录规范，支持插件化扩展&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;可视化管理&lt;/strong&gt;：提供完整的任务、用例、执行和报告管理界面&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="🛠️ 技术栈"&gt;🛠️ 技术栈&lt;/h2&gt;&lt;h3 id="后端技术栈"&gt;后端技术栈&lt;/h3&gt; &lt;table class="table-responsive table table-bordered table-striped"&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;th&gt;技术&lt;/th&gt;
&lt;th&gt;版本&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Python&lt;/td&gt;
&lt;td&gt;3.11+&lt;/td&gt;
&lt;td&gt;编程语言&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FastAPI&lt;/td&gt;
&lt;td&gt;0.109.0&lt;/td&gt;
&lt;td&gt;高性能 Web 框架&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Uvicorn&lt;/td&gt;
&lt;td&gt;0.27.0&lt;/td&gt;
&lt;td&gt;ASGI 服务器&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SQLAlchemy&lt;/td&gt;
&lt;td&gt;2.0.25&lt;/td&gt;
&lt;td&gt;异步 ORM 框架&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;LangChain&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1.3.9&lt;/td&gt;
&lt;td&gt;LLM 应用开发框架&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;LangGraph&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1.2.5&lt;/td&gt;
&lt;td&gt;状态机工作流引擎&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;langchain-openai&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1.3.2&lt;/td&gt;
&lt;td&gt;OpenAI 兼容接口&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Playwright&lt;/td&gt;
&lt;td&gt;1.41.0&lt;/td&gt;
&lt;td&gt;自动化测试工具&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SQLite&lt;/td&gt;
&lt;td&gt;内置&lt;/td&gt;
&lt;td&gt;轻量级数据库&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt; &lt;h3 id="前端技术栈"&gt;前端技术栈&lt;/h3&gt; &lt;table class="table-responsive table table-bordered table-striped"&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;th&gt;技术&lt;/th&gt;
&lt;th&gt;版本&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vue&lt;/td&gt;
&lt;td&gt;3.4+&lt;/td&gt;
&lt;td&gt;前端框架&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Element Plus&lt;/td&gt;
&lt;td&gt;2.6+&lt;/td&gt;
&lt;td&gt;UI 组件库&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pinia&lt;/td&gt;
&lt;td&gt;2.1+&lt;/td&gt;
&lt;td&gt;状态管理&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vue Router&lt;/td&gt;
&lt;td&gt;4.3+&lt;/td&gt;
&lt;td&gt;路由管理&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ECharts&lt;/td&gt;
&lt;td&gt;5.5+&lt;/td&gt;
&lt;td&gt;图表库&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vite&lt;/td&gt;
&lt;td&gt;5.4+&lt;/td&gt;
&lt;td&gt;构建工具&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt; &lt;h2 id="🧠 Agent架构设计"&gt;🧠 Agent 架构设计&lt;/h2&gt; &lt;pre class="highlight plaintext"&gt;&lt;code&gt;Agent = 大模型(LLM) + 思考决策逻辑(LangGraph) + 外部工具集(LangChain Tools) + 标准化SKILL
&lt;/code&gt;&lt;/pre&gt; &lt;h3 id="大模型配置"&gt;大模型配置&lt;/h3&gt; &lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;llm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ChatOpenAI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"deepseek-chat"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;apikey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;base_url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;baseurl&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt; &lt;h3 id="工具调用机制"&gt;工具调用机制&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;使用 &lt;code&gt;@tool&lt;/code&gt; 装饰器定义工具&lt;/li&gt;
&lt;li&gt;LLM 通过语义自主匹配、智能调用&lt;/li&gt;
&lt;li&gt;告别关键词硬匹配&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="✨ 功能特性"&gt;✨ 功能特性&lt;/h2&gt;&lt;h3 id="核心功能"&gt;核心功能&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;任务管理&lt;/strong&gt;：创建、编辑、删除测试任务，支持 URL 和文档导入&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;页面解析&lt;/strong&gt;：自动抓取页面元素，识别可交互元素（按钮、输入框、选择框等）&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;用例生成&lt;/strong&gt;：基于页面元素和需求文档智能生成测试用例&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;测试执行&lt;/strong&gt;：支持批量执行测试用例，实时显示进度（WebSocket 推送）&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;报告生成&lt;/strong&gt;：自动生成详细的测试报告，包含统计图表和截图&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="扩展功能"&gt;扩展功能&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;技能管理&lt;/strong&gt;：标准化技能目录规范，支持自定义技能扩展&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;大模型配置&lt;/strong&gt;：支持多模型切换和 API 配置&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;自动技能发现&lt;/strong&gt;：启动时自动扫描技能目录，动态注册工具&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="📁 标准化技能目录规范"&gt;📁 标准化技能目录规范&lt;/h2&gt;
&lt;p&gt;遵循主流开源企业级 Agent 工程规范：&lt;/p&gt;
 &lt;pre class="highlight plaintext"&gt;&lt;code&gt;skills/
└── &amp;lt;skill_name&amp;gt;/           # 独立可插拔技能目录
    ├── SKILL.md           # 技能说明文档（场景、约束、风险提示）
    ├── metadata.yaml      # 技能元数据（入参规范、输出格式）
    ├── examples/          # 使用示例和错误案例
    ├── templates/         # 模板文件
    ├── resources/         # 资源文件（白名单、权限规则）
    └── scripts/
        └── run.py         # 核心执行逻辑
&lt;/code&gt;&lt;/pre&gt; &lt;h3 id="已实现的技能"&gt;已实现的技能&lt;/h3&gt; &lt;table class="table-responsive table table-bordered table-striped"&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;th&gt;技能名称&lt;/th&gt;
&lt;th&gt;描述&lt;/th&gt;
&lt;th&gt;工具名&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;页面解析器&lt;/td&gt;
&lt;td&gt;抓取网页可交互元素&lt;/td&gt;
&lt;td&gt;&lt;code&gt;parse_page&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;文档解析器&lt;/td&gt;
&lt;td&gt;解析 PDF/Word 需求文档&lt;/td&gt;
&lt;td&gt;&lt;code&gt;parse_document&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;用例生成器&lt;/td&gt;
&lt;td&gt;智能生成测试用例&lt;/td&gt;
&lt;td&gt;&lt;code&gt;generate_cases&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;测试执行器&lt;/td&gt;
&lt;td&gt;执行测试用例&lt;/td&gt;
&lt;td&gt;&lt;code&gt;execute_tests&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;报告生成器&lt;/td&gt;
&lt;td&gt;生成测试报告&lt;/td&gt;
&lt;td&gt;&lt;code&gt;generate_report&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt; &lt;h3 id="添加新技能"&gt;添加新技能&lt;/h3&gt;
&lt;p&gt;只需创建符合规范的技能目录，系统启动时自动发现并注册：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;创建目录 &lt;code&gt;skills/your_skill/&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;添加 &lt;code&gt;metadata.yaml&lt;/code&gt; 定义元数据&lt;/li&gt;
&lt;li&gt;添加 &lt;code&gt;scripts/run.py&lt;/code&gt; 实现核心逻辑&lt;/li&gt;
&lt;li&gt;重启服务即可自动注册&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="📁 项目结构"&gt;📁 项目结构&lt;/h2&gt; &lt;pre class="highlight plaintext"&gt;&lt;code&gt;ai_uitest_agent/
├── agent/                    # Agent核心模块
│   ├── __init__.py
│   ├── core.py               # 代理核心逻辑
│   └── langgraph_agent.py    # LangGraph状态机代理
├── api/                      # API接口模块
│   ├── __init__.py
│   ├── routes.py             # REST API路由
│   ├── schemas.py            # 数据模型定义
│   ├── websocket.py          # WebSocket处理
│   └── websocket_manager.py  # WebSocket连接管理
├── skills/                   # 技能模块（标准化结构）
│   ├── __init__.py
│   ├── langchain_tools.py    # LangChain工具注册
│   ├── page_parser/          # 页面解析器技能
│   │   ├── SKILL.md
│   │   ├── metadata.yaml
│   │   └── scripts/run.py
│   ├── document_parser/      # 文档解析器技能
│   │   ├── SKILL.md
│   │   ├── metadata.yaml
│   │   └── scripts/run.py
│   ├── case_generator/       # 用例生成器技能
│   │   ├── SKILL.md
│   │   ├── metadata.yaml
│   │   └── scripts/run.py
│   ├── test_executor/        # 测试执行器技能
│   │   ├── SKILL.md
│   │   ├── metadata.yaml
│   │   └── scripts/run.py
│   └── report_generator/     # 报告生成器技能
│       ├── SKILL.md
│       ├── metadata.yaml
│       └── scripts/run.py
├── tools/                    # 工具模块
│   ├── __init__.py
│   ├── browser.py            # 浏览器操作工具
│   ├── config.py             # 配置管理
│   ├── database.py           # 数据库操作
│   ├── document_parser.py    # 文档解析工具
│   └── logger.py             # 日志管理
├── ui/                       # 前端模块
│   ├── src/
│   │   ├── api/              # API调用封装
│   │   ├── router/           # 路由配置
│   │   ├── stores/           # 状态管理
│   │   ├── views/            # 页面组件
│   │   ├── App.vue           # 根组件
│   │   └── main.js           # 入口文件
│   ├── index.html
│   ├── package.json
│   └── vite.config.js
├── .env                      # 环境变量配置
├── main.py                   # 应用入口
├── requirements.txt          # Python依赖
└── uitest_agent.db           # SQLite数据库文件
&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;&lt;img src="/uploads/photo/2026/1b541bb5-064e-411b-92d9-2dfafb2be727.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="/uploads/photo/2026/01a20e33-e8a9-43c5-ace5-889730a1ad62.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="/uploads/photo/2026/1af8eef1-108b-4b50-b77c-8d1871d9ce29.png!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h3 id="开源地址"&gt;开源地址&lt;/h3&gt; &lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://gitee.com/fxlysm/ai_uitest_agent.git

&lt;/code&gt;&lt;/pre&gt; </description>
      <author>fxlysm</author>
      <pubDate>Thu, 18 Jun 2026 18:35:27 +0800</pubDate>
      <link>https://testerhome.com/topics/44342</link>
      <guid>https://testerhome.com/topics/44342</guid>
    </item>
    <item>
      <title>软件测试工程师——广东中山</title>
      <description>&lt;p&gt;岗位描述:&lt;br&gt;
1、参与需求评审、接口文档评审，制定测试计划、测试方案与测试用例。&lt;br&gt;
2、负责 Web/APP/后台系统的功能测试、回归测试、兼容性测试、易用性测试。&lt;br&gt;
3、执行接口测试，使用 Postman/meter/Insomnia 等工具完成接口调试、场景化测试。&lt;br&gt;
NeuroDance&lt;br&gt;
4、提交缺陷报告，跟踪问题全生命周期，推动开发修复并验证闭环。&lt;br&gt;
5、编写并维护测试用例、测试报告、版本发布验证、上线前回归校验。配合研发、产品完成版本迭代测试，保障线上质量与发布稳定性。&lt;br&gt;
6、参与测试流程优化，推动用例标准化、问题复盘、质量改进。&lt;br&gt;
岗位要求:&lt;br&gt;
1、本科及以上学历，计算机、软件相关专业，1-3 年 Web/APP/后台系统测试经验。&lt;br&gt;
2、熟悉需求、接口文档评审流程，能独立制定测试计划、方案及测试用例。&lt;br&gt;
3、熟练掌握功能、回归、兼容性等测试方法，会使用 Postman/Jmeter/Insomnia 等接口测试工具。&lt;br&gt;
4、能规范提交缺陷报告，跟踪问题全生命周期，推动问题闭环。&lt;br&gt;
5、具备测试用例、测试报告编写维护能力，能配合团队完成版本迭代测试。&lt;br&gt;
6、有测试流程优化、用例标准化经验者优先，细心严谨、结果导向，沟通协调及抗压能力强。&lt;br&gt;
薪资:7k-16K/月&lt;br&gt;
邮箱：hr@neurodance.cn&lt;/p&gt;</description>
      <author>wubojia</author>
      <pubDate>Thu, 18 Jun 2026 18:15:13 +0800</pubDate>
      <link>https://testerhome.com/topics/44341</link>
      <guid>https://testerhome.com/topics/44341</guid>
    </item>
    <item>
      <title>第二届人工智能与教育系统国际学术会议（ICAIES 2026）</title>
      <description>&lt;p&gt;&lt;img src="/uploads/photo/2026/3613a699-d8de-4c15-8a97-f4de233169dc.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;第二届人工智能与教育系统国际学术会议（ICAIES 2026）&lt;/p&gt;

&lt;p&gt;2026 2nd International Conference on Artificial Intelligence and Educational Systems&lt;/p&gt;

&lt;p&gt;议题投稿入口：&lt;a href="https://ais.cn/u/J7nU32" rel="nofollow" target="_blank"&gt;https://ais.cn/u/J7nU32&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;大会时间：2026 年 7 月 24-26 日&lt;/p&gt;

&lt;p&gt;大会地点：中国 - 成都&lt;/p&gt;

&lt;p&gt;提交检索：EI，Scopus&lt;/p&gt;

&lt;p&gt;大会简介&lt;/p&gt;

&lt;p&gt;第二届人工智能与教育系统国际学术会议（ICAIES 2026）将于 2026 年 7 月 24-26 日在中国成都召开。会议主题主要围绕教育创新与多媒体技术等相关研究领域展开讨论，旨在为相关领域的专家学者及企业发展人提供一个分享研究成果、讨论存在的问题与挑战、探索前沿科技的国际性合作交流平台。热忱欢迎高校，科研机构专家，学者企业界人士及其他相关人员踊跃投稿并参会交流，与会学者们可通过此次会议聆听知名专家的精彩报告，一同分享行业内领先的研究成果与创新想法。&lt;/p&gt;

&lt;p&gt;组织单位&lt;/p&gt;

&lt;p&gt;&lt;img src="/uploads/photo/2026/9ae37910-6fa8-4e91-b613-ae3e4a93ca29.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;征稿主题&lt;/p&gt;

&lt;p&gt;&lt;img src="/uploads/photo/2026/ac48681a-d688-46f8-8d38-30856a372800.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;论文出版&lt;/p&gt;

&lt;p&gt;&lt;img src="/uploads/photo/2026/d7cf9bca-7a61-4b63-ab27-1157ddb7554a.png!large" title="" alt=""&gt;&lt;/p&gt;</description>
      <author>leisigoule</author>
      <pubDate>Thu, 18 Jun 2026 17:19:43 +0800</pubDate>
      <link>https://testerhome.com/topics/44340</link>
      <guid>https://testerhome.com/topics/44340</guid>
    </item>
    <item>
      <title>Claude Code 三层记忆模型实践</title>
      <description>&lt;p&gt;在了解使用 AI 记忆模型的之后，我们要解决一个非常实际的问题：既然已经有 &lt;code&gt;CLAUDE.md&lt;/code&gt;，为什么还需要 claude-mem？&lt;/p&gt;

&lt;p&gt;或者反过来说：&lt;/p&gt;

&lt;p&gt;既然已经用了 claude-mem，&lt;code&gt;CLAUDE.md&lt;/code&gt; 还应该写什么？&lt;/p&gt;

&lt;p&gt;这是很多 Claude Code 用户都会遇到的问题。我的建议很简单：&lt;/p&gt;

&lt;p&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt; 管规则，claude-mem 管经验。&lt;/p&gt;
&lt;h2 id="不要把 CLAUDE.md 写成项目百科"&gt;不要把 CLAUDE.md 写成项目百科&lt;/h2&gt;
&lt;p&gt;很多人刚开始使用 Claude Code，会认真维护 &lt;code&gt;CLAUDE.md&lt;/code&gt;。这是好习惯。&lt;/p&gt;

&lt;p&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt; 可以告诉 Claude Code 当前项目的基本信息，比如技术栈、常用命令、代码风格、测试方式、目录结构、禁止事项。这些信息会在 Claude Code 工作时持续影响它的行为。&lt;/p&gt;

&lt;p&gt;比如：&lt;/p&gt;
 &lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Project Guide&lt;/span&gt;

&lt;span class="gu"&gt;## Tech Stack&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; Node.js
&lt;span class="p"&gt;-&lt;/span&gt; TypeScript
&lt;span class="p"&gt;-&lt;/span&gt; PostgreSQL
&lt;span class="p"&gt;-&lt;/span&gt; pnpm

&lt;span class="gu"&gt;## Common Commands&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; pnpm install
&lt;span class="p"&gt;-&lt;/span&gt; pnpm lint
&lt;span class="p"&gt;-&lt;/span&gt; pnpm test

&lt;span class="gu"&gt;## Rules&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; Do not use npm
&lt;span class="p"&gt;-&lt;/span&gt; Do not modify generated files
&lt;span class="p"&gt;-&lt;/span&gt; Add tests for business logic changes
&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;这样的 &lt;code&gt;CLAUDE.md&lt;/code&gt; 很有价值。它能帮助 Claude Code 快速进入项目，不用每次都重新问这个项目怎么跑测试、包管理器用什么、哪些文件不能改。&lt;/p&gt;

&lt;p&gt;但问题也很容易出现。&lt;/p&gt;

&lt;p&gt;随着使用时间变长，很多人会不断往 &lt;code&gt;CLAUDE.md&lt;/code&gt; 里加内容。今天发现一个 Bug，加进去；明天排查一个线上问题，加进去；后天某个方案失败了，也加进去。再后来，测试注意事项、临时 workaround、历史决策、模块说明、接口兼容问题，全都塞进去。&lt;/p&gt;

&lt;p&gt;最后，&lt;code&gt;CLAUDE.md&lt;/code&gt; 变得越来越长。&lt;/p&gt;

&lt;p&gt;表面上看，这是在补充上下文。实际上，这很可能是在制造上下文污染。&lt;/p&gt;

&lt;p&gt;因为 &lt;code&gt;CLAUDE.md&lt;/code&gt; 通常会在项目会话中被优先读取，它里面的内容越长，Claude Code 每次任务都要承受越多背景噪声。很多历史信息并不是每次都相关，但它们仍然会进入模型注意力范围。&lt;/p&gt;

&lt;p&gt;结果就是：规则被经验淹没，稳定约束和临时状态混在一起，过期内容和当前事实混在一起。Claude Code 看似知道更多，实际更容易分不清重点。&lt;/p&gt;

&lt;p&gt;所以，&lt;code&gt;CLAUDE.md&lt;/code&gt; 不应该写成项目百科。&lt;/p&gt;

&lt;p&gt;它应该保持克制。它不是项目历史档案，也不是 Bug 复盘合集，更不是所有开发经验的垃圾桶。它更像项目宪法：短、稳、明确、长期有效。&lt;/p&gt;
&lt;h2 id="CLAUDE.md 适合保存稳定规则"&gt;CLAUDE.md 适合保存稳定规则&lt;/h2&gt;
&lt;p&gt;判断一条信息是否应该写进 &lt;code&gt;CLAUDE.md&lt;/code&gt;，可以问一个问题：&lt;/p&gt;

&lt;p&gt;这条信息是否应该在几乎每一次 Claude Code 工作时都生效？&lt;/p&gt;

&lt;p&gt;如果答案是肯定的，它就适合进入 &lt;code&gt;CLAUDE.md&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;比如技术栈：&lt;/p&gt;
 &lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Tech Stack&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; Frontend: React + TypeScript
&lt;span class="p"&gt;-&lt;/span&gt; Backend: Node.js + NestJS
&lt;span class="p"&gt;-&lt;/span&gt; Database: PostgreSQL
&lt;span class="p"&gt;-&lt;/span&gt; Package manager: pnpm
&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;这类信息长期稳定，几乎所有任务都相关。&lt;/p&gt;

&lt;p&gt;比如常用命令：&lt;/p&gt;
 &lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Common Commands&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; Install dependencies: pnpm install
&lt;span class="p"&gt;-&lt;/span&gt; Run lint: pnpm lint
&lt;span class="p"&gt;-&lt;/span&gt; Run unit tests: pnpm test
&lt;span class="p"&gt;-&lt;/span&gt; Run e2e tests: pnpm test:e2e
&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;Claude Code 修改代码后，经常需要验证。把命令写清楚，可以减少它乱猜命令的概率。&lt;/p&gt;

&lt;p&gt;比如代码规范：&lt;/p&gt;
 &lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Code Style&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; Use TypeScript strict mode.
&lt;span class="p"&gt;-&lt;/span&gt; Prefer explicit return types for exported functions.
&lt;span class="p"&gt;-&lt;/span&gt; Do not introduce new dependencies without approval.
&lt;span class="p"&gt;-&lt;/span&gt; Keep business logic out of controllers.
&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;这些规则会影响 Claude Code 的实现方式，适合长期保留。&lt;/p&gt;

&lt;p&gt;比如禁止事项：&lt;/p&gt;
 &lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Do Not&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; Do not modify files under generated/.
&lt;span class="p"&gt;-&lt;/span&gt; Do not commit secrets or credentials.
&lt;span class="p"&gt;-&lt;/span&gt; Do not call real payment gateway in tests.
&lt;span class="p"&gt;-&lt;/span&gt; Do not use npm or yarn in this repo.
&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;这类内容尤其适合写进 &lt;code&gt;CLAUDE.md&lt;/code&gt;，因为它们是强约束，不应该依赖 Claude Code 临时检索记忆。&lt;/p&gt;

&lt;p&gt;比如重要路径：&lt;/p&gt;
 &lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Important Paths&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; src/modules/order: order domain logic
&lt;span class="p"&gt;-&lt;/span&gt; src/modules/payment: payment and callback handling
&lt;span class="p"&gt;-&lt;/span&gt; tests/e2e: end-to-end tests
&lt;span class="p"&gt;-&lt;/span&gt; scripts/db: database migration scripts
&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;这些信息能帮助 Claude Code 快速定位项目结构。&lt;/p&gt;

&lt;p&gt;总结一下，&lt;code&gt;CLAUDE.md&lt;/code&gt; 适合保存四类内容：&lt;/p&gt;
 &lt;table class="table-responsive table table-bordered table-striped"&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;特点&lt;/th&gt;
&lt;th&gt;示例&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;项目事实&lt;/td&gt;
&lt;td&gt;长期稳定&lt;/td&gt;
&lt;td&gt;技术栈、目录结构&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;工作命令&lt;/td&gt;
&lt;td&gt;高频使用&lt;/td&gt;
&lt;td&gt;测试、构建、Lint&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;强约束&lt;/td&gt;
&lt;td&gt;每次都应遵守&lt;/td&gt;
&lt;td&gt;禁止修改生成文件、禁止真实调用外部服务&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;通用规范&lt;/td&gt;
&lt;td&gt;影响实现风格&lt;/td&gt;
&lt;td&gt;代码风格、测试要求、依赖管理&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt; 
&lt;p&gt;这些内容的共同特点是：稳定、通用、强约束、高频使用。&lt;/p&gt;

&lt;p&gt;只要一条信息不满足这些特点，就要谨慎放进 &lt;code&gt;CLAUDE.md&lt;/code&gt;。&lt;/p&gt;
&lt;h2 id="claude-mem 适合保存动态经验"&gt;claude-mem 适合保存动态经验&lt;/h2&gt;
&lt;p&gt;和 &lt;code&gt;CLAUDE.md&lt;/code&gt; 不同，claude-mem 更适合保存动态经验。&lt;/p&gt;

&lt;p&gt;所谓动态经验，是指那些不一定每次任务都相关，但在特定任务中非常有价值的信息。&lt;/p&gt;

&lt;p&gt;比如历史 Bug：&lt;/p&gt;

&lt;p&gt;订单取消接口历史上出现过库存重复释放问题。根因是取消操作缺少幂等保护。后续修改订单状态流转时，必须覆盖重复取消、并发取消、已取消订单再次取消。&lt;/p&gt;

&lt;p&gt;这条信息不需要每次 Claude Code 工作都看到。你让它改登录模块时，这条记忆就是噪声。但当它处理订单取消、库存释放、订单状态流转时，这条记忆价值很高。&lt;/p&gt;

&lt;p&gt;所以它适合 claude-mem，不适合 &lt;code&gt;CLAUDE.md&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;再比如失败方案：&lt;/p&gt;

&lt;p&gt;支付回调曾尝试通过请求时间戳去重，但由于第三方网关存在重试延迟，时间戳不能作为可靠幂等依据。后续应使用 callback_id + order_id 做幂等键。&lt;/p&gt;

&lt;p&gt;这类信息非常重要，但它是经验，不是全局规则。只有在处理支付回调时才需要出现。&lt;/p&gt;

&lt;p&gt;再比如测试策略：&lt;/p&gt;

&lt;p&gt;优惠券金额计算修改后，必须回归满减券、折扣券、会员价、退款重算、活动叠加五类场景。历史缺陷主要集中在活动叠加和退款重算。&lt;/p&gt;

&lt;p&gt;这类内容对测试开发很有价值，但也不该默认出现在所有任务里。它应该在处理优惠券、价格计算、退款、活动叠加时被检索出来。&lt;/p&gt;

&lt;p&gt;再比如模块历史：&lt;/p&gt;

&lt;p&gt;权限模块没有直接依赖组织架构服务，是为了避免权限校验链路引入远程调用。后续修改权限判断逻辑时，应优先使用本地缓存的组织关系快照。&lt;/p&gt;

&lt;p&gt;这条记忆能解释为什么代码现在这样写。它对相关模块很重要，但不属于全局项目规则。&lt;/p&gt;

&lt;p&gt;所以 claude-mem 适合保存这些内容：&lt;/p&gt;
 &lt;table class="table-responsive table table-bordered table-striped"&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;特点&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;历史 Bug&lt;/td&gt;
&lt;td&gt;真实发生过，未来可能复发&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;失败尝试&lt;/td&gt;
&lt;td&gt;防止重复走错路&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;模块历史&lt;/td&gt;
&lt;td&gt;解释当前设计原因&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;测试策略&lt;/td&gt;
&lt;td&gt;记录风险场景和回归重点&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;排查结论&lt;/td&gt;
&lt;td&gt;保存根因和排除项&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;临时阶段状态&lt;/td&gt;
&lt;td&gt;支持长任务连续推进&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt; 
&lt;p&gt;这些内容的共同特点是：动态、局部、经验性、按需使用。&lt;/p&gt;

&lt;p&gt;它们不应该常驻上下文，但应该在相关任务中被召回。这正是 claude-mem 的价值。&lt;/p&gt;
&lt;h2 id="推荐三层上下文模型"&gt;推荐三层上下文模型&lt;/h2&gt;
&lt;p&gt;如果把 Claude Code 的上下文系统分层，我建议使用三层模型：&lt;/p&gt;
 &lt;table class="table-responsive table table-bordered table-striped"&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;th&gt;层级&lt;/th&gt;
&lt;th&gt;角色&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;项目规则层&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;claude-mem&lt;/td&gt;
&lt;td&gt;项目经验层&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;当前会话&lt;/td&gt;
&lt;td&gt;当前任务现场&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt; 
&lt;p&gt;这三层各自承担不同职责。&lt;/p&gt;

&lt;p&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt; 是项目规则层。它回答的是：这个项目长期遵守什么规则？&lt;/p&gt;

&lt;p&gt;比如用什么技术栈，怎么运行测试，哪些文件不能改，代码风格是什么，外部服务测试怎么 mock。&lt;/p&gt;

&lt;p&gt;claude-mem 是项目经验层。它回答的是：这个项目过去发生过什么，哪些经验会影响当前判断？&lt;/p&gt;

&lt;p&gt;比如哪些模块出过 Bug，哪些方案失败过，哪些场景必须回归，哪些设计是历史原因导致的。&lt;/p&gt;

&lt;p&gt;当前会话是任务现场层。它回答的是：这次具体要做什么？当前看到的代码、日志、测试结果是什么？&lt;/p&gt;

&lt;p&gt;比如这次要修哪个 Bug，刚刚读了哪些文件，当前测试为什么失败，最近一次命令输出是什么。&lt;/p&gt;

&lt;p&gt;这三层不要混用。&lt;/p&gt;

&lt;p&gt;不要把所有历史经验写进 &lt;code&gt;CLAUDE.md&lt;/code&gt;。不要把长期稳定规则丢给 claude-mem 临时检索。不要把当前会话里的临时日志沉淀成长期记忆。&lt;/p&gt;

&lt;p&gt;一个更合理的分工是：&lt;/p&gt;
 &lt;table class="table-responsive table table-bordered table-striped"&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;th&gt;信息类型&lt;/th&gt;
&lt;th&gt;放在哪里&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;技术栈、测试命令、代码规范&lt;/td&gt;
&lt;td&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;禁止事项、强约束&lt;/td&gt;
&lt;td&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;历史 Bug、失败方案&lt;/td&gt;
&lt;td&gt;claude-mem&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;模块设计原因、测试策略&lt;/td&gt;
&lt;td&gt;claude-mem&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;当前报错、当前命令输出&lt;/td&gt;
&lt;td&gt;当前会话&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;临时探索过程&lt;/td&gt;
&lt;td&gt;当前会话，必要时总结后进入 claude-mem&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt; 
&lt;p&gt;这样做的好处是，Claude Code 既能稳定遵守项目规则，又能在需要时想起项目经验，同时不会让上下文被无关历史淹没。&lt;/p&gt;
&lt;h2 id="可直接复用的模板"&gt;可直接复用的模板&lt;/h2&gt;
&lt;p&gt;最后给一个比较实用的模板。&lt;/p&gt;

&lt;p&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt; 可以保持简洁，重点写长期稳定规则：&lt;/p&gt;
 &lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Project Guide&lt;/span&gt;

&lt;span class="gu"&gt;## Tech Stack&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; Runtime:
&lt;span class="p"&gt;-&lt;/span&gt; Language:
&lt;span class="p"&gt;-&lt;/span&gt; Framework:
&lt;span class="p"&gt;-&lt;/span&gt; Database:
&lt;span class="p"&gt;-&lt;/span&gt; Package manager:

&lt;span class="gu"&gt;## Common Commands&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; Install:
&lt;span class="p"&gt;-&lt;/span&gt; Lint:
&lt;span class="p"&gt;-&lt;/span&gt; Unit test:
&lt;span class="p"&gt;-&lt;/span&gt; E2E test:
&lt;span class="p"&gt;-&lt;/span&gt; Build:

&lt;span class="gu"&gt;## Code Style&lt;/span&gt;
&lt;span class="p"&gt;
-
-
-
&lt;/span&gt;
&lt;span class="gu"&gt;## Testing Rules&lt;/span&gt;
&lt;span class="p"&gt;
-
-
-
&lt;/span&gt;
&lt;span class="gu"&gt;## Do Not&lt;/span&gt;
&lt;span class="p"&gt;
-
-
-
&lt;/span&gt;
&lt;span class="gu"&gt;## Important Paths&lt;/span&gt;
&lt;span class="p"&gt;
-
-
-
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;注意，这里不要写太多历史 Bug，也不要写大量模块复盘。&lt;code&gt;CLAUDE.md&lt;/code&gt; 的目标是让 Claude Code 快速理解项目基本规则，而不是承担长期记忆系统。&lt;/p&gt;

&lt;p&gt;claude-mem 则可以沉淀类似这样的经验：&lt;/p&gt;

&lt;p&gt;订单取消接口历史上出现过库存重复释放问题。根因是取消操作缺少幂等保护。后续修改订单状态流转时，必须覆盖重复取消、并发取消、已取消订单再次取消，重点验证库存只释放一次。&lt;/p&gt;

&lt;p&gt;支付回调曾尝试通过请求时间戳去重，但由于第三方网关存在重试延迟，时间戳不能作为可靠幂等依据。后续应使用 callback_id + order_id 做幂等键，并覆盖重复通知和乱序通知。&lt;/p&gt;

&lt;p&gt;权限模块没有直接依赖组织架构服务，是为了避免权限校验链路引入远程调用。后续修改权限判断逻辑时，应优先使用本地缓存的组织关系快照，并关注缓存刷新延迟。&lt;/p&gt;

&lt;p&gt;当前会话则保留当下正在发生的任务信息：&lt;/p&gt;

&lt;p&gt;当前任务：修复订单取消后库存偶发不一致。&lt;/p&gt;

&lt;p&gt;已读文件：order.service.ts、inventory.service.ts、cancel-order.spec.ts。&lt;/p&gt;

&lt;p&gt;当前发现：重复取消请求会进入库存释放逻辑，但缺少幂等拦截。&lt;/p&gt;

&lt;p&gt;下一步：补充并发取消测试，并验证库存释放次数。&lt;/p&gt;

&lt;p&gt;如果当前会话最终形成了稳定结论，再让它进入 claude-mem。否则就只停留在当前任务现场，不要污染长期记忆。&lt;/p&gt;

&lt;p&gt;所以，这一篇的核心结论可以压缩成一句话：&lt;/p&gt;

&lt;p&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt; 写稳定规则，claude-mem 记动态经验，当前会话处理即时现场。&lt;/p&gt;

&lt;p&gt;这样分工之后，Claude Code 的上下文会更干净，也更接近一个真实工程师的工作方式。&lt;/p&gt;
&lt;h5 id="FunTester 名片｜万粉千文，百无一用"&gt;FunTester 名片｜万粉千文，百无一用&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://mp.weixin.qq.com/s/o1dGKThvC64LvJDnKLybOw" rel="nofollow" target="_blank" title=""&gt;软件测试的道与术&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mp.weixin.qq.com/s/MFgZ3Btx0TKOd5sFGFDxcg" rel="nofollow" target="_blank" title=""&gt;测试开发成长史&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mp.weixin.qq.com/s/wyyn32HrwmH5uV8F0d2HDQ" rel="nofollow" target="_blank" title=""&gt;AI ，测试有点东西&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mp.weixin.qq.com/s/5ssJH4yOE3H1Vk4ziJ0Bdg" rel="nofollow" target="_blank" title=""&gt;《从 Java 开始性能测试》连载全集&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mp.weixin.qq.com/s/3ilD1fnJFo9Gt8puO7zFiQ" rel="nofollow" target="_blank" title=""&gt;性能测试修炼之道&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mp.weixin.qq.com/s/t2k6IITzaFicaEi77iZ5tA" rel="nofollow" target="_blank" title=""&gt;故障测试与混沌工程实战合集&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mp.weixin.qq.com/s/CSE2UmQNFrHaEnyyM4U41w" rel="nofollow" target="_blank" title=""&gt;我的语言迁徙：Java &amp;amp; Groovy &amp;amp; Go&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mp.weixin.qq.com/s/0F41EuPsziWwr25o7Cj3-A" rel="nofollow" target="_blank" title=""&gt;懂点 Web 前端&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mp.weixin.qq.com/s/LKEfCkx_iEr8JrA1lT3HnQ" rel="nofollow" target="_blank" title=""&gt;视频教程全收录&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <author>Fhaohaizi</author>
      <pubDate>Thu, 18 Jun 2026 17:08:09 +0800</pubDate>
      <link>https://testerhome.com/topics/44339</link>
      <guid>https://testerhome.com/topics/44339</guid>
    </item>
    <item>
      <title>有没有老哥在做大模型测试或 AI 业务相关的测试啊</title>
      <description>&lt;p&gt;如题，我在公司的产品也有接入一些 AI 功能，但是公司没有开展大模型测试、例如鲁棒性，agent 业务流、prompt 测试、RAG 专项和各类指标啥的，就只是单纯的走一下业务，想知道一下现在企业主要做大模型测试的工作流程有哪些，以及如何开展的&lt;/p&gt;</description>
      <author>lemon-Yuki</author>
      <pubDate>Thu, 18 Jun 2026 10:45:42 +0800</pubDate>
      <link>https://testerhome.com/topics/44338</link>
      <guid>https://testerhome.com/topics/44338</guid>
    </item>
    <item>
      <title>活动报名：来 Agentopia，对话 AI，也对话彼此 丨 RTE 社区将参加亚马逊云科技中国峰会，6 月 23-24 日</title>
      <description>&lt;p&gt;6 月 23 日到 24 日，上海世博中心，又一年亚马逊云科技中国峰会就要开始了。这次，RTE 开发者社区也会在现场，我们会在一个充满探索感的开发者冒险空间——「Agentopia」等大家！&lt;/p&gt;

&lt;p&gt;参加技术大会，除了坐在台下倾听分享，人和人之间真实、随性的交流也很珍贵。就像我们这次海报上写的一句话：&lt;strong&gt;「对话 AI，也对话彼此」&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;&lt;img src="https://717KLO2znpj6gvtH.public.blob.vercel-storage.com/uploads/RTE_Dev_Comm/thCQf5CLhKPw_640" title="" alt="图片"&gt;&lt;/p&gt;

&lt;p&gt;作为一个 Developer and Founder Builder 社区，我们非常期待和你在线下见一面。如果你最近也在动手做 Voice AI 或者 Physical AI 相关的尝试，一定要来找我们聊聊。无论是最近刚学到的新技术、正在琢磨的新产品，还是开发过程中遇到的一些好玩的坑，我们都很想听。&lt;/p&gt;

&lt;p&gt;为了这次见面，我们还特意准备了专属的「建造卡片」和「成长卡片」。不管你正在搭建什么，这两套卡片都能帮你记录下当下的状态和想法，陪伴你更好地 build and grow。&lt;/p&gt;

&lt;p&gt;&lt;img src="https://717KLO2znpj6gvtH.public.blob.vercel-storage.com/uploads/RTE_Dev_Comm/s3655bY6kG0W_image.png" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;📍&amp;nbsp;&lt;strong&gt;我们在哪里？&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;上海世博中心，亚马逊云科技中国峰会 · 开发者展区「Agentopia」&lt;/p&gt;

&lt;p&gt;⏰&amp;nbsp;&lt;strong&gt;什么时候见？&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;6 月 23 日到 24 日&lt;/p&gt;

&lt;p&gt;带上你的好奇心，来找我们聊聊吧！记得来领取你的「Build 卡片」和「Grow 卡片」，还有我们准备的专属小礼物。&lt;/p&gt;

&lt;p&gt;👉&amp;nbsp;&lt;strong&gt;扫描海报二维码报名。&lt;/strong&gt;&lt;/p&gt;

&lt;hr&gt;

&lt;p&gt;我们现场见！👋&lt;/p&gt;

&lt;p&gt;&lt;img src="https://717KLO2znpj6gvtH.public.blob.vercel-storage.com/uploads/RTE_Dev_Comm/eYGHGD0mXeGh_image.png" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="https://717KLO2znpj6gvtH.public.blob.vercel-storage.com/uploads/RTE_Dev_Comm/btWtqOvDDrfJ_image.png" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="/uploads/photo/2026/23baf1eb-c362-4036-ade7-3b655a367436.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="https://717KLO2znpj6gvtH.public.blob.vercel-storage.com/uploads/RTE_Dev_Comm/Sxx63qZ__dNG_640" title="" alt="图片"&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.rtecommunity.dev" rel="nofollow" target="_blank" title=""&gt;阅读更多 Voice Agent 学习笔记：了解最懂 AI 语音的头脑都在思考什么&lt;/a&gt;&lt;/p&gt;

&lt;hr&gt;

&lt;p&gt;&lt;img src="https://717KLO2znpj6gvtH.public.blob.vercel-storage.com/uploads/RTE_Dev_Comm/uv9tGDNrZqw4_640" title="" alt="图片"&gt;&lt;/p&gt;</description>
      <author>RTE</author>
      <pubDate>Thu, 18 Jun 2026 00:37:12 +0800</pubDate>
      <link>https://testerhome.com/topics/44337</link>
      <guid>https://testerhome.com/topics/44337</guid>
    </item>
    <item>
      <title>Cartesia 推出双榜首 SSM 语音模型，延迟低于百毫秒；贝佐斯旗下 Prometheus 融资 120 亿研发物理 AI 工程师丨日报</title>
      <description>&lt;p&gt;&lt;img src="https://717KLO2znpj6gvtH.public.blob.vercel-storage.com/uploads/RTE_Dev_Comm/-c3QaHxL0uny_640" title="" alt="图片"&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;开发者朋友们大家好：&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;这里是&lt;strong&gt;「RTE 开发者日报」&lt;/strong&gt;，每天和大家一起看新闻、聊八卦。我们的社区编辑团队会整理分享&amp;nbsp;RTE（Real-Time Engagement）&amp;nbsp;领域内「有话题的&lt;strong&gt;技术&lt;/strong&gt;」、「有亮点的&lt;strong&gt;产品&lt;/strong&gt;」、「有思考的&lt;strong&gt;文章&lt;/strong&gt;」、「有态度的&lt;strong&gt;观点&lt;/strong&gt;」、「有看点的&lt;strong&gt;活动&lt;/strong&gt;」，但内容仅代表编辑的个人观点，欢迎大家留言、跟帖、讨论。&lt;/p&gt;

&lt;p&gt;&lt;em&gt;本期编辑：&lt;a href="/koki" class="user-mention" title="@koki"&gt;&lt;i&gt;@&lt;/i&gt;koki&lt;/a&gt;、@ 鲍勃&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="01 有话题的技术"&gt;&lt;strong&gt;01 有话题的技术&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;1、Cartesia 发布 Sonic-3.5 与 Ink-2 语音模型：基于状态空间模型，首音延迟降至&amp;nbsp;82ms 并集成原生轮次检测&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Cartesia 推出新一代实时语音端到端模型 Sonic-3.5 与 Ink-2。该系列模型基于状态空间模型架构开发，通过单一 API 整合语音转文字与文字转语音能力，旨在消除实时语音智能体在响应速度、自然度与准确度之间的传统架构折衷。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;双向超低延迟&lt;/strong&gt;：Sonic-3.5 的合成延迟控制在 90ms 以内，P90 首字节响应延迟为 100ms；Ink-2 的转录延迟降至 100ms，配合原生轮换检测实现极速交互。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;状态空间模型架构&lt;/strong&gt;：放弃传统的 Transformer 架构，采用状态空间模型作为基础原语，在保持长上下文推理和高并发扩展效率的同时，大幅度降低计算开销与延迟。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;多语言与复杂文本解析&lt;/strong&gt;：Sonic-3.5 支持 40 多种语言，具备更强的韵律节奏与情感表现力，并原生支持字母数字混合解析，优化了多语言混杂电话场景下的识别与合成表现。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;双向榜单首位&lt;/strong&gt;：在 Artificial Analysis 发布的 Speech Arena 语音合成榜单与语音转文字榜单中，两款模型均位列行业第一。&lt;/p&gt;

&lt;p&gt;（&lt;a href="/krandiash" class="user-mention" title="@krandiash"&gt;&lt;i&gt;@&lt;/i&gt;krandiash&lt;/a&gt;\&lt;a href="/X" class="user-mention" title="@X"&gt;&lt;i&gt;@&lt;/i&gt;X&lt;/a&gt;、&lt;a href="/cartesia.ai" class="user-mention" title="@cartesia.ai"&gt;&lt;i&gt;@&lt;/i&gt;cartesia.ai&lt;/a&gt;）&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2、Qwen 发布 Qwen-Robot 具身智能模型套件：涵盖导航、操作与世界模型，实现多机型统一动作表征&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Qwen 正式发布 Qwen-Robot&amp;nbsp;&lt;strong&gt;具身智能模型套件&lt;/strong&gt;，由&lt;strong&gt;导航（RobotNav）、操作（RobotManip）和世界模型（RobotWorld）三个专业模块组成&lt;/strong&gt;。该套件通过参数化视觉接口、统一的状态 - 动作空间以及自然语言动作接口，统一了不同机器人的动作表征，使具身智能体能够实现&lt;strong&gt;跨本体迁移与零样本控制&lt;/strong&gt;。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;自适应参数化视觉导航&lt;/strong&gt;：Qwen-RobotNav 将导航任务（指令跟随、目标搜索、目标追踪、自动驾驶）的视觉分配策略参数化，暴露视觉 token 预算、时间衰减、逐相机权重、帧采样模式四个推理参数。模型在 15.6M 样本上训练，在 Unitree Go2 四足机器人（搭载 NVIDIA Jetson Thor）上的部署延迟为 196ms。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src="https://717KLO2znpj6gvtH.public.blob.vercel-storage.com/uploads/RTE_Dev_Comm/Oj-89XoONfpV_640" title="" alt="图片"&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;统一状态表征与大规模跨本体训练&lt;/strong&gt;：Qwen-RobotManip 采用 Qwen3.5-4B VL 骨干网络与流匹配 DiT 动作头，定义了统一的 80 维状态 - 动作空间，以相机坐标系下的末端执行器增量位姿屏蔽硬件形态差异。模型基于超 38,100 小时操作数据（含 24,808 小时人 - 机迁移合成数据）训练，在 LIBERO-Plus 取得 91.4% 成功率。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src="https://717KLO2znpj6gvtH.public.blob.vercel-storage.com/uploads/RTE_Dev_Comm/LKpfNMijq7Ro_640" title="" alt="图片"&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;双流 MMDiT 架构世界模型&lt;/strong&gt;：Qwen-RobotWorld 采用 60 层双流 MMDiT 架构，深度耦合 Qwen2.5-VL 的语义表示与视频隐变量，以完整多模态大语言模型作为动作编码器。该模型将动作标准化为自然语言接口，在 8.6M 视频 - 文本对（超 2 亿帧）上训练，支持跨 8 种以上本体的多视角一致生成。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src="https://717KLO2znpj6gvtH.public.blob.vercel-storage.com/uploads/RTE_Dev_Comm/Wm5CMNEYGaJ2_640" title="" alt="图片"&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;智能体闭环框架 Qwen-RobotClaw&lt;/strong&gt;：该框架支持通用 VLM 智能体调用上述模块作为物理工具。在上层规划器（Qwen3.6-Plus）调度和双层记忆机制下，该系统在 EXPRESS-Bench 具身问答基准上提升了 15.4% 的表现，并减少了 77% 的导航步数。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;（@ 通义实验室）&lt;/p&gt;
&lt;h2 id="02 有亮点的产品"&gt;&lt;strong&gt;02 有亮点的产品&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;1、Prometheus 获 120 亿美元融资：估值达 410 亿美元，研发物理 AI「通用人工工程师」&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="https://717KLO2znpj6gvtH.public.blob.vercel-storage.com/uploads/RTE_Dev_Comm/tLR5cuENLqA7_640" title="" alt="图片"&gt;&lt;/p&gt;

&lt;p&gt;贝佐斯与谷歌旗下生命科学部门 Verily 联合创始人 Vik Bajaj 创立的物理 AI 公司 Prometheus 宣布完成&amp;nbsp;&lt;strong&gt;120 亿美元的第二轮融资，投后估值达 410 亿美元&lt;/strong&gt;。该笔资金将主要用于&lt;strong&gt;建设大规模算力集群&lt;/strong&gt;，加速研发旨在自动化设计和制造复杂物理系统的「通用人工工程师」软件。Prometheus 的超大体量融资将 AI 竞争&lt;strong&gt;从数字空间直接推向物理制造深水区&lt;/strong&gt;。其定义的「通用人工工程师」实质上是在尝试用大模型重构航空航天、制药等硬科技研发链条。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;120 亿美元第二轮融资&lt;/strong&gt;：由&lt;strong&gt;贝佐斯、摩根大通、高盛和贝莱德等联合领投&lt;/strong&gt;，公司累计融资额已超 180 亿美元（首轮融资为 62 亿美元），估值达到 410 亿美元。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;研发通用人工工程师&lt;/strong&gt;：定位于物理世界 AI 软件，旨在实现从喷气发动机到药物化合物等复杂物理实体的全自动化设计与制造，&lt;strong&gt;替代大量传统工程研发流程&lt;/strong&gt;。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;算力资源密集型投入&lt;/strong&gt;：公司目前在旧金山、伦敦和苏黎世仅拥有 150 名员工，贝佐斯确认本次募集的百亿级资金将有极大部分&lt;strong&gt;直接转化为计算资源采购&lt;/strong&gt;。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;高壁垒物理护城河&lt;/strong&gt;：相较于纯软件层面的大语言模型，Prometheus 专注的物理 AI 领域因涉及物理实体的反馈与制造闭环，具备更强的行业防壁。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;(&lt;a href="/TechCrunch" class="user-mention" title="@TechCrunch"&gt;&lt;i&gt;@&lt;/i&gt;TechCrunch&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2、AI 截图管理应用 Pool 获超 200 万美元融资：通过 AI 逆向检索原始链接并提取非结构化元数据&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="https://717KLO2znpj6gvtH.public.blob.vercel-storage.com/uploads/RTE_Dev_Comm/3RgoVDwkSjZf_640" title="" alt="图片"&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="https://717KLO2znpj6gvtH.public.blob.vercel-storage.com/uploads/RTE_Dev_Comm/w-GIjTmh7mPu_640" title="" alt="图片"&gt;&lt;/p&gt;

&lt;p&gt;由 Spinoff Studio 开发的 AI 个人数据管理应用 Pool&amp;nbsp;&lt;strong&gt;宣布获得超过 200 万美元的种子前轮融资&lt;/strong&gt;。该应用利用 AI 将&lt;strong&gt;手机相册中的非结构化截图转化为结构化数据&lt;/strong&gt;，通过逆向检索还原截图的原始上下文和链接。此轮融资将用于加速产品迭代以及开发下一代基于智能体的个人助理应用。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;逆向链接与上下文检索&lt;/strong&gt;：Pool 能自动识别截图内容并还原其原始数字路径。例如，&lt;strong&gt;商品截图可直接逆向链接至零售商官网，社交媒体的食谱截图可提取出具体的配料清单与制作步骤&lt;/strong&gt;。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;非结构化图片结构化分类&lt;/strong&gt;：应用将用户授权的相册截图自动聚合，按「食谱、播客、产品、地点」等维度分类归档入特定存储区，&lt;strong&gt;实现静态图片的动态资产化&lt;/strong&gt;。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;时效性上下文感知与智能体引导&lt;/strong&gt;：系统可根据截图内容的时效性进行动态管理（如活动结束后自动隐退门票条形码）。内置的 AI 智能体可识别活动海报截图，自动定位票务源并生成购票链接。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;多模态搜索与内置 AI 助手&lt;/strong&gt;：支持自然语言搜索相册内容，用户可通过内置的 AI 智能体助理进行多轮对话式查询与任务调度。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;（@ 多知）&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3、Mistral AI 拟融资 35 亿美元，估值 234 亿美元，专注工业场景定制 AI 和安全模型&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="https://717KLO2znpj6gvtH.public.blob.vercel-storage.com/uploads/RTE_Dev_Comm/Hp1tEEVoHhm5_640" title="" alt="图片"&gt;&lt;/p&gt;

&lt;p&gt;法国初创公司 Mistral AI 正洽谈融资约 30 亿欧元（合 35 亿美元），估值约 200 亿欧元，据知情人士透露，这为欧洲人工智能领军企业提供了资金注入，使其在与美国和中国竞争对手的昂贵计算竞赛中保持竞争力。&lt;/p&gt;

&lt;p&gt;知情人士表示，与投资者的讨论仍处于早期阶段，条款可能发生变化，由于讨论属于私下性质，他们要求匿名。他们称，估值可能根据投资者需求进一步上升。这家总部位于巴黎的公司在 9 月融资时估值为 117 亿欧元。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;由谷歌 DeepMind 和 Meta 的研究人员于 2023 年创立&lt;/strong&gt;，Mistral 将自己定位为欧洲对抗硅谷在人工智能领域主导地位的答案。该公司专注于为欧洲各国政府和企业提供基础设施服务，正在法国和瑞典建设由其管理的云计算设施。&lt;/p&gt;

&lt;p&gt;近期，&lt;strong&gt;Mistral 将其 AI 服务定位为专为工程和制造流程量身定制，并与空中客车公司和宝马集团等欧洲大型工业企业签署了合作协议&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;尽管如此，这家法国初创公司的模型和聊天机器人在企业和消费者中的吸引力远不及 OpenAI、Anthropic 以及中国竞争对手的产品。OpenAI 和 Anthropic 计划在今年上市，此前 xAI 所有者 SpaceX 已进行首次公开募股，该公司在首个交易日前的估值约为 1.8 万亿美元。OpenAI 在 3 月以 8520 亿美元的估值完成最新一轮融资，而 Anthropic 上月的估值为 9650 亿美元。&lt;/p&gt;

&lt;p&gt;Mistral 已讨论向欧洲银行及其他机构提供其及他机构提供其替代 Anthropic 的 Mythos 的产品，这是一款善于发现网络安全漏洞的 AI 模型。Mistral 首席执行官 Arthur Mensch 将这种能力描述为国家安全风险。"我们必须掌握这项技术，"他上个月表示。&lt;/p&gt;

&lt;p&gt;Mistral 此前的投资者包括法国国家银行 Bpifrance，以及 Lightspeed Venture Partners、General Catalyst 和 Andreessen Horowitz 等知名美国风投公司。&lt;/p&gt;

&lt;p&gt;( &lt;a href="/Z" class="user-mention" title="@Z"&gt;&lt;i&gt;@&lt;/i&gt;Z&lt;/a&gt; Potentials)&lt;/p&gt;
&lt;h2 id="03 有态度的观点"&gt;&lt;strong&gt;03 有态度的观点&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;1、微软 CEO 纳德拉提出「token 资本」：AI 时代企业竞争力的新坐标&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="https://717KLO2znpj6gvtH.public.blob.vercel-storage.com/uploads/RTE_Dev_Comm/VP5TFiUsQhd1_640" title="" alt="图片"&gt;&lt;/p&gt;

&lt;p&gt;微软 CEO 萨提亚·纳德拉昨日发布长文，提出「token 资本」概念，认为&lt;strong&gt;&amp;nbsp;AI 时代企业的竞争力将取决于能否在人力资本与 token 资本之间构建持续复利的学习闭环&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;每家企业都必须同时构建两种资本：人力资本与 token 资本。人力资本，是员工所拥有的知识、判断力、人际关系、创造力与规律洞察；token 资本，则是企业自主构建和掌控的 AI 能力。&lt;/p&gt;

&lt;p&gt;他强调，&lt;strong&gt;token 资本的增长并不会削弱人力资本的价值，反而会使其更加珍贵&lt;/strong&gt;。在他看来，人的主动性才是驱动 token 资本增长的核心动力——人类负责设定目标、跨域连接、识别关键规律，「没有人的引领，计算只不过是在原地打转」。&lt;/p&gt;

&lt;p&gt;纳德拉认为，这一轮 AI 转型与以往任何平台变革都不同：过去，数字系统用于增强人力资本；而这一次，人与数字系统之间首次能够形成真正的认知闭环，从&lt;strong&gt;根本上改变了企业对「工作」本身的定义&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;他还提出了一个检验标准：&lt;strong&gt;企业应该能够随时替换底层通用模型，而不丢失已沉淀在系统中的专业判断&lt;/strong&gt;——即「公司老兵经验」。这被他视为企业在 AI 时代是否真正掌握主权与控制权的核心标准。&lt;/p&gt;

&lt;p&gt;(&lt;a href="/APPSO" class="user-mention" title="@APPSO"&gt;&lt;i&gt;@&lt;/i&gt;APPSO&lt;/a&gt;)&lt;/p&gt;
&lt;h2 id="04 社区黑板报"&gt;&lt;strong&gt;04 社区黑板报&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;招聘、项目分享、求助……任何你想和社区分享的信息，请联系我们投稿。（加微信 creators2022，备注「社区黑板报」）&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1、活动推荐｜Physical AI Builders Night&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="https://717KLO2znpj6gvtH.public.blob.vercel-storage.com/uploads/RTE_Dev_Comm/hURVRJX4e8Co_640" title="" alt="图片"&gt;&lt;/p&gt;

&lt;p&gt;Come hang out with people who love hardware, build hardware, and invest in hardware. Brought to you by RiseLink, informal, and Agora.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Panel + Demo Night!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We're bringing together a fun group of physical AI builders in SF for a panel &amp;amp; demo night!&lt;/p&gt;

&lt;p&gt;Chat with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;a founder in ed tech who has shipped millions of pieces of ed-tech robotics,&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;builders in the wearables space,&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;a global voice AI provider,&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;the founder of a hardware incubator.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And we will talk everything hardware building - trends in physical AI, the real challenges each faces from prototype to production.&lt;/p&gt;

&lt;p&gt;📢** Want to demo?**&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;RSVP and Answer the questions, then we'll choose 3-5 projects to showcase.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;时间&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;6.18 Thursday&lt;/p&gt;

&lt;p&gt;17:30 - 20:30 GMT-7&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;地点&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Studio 45 - informal spaces SF&lt;/p&gt;

&lt;p&gt;San Francisco, CA&lt;/p&gt;

&lt;p&gt;Luma page：https://luma.com/scsjhpdo&lt;/p&gt;

&lt;p&gt;&lt;img src="/uploads/photo/2026/fab5a1c5-acfb-4597-911e-90d0c8167e40.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="https://717KLO2znpj6gvtH.public.blob.vercel-storage.com/uploads/RTE_Dev_Comm/Sxx63qZ__dNG_640" title="" alt="图片"&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.rtecommunity.dev" rel="nofollow" target="_blank" title=""&gt;阅读更多 Voice Agent 学习笔记：了解最懂 AI 语音的头脑都在思考什么&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;写在最后：&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;我们欢迎更多的小伙伴参与&lt;strong&gt;「RTE 开发者日报」&lt;/strong&gt;内容的共创，感兴趣的朋友请通过开发者社区或公众号留言联系，记得报暗号「共创」。&lt;/p&gt;

&lt;p&gt;对于任何反馈（包括但不限于内容上、形式上）我们不胜感激、并有小惊喜回馈，例如你希望从日报中看到哪些内容；自己推荐的信源、项目、话题、活动等；或者列举几个你喜欢看、平时常看的内容渠道；内容排版或呈现形式上有哪些可以改进的地方等。&lt;/p&gt;

&lt;p&gt;&lt;img src="https://717KLO2znpj6gvtH.public.blob.vercel-storage.com/uploads/RTE_Dev_Comm/uv9tGDNrZqw4_640" title="" alt="图片"&gt;&lt;/p&gt;

&lt;p&gt;作者提示: 个人观点，仅供参考&lt;/p&gt;</description>
      <author>RTE</author>
      <pubDate>Thu, 18 Jun 2026 00:16:34 +0800</pubDate>
      <link>https://testerhome.com/topics/44336</link>
      <guid>https://testerhome.com/topics/44336</guid>
    </item>
    <item>
      <title>构建 AI Agent，先设计上下文系统</title>
      <description>&lt;h2 id="Agent 时代，Prompt 不再是核心"&gt;Agent 时代，Prompt 不再是核心&lt;/h2&gt;
&lt;p&gt;过去一两年，很多人都在研究 Prompt Engineering。怎么提问、怎么设定角色、怎么拆解任务、怎么让模型输出更稳定，似乎只要提示词写得足够好，就能把大模型的能力榨干。这在 ChatGPT 早期确实有效，因为那时大多数任务还是单轮问答：写一段文案、解释一个概念、生成一段代码、总结一篇文章。单轮任务里，Prompt 的影响非常明显，指令写得清楚，结果通常就会更好。&lt;/p&gt;

&lt;p&gt;但到了 AI Agent 时代，情况变了。Agent 不是回答一次就结束，它要连续推理、调用工具、读取文件、搜索资料、修改代码、运行测试、根据反馈继续调整。这个过程中，真正决定结果的，往往不再是某一句 Prompt 写得多漂亮，而是模型在每一步推理时，到底看到了什么信息。换句话说，AI Agent 的核心能力，正在从 Prompt Engineering 转向 Context Engineering。&lt;/p&gt;

&lt;p&gt;这个变化会影响很多工程实践。过去我们优化 Prompt，重点是把任务说清楚；现在我们优化 Agent，重点是让它在正确时刻拿到正确材料。一个提示词可以告诉模型你要认真分析，但如果上下文里没有日志、没有接口约束、没有历史决策、没有失败尝试，它也只能认真地猜。&lt;/p&gt;
&lt;h2 id="Context Engineering 管的是信息质量"&gt;Context Engineering 管的是信息质量&lt;/h2&gt;
&lt;p&gt;所谓 Context Engineering，可以理解为：为模型构造最合适的上下文。更准确地说，是找到一组最小但高信号的 Token，让模型完成目标的概率最大化。这里有两个关键词：最小，高信号。最小，不是越短越好，而是不塞无关内容；高信号，不是信息越多越好，而是信息要真正有用。Context Engineering 的目标不是把所有资料都丢给模型，而是让模型在正确的时间，看到正确的信息。&lt;/p&gt;

&lt;p&gt;这和 Prompt Engineering 的区别非常关键。Prompt Engineering 关心的是怎么说，Context Engineering 关心的是给什么。前者更像写指令，后者更像设计信息系统。一个优秀 Agent 的上下文里，不只有用户问题，还包括系统提示、工具描述、历史消息、外部数据、代码文件、运行结果、长期记忆、当前任务状态等。Prompt 只是 Context 的一部分，而且在复杂任务中，它往往不是最重要的那一部分。&lt;/p&gt;

&lt;p&gt;很多人误以为模型表现不好，是因为模型不够强，或者 Prompt 不够细。但在 Agent 场景里，更常见的问题其实是上下文质量太差。就像你让一个工程师排查线上故障，却不给日志、不给最近发布记录、不给架构说明，只说一句帮我看下为什么报错，再强的工程师也只能猜。模型也是一样。它不是不会推理，而是缺少足够准确、相关、及时的上下文。&lt;/p&gt;

&lt;p&gt;所以，Context Engineering 真正要解决的不是把上下文塞满，而是做信息治理：哪些内容能直接影响判断，哪些内容只是背景噪声；哪些内容应该常驻，哪些内容应该按需加载；哪些历史经验值得保留，哪些旧信息会误导当前任务。&lt;/p&gt;
&lt;h2 id="强 Agent 赢在会找上下文"&gt;强 Agent 赢在会找上下文&lt;/h2&gt;
&lt;p&gt;这也是为什么同一个模型，在不同产品里的表现差距会很大。同样是 Claude，在普通聊天窗口里，它可能只是一个回答问题的助手；但在 Claude Code 里，它更像一个会工作的工程师。差异不完全来自模型本身，而来自上下文系统。Claude Code 不会指望用户一次性把所有信息说清楚，它会自己查看目录、读取文件、搜索关键字、定位调用链、运行命令，再根据结果继续探索。它的优势不是更会答，而是更会找。&lt;/p&gt;

&lt;p&gt;这就是 Context Engineering 里非常重要的一种策略：Just-in-Time Context，即即时上下文。传统 RAG 通常是在推理前先检索一批资料，然后塞进上下文里。这个方法适合静态知识问答，但对复杂工程任务不一定够用。真实开发不是这样做的。程序员不会先把整个代码仓库背下来，再开始修 Bug；程序员会先看入口，再查调用，再读相关文件，再运行测试。即时上下文就是模拟这种工作方式：先保留轻量线索，需要时再动态加载。&lt;/p&gt;

&lt;p&gt;即时上下文的关键不是多查几次资料，而是把探索过程变成一条可收敛的路径。先用目录、文件名、错误信息建立粗粒度判断，再读取最相关的局部内容，接着用工具结果验证假设。如果发现方向错了，就缩小范围重新定位。强 Agent 不是永远一开始就知道答案，而是能通过工具把不确定性一步步压低。&lt;/p&gt;
&lt;h2 id="长上下文会稀释注意力"&gt;长上下文会稀释注意力&lt;/h2&gt;
&lt;p&gt;这背后有一个很现实的问题：上下文不是越长越好。很多人看到 128K、200K、1M token 的上下文窗口，会自然觉得窗口越大，效果越强。但长上下文并不等于有效上下文。信息塞得越多，模型的注意力越容易被稀释。重要内容虽然还在上下文里，但模型不一定还能稳定关注到它。这就是所谓的 Context Rot，中文可以理解为上下文腐烂。&lt;/p&gt;

&lt;p&gt;Context Rot 的本质不是信息丢失，而是信噪比下降。比如在对话前面你明确告诉模型认证必须使用 JWT，后面又塞进去几万 token 的代码、日志、工具输出和历史讨论，模型最后写代码时可能就不再严格遵守最开始的约束。不是因为那条信息消失了，而是因为它被大量噪声淹没了。上下文窗口越大，这个问题越隐蔽：你以为信息还在，模型就一定能用上；但实际上，模型的注意力预算已经被消耗掉了。&lt;/p&gt;

&lt;p&gt;所以 Context Engineering 的本质，其实是管理模型的注意力预算。每一个放进上下文的 Token，都在争夺模型的注意力。放进去的信息越多，单个关键信息获得的注意力就可能越少。因此，真正优秀的上下文系统，不是不断扩大输入，而是持续优化信噪比：哪些信息必须保留，哪些信息应该压缩，哪些信息应该延迟加载，哪些信息应该写入外部记忆，哪些信息应该直接丢弃。&lt;/p&gt;

&lt;p&gt;这也是很多长任务最后变形的原因。任务刚开始时，约束很清楚，目标也很清楚；中间经过几十轮工具调用和讨论后，模型看到的是一堆局部事实、临时判断和过期结论。真正重要的约束没有消失，但它不再显眼。Context Engineering 要做的，就是持续把关键约束重新放回可见位置。&lt;/p&gt;
&lt;h2 id="工具决定上下文入口"&gt;工具决定上下文入口&lt;/h2&gt;
&lt;p&gt;这也解释了为什么工具设计对 Agent 如此重要。Agent 不是只靠大脑工作，它还要靠工具获取上下文。如果工具设计混乱，Agent 就很难稳定完成任务。文档里有一句非常实用的原则：如果人类工程师都无法明确判断某个场景应该使用哪个工具，就不能指望 AI Agent 判断得更好。很多失败的 Agent 系统，不是模型能力不够，而是工具集太臃肿、职责重叠、命名模糊、返回结果冗余，导致模型不知道该用哪个工具、什么时候用、失败后如何补救。&lt;/p&gt;

&lt;p&gt;好的工具应该像好的工程接口：单一职责、用途明确、参数清晰、返回高信号结果。比如 &lt;code&gt;read_file&lt;/code&gt; 就是读文件，&lt;code&gt;grep_code&lt;/code&gt; 就是搜索代码，&lt;code&gt;run_test&lt;/code&gt; 就是运行测试。不要设计一堆 &lt;code&gt;search_docs&lt;/code&gt;、&lt;code&gt;query_docs&lt;/code&gt;、&lt;code&gt;lookup_docs&lt;/code&gt;、&lt;code&gt;find_docs&lt;/code&gt; 这种边界模糊的工具。工具越清晰，模型越容易形成稳定策略；工具越模糊，模型越容易在选择工具这一步就开始偏航。&lt;/p&gt;

&lt;p&gt;工具的返回结果也同样重要。一个工具如果每次返回几千行日志，却没有摘要、错误定位和可继续操作的线索，模型仍然要在噪声里找重点。更好的工具应该帮助 Agent 缩小问题范围，比如返回匹配位置、相关上下文、失败原因、下一步建议，而不是把原始材料一股脑倒进上下文。&lt;/p&gt;
&lt;h2 id="长任务需要外部记忆"&gt;长任务需要外部记忆&lt;/h2&gt;
&lt;p&gt;当任务变长之后，仅靠即时上下文还不够，还需要长期任务管理机制。第一种机制是压缩，也就是当上下文接近上限时，把历史对话、工具调用、关键决策和中间结果总结成更短的摘要，再继续推进。压缩的目标不是保留所有细节，而是保留任务继续所需的关键事实，比如已经尝试过什么、做过哪些架构决策、遇到过哪些错误、当前进展到哪里。&lt;/p&gt;

&lt;p&gt;第二种机制是结构化笔记。相比把所有内容塞在聊天历史里，更好的做法是让 Agent 把关键信息写到外部状态中，比如 &lt;code&gt;TODO.md&lt;/code&gt;、&lt;code&gt;NOTES.md&lt;/code&gt;、&lt;code&gt;STATE.md&lt;/code&gt; 或项目进度日志。这些笔记不必长期占用上下文窗口，但可以在需要时被重新读取。它们相当于 Agent 的外部记忆，既能保持任务连续性，又不会持续污染当前上下文。&lt;/p&gt;

&lt;p&gt;第三种机制是子代理架构。当任务足够复杂时，单个 Agent 的上下文窗口很容易被拖垮。更合理的方式是让主 Agent 负责规划和协调，让多个子 Agent 分别处理局部任务。比如一个子 Agent 分析代码，一个子 Agent 分析日志，一个子 Agent 查数据库变更，最后各自返回精简摘要给主 Agent。这样做的好处是，每个子 Agent 都拥有相对干净的上下文窗口，可以深入探索自己的问题域，而主 Agent 只需要接收高信号结论。&lt;/p&gt;

&lt;p&gt;这些机制看起来像流程设计，其实都是上下文管理。压缩解决的是历史太长的问题，结构化笔记解决的是任务状态容易丢的问题，子代理解决的是单个上下文窗口装不下所有细节的问题。它们共同指向同一个目标：让模型在需要判断时，看到的是经过筛选的有效信息，而不是全过程录像。&lt;/p&gt;
&lt;h2 id="未来壁垒是上下文基础设施"&gt;未来壁垒是上下文基础设施&lt;/h2&gt;
&lt;p&gt;从这个角度看，未来 Agent 系统的竞争重点会非常明确：不是谁的 Prompt 更花哨，而是谁的上下文系统更强。模型会越来越强，上下文窗口会越来越大，工具也会越来越多，但这些并不会自动解决复杂任务中的一致性问题。恰恰相反，随着 Agent 能做的事情越来越复杂，上下文管理会变得越来越关键。&lt;/p&gt;

&lt;p&gt;对开发者来说，这个变化非常现实。如果你正在做 Coding Agent、测试 Agent、知识库助手、自动化运维助手，真正应该投入的不是神奇 Prompt 模板，而是上下文基础设施。你需要考虑：任务开始前加载什么，任务过程中如何检索，工具结果如何裁剪，历史信息如何压缩，长期状态如何保存，失败模式如何反馈到系统提示里，哪些内容应该让模型自主探索，哪些内容应该提前提供。&lt;/p&gt;

&lt;p&gt;尤其是在测试开发场景里，Context Engineering 的价值非常直接。一个测试 Agent 如果只知道帮我生成测试用例，它的输出大概率泛泛而谈；但如果它能读取接口文档、历史缺陷、代码变更、业务规则、Mock 约束、线上事故记录，它就有可能生成真正有效的测试策略。测试本身就是上下文密集型工作。缺上下文的测试，是模板化测试；有上下文的测试，才可能接近真实风险分析。&lt;/p&gt;

&lt;p&gt;这也意味着，测试开发以后不只是写自动化脚本，还要参与上下文资产建设：接口文档是否准确，缺陷记录是否可检索，业务规则是否结构化，测试策略是否能沉淀成可复用经验。Agent 能不能用好这些资料，取决于我们有没有把资料整理成它能稳定获取、理解和执行的形态。&lt;/p&gt;
&lt;h2 id="Prompt 会降级，注意力不会消失"&gt;Prompt 会降级，注意力不会消失&lt;/h2&gt;
&lt;p&gt;这也是为什么我认为，Prompt Engineering 不会消失，但它会降级。它仍然重要，但不再是核心壁垒。未来更重要的是 Context Engineering：如何让模型持续获得高质量上下文，如何让它在复杂任务中保持方向感，如何避免信息污染，如何把有限 Token 用在最有价值的地方。&lt;/p&gt;

&lt;p&gt;一句话总结：Prompt Engineering 研究的是如何向模型提问，Context Engineering 研究的是如何组织模型看到的世界。AI Agent 的上限，不只取决于模型会不会思考，更取决于它思考之前看到了什么。模型能力会继续提升，但注意力预算始终有限。谁能把最高信号的信息放进有限上下文里，谁就能构建更强的 Agent。&lt;/p&gt;
&lt;h5 id="FunTester 名片｜万粉千文，百无一用"&gt;FunTester 名片｜万粉千文，百无一用&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://mp.weixin.qq.com/s/o1dGKThvC64LvJDnKLybOw" rel="nofollow" target="_blank" title=""&gt;软件测试的道与术&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mp.weixin.qq.com/s/MFgZ3Btx0TKOd5sFGFDxcg" rel="nofollow" target="_blank" title=""&gt;测试开发成长史&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mp.weixin.qq.com/s/wyyn32HrwmH5uV8F0d2HDQ" rel="nofollow" target="_blank" title=""&gt;AI ，测试有点东西&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mp.weixin.qq.com/s/5ssJH4yOE3H1Vk4ziJ0Bdg" rel="nofollow" target="_blank" title=""&gt;《从 Java 开始性能测试》连载全集&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mp.weixin.qq.com/s/3ilD1fnJFo9Gt8puO7zFiQ" rel="nofollow" target="_blank" title=""&gt;性能测试修炼之道&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mp.weixin.qq.com/s/t2k6IITzaFicaEi77iZ5tA" rel="nofollow" target="_blank" title=""&gt;故障测试与混沌工程实战合集&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mp.weixin.qq.com/s/CSE2UmQNFrHaEnyyM4U41w" rel="nofollow" target="_blank" title=""&gt;我的语言迁徙：Java &amp;amp; Groovy &amp;amp; Go&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mp.weixin.qq.com/s/0F41EuPsziWwr25o7Cj3-A" rel="nofollow" target="_blank" title=""&gt;懂点 Web 前端&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mp.weixin.qq.com/s/LKEfCkx_iEr8JrA1lT3HnQ" rel="nofollow" target="_blank" title=""&gt;视频教程全收录&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <author>Fhaohaizi</author>
      <pubDate>Wed, 17 Jun 2026 21:37:20 +0800</pubDate>
      <link>https://testerhome.com/topics/44335</link>
      <guid>https://testerhome.com/topics/44335</guid>
    </item>
  </channel>
</rss>
