AI应用 Markdown 渲染对比与原生实现方案

DeepSeek、豆包、腾讯元宝、ChatGPT 渲染实现对比表

产品解析方式渲染引擎/库UI 组件架构Markdown支持范围流程图/导图支持扩展架构及裁剪流式解析渲染
DeepSeek原生解析(非WebView)采用 CommonMark 标准解析器(推测使用 Markwon 库); 代码高亮使用 Prism4J; 数学公式使用 JLatexMathRecyclerView 列表 + TextView(Spannable 富文本渲染)支持完整 Markdown 语法:标题、列表、链接、图像等; 支持代码块语法高亮、表格、LaTeX公式支持 Mermaid 流程图,内置渲染预览(直接将Mermaid代码生成图表); 思维导图输出为 Markdown(需借助外部工具预览)渲染功能模块化(若基于Markwon插件体系),可按需增减功能以控制体积支持,多轮对话逐字流式输出; 解析可异步进行,性能高(本地无网络延迟)
豆包原生解析(非WebView)采用 CommonMark 标准解析(内部实现或第三方库,如 Markwon); 代码高亮库未知(可能 Prism4J 或定制); 数学公式可能使用 JLatexMathRecyclerView + TextView(富文本); Chat UI 界面原生实现支持大部分 Markdown 元素:标题、列表、引用、表格、代码块等; 支持代码高亮、LaTeX 公式(满足教育场景需求)流程图:当前未直接内置 Mermaid 渲染(输出Mermaid代码需外部工具预览);思维导图:支持生成包含导图结构的 Markdown,需借助如 Markmap/XMind 查看内部架构支持插件式扩展(推测),功能可插拔裁剪(字节系注重模块化)支持流式响应,逐字逐句输出; 解析渲染在后台完成,UI 实时刷新(体验流畅)
腾讯元宝原生解析(非WebView)采用 CommonMark 解析(可能基于 Markwon 或自研); 集成 Prism4J 等实现代码高亮; 集成数学公式库(未明确,可能KaTeX或JLatexMath)RecyclerView + TextView(或自定义View组合); 代码块区域提供复制按钮等交互定制支持完整 Markdown:标题、列表、表格、代码块等均正确渲染; 支持代码块语法高亮、LaTeX公式渲染流程图:内置 Mermaid 图表生成功能,一句话生成并预览流程图/饼图/时序图等; 用户可复制Mermaid源码;思维导图:支持生成导图格式Markdown,需外部工具转换支持插件化扩展(功能组件可按需加载,减小APK体积)支持流式输出; Markdown 内容异步解析、增量渲染更新,复杂图表生成稍有延迟但总体流畅
OpenAI ChatGPT原生解析(非WebView)采用CommonMark解析器(官方未明示,可能使用自研或Markwon库); 前端渲染规则与网页版一致; 代码高亮采用类似Highlight.js方案(在App内实现)RecyclerView + TextView(每条消息富文本显示); 代码块区域带独立样式和“复制”按钮(原生定制视图)支持标准 Markdown:标题(渲染为加粗大字)、列表、链接、表格等; 支持代码块高亮显示,表格样式良好; 支持行内$\LaTeX$和块公式渲染(使用KaTeX引擎,与网页版一致)流程图:不支持 Mermaid 等图表直接渲染(输出Mermaid源码时仅按普通文本显示); 思维导图:不支持封闭架构,功能固定(内部模块化开发,但非公开插件); 官方App未提供裁剪配置支持流式回答输出,逐字符更新界面; 原生富文本解析高效,长答案也能平滑呈现

:以上基于公开信息与推测整理,各产品具体实现可能存在内部差异。

各家架构方案优缺点简要分析

DeepSeek: 强调本地高性能渲染和丰富排版支持。其客户端很可能使用了 Markwon 等原生方案,实现 Markdown、公式、代码的全面渲染,不依赖WebView。优点是渲染速度快、离线可用,支持 LaTeX、Mermaid 等高级特性。用户得到的答案排版专业美观,适合技术和学术场景。但集成众多解析插件会增大应用体积,Mermaid 图表渲染需内置额外组件(如JS引擎或WebView片段),实现较复杂。整体架构灵活,可扩展性强,但需平衡功能全面与应用大小。

豆包: 作为字节跳动的大模型应用,架构成熟稳健。采用原生 Markdown 解析,保证性能和体验。其优势在于综合性:既能满足日常问答,又侧重教育和编程场景,因而支持公式和代码高亮。UI 上可能针对代码块、公式做了优化处理,保证展示效果清晰。豆包的优点是 ByteDance 在移动端优化和模块化方面的经验,使其应用在支持丰富格式的同时保持较佳的流畅度和包体积。缺点是在某些高级扩展上相对谨慎,比如暂未直接支持流程图/思维导图的即时渲染,用户需要自行使用外部工具,将Markdown输出转换为图表。这体现了功能取舍:豆包优先保证核心场景下的稳定体验,延迟了对小众特性的支持。

腾讯元宝: 架构设计注重功能完整性创新性。在Markdown渲染方面,其基础实现与同行类似(原生解析+富文本显示),确保代码高亮、表格、公式等常规元素到位。同时,元宝积极扩展了Markdown的边界,例如集成 Mermaid 引擎,实现“一句话生成图表”的新功能。这使其在可视化呈现上领先一步,满足工作报告、数据分析等需求。优点是用户无需离开聊天界面即可预览流程图等,可复制Mermaid源码用于他处,体验流畅直观。此架构的挑战在于,需要安全高效地在Android端渲染图表。元宝可能通过插件或嵌入小型WebView来完成图表渲染,这部分稍增加了实现复杂度和内存开销。此外,引入更多解析组件也增加了应用体积。不过通过模块化设计,腾讯或允许按需裁剪不常用的特性,尽量减小性能影响。总体而言,元宝架构功能丰富,在可视化扩展上具备优势,但实现和维护成本相对更高。

OpenAI ChatGPT(官方App): 架构以还原网页版体验为目标,呈现标准的Markdown排版效果。其优势是对常用元素支持良好:代码块有高亮和复制功能,表格、美术排版与网页一致,数学公式通过KaTeX渲染精准美观(与网页相同的公式渲染引擎)。由于官方应用面对全球大众用户,稳定性和一致性是首要考虑,所以架构上相对保守,不支持超出常规Markdown范围的扩展(如流程图语法不解析渲染)。优点是实现成熟,输出结果可靠统一;Markdown解析在客户端原生完成,无需网络额外渲染,速度快且安全。由于OpenAI官方未公开实现细节,我们推测其内部可能也使用了类似CommonMark的解析库,并针对代码块等做了专门处理。其封闭式架构的缺点是灵活性略逊:用户无法自定义扩展新的Markdown语法,开发者在引入新特性时也需非常慎重(确保安全和跨平台一致)。不过,ChatGPT官方App经过优化,在流式输出时几乎感觉不到卡顿或延迟,这说明其在异步解析、渐进渲染上做得非常出色。整体来说,ChatGPT架构稳健,覆盖了主流需求,但在新奇特的Markdown扩展上不如国内产品激进。

Android 原生 Markdown 渲染推荐方案

基于以上对比,我们推荐采用Android 原生方案构建Markdown富文本渲染模块,以满足多元素混排、插件化扩展和高性能流式显示的要求。下文从架构、所用库、扩展设计和性能策略等方面给出完整方案。

方案架构概览

推荐架构采用分层设计,核心思路是:“Markdown 源文本 -> 解析引擎 -> 树结构(AST) -> 渲染插件处理 -> 本地视图呈现”。整个流程在Android本地完成,无需加载网页。架构示意如下:

  • Markdown解析层:使用符合CommonMark规范的解析库,将Markdown文本解析为中间的语法树或节点列表。建议采用开源的 Markwon 库,它基于 commonmark-java 将 Markdown 解析为 Android 原生 Spannable 内容。Markwon 遵循CommonMark标准,支持所有基础Markdown语法,并且无需借助HTML/WebView。这保证了解析的正确性性能

  • 渲染插件层:在解析结果的基础上,应用各类插件进行富文本渲染扩展。Markwon本身提供插件机制,允许为特定Markdown元素定制渲染。我们可以按需集成下列插件/组件:

    • 代码高亮插件:集成 Prism4J 作为代码语法高亮引擎,与 Markwon 的 SyntaxHighlightPlugin 配合使用。Prism4J 提供多语言的语法定义,可针对代码块的语言标识实现着色渲染。通过只加载必要语言的语法模块,可以控制体积。代码块最终以 Spannable 实现高亮,在 TextView 中显示有底色的等宽字体区域,支持横向滚动和复制功能(可为代码块元素定制附加一个“复制”按钮Span或使用覆盖View实现)。

    • 表格插件:启用 Markwon 的 TablePlugin,对 Markdown 表格语法进行支持。该插件会计算单元格布局并绘制表格线条,使表格内容以 Spannable 形式在 TextView 中呈现。这样无需使用WebView也能显示表格。

    • LaTeX 公式插件:集成 Markwon-Ext-LaTeX(底层使用 JLatexMath)。当 Markdown 中出现 $...$ 行内公式或 $$...$$ 块级公式时,插件调用 JLatexMath 将其解析为本地图像并作为 ImageSpan 嵌入文本。这样应用即可渲染数学符号和公式推导。公式图片在解析时生成,可缓存以提升重复显示性能。

    • 流程图/思维导图插件:自定义 Mermaid/MindMap 扩展支持。由于 Mermaid 等图表语法不是标准Markdown,可通过 Markwon 提供的插件接口,拦截特定的代码块:例如语言标识为mermaid的代码段。插件的实现思路是在后台调用 Mermaid 图表渲染引擎,将代码转换为图形,然后在文本中以图片形式嵌入。具体实现可以有两种方案:其一,内置轻量WebView加载 Mermaid 的官方脚本,将Mermaid代码渲染成SVG/PNG(渲染过程可离线进行,因为Mermaid是前端JS库),再将生成的图像截取嵌入;其二,利用第三方开源工具,如 KrokiQuickChart API,将Mermaid代码发送到服务端换取图片(在线时可用)。考虑纯离线需求,前者更合适。虽然引入WebView组件,但只用于离屏渲染图表,不影响整个界面的原生性质。对于思维导图,可采用类似方案:例如使用 Markmap 库,将带有特殊标识的 Markdown 转换为导图SVG。插件应设计为可插拔,即应用可以选择是否包含 Mermaid/Markmap 功能,以便在不需要时剔除相关库减少体积。

    • 图片和多媒体插件:启用 Markwon 内置的 图像插件(例如 Coil or Glide 插件)以支持 Markdown 中的 ![]() 图片语法。这样聊天回复中包含的图片链接可以自动加载显示。插件支持 GIF 等动图显示,并可定制占位符和加载策略。此插件同样可按需裁剪(如果不需要用户插入图片,可移除)。

    • 其他定制插件:利用 Markwon的扩展点,可添加自定义Markdown标记支持。例如,如果需要支持CheckBox任务列表、下标上标、自定义表情标签等,都可以通过插件扩展解析和渲染逻辑,实现对Markdown语法的功能超集支持。由于Markwon高度可扩展,这些均可作为独立模块添加或移除。

  • 展示层(UI组件层):使用 Android 原生 UI 来承载渲染后的文本和图表。推荐使用 RecyclerView 列表显示聊天对话,每条消息作为一个独立的 item。对于Markdown内容,可采用一个自定义的 MarkdownTextView(内部继承自TextView)。Markwon 解析后的 Spannable 内容直接通过 markdownTextView.setText(spannable) 显示,保留各种格式和嵌入的图像/公式。由于 Markwon 渲染结果是原生文本组件,可以在 TextView 内部良好支持样式、点击和滚动等交互。对于代码块复制按钮等特殊UI,可有两种方案:其一,通过在代码块区域添加一个 ClickableSpanImageSpan 来模拟按钮,点击时执行复制动作;其二,在MarkdownTextView的外围布局覆盖一个小的“复制”ImageButton,于渲染后根据代码块位置定位。这两种方式都能实现代码块的交互,但第一种方式更模块化,无需额外测量位置。整体UI架构保持“列表项RecyclerView + 文本视图渲染内容”的简单模式,充分利用Android富文本能力,避免复杂的WebView嵌套。

插件化与裁剪能力设计

在架构实现上,应当遵循插件化原则。各项Markdown扩展(表格、公式、代码高亮、图表等)作为独立模块集成。开发时通过 Markwon 的 MarkwonPlugin 接口注册这些插件;构建时可以通过 Gradle 配置来选择包含哪些模块。例如:

  • 基础版本仅包含 core 和必要的插件(如图片),APK 更小;

  • 完整版本则加入 Tables, Strikethrough, SyntaxHighlight, Latex, Mermaid 等插件,提供最强渲染能力。

由于Markwon及其插件大多开源,裁剪非常灵活。未使用的插件库可以不打包,减少包体积。比如如果某场景不需要LaTeX支持,剔除相关依赖即可减少几百KB库体积。Mermaid渲染所需的JS脚本也可以在需要时动态加载,从而不让所有用户都承担体积成本。整体架构确保按需组装:对于不同发行版本或不同功能开关,可以启用/关闭特定插件模块,以满足定制化需求。

值得注意的是,插件化设计不仅是模块划分,还意味着低耦合易扩展。开发者后续如果需要支持新的Markdown扩展语法(比如 UML 图、音乐播放器嵌入等),可以独立编写插件并集成,而不用修改核心解析流程。这种架构为将来功能迭代留出了空间。

高性能增量渲染与流式输出策略

为提供丝滑的用户体验,需要在性能上采取多种优化策略:

  • 异步解析:Markdown 内容的解析和渲染尽量放在工作线程完成。当AI回答以流式方式返回时,应用可以累积收到的部分文本,在后台线程增量解析。解析完成后,将结果切换到主线程更新UI。这样可避免主线程卡顿,使得即使长段文本也能实时呈现出来。Markwon 支持在非UI线程构建 Spannable,然后通过setText在UI线程应用,非常适合此场景。

  • 增量合并渲染:针对流式输出,应用应实现增量渲染算法。即每当新文本块从后台模型抵达时,将其附加到当前消息的Markdown源码,并对新增部分进行解析,再与已有的Spannable结果合并。由于Markdown语法可能因为新内容改变先前段落的解析(例如刚结束的段落可能其实是尚未闭合的列表),最稳妥的方式是在每次更新时对整条消息的Markdown进行重新解析,然后diff计算出与上一版本的差异,再仅更新变动的部分。不过,为简化实现,也可以每次直接重新设置整条消息内容(Markwon 解析结果)到TextView。因Markwon解析效率较高,这种方式也能每秒多次更新而无明显性能问题。为了避免闪烁,可在流式更新时暂时禁用某些昂贵元素的解析,例如Mermaid代码块在内容未完时可先渲染为占位图或延后渲染,待代码块结束符出现再触发真正图表生成。这样逐步输出文字的同时,大块元素最终完整渲染,一次性展现,减小中途抖动。

  • 预排版优化:利用 PrecomputedText 或 Markwon 提供的预计算功能,对最终的大段富文本提前计算布局,以加速在UI线程的呈现。Markwon 从4.2.0开始内置了对 PrecomputedText 的支持,可在后台完成文本测量。这对特别长的回答能显著减少首次绘制延迟。

  • 内存与复用:通过RecyclerView复用消息项视图,确保长对话滚动时仍然流畅。同时可以对公式图片、代码样式等结果做缓存,例如将最近渲染过的LaTeX公式和Mermaid图表结果缓存Bitmap或SVG,避免相同内容重复解析。图片则依赖Glide/Coil缓存策略自动优化。

  • 流式输出的节奏控制:为了提升打字机效果的平滑度,可以设置一个最小刷新间隔(例如50ms),批量处理在该时间窗内积累的多个token,再统一解析更新UI。这样避免过于频繁的UI刷新,又保证用户看到字符持续流动。许多开源实现已验证了通过定时批量更新可实现打字机式顺滑动画。

综合以上策略,应用将具备高并发解析、高帧率渲染的能力。在用户发送提问后,模型逐字回应的过程中,应用可以一边接收一边解析、一边显示,几乎实时地把Markdown富文本结果呈现在屏幕上。这种架构在各类AI助手中已被证明行之有效——无论是深思考(DeepSeek)还是ChatGPT,都采用了类似思路,只是具体实现细节略有不同。

方案总结

综上所述,推荐的Markdown原生渲染方案以 Markwon 为核心,实现CommonMark解析和可扩展的渲染管线。配合代码高亮(Prism4J)、公式(JLatexMath)、图表(Mermaid/Markmap)等插件,实现 Markdown多元素混排 的完美支持,涵盖表格、公式、代码、流程图等格式。架构设计突出插件化,方便按需增减功能模块,从而控制包体积并适应不同应用场景。另一方面,通过异步解析、增量更新和缓存等优化策略,确保高性能地进行流式输出,给用户带来流畅的“即时呈现”体验。这个方案在技术上较为成熟,很多优秀实践(如Markwon库及其插件)已有现成支持。采用该方案,可以让Android客户端在无需WebView的情况下,实现媲美网页的Markdown渲染效果、甚至超越网页的定制扩展能力,为AI对话应用提供强大的富文本展示功能。

参考资料:

  • Markwon 官方文档(Android Markdown解析库特性)

  • 开源实现示例:Android端通过Markwon+Prism4J渲染Markdown和代码高亮

  • DeepSeek/NextChat 等应用特性介绍(支持LaTeX、Mermaid、代码高亮等富文本)

  • 腾讯元宝 Mermaid 图表功能新闻 (Markdown 扩展应用案例)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/bicheng/82726.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Ubuntu20.04系统安装,使用系统盘安装

1、系统安装 Ubuntu20.04系统安装,使用系统盘安装 查看ubuntu系统版本 lsb_release -a:显示发行版名称、版本号及代号 (base) rootai-System-Product-Name:/media/ai/wh/clash-for-linux-master# lsb_release -a No LSB modules are available. Distri…

(自用)Java学习-5.19(地址管理,三级联动,预支付)

1. 地址管理模块 地址展示 前端:通过 showAddress() 发起 Ajax GET 请求,动态渲染地址列表表格,使用 #{tag}、#{name} 等占位符替换真实数据。 后端: 控制器层调用 AddressService,通过 AddressMapper 查询用户地址数…

Spring 循环依赖:从原理到解决方案的全面解析

Spring 循环依赖:从原理到解决方案的全面解析 一、循环依赖的定义与分类 1. 什么是循环依赖? 在 Spring 框架中,循环依赖指的是多个 Bean 之间形成了依赖闭环。例如: Bean A 依赖 Bean BBean B 依赖 Bean CBean C 又依赖 Bean…

n 阶矩阵 A 可逆的充分必要条件是 ∣ A ∣ ≠ 0

n 阶矩阵 A 可逆的充分必要条件是 ∣ A ∣ ≠ 0 |A| \neq 0 ∣A∣0 的几何意义 1. 行列式的几何意义回顾 行列式 ∣ A ∣ |A| ∣A∣(或 det ⁡ ( A ) \det(A) det(A))表示矩阵 A A A 所对应的线性变换对空间的体积缩放因子: ∣ A ∣ &…

Rockey Linux 安装ffmpeg

1.环境准备 Rockey linux 9.2 ffmpeg 静态资源包 这个是我自己的: https://download.csdn.net/download/liudongyang123/90920340https://download.csdn.net/download/liudongyang123/90920340 这个是官网的 Releases BtbN/FFmpeg-Builds GitHub 以上两个资…

wordcount在集群上的测试

1.将louts.txt文件从cg计算机复制到master节点上面,存放在/usr/local/hadoop 需要输入密码:83953588abc scp /root/IdeaProjects/mapReduceTest/lotus.txt root172.18.0.2:/usr/local/hadoop /WordCountTest/input 2.将lotus.txt文件从master这台机器…

AI+制造:中小企业的低成本智能化转型

文章内容过长,可以考虑直接跳转到文章末尾查看概要图 在制造业竞争日益激烈的今天,中小企业正面临着前所未有的挑战:人力成本持续攀升、能源消耗居高不下、质量控制难度增加。与此同时,数字化转型已成为行业共识,但高…

Linux C/C++编程 —— 线程技术总结

一、线程基本概念 线程是进程内的一个执行单元,多个线程共享进程的资源(如内存、文件描述符等),但每个线程拥有自己的寄存器、栈等。与进程相比,线程的创建、切换开销较小,能更高效地利用 CPU 资源。 二、…

Femap许可证与网络安全策略

随着科技的快速发展,网络安全问题已成为各行各业关注的焦点。在电磁仿真领域,Femap作为一款领先的软件,其许可证的安全性和网络策略的重要性不言而喻。本文将探讨Femap许可证与网络安全策略的关系,确保您的电磁仿真工作能够在一个…

深度解析:SQLynx 如何筑牢数据库安全防线​

在数据驱动业务发展的时代,数据库作为企业核心资产的 “保险箱”,其安全性至关重要。一旦数据库遭遇攻击、数据泄露或被误操作,将给企业带来不可估量的损失。而 SQLynx 作为一款专注于数据库安全管理的工具,凭借其多项创新技术与功…

更新时间相差8个小时

下面的java代码在updateFill方法里面生成的modifiedTime时间是当前时间是正确的,为什么到service层testCommonFieldAutoUpdate方法里面去更新的时候modifiedTime就差8个小时呢?代码如下所示: Slf4j Component public class MpMetaObjectHand…

Windows逆向工程提升之IMAGE_TLS_DIRECTORY

公开视频 -> 链接点击跳转公开课程博客首页 -> ​​​链接点击跳转博客主页 目录 TLS的作用 TLS的实现 静态 TLS​​ 动态 TLS​​ 内部实现 回调机制 TLS Directory 的结构 TLS的作用 TLS (Thread Local Storage) 是一种用于为多线程应用程序提供线程独立存储空…

云效流水线Flow使用记录

概述 最近在频繁使用阿里云云效的几款产品,如流水线。之前写过一篇,参考云效流水线缓存问题。 这篇文章来记录更多问题。 环境变量 不管是云效流水线Flow还是应用交付AppStack(基于流水线,后文不再赘述)&#xff0…

Android中获取控件尺寸进阶方案

在Android开发中,很多场景都需要获取控件(View)的宽高信息,比如动态布局调整、动画效果实现等。然而,直接在Activity的onCreate()中调用控件的getWidth()或getHeight()`方法,得到结果却是0,因为控件还没完成布局测量。 本文总结了几种获取控件大小的实用方法,并对各方…

android 输入系统

一、输入系统的核心角色与分层架构 Android 输入系统的本质是桥梁:一端连接硬件驱动产生的原始事件,另一端将事件精准派发给应用窗口。整个过程涉及三层架构和多个关键组件,可类比为 “快递分拣系统”: 1. 硬件与内核层&#xf…

ubuntu中,c和c+程序,预编译、编译、链接和运行命令

目录 安装编译器一.c编译运行(粗暴简单)1.编写 C 程序:2. 预处理:3.编译:4. 汇编:5. 链接:6.运行 二.c编译运行(粗暴简单)1.编写 C 程序:2.预处理&#xff1a…

【FastAPI】--2.进阶教程(一)

【FastAPI】--基础教程-CSDN博客 app.get和post的参数: 参数类型说明pathstr路由路径(如 "/marks"),必填。response_modelType[T]定义响应数据的模型(如 percent),会自动校验和序列…

KT6368A通过蓝牙芯片获取手机时间详细说明,对应串口指令举例

一、功能简介 KT6368A双模蓝牙芯片支持连接手机,获取手机的日期、时间信息,可以同步RTC时钟 1、无需安装任何app,直接使用系统蓝牙即可实现 2、同时它不影响音频蓝牙,还支持一些简单的AT指令进行操作 3、实现的方式&#xff1…

【平面波导外腔激光器专题系列】用于光纤传感的低噪声PLC外腔窄线宽激光器

----翻译自Mazin Alalusi等人的文章 摘要 高性价比的 1550 nm DWDM平面外腔 &#xff08;PLANEX&#xff09; 激光器是干涉测量、布里渊、LIDAR 和其他光传感应用的最佳选择。其线宽<3kHz、低相位/频率噪声和极低的RIN。 简介 高性能光纤分布式传感技术是在过去几年中开发…

企业微信内部网页开发流程笔记

背景 基于ai实现企微侧边栏和工作台快速问答小助&#xff0c;需要h5开发&#xff0c;因为流程不清楚摸索半天&#xff0c;所以记录一下 一、网页授权登录 1. 配置步骤 1.1 设置可信域名 登录企业微信管理后台 进入"应用管理" > 选择开发的具体应用 > “网…