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

查看生态总览
跳转到内容

DiffView 差异视图 ​

一份改动的逐行呈现:并排或单栏、双侧行号、变更类型的读屏文字,以及远离变更处的折叠。

用法 ​

两个入口归一到同一个模型:这里用新旧两版全文计算,着色在建模时一次算好

src/clamp.ts
Removedexport function clamp(n: number, min: number) {
Removed return Math.max(n, min)
Addedexport function clamp(n: number, min: number, max: number) {
Added return Math.min(Math.max(n, min), max)
Unchanged}

组件结构 ​

加粗的是必需部件。

data-scope="diff-view":root · header · summary · viewport · body · row · line-number · line-content · change-label · inline-change · token · gap · gap-cell · gap-trigger · empty · truncation · comment-trigger · comment-thread

示例 ​

并排与折叠 ​

并排两列都发出格子,空的一侧照发;远离变更的连续上下文折为一格,点击即展开

src/pipeline.ts+2−2
Unchangedconst step0 = pipeline.at(0)Unchangedconst step0 = pipeline.at(0)
Removedconst step1 = pipeline.at(1)Removed
AddedAddedconst step1 = pipeline.head()
Unchangedconst step2 = pipeline.at(2)Unchangedconst step2 = pipeline.at(2)
Unchangedconst step3 = pipeline.at(3)Unchangedconst step3 = pipeline.at(3)
Unchangedconst step4 = pipeline.at(4)Unchangedconst step4 = pipeline.at(4)
Unchangedconst step19 = pipeline.at(19)Unchangedconst step19 = pipeline.at(19)
Unchangedconst step20 = pipeline.at(20)Unchangedconst step20 = pipeline.at(20)
Unchangedconst step21 = pipeline.at(21)Unchangedconst step21 = pipeline.at(21)
Removedconst step22 = pipeline.at(22)Removed
AddedAddedconst step22 = pipeline.tail()
Unchangedconst step23 = pipeline.at(23)Unchangedconst step23 = pipeline.at(23)

长行换行与词级差异 ​

开启 wrap 使长行原地折行;配对的删改行之间再比较一次词,只有真正改动的片段上底色

src/client.ts+2−2
Removedconst endpoint = "https://api.example.com/v1/workspaces/{id}/documents?include=revisions&limit=50"
Removedconst timeout = 3000
Addedconst endpoint = "https://api.example.com/v2/workspaces/{id}/documents?include=revisions,authors&limit=100"
Addedconst timeout = 8000
Unchangedexport const client = createClient({ endpoint, timeout })

超长差异的截断提示 ​

超过 maxLines 的部分被截去,提示条向读者说明截去了多少行

src/items.ts
Unchangedconst item0 = 0
Unchangedconst item1 = 1
Removedconst item2 = 2
Addedconst item2 = 200
Unchangedconst item3 = 3
Unchangedconst item4 = 4
Unchangedconst item5 = 5
48 more lines were cut off and are not shown

尺寸 ​

size 改变字号、行高与行号槽的宽度,三档并列对照

src/clamp.ts · sm
Removedexport function clamp(n: number, min: number) {
Removed return Math.max(n, min)
Addedexport function clamp(n: number, min: number, max: number) {
Added return Math.min(Math.max(n, min), max)
Unchanged}
src/clamp.ts · md
Removedexport function clamp(n: number, min: number) {
Removed return Math.max(n, min)
Addedexport function clamp(n: number, min: number, max: number) {
Added return Math.min(Math.max(n, min), max)
Unchanged}
src/clamp.ts · lg
Removedexport function clamp(n: number, min: number) {
Removed return Math.max(n, min)
Addedexport function clamp(n: number, min: number, max: number) {
Added return Math.min(Math.max(n, min), max)
Unchanged}

行评论 ​

commentable 在每行正文前给一颗评论钮,点它报出 comment-request;挂在 commentLines 里的行在代码下方铺出评论容器,内容由 comment 插槽写

src/client.ts+3−2
Removedexport function createClient(base: string) {
Removed return fetchJson(base, { timeout: 5000 })
Addedexport function createClient(base: string, token?: string) {
Added const headers = token ? { authorization: token } : {}
Added return fetchJson(base, { timeout: 30000, headers })
超时从 5 秒放到 30 秒,网关那边的超时也要一起改。
Unchanged}

多文件 ​

parseUnifiedPatch 把一份多文件补丁拆成每个文件一份模型;逐份放进折叠面板,标题栏写路径与增删数,差异视图不再写头部,表格直接以路径为名

2 个文件,+3 −2

Unchangedexport function createClient(base: string) {
Removed return fetchJson(base, { timeout: 5000 })
Added const headers = { accept: "application/json" }
Added return fetchJson(base, { timeout: 30000, headers })
Unchanged}

Removedexport const MAX_RETRIES = 3
Addedexport const MAX_RETRIES = 5
Unchangedexport function shouldRetry(status: number) {
Unchanged return status >= 500
Unchanged}

设计指引 ​

何时使用 ​

  • 展示 AI 提议的代码改动,或两版文本的对比。
  • 已有统一格式的补丁,或有新旧两版全文。

何时不用 ​

  • 只展示一段代码时,使用代码视图。
  • 展示 AI 提议的数据编辑并逐条取舍时,使用带多选的表格。

特性 ​

  • 两个入口归一到同一个模型:computeTextDiff(before, after) 用两版全文计算,parseUnifiedPatch(patch) 解析补丁;组件只接受模型。
  • 自定义渲染器可调用 diffViewSides(view) 取得列序:单栏为旧侧,分栏按旧侧、新侧排列。
  • 着色在建模时一次计算,不在连接层执行:computeTextDiff 持有完整文本,整体切分一次再按行取用,跨行的块注释与多行字符串才不会着错色。parseUnifiedPatch 拿不到完整文件,因此一律不着色。
  • 词级差异:配对的一条删除行与一条新增行之间再比较一次词,只有实际变动的片段加底色;整行改写与超长行不比较。两个入口都产出,wordDiff: false 关闭。
  • contextLines 把 hunk 内远离变更的连续上下文折成一格,点击展开。展开集合可受控,便于“全部展开”等操作统一持有。
  • wrap 让长行原地折行,容器不再横向滚动;窄栏与并排视图下尤其有用。
  • 头部自带增删统计位 summary,增删各一个,数字取自模型,着色跟随变更类型。
  • maxLines 是必需的上限:AI 可能输出超大文件,新旧两侧各自超出时从尾部截断。截断行数由模型带出,truncation 提示条向读者说明。
  • 行号与列号一律从模型计算,不从 DOM 反推。
  • 行评论:commentable 在每行正文前给一颗评论钮,点它报出 comment-request(这一行的侧、行号、变更类型与文本);挂在 commentLines 里的行在代码下方铺出 comment-thread 容器,内容由作者写(Vue 用 comment 插槽,React 用 XhDiffViewBody 的 renderComment,Web Components 监听 comment-mount 往容器里放)。评论落在哪一行按侧与行号记:单栏里删除行落旧侧、其余落新侧,并排按所在的那一侧。评论与代码同住一个格,表格的行序与列号不受影响。组件只管位置与入口,评论的存储、输入与多条回复都归宿主。

组合 ​

  • 单栏与并排的切换使用切换按钮组;增删统计已有成品位(只需要数字时可用 diffStats(model))。
  • 多文件:parseUnifiedPatch 把一份多文件补丁拆成每个文件一份模型,逐份渲染一个差异视图。文件多时外面套折叠面板(multiple),标题栏写路径与 diffStats 算出的增删数;这时差异视图不必再写头部,表格直接以路径为名。git 补丁的路径带 a/、b/ 前缀,展示前自行去掉。组件不内建文件列表:列表的排序、筛选、已读标记与跳转都随产品而变。
  • 放入工具调用的详情区,展示本次调用的改动。
  • 需要对 AI 提议的编辑逐条取舍并应用时,使用表格的选择机制承载行级取舍,单元格内放复选框,页脚的计数与“应用”使用按钮。差异视图本身只读,不接这套交互。

最佳实践 ​

  • 并排视图给足宽度:两列各自还要横向滚动;窄栏下单栏更易读,或开启 wrap 让长行折行。
  • 折叠阈值取三到五行:过少时需要频繁展开,过多时折叠失去意义。

反模式 ​

  • 截断后不渲染 truncation:截断的差异看起来仍像完整差异,评审者会误以为已经看完。
  • 对补丁计算出的差异着色:文本不完整,跨行的记号必然切错。
  • 用颜色作为变更类型的唯一线索:色觉障碍与高对比度模式下会失效。

API 参考 ​

产物 ​

层值
自定义元素<xh-diff-view>
Vue 组件XhDiffViewBody XhDiffViewEmpty XhDiffViewHeader XhDiffViewRoot XhDiffViewSummary XhDiffViewTruncation XhDiffViewViewport
组合式函数useDiffView
状态机diffViewMachine
皮肤@xihan-ui/styles/diff-view.css

Props ​

属性类型必填说明
modelDiffModel差异模型,唯一入口。补丁与新旧两版文本都先归一到它。
viewDiffViewMode
contextLinesnumber变更两侧各显示的上下文行数,其余折叠;未提供或非有限值时不折叠。
expandedValuereadonly string[]展开的折叠格 id 集合,提供即受控。
defaultExpandedValuereadonly string[]
wrapboolean长行原地折行,不再横向滚动;默认关闭。
labelledboolean作者渲染了 header 部件时置真,由适配器统计。为真且模型带路径时表格的可访问名指向头部; 否则直接用路径(没有路径时用 translations.diff)作名字——指向未渲染的 id 会让读屏读空。
commentableboolean每行正文前给一颗评论钮,点它报出 comment-request,默认关闭。 一组钮只占一个 Tab 位,上下方向键在组内走;指针设备上悬停到这一行或键盘聚焦时才露出来。
commentLinesreadonly DiffViewLineRef[]挂着评论的行:这些行的正文格里、代码下方铺出 comment-thread 部件,内容由作者写。
sizeSize
translationsPartial<DiffViewTranslations>
onExpandedValueChange(details: DiffViewExpandedValueChangeDetails) => void
onCommentRequest(details: DiffViewCommentRequestDetails) => void在某一行上点了评论钮:宿主据此打开输入框,把这一行加进 commentLines。

DiffViewLineRef ​

commentLines 的元素。

字段类型必填说明
sideDiffSide是
linenumber是

事件 ​

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

事件载荷说明
comment-mountCustomEvent新铺出一个评论容器;detail 为 { side, line, change, text, element }
expanded-value-changeDiffViewExpandedValueChangeDetails展开集合变化;detail 为 { value: string[] }
comment-requestDiffViewCommentRequestDetails在某一行上点了评论钮;detail 为 { side, line, change, text }

插槽 ​

仅列出带载荷的插槽。

Vue 组件插槽载荷说明
XhDiffViewBodydefault—
XhDiffViewBodycommentDiffViewCommentRequestDetails
XhDiffViewRootdefaultDiffViewRootSlotProps
XhDiffViewSummarydefault{ count: number }
XhDiffViewTruncationdefault{ count: number }

React 适配器 props ​

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

React 组件属性类型必填说明
XhDiffViewBodyrenderComment(details: DiffViewCommentRequestDetails) => ReactNode挂着评论的那一行在代码下方铺出评论容器,内容由它给;入参是这一行的侧、行号、变更类型与文本。
XhDiffViewRootchildrenSlotChildren<DiffViewRootSlotProps>
XhDiffViewSummarychangeExtract<DiffChange, 'added' | 'removed'>是
XhDiffViewSummarychildrenSlotChildren<{ count: number }>
XhDiffViewTruncationchildrenSlotChildren<{ count: number }>

状态 ​

以下名称仅用于内部状态机。

状态:idle

事件:GAP.EXPAND · GAP.COLLAPSE · CONTROLLED.EXPANDED.SET · PRESS.START · PRESS.END · COMMENT.FOCUS

判据:isExpandedControlled

connect API ​

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

成员类型说明
viewDiffViewMode
rowsreadonly DiffViewRow[]折叠后的可见行序,含折叠的格。
expandedValuestring[]
stats{ added: number, removed: number }增删的行数。
truncatedboolean模型被上限截断过。
truncatedLinesnumber被上限截断、未进入模型的源文本行数;未截断时为 0。
truncationTextstring截断提示条的文字,已代入行数;未截断时为空串。
isEmptyboolean没有任何变更。
setExpandedValue(next: string[]) => void
toggleGap(id: string) => void
commentableboolean开了行评论;适配器据此决定要不要在正文格里建评论钮。
commentRefAt(props: DiffViewCellProps) => DiffViewLineRef | undefined这一格的评论落在哪一行;空侧与折叠格为 undefined。
hasComment(props: DiffViewCellProps) => boolean这一格挂着评论(在 commentLines 里);适配器据此在代码下方铺出 comment-thread 部件。
getCommentTriggerProps(props: DiffViewCellProps) => T['button']
getCommentThreadProps(props: DiffViewCellProps) => T['element']挂在这一格里的评论容器,内容由作者写。
getRootProps() => T['element']
getHeaderProps() => T['element']
getSummaryProps(props: { change: DiffChange }) => T['element']头部右侧的增删统计位,增删各一个。
getViewportProps() => T['element']
getBodyProps() => T['element']
getRowProps(props: DiffViewRowProps) => T['element']
getLineNumberProps(props: DiffViewCellProps) => T['element']
getLineContentProps(props: DiffViewCellProps) => T['element']
getChangeLabelProps(props: { change: DiffChange }) => T['element']
getInlineChangeProps(props: DiffViewInlineChangeProps) => T['element']
getTokenProps(token: CodeToken) => T['element']
getGapProps(props: DiffViewGapProps) => T['element']
getGapCellProps() => T['element']
getGapTriggerProps(props: DiffViewGapProps) => T['button']
getEmptyProps() => T['element']
getTruncationProps() => T['element']截断提示条;未截断时带 hidden。
changeLabel(change: DiffChange) => string变更类型对应的读屏文字,写入视觉隐藏的格。
cellText(props: DiffViewCellProps) => string | undefined该行在该侧的文本;split 下空侧为 undefined。
cellNumber(props: DiffViewCellProps) => number | undefined该行在该侧的行号;不存在时为 undefined。
cellTokens(props: DiffViewCellProps) => readonly CodeToken[]该行在该侧的着色片段;不着色或空侧时为空数组。
cellSegments(props: DiffViewCellProps) => readonly DiffViewSegment[]该行在该侧的词级片段,着色记号已按片段边界切分。 未计算词级差异时为空数组,此时按 cellTokens / cellText 铺设。

无障碍 ​

键盘 ​

规格出处:W3C APG

按键生效条件行为
Tab差异视图在 Tab 序列中滚动容器自身可聚焦,随后方向键的横纵滚动交给浏览器,组件不接管
Enter / Space焦点在展开按钮上展开该处折起来的上下文行;组件只接 click,按键走原生 button 的默认行为
Enter / Space按住展开按钮按住期间该格的 gap-trigger 投影 data-pressed,与指针 :active 同一副按压面(disclosure trigger 只换面不缩放);抬起、失焦或该格展开撤下
Tab开了行评论一组评论钮只占一个 Tab 位:落在上次聚焦的那颗,它不在可见行里时落在第一颗
Enter / Space焦点在评论钮上报出 comment-request(这一行的侧、行号、变更类型与文本),走原生 button 的激活
ArrowDown / ArrowUp焦点在评论钮上移到下一颗 / 上一颗评论钮,到头不回绕;并排视图按先旧侧后新侧、逐行往下
Home / End焦点在评论钮上移到第一颗 / 最后一颗评论钮

ARIA ​

以下属性由 connect 生成。

部件属性值
bodyaria-colcount2 | 1
bodyaria-labelundefined | model.newPath
bodyaria-labelledbyheader 部件的 id | undefined
bodyaria-rowcountrows.length
bodyrole'table'
rowaria-rowindexrowIndex
rowrole'row'
line-numberaria-hidden'true'
line-contentaria-colindex2 | 1
line-contentrole'cell'
gaprole'row'
gap-cellaria-colindex1
gap-cellrole'cell'
gap-triggeraria-expanded'true' | 'false'
gap-triggeraria-labelexpandGapLabel(hiddenCountOf(gapId))
comment-triggeraria-labelundefined | commentLabel(ref.line, ref.side)
  • 表格语义:role=table 配 role=row 与 role=cell,带 aria-rowcount / aria-rowindex / aria-colcount / aria-colindex。列数只计算实际暴露的内容列,行号不算列。
  • 表格的名字:渲染了头部且模型带路径时指向头部;没渲染头部时直接用路径,没有路径时用 translations.diff。
  • 每一行都带一段视觉隐藏的变更类型文字,变更不只靠颜色传达。
  • 变更行还有一条非颜色线索:新增绘制实心色条,删除绘制同宽的斜纹条,灰度与高对比度下也可区分。
  • 行号对读屏隐藏,由皮肤用 attr() 绘制,复制差异不会带上行号。
  • 不采用表格的行级 roving:只读差异不是网格,吞掉方向键的焦点组会抢走页面滚动,读屏本身也有表格浏览模式。这是显式决定。
  • 评论钮合起来只占一个 Tab 位,上下方向键在钮之间走、Home / End 到首末;方向键只在焦点落在钮上时才被接管,视口本身的滚动不受影响。可访问名写侧与行号(translations.commentOn)。指针设备上钮平时透明但仍可聚焦,悬停到这一行或焦点进了视口就显出来。

样式参考 ​

皮肤 ​

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

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

数据属性 ​

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

部件属性值
rootdata-commentable''(条件成立时才出现)
rootdata-sizeprops.size
rootdata-truncated''(条件成立时才出现)
rootdata-viewprops.view
rootdata-wrap''(条件成立时才出现)
summarydata-changechange
rowdata-changelineAt(rowIndex)?.change
rowdata-revealed''(条件成立时才出现)
line-numberdata-changelineAt(rowIndex)?.change
line-numberdata-line-numbercellNumber({ rowIndex, side })?.toString()
line-numberdata-sideside
line-contentdata-changelineAt(rowIndex)?.change
line-contentdata-empty''(条件成立时才出现)
line-contentdata-sideside
change-labeldata-changechange
inline-changedata-changelineAt(rowIndex)?.change | undefined
tokendata-kindtoken.kind
gapdata-expanded''(条件成立时才出现)
gapdata-valuehunkIndex:0
gap-triggerdata-pressed''(条件成立时才出现)
gap-triggerdata-valuehunkIndex:0
gap-triggerdata-xh-action-control''
gap-triggerdata-xh-action-display'always'
gap-triggerdata-xh-action-profile'disclosure-trigger'
gap-triggerdata-xh-action-sizeprops.size
gap-triggerdata-xh-action-variant'ghost'
comment-triggerdata-sideref?.side
comment-triggerdata-xh-action-control''
comment-triggerdata-xh-action-display'always'
comment-triggerdata-xh-action-profile'icon'
comment-triggerdata-xh-action-sizeprops.size
comment-triggerdata-xh-action-variant'ghost'
comment-threaddata-sideref?.side

CSS 变量 ​

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

变量部件CSS 属性状态默认来源说明
--xh-diff-view-added-bgrowbackgroundchange=added--xh-diff-added-bgdiff-view 的 row 部件 background 覆盖槽。
--xh-diff-view-added-fginline-change
line-content
line-number
row
summary
background
box-shadow
color
change=added--xh-diff-added-fgdiff-view 的 inline-change、line-content、line-number、row、summary 部件 background、box-shadow、color 覆盖槽。
--xh-diff-view-bgrootbackgrounddefault--xh-bg-surfacediff-view 的 root 部件 background 覆盖槽。
--xh-diff-view-borderrootborderdefault--xh-border-defaultdiff-view 的 root 部件 border 覆盖槽。
--xh-diff-view-change-barrowbackground
box-shadow
change=added
change=removed
--xh-stroke-thickdiff-view 的 row 部件 background、box-shadow 覆盖槽。
--xh-diff-view-comment-colcomment-trigger
line-content
root
block-size
inline-size
min-block-size
min-inline-size
padding-inline-start
commentable
xh-action-profile=disclosure-trigger
xh-action-profile=icon
--xh-diff-view-line-heightdiff-view 的 comment-trigger、line-content、root 部件 block-size、inline-size、min-block-size、min-inline-size、padding-inline-start 覆盖槽。
--xh-diff-view-comment-fgtokencolorkind=comment--xh-fg-muteddiff-view 的 token 部件 color 覆盖槽。
--xh-diff-view-comment-thread-bgcomment-threadbackgrounddefault--xh-bg-surfacediff-view 的 comment-thread 部件 background 覆盖槽。
--xh-diff-view-comment-thread-bordercomment-threadborderdefault--xh-border-defaultdiff-view 的 comment-thread 部件 border 覆盖槽。
--xh-diff-view-comment-thread-fgcomment-threadcolordefault--xh-fg-defaultdiff-view 的 comment-thread 部件 color 覆盖槽。
--xh-diff-view-comment-thread-max-wcomment-threadmax-inline-sizedefault--xh-measure-prosediff-view 的 comment-thread 部件 max-inline-size 覆盖槽。
--xh-diff-view-comment-thread-mycomment-threadmargin-blockdefault--xh-space-2diff-view 的 comment-thread 部件 margin-block 覆盖槽。
--xh-diff-view-comment-thread-pcomment-threadpaddingdefault--xh-space-3diff-view 的 comment-thread 部件 padding 覆盖槽。
--xh-diff-view-comment-thread-radiuscomment-threadborder-radiusdefault--xh-shape-surfacediff-view 的 comment-thread 部件 border-radius 覆盖槽。
--xh-diff-view-comment-trigger-fgcomment-triggercolordefault--xh-fg-muteddiff-view 的 comment-trigger 部件 color 覆盖槽。
--xh-diff-view-comment-trigger-icon-sizecomment-trigger--xh-icon-sizedefault--xh-glyph-size-smdiff-view 的 comment-trigger 部件 --xh-icon-size 覆盖槽。
--xh-diff-view-comment-trigger-radiuscomment-triggerborder-radiusdefault--xh-shape-controldiff-view 的 comment-trigger 部件 border-radius 覆盖槽。
--xh-diff-view-dividerheader
line-number
root
border-block-end
border-inline-end
border-inline-start
@media (min-width: 1024px)
default
side=new
view=split
--xh-border-subtlediff-view 的 header、line-number、root 部件 border-block-end、border-inline-end、border-inline-start 覆盖槽。
--xh-diff-view-empty-bgline-contentbackgroundempty--xh-bg-subtlediff-view 的 line-content 部件 background 覆盖槽。
--xh-diff-view-empty-fgemptycolordefault--xh-fg-muteddiff-view 的 empty 部件 color 覆盖槽。
--xh-diff-view-fontbody
header
font-familydefault--xh-font-family-monodiff-view 的 body、header 部件 font-family 覆盖槽。
--xh-diff-view-font-sizebody
gap-trigger
font-sizedefault--xh-_diff-view-font-sizediff-view 的 body、gap-trigger 部件 font-size 覆盖槽。
--xh-diff-view-gap-bggapbackgrounddefault--xh-bg-subtlediff-view 的 gap 部件 background 覆盖槽。
--xh-diff-view-gap-bg-hovergap-triggerbackground-colordisabled
hover
loading
not([data-disabled])
not([data-loading])
--xh-_action-variant-bg-hoverdiff-view 的 gap-trigger 部件 background-color 覆盖槽。
--xh-diff-view-gap-fggap-triggercolordefault--xh-fg-muteddiff-view 的 gap-trigger 部件 color 覆盖槽。
--xh-diff-view-gutterline-numberinline-sizedefault4chdiff-view 的 line-number 部件 inline-size 覆盖槽。
--xh-diff-view-header-fgheadercolordefault--xh-fg-muteddiff-view 的 header 部件 color 覆盖槽。
--xh-diff-view-header-font-sizeempty
header
truncation
font-sizedefault--xh-text-secondary-sizediff-view 的 empty、header、truncation 部件 font-size 覆盖槽。
--xh-diff-view-header-gapheadergapdefault--xh-space-2diff-view 的 header 部件 gap 覆盖槽。
--xh-diff-view-icon-sizeroot--xh-icon-sizedefault
size=lg
size=sm
--xh-glyph-size-lg
--xh-glyph-size-md
--xh-glyph-size-sm
diff-view 的 root 部件 --xh-icon-size 覆盖槽。
--xh-diff-view-inline-change-radiusinline-changeborder-radiuschange--xh-shape-insetdiff-view 的 inline-change 部件 border-radius 覆盖槽。
--xh-diff-view-keyword-fgtokencolorkind=keyword--xh-syntax-keyworddiff-view 的 token 部件 color 覆盖槽。
--xh-diff-view-keyword-weighttokenfont-weightkind=keyword--xh-font-weight-semibolddiff-view 的 token 部件 font-weight 覆盖槽。
--xh-diff-view-line-heightbody
comment-trigger
gap
gap-trigger
line-content
root
row
block-size
inline-size
line-height
min-block-size
min-inline-size
padding-inline-start
commentable
default
xh-action-profile=disclosure-trigger
xh-action-profile=icon
--xh-text-code-leadingdiff-view 的 body、comment-trigger、gap、gap-trigger、line-content、root、row 部件 block-size、inline-size、line-height、min-block-size、min-inline-size、padding-inline-start 覆盖槽。
--xh-diff-view-max-hviewportmax-block-sizedefault--xh-viewport-max-hdiff-view 的 viewport 部件 max-block-size 覆盖槽。
--xh-diff-view-number-fgline-numbercolordefault--xh-fg-subtlediff-view 的 line-number 部件 color 覆盖槽。
--xh-diff-view-number-token-fgtokencolorkind=number--xh-syntax-numberdiff-view 的 token 部件 color 覆盖槽。
--xh-diff-view-punctuation-fgtokencolorkind=punctuation--xh-fg-subtlediff-view 的 token 部件 color 覆盖槽。
--xh-diff-view-pxempty
gap-trigger
header
line-content
line-number
truncation
padding-inline
padding-inline-end
default--xh-_diff-view-pxdiff-view 的 empty、gap-trigger、header、line-content、line-number、truncation 部件 padding-inline、padding-inline-end 覆盖槽。
--xh-diff-view-pyempty
header
truncation
padding-blockdefault--xh-_diff-view-pydiff-view 的 empty、header、truncation 部件 padding-block 覆盖槽。
--xh-diff-view-radiusrootborder-radiusdefault--xh-shape-surfacediff-view 的 root 部件 border-radius 覆盖槽。
--xh-diff-view-removed-bgrowbackgroundchange=removed--xh-diff-removed-bgdiff-view 的 row 部件 background 覆盖槽。
--xh-diff-view-removed-fginline-change
line-content
line-number
row
summary
background
color
change=removed--xh-diff-removed-fgdiff-view 的 inline-change、line-content、line-number、row、summary 部件 background、color 覆盖槽。
--xh-diff-view-shadowrootbox-shadowdefaultnonediff-view 的 root 部件 box-shadow 覆盖槽。
--xh-diff-view-string-fgtokencolorkind=string--xh-syntax-stringdiff-view 的 token 部件 color 覆盖槽。
--xh-diff-view-truncation-bgtruncationbackgrounddefault--xh-fg-warningdiff-view 的 truncation 部件 background 覆盖槽。
--xh-diff-view-truncation-bordertruncationborder-block-startdefault--xh-border-subtlediff-view 的 truncation 部件 border-block-start 覆盖槽。
--xh-diff-view-truncation-fgtruncationcolordefault--xh-fg-warningdiff-view 的 truncation 部件 color 覆盖槽。
--xh-diff-view-truncation-gaptruncationgapdefault--xh-space-2diff-view 的 truncation 部件 gap 覆盖槽。

动效 ​

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

共享关键帧 xh-drop-in 由 family/motion.css 提供,皮肤 @import 它,单独引入仍成立。时长与缓动读动效令牌,改令牌即改全局节奏。

系统开启减弱动效时由令牌层统一收敛,皮肤不另作判断。

响应式 ​

皮肤按视口分档:min-width: 1024px。

皮肤另按输入能力分档:hover: hover:同一份皮肤在触屏与带指针的设备上不一样,与视口宽度无关。

RTL ​

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

Released under The MIT License