import { h } from '@ui-arch/frame'; import { diffLines } from './diffKit.js'; import type { DiffViewProps } from './types.js'; /*** 文本差异对比:行级对齐 + 增删着色 + 双行号列,超限时退化为整段替换(只影响精度不影响语义) ***/ export function diffView(props: DiffViewProps): HTMLElement { const ignore = props.ignoreWhitespace === true; // 只在显式开启时归一行内空白 const ops = diffLines(props.before, props.after, ignore); // 纯逻辑在 diffKit:这里只负责把它画出来 const added = ops.filter((o) => o.type === 'add').length; // 新增行数:统计条与读屏共用 const removed = ops.filter((o) => o.type === 'del').length; // 删除行数 let oldNo = 0; let newNo = 0; const rows = ops.map((op) => { if (op.type !== 'add') oldNo += 1; // same 与 del 占旧行号 if (op.type !== 'del') newNo += 1; // same 与 add 占新行号 const sign = op.type === 'add' ? '+' : op.type === 'del' ? '−' : ' '; return h( 'div', { class: `ua-diff-row is-${op.type}` }, h('span', { class: 'ua-diff-no' }, op.type === 'add' ? '' : String(oldNo)), h('span', { class: 'ua-diff-no' }, op.type === 'del' ? '' : String(newNo)), h('span', { class: 'ua-diff-sign', 'aria-hidden': 'true' }, sign), h('span', { class: 'ua-diff-text' }, op.text || ' '), ); }); const stats = props.showStats === false ? null : h( 'div', { class: 'ua-diff-stats' }, h('span', { class: 'ua-diff-add' }, `+${added}`), h('span', { class: 'ua-diff-del' }, `−${removed}`), ignore ? h('span', { class: 'ua-diff-note' }, '已忽略行内空白差异') : null, // 归一口径必须写在脸上,否则「为什么不报这几行」说不清 ); return h( 'div', { class: 'ua-diff', role: 'group', 'aria-label': props.ariaLabel ?? `文本差异:新增 ${added} 行,删除 ${removed} 行` }, stats, h('div', { class: 'ua-diff-body' }, ...rows), ); }