生态产品多个设计器现已发布候选版本

查看生态总览
跳转到内容

Reasoning 思考过程 alpha ​

模型的推理片段:默认随写入自动展开,完成后自动收起,用户手动操作过一次后不再自动开合。

用法 ​

思考时自动展开、每秒更新已经想了多久,思考完成后自动收起;状态文案由组件按是否在思考、已用时与时长给出

组件结构 ​

加粗的是必需部件。

data-scope="reasoning":root · trigger · icon · indicator · label · duration · content

示例 ​

无壳内联形态 ​

ghost 档不占用一块面,开关收为只占文字宽度的小药丸,适合在一段回答中穿插多处

把两处思考穿插在同一段回答里,它们不再各自占一张卡:

语气与尺寸 ​

tone 切换指示符与状态文案的色族,size 切换标题行与正文的几何档;五份都处于思考中,正文自动展开

success:先看约束,再看目标。
warning:先看约束,再看目标。
danger:先看约束,再看目标。
sm:先看约束,再看目标。
lg:先看约束,再看目标。

受控开合与禁用 ​

open 交给宿主:外部一个按钮统一开合几段思考,自动开合让位;disabled 的一段开关不可按下,停在给定的档位

先确认这次只改皮肤:解剖与事件都不动,公开面就只增不减。

设计指引 ​

何时使用 ​

  • 展示推理模型输出的思考过程,且它边生成边显示。
  • 希望读者可以回看推理过程,但默认不占版面。

何时不用 ​

  • 展示一次工具调用时,使用工具调用,两者共用同一台状态机但正文形态不同。
  • 内容不是散文而是结构化数据时,属于工具调用的参数与结果。

特性 ​

  • 自动开合与工具调用是同一台状态机:锁存依靠转移的放置位置,不依靠布尔位,用户点击过一次之后阶段变化就不再触发自动开合。
  • 思考时长由两个时刻计算,任一缺席即无法计算:流被中止时兜底收尾不写结束时刻,推理块只有起点没有终点,这一情况必须被处理。
  • 名称与时长都排在开关内,“思考过程,用时 12 秒”整句构成开关的可访问名称。
  • 状态文案由组件提供:进行中知道开始时刻(startTime)就把已经想了的整秒数代入 thinkingFor 的 {seconds},每秒跟着走,不知道时显示 thinking;完成后把秒数代入 thoughtFor 的 {seconds},无法计算时长时回落到折叠区的名称。名称位不写内容时显示的就是它。只换了 thinking 没给 thinkingFor 时照旧显示 thinking,不把英文缺省串混进本地化过的文案。
  • 已用时的表只在思考中走:机器每秒记一次当前时刻,想完即拆掉计时器,一段会话里的几十个推理块不会各挂一个空转的计时器。api.elapsedMs 在思考中是已用时、想完即时长。
  • 形态三档:outline 描边(默认档)、subtle 底色分区、ghost 无壳内联。一段回答中穿插多处思考时使用 ghost,它不占一块面,开关收为只占文字宽度的小圆角块。
  • 开合有动画:展开与收起是行高与内缩同帧动画,收起在动画完成后才真正隐藏。

组合 ​

  • 正文使用流式正文:思考过程是散文,与工具调用的等宽结构块不同。正文放在一个容器内:展开动画测量的是第一行的行高,散落的多个兄弟节点无法正确收起。
  • 多段推理并排且一次只展开一段时使用手风琴。
  • 需要让“进行中 → 完成”被读屏播报时,把会话级的活动区域放在推理块外,由它读出结果。

最佳实践 ​

  • 完成后显示时长,读者据此判断是否值得展开。
  • 默认收起。思考过程是给需要查看的人看的,不是回答本身。

反模式 ​

  • 把思考过程当作回答显示:两者混在一起时读者分不清结论。
  • 用它承载工具调用的参数与结果:正文排版是散文形态,等宽结构块会挤在一起。

API 参考 ​

产物 ​

层值
自定义元素<xh-reasoning>
Vue 组件XhReasoningContent XhReasoningDuration XhReasoningIcon XhReasoningIndicator XhReasoningLabel XhReasoningRoot XhReasoningTrigger
组合式函数useReasoning
状态机无,connect 直接由 props 算属性
皮肤@xihan-ui/styles/reasoning.css

Props ​

属性类型必填说明
autoDisclosureboolean运行时自动展开、结束时自动收起,默认开启;用户手动开合过一次即永久停用。
defaultOpenboolean
disabledboolean
endTimenumber思考结束的时刻。可能缺席:流被中止时兜底收尾不写该字段。
onOpenChange(details: ToolCallOpenChangeDetails) => void
openboolean
sizeSize
startTimenumber开始思考的时刻,毫秒时间戳。
streamingboolean仍在思考。适配器把它折叠为状态机的 running。
toneTone
translationsPartial<ReasoningTranslations>
variantControlVariant形态:outline 描边、subtle 底色分区、ghost 无壳内联。默认 outline。

事件 ​

自定义元素将载荷放在 detail;Vue 使用同名 emit。

事件载荷说明
open-changeToolCallOpenChangeDetails开合变化;detail 为 { open: boolean, source: 'user' | 'auto' | 'api' }

插槽 ​

仅列出带载荷的插槽。

Vue 组件插槽载荷说明
XhReasoningRootdefaultReasoningRootSlotProps

React 适配器 props ​

只列各组件自己声明的那些:继承自 ComponentPropsWithRef 的 DOM 属性不在其中,根组件上与上面 Props 表同名的也不重复列。Vue 的对应物是上面的插槽表。

React 组件属性类型必填说明
XhReasoningRootchildrenSlotChildren<ReasoningRootSlotProps>

状态 ​

公开状态写入 data-state。

部件取值
root'open' | 'closed'
trigger'open' | 'closed'
indicator'open' | 'closed'
content'open' | 'closed'

connect API ​

getXxxProps() 返回对应部件的宿主属性。

成员类型说明
openboolean
streamingboolean
disabledboolean
durationMsnumber | undefined思考时长,毫秒;两个时刻任一缺席即 undefined。
elapsedMsnumber | undefined已经想了多久,毫秒:思考中按开始时刻与每秒走一次的表算,想完即 durationMs。 缺开始时刻时为 undefined。
statusTextstring当前应显示的状态文案,已按 streaming 与时长选定。
setOpen(next: boolean) => void
getRootProps() => T['element']
getTriggerProps() => T['button']
getIconProps() => T['element']
getIndicatorProps() => T['element']
getLabelProps() => T['element']
getDurationProps() => T['element']
getContentProps() => T['element']

无障碍 ​

键盘 ​

规格出处:W3C APG

按键生效条件行为
Enter / Space焦点在折叠开关上且未禁用展开或收起思考正文,并把自动开合永久停用
Enter / Space按住折叠开关且未禁用按住期间 trigger 投影 data-pressed,与指针 :active 同一副按压面(disclosure trigger 只换面不缩放);抬起、失焦或转禁用撤下。思考中照常接

ARIA ​

以下属性由 connect 生成。

部件属性值
triggeraria-controlscontent 部件的 id
triggeraria-expanded'true' | 'false'
iconaria-hidden'true'
indicatoraria-hidden'true'
contentaria-labelledbytrigger 部件的 id
contentrole'region'
  • 开关带 aria-expanded 与 aria-controls,正文区是 role=region 且由开关命名。
  • 不另发 aria-label:另发会覆盖节点内的文字,两者不一致时读屏读出的与屏幕不符。
  • 组件自身不开活动区域:整段思考每来一个字都播报会淹没读屏。

样式参考 ​

皮肤 ​

@xihan-ui/styles/reasoning.css 按 [data-scope="reasoning"][data-part="root"] 部件选择器书写,发布产物以挂载类 .xh-scope-reasoning 代替其中的 data-scope(特异性相同),位于 xihan.components 与 xihan.motion 层。覆盖样式使用 xihan.overrides,部件选择器照常可用。

forced-colors: active 下另有一套规则:颜色交给系统,边框与状态标记改用系统色关键字。

数据属性 ​

由 connect 生成;条件不成立时不输出无值属性。

部件属性值
rootdata-disabled''(条件成立时才出现)
rootdata-sizeprops.size
rootdata-state'open' | 'closed'
rootdata-streaming''(条件成立时才出现)
rootdata-toneprops.tone
rootdata-variantprops.variant
triggerdata-disabled''(条件成立时才出现)
triggerdata-pressed''(条件成立时才出现)
triggerdata-state'open' | 'closed'
triggerdata-streaming''(条件成立时才出现)
triggerdata-xh-action-control''
triggerdata-xh-action-display'always'
triggerdata-xh-action-profile'disclosure-trigger'
triggerdata-xh-action-sizeprops.size
triggerdata-xh-action-variant'ghost'
icondata-streaming''(条件成立时才出现)
indicatordata-instant''(条件成立时才出现)
indicatordata-state'open' | 'closed'
labeldata-instant''(条件成立时才出现)
labeldata-streaming''(条件成立时才出现)
durationdata-instant''(条件成立时才出现)
durationdata-streaming''(条件成立时才出现)
contentdata-instant''(条件成立时才出现)
contentdata-state'open' | 'closed'

CSS 变量 ​

本组件公开覆盖槽由独立皮肤的实际消费位生成;默认来源、作用部件和状态均与 CSS 同源。

变量部件CSS 属性状态默认来源说明
--xh-reasoning-bgrootbackgrounddefault
variant=outline
variant=subtle
--xh-bg-subtle
--xh-bg-surface
reasoning 的 root 部件 background 覆盖槽。
--xh-reasoning-borderrootborderdefault
variant=outline
--xh-border-defaultreasoning 的 root 部件 border 覆盖槽。
--xh-reasoning-content-fgcontentcolordefault--xh-fg-mutedreasoning 的 content 部件 color 覆盖槽。
--xh-reasoning-content-font-sizecontentfont-sizedefault--xh-text-secondary-sizereasoning 的 content 部件 font-size 覆盖槽。
--xh-reasoning-content-leadingcontentline-heightdefault--xh-text-prose-leadingreasoning 的 content 部件 line-height 覆盖槽。
--xh-reasoning-content-pecontentpadding-inline-enddefault--xh-reasoning-pxreasoning 的 content 部件 padding-inline-end 覆盖槽。
--xh-reasoning-content-pscontentpadding-inline-startdefault--xh-space-3reasoning 的 content 部件 padding-inline-start 覆盖槽。
--xh-reasoning-duration-fgdurationcolordefault--xh-fg-subtlereasoning 的 duration 部件 color 覆盖槽。
--xh-reasoning-duration-font-sizedurationfont-sizedefault--xh-text-caption-sizereasoning 的 duration 部件 font-size 覆盖槽。
--xh-reasoning-font-sizetriggerfont-sizedefault--xh-_reasoning-font-sizereasoning 的 trigger 部件 font-size 覆盖槽。
--xh-reasoning-icon-fgiconcolordefault--xh-fg-subtlereasoning 的 icon 部件 color 覆盖槽。
--xh-reasoning-icon-sizeroot--xh-icon-sizedefault--xh-_reasoning-icon-sizereasoning 的 root 部件 --xh-icon-size 覆盖槽。
--xh-reasoning-icon-streaming-fgiconcolorstreaming--xh-fg-mutedreasoning 的 icon 部件 color 覆盖槽。
--xh-reasoning-indicator-fgindicatorcolordefault--xh-fg-subtlereasoning 的 indicator 部件 color 覆盖槽。
--xh-reasoning-label-font-weightlabelfont-weightdefault--xh-text-label-weightreasoning 的 label 部件 font-weight 覆盖槽。
--xh-reasoning-label-streaming-fglabelcolor@media (prefers-reduced-motion: reduce)
@media print
motion=reduce
streaming
where([data-motion='reduce'])
--xh-fg-defaultreasoning 的 label 部件 color 覆盖槽。
--xh-reasoning-pxcontent
trigger
margin-inline-start
padding-inline
padding-inline-end
default--xh-_reasoning-pxreasoning 的 content、trigger 部件 margin-inline-start、padding-inline、padding-inline-end 覆盖槽。
--xh-reasoning-pycontent
trigger
padding-block
padding-block-end
@keyframes xh-disclosure-collapse
@keyframes xh-disclosure-expand
default
xh-action-profile=disclosure-trigger
--xh-_reasoning-pyreasoning 的 content、trigger 部件 padding-block、padding-block-end 覆盖槽。
--xh-reasoning-radiusrootborder-radiusdefault--xh-shape-surfacereasoning 的 root 部件 border-radius 覆盖槽。
--xh-reasoning-railcontentborder-inline-startdefault--xh-border-subtlereasoning 的 content 部件 border-inline-start 覆盖槽。
--xh-reasoning-rail-insetcontentmargin-inline-startdefault--xh-reasoning-pxreasoning 的 content 部件 margin-inline-start 覆盖槽。
--xh-reasoning-rail-widthcontentborder-inline-startdefault--xh-stroke-thinreasoning 的 content 部件 border-inline-start 覆盖槽。
--xh-reasoning-shadowrootbox-shadowdefault
tone
0 0 0 transparent
none
reasoning 的 root 部件 box-shadow 覆盖槽。
--xh-reasoning-shimmer-durationlabelanimationstreaming--xh-motion-loop-shimmerreasoning 的 label 部件 animation 覆盖槽。
--xh-reasoning-shimmer-fromlabelbackground-imagestreaming--xh-fg-subtlereasoning 的 label 部件 background-image 覆盖槽。
--xh-reasoning-shimmer-tolabelbackground-imagestreaming--xh-fg-defaultreasoning 的 label 部件 background-image 覆盖槽。
--xh-reasoning-tone-barrootbox-shadowtone--xh-stroke-thickreasoning 的 root 部件 box-shadow 覆盖槽。
--xh-reasoning-tone-fgrootbox-shadowtone--xh-_tone-softreasoning 的 root 部件 box-shadow 覆盖槽。
--xh-reasoning-trigger-bg-hovertriggerbackground-colordisabled
hover
loading
not([data-disabled])
not([data-loading])
--xh-_action-variant-bg-hoverreasoning 的 trigger 部件 background-color 覆盖槽。
--xh-reasoning-trigger-fgtriggercolordefault
disabled
hover
is(:active, [data-pressed])
loading
not([data-disabled])
not([data-loading])
pressed
--xh-fg-mutedreasoning 的 trigger 部件 color 覆盖槽。
--xh-reasoning-trigger-gaptriggergapdefault--xh-space-2reasoning 的 trigger 部件 gap 覆盖槽。
--xh-reasoning-trigger-radiusroot
trigger
border-radiusvariant=ghost--xh-shape-controlreasoning 的 root、trigger 部件 border-radius 覆盖槽。

动效 ​

动效角色:按压 · 状态 · 披露 · 出现 · 循环(见动效规范)。

可覆盖的动效槽:--xh-reasoning-shimmer-duration。

共享关键帧 xh-disclosure-collapse · xh-disclosure-expand · xh-fade-in · xh-shimmer 由 family/motion.css 提供,皮肤 @import 它,单独引入仍成立;color · rotate 走 transition 过渡。时长与缓动读动效令牌,改令牌即改全局节奏。

皮肤之外还有一段:退场由适配器的退场闸门把关,动画播完才真收起。

prefers-reduced-motion: reduce 下本组件另有降级规则。

RTL ​

皮肤用逻辑属性排布(inline-start 一族),dir="rtl" 下自动镜像。

Released under The MIT License