text-compare-vue3
Version:
A powerful text comparison plugin for Vue.js with character-level diff support
2 lines (1 loc) • 7.11 kB
JavaScript
(function(m,s){typeof exports=="object"&&typeof module<"u"?s(exports,require("vue")):typeof define=="function"&&define.amd?define(["exports","vue"],s):(m=typeof globalThis<"u"?globalThis:m||self,s(m.TextCompareVue3={},m.Vue))})(this,function(m,s){"use strict";var b=Object.defineProperty;var j=(m,s,g)=>s in m?b(m,s,{enumerable:!0,configurable:!0,writable:!0,value:g}):m[s]=g;var _=(m,s,g)=>(j(m,typeof s!="symbol"?s+"":s,g),g);function g(n,t,r=!0){if(!r||!n||!t)return{oldParts:[{value:n||"-",removed:!1}],newParts:[{value:t||"-",added:!1}]};const a=[],e=[],o=Array.from(n),i=Array.from(t),c=[];for(let d=0;d<=o.length;d++){c[d]=[];for(let f=0;f<=i.length;f++)d===0||f===0?c[d][f]=0:o[d-1]===i[f-1]?c[d][f]=c[d-1][f-1]+1:c[d][f]=Math.max(c[d-1][f],c[d][f-1])}let u=o.length,l=i.length;const h=[];for(;u>0&&l>0;)o[u-1]===i[l-1]?(h.unshift({char:o[u-1],oldIndex:u-1,newIndex:l-1}),u--,l--):c[u-1][l]>c[u][l-1]?u--:l--;h.length>0&&(h[0].oldIndex>0&&a.push({value:n.slice(0,h[0].oldIndex),removed:!0}),h[0].newIndex>0&&e.push({value:t.slice(0,h[0].newIndex),added:!0}));for(let d=0;d<h.length;d++){const f=h[d],p=h[d+1];a.push({value:f.char,removed:!1}),e.push({value:f.char,added:!1}),p&&(p.oldIndex-f.oldIndex>1&&a.push({value:n.slice(f.oldIndex+1,p.oldIndex),removed:!0}),p.newIndex-f.newIndex>1&&e.push({value:t.slice(f.newIndex+1,p.newIndex),added:!0})),f.oldIndex,f.newIndex}if(h.length>0){const d=h[h.length-1];d.oldIndex<o.length-1&&a.push({value:n.slice(d.oldIndex+1),removed:!0}),d.newIndex<i.length-1&&e.push({value:t.slice(d.newIndex+1),added:!0})}else a.push({value:n,removed:!0}),e.push({value:t,added:!0});return{oldParts:a,newParts:e}}function y(n,t,r={}){const a=s.ref(n instanceof Object?n.value:n),e=s.ref(t instanceof Object?t.value:t),o=s.computed(()=>{try{return g(a.value||"",e.value||"",!0)}catch(l){return console.error("Error computing diff:",l),{oldParts:[{value:a.value||"",removed:!1}],newParts:[{value:e.value||"",added:!1}]}}}),i=s.computed(()=>{try{const l=(a.value||"").length+(e.value||"").length;return l===0?100:a.value.length===0||e.value.length===0?0:o.value.oldParts.filter(f=>!f.removed).reduce((f,p)=>f+p.value.length,0)*2/l*100}catch(l){return console.error("Error computing similarity:",l),0}}),c=s.computed(()=>{const{customColors:l={}}=r;return{common:{color:l.commonColor||"inherit"},removed:{backgroundColor:"#ffeef0",color:l.removedColor||"#f56c6c"},added:{backgroundColor:"#e6ffed",color:l.addedColor||"#28a745"}}});return{diffResult:o,similarity:i,styles:c,updateTexts:(l,h)=>{a.value=l||"",e.value=h||""}}}const k=s.defineComponent({name:"TextDiff",props:{oldText:{type:String,default:""},newText:{type:String,default:""},isDifferent:{type:Boolean,default:!1},mode:{type:String,default:"diff",validator:n=>typeof n=="string"&&["diff","full"].includes(n)},options:{type:Object,default:()=>({})},showSimilarity:{type:Boolean,default:!1}},setup(n){const{diffResult:t,similarity:r,styles:a,updateTexts:e}=y(n.oldText||"",n.newText||"",n.options),o=s.computed(()=>!n.oldText||!n.newText||n.oldText.trim()===""||n.newText.trim()==="");return s.watchEffect(()=>{e(n.oldText||"",n.newText||"")}),{diffResult:t,similarity:r,styles:a,hasEmptyValue:o}}}),L="",C=(n,t)=>{const r=n.__vccOpts||n;for(const[a,e]of t)r[a]=e;return r},D={class:"text-diff"},w={class:"text-diff__content"},I={key:0,class:"text-diff__similarity"};function E(n,t,r,a,e,o){return s.openBlock(),s.createElementBlock("div",D,[s.createElementVNode("div",w,[n.mode==="diff"?(s.openBlock(),s.createElementBlock(s.Fragment,{key:0},[n.diffResult?(s.openBlock(),s.createElementBlock("div",{key:0,class:s.normalizeClass(["text-diff__row",{"text-diff__row--different":n.hasEmptyValue}])},[(s.openBlock(!0),s.createElementBlock(s.Fragment,null,s.renderList(n.diffResult.oldParts,(i,c)=>(s.openBlock(),s.createElementBlock("span",{key:"old-"+c,class:s.normalizeClass({"text-diff__part":!0,"text-diff__part--removed":i.removed||n.hasEmptyValue,"text-diff__part--unchanged":!i.removed&&!n.hasEmptyValue}),style:s.normalizeStyle(i.removed||n.hasEmptyValue?n.styles.removed:n.styles.common)},s.toDisplayString(i.value||"-"),7))),128))],2)):s.createCommentVNode("",!0)],64)):(s.openBlock(),s.createElementBlock("div",{key:1,class:s.normalizeClass(["text-diff__row",{"text-diff__row--different":n.isDifferent||n.hasEmptyValue}])},s.toDisplayString(n.oldText||"-"),3))]),n.showSimilarity&&n.similarity!==null?(s.openBlock(),s.createElementBlock("div",I," 相似度: "+s.toDisplayString(n.similarity.toFixed(2))+"% ",1)):s.createCommentVNode("",!0)])}const v=C(k,[["render",E],["__scopeId","data-v-1d465779"]]);class S{constructor(t={}){_(this,"options");this.options={ignoreCase:t.ignoreCase??!1,ignoreSpace:t.ignoreSpace??!1,timeout:t.timeout??5e3,ignoreRepeat:t.ignoreRepeat??!0}}compare(t,r){if(this.options.ignoreCase&&(t=t.toLowerCase(),r=r.toLowerCase()),this.options.ignoreSpace&&(t=t.trim(),r=r.trim()),!this.options.ignoreRepeat){const i=this.countDuplicates(t,r);if(i>0)return{oldParts:[{value:t,removed:!0}],newParts:[{value:r,added:!0}],statistics:{additions:1,deletions:1,changes:0,duplicates:i}}}if(t===r)return{oldParts:[{value:t}],newParts:[{value:r}],statistics:{additions:0,deletions:0,changes:0,duplicates:0}};if(!t||!r)return{oldParts:[{value:t||"",removed:!t}],newParts:[{value:r||"",added:!r}],statistics:{additions:t?0:1,deletions:r?0:1,changes:0,duplicates:0}};const a=Date.now(),e=this.createLCSMatrix(t,r),o=this.backtrack(e,t,r);return Date.now()-a>this.options.timeout?(console.warn("Diff computation timed out"),{oldParts:[{value:t,removed:!0}],newParts:[{value:r,added:!0}],statistics:{additions:1,deletions:1,changes:0,duplicates:0}}):o}createLCSMatrix(t,r){const a=Array(t.length+1).fill(0).map(()=>Array(r.length+1).fill(0));for(let e=1;e<=t.length;e++)for(let o=1;o<=r.length;o++)t[e-1]===r[o-1]?a[e][o]=a[e-1][o-1]+1:a[e][o]=Math.max(a[e-1][o],a[e][o-1]);return a}backtrack(t,r,a){let e=r.length,o=a.length;const i=[],c=[];let u="",l="",h=0,d=0,f=0;for(;e>0||o>0;)e>0&&o>0&&r[e-1]===a[o-1]?(u=r[e-1]+u,l=a[o-1]+l,e--,o--):o>0&&(e===0||t[e][o-1]>=t[e-1][o])?(u&&(i.unshift({value:u}),c.unshift({value:l}),u="",l=""),l=a[o-1]+l,h++,o--):e>0&&(o===0||t[e][o-1]<t[e-1][o])&&(u&&(i.unshift({value:u}),c.unshift({value:l}),u="",l=""),u=r[e-1]+u,d++,e--);(u||l)&&(i.unshift({value:u}),c.unshift({value:l}));const p=this.mergeParts(i,!0),V=this.mergeParts(c,!1);return{oldParts:p,newParts:V,statistics:{additions:h,deletions:d,changes:f,duplicates:this.countDuplicates(r,a)}}}mergeParts(t,r){const a=[];let e=null;for(const o of t){if(!o.value)continue;const i=r?"removed":"added",c=o[i];!e||e[i]!==c?(e&&a.push(e),e={value:o.value},c&&(e[i]=!0)):e.value+=o.value}return e&&a.push(e),a}countDuplicates(t,r){const a=t.split(/\s+/).filter(Boolean),e=r.split(/\s+/).filter(Boolean),o=new Set(a),i=new Set(e);let c=0;for(const u of o)i.has(u)&&c++;return c}}const P=n=>{n.component("TextDiff",v)},B={install:P};m.DiffEngine=S,m.TextDiff=v,m.default=B,m.getDiffParts=g,m.install=P,m.useTextComparison=y,Object.defineProperties(m,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})});