@evotecit/canopyx 0.3.7 → 0.3.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,224 @@
1
+ (function(global){"use strict";const installed=new WeakSet();const part={version:"2db8e138da4b5658",install(context){if(installed.has(context.CHANGES_IMPL))return;const{CHANGES_IMPL,h,setText,fill,plural,numberFormat,gridFreeze,toneOf,applyTone,svgIcon,PROTOCOL}=context;const CHANGE_LABELS={changeInline:"Inline",changeSplit:"Side by side",changeLayout:"Layout",changeCopyDiff:"Copy diff",changeCopied:"Copied",changeCopyFailed:"Could not copy",changeNext:"Next change",changePrevious:"Previous change",changePosition:"Change {index} of {count}",changeShowLines:"[one]Show {count} more line[other]Show {count} more lines",changeHiddenLines:"[one]{count} unchanged line not included[other]{count} unchanged lines not included",changeLineCounts:"Lines added: {added}, removed: {removed}",changeLineAdded:"Added line {line}",changeLineRemoved:"Removed line {line}",changeLineContext:"Line {line}",changeBefore:"Before",changeAfter:"After",changeBinary:"Binary file: compared by size and hash, not by lines.",changeUnavailable:"The content of this file is not available.",changeIdentical:"The text did not change.",changeTruncated:"Some changes are not shown: the comparison was too large.",changeTooLarge:"These texts are too long to compare here.",changeDiffOf:"Changes in {name}",changeAdded:"Added",changeRemoved:"Removed",changeModified:"Modified",changeRenamed:"Renamed",changeFlaggedOnly:"Flagged only",changeFlaggedCount:"{count} flagged",changeNoItems:"No changes to show.",changeSize:"Size",changeHash:"Hash",changeTime:"Time",changeCopyHash:"Copy the full hash",changeDetails:"Details of {name}",changeFileCount:"[one]{count} file[other]{count} files",changeFrom:"from {path}",changeShowMore:"Show more",changeFlags:"Flags",changeFacts:"Details",changeRoot:"Top level",changeShown:"{shown} of {total} shown",changeFilter:"Show changes",changeActions:"Actions for {name}",changeConverged:"Converged",changePropagating:"Propagating",changeFault:"Fault",changeUnknown:"Unknown",changeReplica:"Replica",changeState:"State",changeFirstSeen:"First seen",changeLastSeen:"Last seen",changeTimeline:"Delay",changeNotSeen:"Not seen yet",changeDelay:"+{duration}",changeSource:"Source",changeProgress:"{converged} of {total} have the change",changeSeconds:"{count} s",changeMinutes:"{count} min",changeHours:"{count} h",changeDays:"{count} d",changeUtcTime:"{time} UTC",changeExportCsv:"Export CSV",changeExportJson:"Export JSON",changeReason:"Reason",changeReasonHint:"{action}: say why. The reason is kept with the action.",changeReasonRequired:"Enter a reason first.",changeCancel:"Cancel",changeUnclaimed:"This page cannot carry out “{action}” itself. Copy the request to pass it on.",changeCopyRequest:"Copy request",changeFiltered:"Filtered: {filters}",changePrintLimit:"[one]{count} more line is not printed.[other]{count} more lines are not printed."};function changeLabels(...sources){const labels={...CHANGE_LABELS};for(const source of sources){if(source==null)continue;if(typeof source!=="object"||Array.isArray(source))changeFail("labels must be an object of strings");for(const[name,text]of Object.entries(source)){if(!Object.hasOwn(labels,name)){if(/^change[A-Z]/.test(name)) changeFail("there is no change label '" + name + "'"); continue; }
2
+ if(typeof text!=="string"||!/\S/.test(text)) changeFail("label '" + name + "' must be a string with visible text");
3
+ labels[name]=text}}
4
+ return Object.freeze(labels)}
5
+ function changeFail(message){throw new Error("CanopyX: the change document is not valid: "+message+".")}
6
+ function changeCount(template,count,lang,values){return fill(plural(template,count,lang),values)}
7
+ const changeObject=value=>value!==null&&typeof value==="object"&&!Array.isArray(value);function changeKeys(value,keys,what){if(!changeObject(value))changeFail(what+" must be an object");for(const key of Object.keys(value))if(!keys.includes(key))changeFail(what+" has the unknown property '"+key+"'");return value}
8
+ function changeText(value,what,max,optional){if(value===undefined&&optional)return null;if(typeof value!=="string"||!/\S/.test(value) || value.length > max) changeFail(what + " must be visible text of at most " + max + " characters");
9
+ return value}
10
+ function changeLine(value,what,max){if(value===undefined)return null;if(typeof value!=="string"||value.length>max||/[\r\n]/.test(value)) changeFail(what + " must be one line of at most " + max + " characters");
11
+ return value}
12
+ function changeId(value,what,max=256){if(typeof value!=="string"||!value.length||value.length>max||/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:^|[^\uD800-\uDBFF])[\uDC00-\uDFFF]/.test(value)) changeFail(what + " must be a nonempty id of at most " + max + " characters");
13
+ return value}
14
+ function changeTone(value,what){if(value===undefined)return null;if(typeof value!=="string"||!/^[a-z][a-z0-9-]{0,31}$/.test(value) || /-bg$/.test(value)) changeFail(what + " has an invalid tone");
15
+ return value}
16
+ function changeFlag(value,what){if(value!==undefined&&typeof value!=="boolean")changeFail(what+" takes true or false");return value===true}
17
+ function changeInteger(value,what,min,max,fallback){if(value===undefined)return fallback;if(!Number.isInteger(value)||value<min||value>max)changeFail(what+" is a whole number from "+min+" to "+max);return value}
18
+ function changeTime(value,what){if(value===undefined)return null;const time=typeof value==="string"&&value.includes("T")?Date.parse(/(?:Z|[+-]\d\d:\d\d)$/.test(value) ? value : value + "Z") : NaN;
19
+ if(!Number.isFinite(time))changeFail(what+" needs an ISO 8601 date and time");return time}
20
+ function changeEnum(value,names,what,fallback){if(value===undefined)return fallback;if(!names.includes(value))changeFail(what+" has an unknown value");return value}
21
+ function changeList(value,what,max){if(value===undefined)return[];if(!Array.isArray(value)||value.length>max)changeFail(what+" is a list of at most "+max+" items");return value}
22
+ function changePluralText(text){if(!text.startsWith("["))return text.includes("{count}");const forms=[...text.matchAll(/\[(zero|one|two|few|many|other)\]([^[]*)/g)];
23
+ return forms.reduce((sum,form)=>sum+form[0].length,0)===text.length&&forms.some(form=>form[1]==="other")&&forms.every(form=>form[2].includes("{count}"))}
24
+ function changeEnvelope(input,type,keys){const doc=changeKeys(input,["protocol","type",...keys],"the document");if(doc.protocol!==PROTOCOL||doc.type!==type)changeFail("it needs protocol "+PROTOCOL+" and type \""+type+"\"");return doc}
25
+ function changeSplit(text){const lines=text.split(/\r\n|\n|\r/);
26
+ if(lines.length&&lines[lines.length-1]==="")lines.pop();return lines}
27
+ const DIFF_KEYS=["id","label","path","previousPath","content","message","beforeTitle","afterTitle","before","after","hunks","context","layout","notes","truncated","unified","labels"];const DIFF_MAX_LINES=200000,DIFF_MAX_LINE=20000,DIFF_MAX_TEXT=2000000,DIFF_CLIENT_LINES=5000;function diffBody(input,what){const doc=changeKeys(input,what==="the document"?["protocol","type",...DIFF_KEYS]:DIFF_KEYS,what);const content=changeEnum(doc.content,["binary","unavailable","identical"],"content","text");const before=doc.before===undefined?null:doc.before,after=doc.after===undefined?null:doc.after;for(const text of[before,after])if(text!==null&&(typeof text!=="string"||text.length>DIFF_MAX_TEXT))changeFail("a diff text is text of at most "+DIFF_MAX_TEXT+" characters");if((before===null)!==(after===null))changeFail("a diff has both texts or neither");const hunks=changeList(doc.hunks,"hunks",DIFF_MAX_LINES).map(entry=>{const hunk=changeKeys(entry,["before","after","lines"],"a hunk");const lines=changeList(hunk.lines,"a hunk's lines",DIFF_MAX_LINES).map(line=>{if(typeof line!=="string"||!/^[ +-]/.test(line) || line.length > DIFF_MAX_LINE + 1 || /[\r\n]/.test(line)) changeFail("a diff line is one line that starts with \" \", \"-\" or \"+\"");
28
+ return{kind:line[0]===" "?"=":line[0],text:line.slice(1)}});return{before:changeInteger(hunk.before,"a hunk's before",1,2147483647),after:changeInteger(hunk.after,"a hunk's after",1,2147483647),lines}});if(content!=="text"&&(hunks.length||before!==null))changeFail("a diff without text content has no hunks and no texts");if(content==="text"&&!hunks.length&&before===null)changeFail("a text diff needs hunks or both texts");const left=before===null?null:changeSplit(before),right=after===null?null:changeSplit(after);const mismatch=reason=>changeFail("This diff can't be shown: the supplied hunks don't match the file text ("+reason+")");let nextBefore=1,nextAfter=1,total=0;const unchanged=(b,a,count)=>{if(!left||!right)return;if(b+count-1>left.length||a+count-1>right.length)mismatch("the diff's hunks do not account for every difference between its texts");for(let i=0;i<count;i++)if(left[b-1+i]!==right[a-1+i])mismatch("the diff's hunks do not account for every difference between its texts")};for(const hunk of hunks){if(!hunk.lines.length||hunk.before<nextBefore||hunk.after<nextAfter||hunk.before-nextBefore!==hunk.after-nextAfter)changeFail("diff hunks are in order, do not overlap and leave as many unchanged lines on both sides");unchanged(nextBefore,nextAfter,hunk.before-nextBefore);let b=hunk.before,a=hunk.after;for(const line of hunk.lines){if(line.kind!=="+"&&left&&left[b-1]!==line.text||line.kind!=="-"&&right&&right[a-1]!==line.text)mismatch("a hunk's line does not match its text");if(line.kind!=="+")b++;if(line.kind!=="-")a++}
29
+ total +=hunk.lines.length;if(total>DIFF_MAX_LINES)changeFail("a diff holds at most "+DIFF_MAX_LINES+" lines");nextBefore=b;nextAfter=a}
30
+ if(left&&right&&hunks.length&&left.length-nextBefore!==right.length-nextAfter)mismatch("the diff's hunks do not account for every difference between its texts");if(left&&hunks.length)unchanged(nextBefore,nextAfter,left.length-nextBefore+1);const notes=changeList(doc.notes,"notes",16).map(entry=>{const note=changeKeys(entry,["text","tone"],"a note");return Object.freeze({text:changeText(note.text,"a note",500),tone:changeTone(note.tone,"a note")})});if(doc.unified!==undefined&&(typeof doc.unified!=="string"||doc.unified.length>2*DIFF_MAX_TEXT))changeFail("the unified diff is too long");return Object.freeze({id:changeId(doc.id,"the diff id"),label:changeText(doc.label,"the diff label",200),path:changeLine(doc.path,"the path",2048),previousPath:changeLine(doc.previousPath,"the previous path",2048),content,message:changeText(doc.message,"the message",1000,true),beforeTitle:changeText(doc.beforeTitle,"the before title",200,true),afterTitle:changeText(doc.afterTitle,"the after title",200,true),before,after,left,right,hunks,context:changeInteger(doc.context,"context",0,20,3),layout:changeEnum(doc.layout,["inline","split"],"layout","auto"),notes,truncated:changeFlag(doc.truncated,"truncated"),unified:doc.unified??null,labels:doc.labels})}
31
+ function diffDocument(input){changeEnvelope(input,"diff",DIFF_KEYS);return diffBody(input,"the document")}
32
+ const CHANGE_KINDS=["added","removed","modified","renamed"],CHANGE_ICONS=["none","copy","diff","check","open","download","compare","flag"];const PROPAGATION_STATES=["converged","propagating","fault","unknown"];function changeVersion(value,what){if(value===undefined)return null;const version=changeKeys(value,["size","hash","time"],what);if(version.size!==undefined&&(!Number.isSafeInteger(version.size)||version.size<0))changeFail(what+" has a size outside 0 to 2^53 - 1");const hash=changeLine(version.hash,what+" hash",256);if(hash!==null&&!/\S/.test(hash)) changeFail(what + " has an empty hash");
33
+ return Object.freeze({size:version.size??null,hash,time:changeTime(version.time,what+" time")})}
34
+ function changeAction(entry,ids){const action=changeKeys(entry,["id","label","icon","copy","link","reveal","reason"],"an action"),id=changeId(action.id,"an action id",64);if(ids.has(id))changeFail("the action '"+id+"' is declared twice");ids.add(id);for(const key of["copy","link"])if(action[key]!==undefined&&(typeof action[key]!=="string"||!action[key].length||action[key].length>64))changeFail("the action '"+id+"' "+key+"s a value key of 1 to 64 characters");const reveal=changeEnum(action.reveal,["none","details","diff"],"an action reveal","none"),reason=changeFlag(action.reason,"reason");const kinds=(action.copy!==undefined)+(action.link!==undefined)+(reveal!=="none");if(kinds>1||reason&&kinds)changeFail("the action '"+id+"' copies, opens a link, reveals or asks for a reason: one of them");return Object.freeze({id,label:changeText(action.label,"the label of action '"+id+"'",64),icon:changeEnum(action.icon,CHANGE_ICONS,"an action icon","none"),copy:action.copy??null,link:action.link??null,reveal,reason})}
35
+ function changeValues(input,what){const values=Object.create(null);if(input===undefined)return Object.freeze(values);if(!changeObject(input)||Object.keys(input).length>16)changeFail(what+" has at most 16 values");for(const[key,value]of Object.entries(input)){if(!key.length||key.length>64||typeof value!=="string"||value.length>4096)changeFail(what+" has an invalid value");values[key]=value}
36
+ return Object.freeze(values)}
37
+ function changeDiffChecked(diff){changeLabels(diff.labels);return diff}
38
+ function changesDocument(input){const doc=changeEnvelope(input,"changes",["id","label","actions","toolbar","values","exports","items","groupByFolder","flaggedOnly","expandFlagged","hashLength","pageSize","timeZone","labels"]);changeLabels(doc.labels);const actionIds=new Set(),actions=changeList(doc.actions,"actions",8).map(entry=>changeAction(entry,actionIds));const barIds=new Set(actionIds),listValues=changeValues(doc.values,"the change list"),toolbar=changeList(doc.toolbar,"toolbar",8).map(entry=>{const action=changeAction(entry,barIds),key=action.copy??action.link;if(action.reveal!=="none")changeFail("the bar action '"+action.id+"' has no item to reveal");if(key!==null&&!Object.hasOwn(listValues,key))changeFail("the bar action '"+action.id+"' uses the list value '"+key+"', which the list does not have");return action});const exports=changeList(doc.exports,"exports",2);if(exports.some(name=>name!=="csv"&&name!=="json")||new Set(exports).size!==exports.length)changeFail("exports are csv and json, each once");const ids=new Set(),items=changeList(doc.items,"items",10000).map((entry,index)=>{const item=changeKeys(entry,["id","kind","path","previousPath","kindLabel","secondary","before","after","facts","flags","diff","actions","values"],"an item");const id=changeId(item.id,"an item id"),what="the item '"+id+"'";if(ids.has(id))changeFail(what+" appears twice");ids.add(id);const kind=changeEnum(item.kind,CHANGE_KINDS,what+" kind"),path=changeLine(item.path,what+" path",2048),previousPath=changeLine(item.previousPath,what+" previous path",2048);if(kind===undefined||path===null||!/\S/.test(path)) changeFail(what + " needs a kind and a path");
39
+ if(kind==="renamed"&&previousPath===null||(kind==="added"||kind==="removed")&&previousPath!==null)changeFail(what+" has a previous path only when it is renamed or modified, and always when renamed");const facts=changeList(item.facts,what+" facts",32).map(value=>{const fact=changeKeys(value,["label","before","after","tone","note"],"a fact");if(fact.before===undefined&&fact.after===undefined)changeFail(what+" has a fact without a value");return Object.freeze({label:changeText(fact.label,"a fact label",200),before:changeText(fact.before,"a fact value",2000,true),after:changeText(fact.after,"a fact value",2000,true),tone:changeTone(fact.tone,"a fact"),note:changeText(fact.note,"a fact note",500,true)})});const flagIds=new Set(),flags=changeList(item.flags,what+" flags",16).map(value=>{const flag=changeKeys(value,["id","label","reason","tone"],"a flag"),flagId=changeId(flag.id,"a flag id",64);if(flagIds.has(flagId))changeFail(what+" has the flag '"+flagId+"' twice");flagIds.add(flagId);return Object.freeze({id:flagId,label:changeText(flag.label,"a flag label",64),reason:changeText(flag.reason,"a flag reason",500),tone:changeTone(flag.tone,"a flag")||"warning"})});const offered=changeList(item.actions,what+" actions",8);for(const action of offered)if(!actionIds.has(action))changeFail(what+" offers the undeclared action '"+action+"'");const values=changeValues(item.values,what);return Object.freeze({id,index,kind,path,previousPath,kindLabel:changeText(item.kindLabel,what+" kind label",64,true),secondary:changeText(item.secondary,what+" secondary text",200,true),before:changeVersion(item.before,what+" before"),after:changeVersion(item.after,what+" after"),facts:Object.freeze(facts),flags:Object.freeze(flags),diff:item.diff===undefined?null:changeDiffChecked(diffBody(item.diff,what+" diff")),actions:Object.freeze([...offered]),values})});return Object.freeze({id:changeId(doc.id,"the change list id"),label:changeText(doc.label,"the label",200),actions:Object.freeze(actions),toolbar:Object.freeze(toolbar),values:listValues,exports:Object.freeze([...exports]),items:Object.freeze(items),groupByFolder:doc.groupByFolder!==false&&changeFlag(doc.groupByFolder??true,"groupByFolder"),flaggedOnly:changeFlag(doc.flaggedOnly,"flaggedOnly"),expandFlagged:changeFlag(doc.expandFlagged,"expandFlagged"),hashLength:changeInteger(doc.hashLength,"hashLength",4,64,8),pageSize:changeInteger(doc.pageSize,"pageSize",10,1000,200),local:changeEnum(doc.timeZone,["utc","local"],"timeZone","utc")==="local",labels:doc.labels})}
40
+ function propagationDocument(input){const doc=changeEnvelope(input,"propagation",["id","label","start","asOf","state","summary","title","groupCount","witnesses","timeZone","labels"]);const start=changeTime(doc.start,"start"),asOf=changeTime(doc.asOf,"asOf");if(start!==null&&asOf!==null&&start>asOf)changeFail("a propagation view starts before its evidence time");const groupCount=changeText(doc.groupCount,"the group count",200,true);if(groupCount!==null&&!changePluralText(groupCount))changeFail("the group count needs {count} in every form, and plural forms that end with [other]");const ids=new Set(),witnesses=changeList(doc.witnesses,"witnesses",2000).map(entry=>{const witness=changeKeys(entry,["id","label","secondary","state","firstSeen","lastSeen","note","source","group"],"a replica"),id=changeId(witness.id,"a replica id");if(ids.has(id))changeFail("the replica '"+id+"' appears twice");ids.add(id);const firstSeen=changeTime(witness.firstSeen,"firstSeen"),lastSeen=changeTime(witness.lastSeen,"lastSeen");if(firstSeen!==null&&lastSeen!==null&&firstSeen>lastSeen)changeFail("the replica '"+id+"' was first seen after it last reported");const state=changeEnum(witness.state,PROPAGATION_STATES,"a replica state");if(state===undefined)changeFail("the replica '"+id+"' needs a state");return Object.freeze({id,label:changeText(witness.label,"a replica label",200),secondary:changeText(witness.secondary,"a replica's secondary text",200,true),state,firstSeen,lastSeen,note:changeText(witness.note,"a replica note",500,true),source:changeFlag(witness.source,"source"),group:changeText(witness.group,"a replica group",200,true)})});if(!witnesses.length)changeFail("a propagation view has 1 to 2000 replicas");return Object.freeze({id:changeId(doc.id,"the propagation id"),label:changeText(doc.label,"the label",200),start,asOf,state:changeEnum(doc.state,PROPAGATION_STATES,"state",null),summary:changeText(doc.summary,"the summary",500,true),title:changeText(doc.title,"the title",200,true),groupCount,witnesses:Object.freeze(witnesses),local:changeEnum(doc.timeZone,["utc","local"],"timeZone","utc")==="local",labels:doc.labels})}
41
+ const CHANGE_UNITS=["B","KB","MB","GB","TB"];function changeSize(bytes,lang){if(bytes<1024)return numberFormat(lang,0,0).format(bytes)+ " B";let value=bytes,unit=0;while(value>=1024&&unit<CHANGE_UNITS.length-1){value /= 1024; unit++; }
42
+ return numberFormat(lang,0,1).format(value)+ " "+CHANGE_UNITS[unit]}
43
+ const changePad=value=>(value<10?"0":"")+ value;function changeTimeText(ms,local,labels){const d=new Date(ms),get=local?["getFullYear","getMonth","getDate","getHours","getMinutes"]:["getUTCFullYear","getUTCMonth","getUTCDate","getUTCHours","getUTCMinutes"];const[year,month,day,hours,minutes]=get.map(name=>d[name]());const text=String(year).padStart(4,"0")+ "-"+changePad(month+1)+ "-"+changePad(day)+ " "+changePad(hours)+ ":"+changePad(minutes);return local?text:fill(labels.changeUtcTime,{time:text})}
44
+ function changeDuration(ms,labels,format){const seconds=Math.max(0,Math.floor(ms / 1000)), unit = (template, count) => fill(template, { count: format.format(count) });
45
+ if(seconds<60)return unit(labels.changeSeconds,seconds);const minutes=Math.floor(seconds / 60);
46
+ if(minutes<60)return unit(labels.changeMinutes,minutes);const hours=Math.floor(minutes / 60);
47
+ if(hours<24)return unit(labels.changeHours,hours)+(minutes%60?" "+unit(labels.changeMinutes,minutes%60):"");return unit(labels.changeDays,Math.floor(hours / 24)) + (hours % 24 ? " " + unit(labels.changeHours, hours % 24) : "");
48
+ }
49
+ function changePath(path){const at=Math.max(path.lastIndexOf("/"),path.lastIndexOf("\\"));return at<0?["",path]:[path.slice(0,at),path.slice(at+1)]}
50
+ const CHANGE_TONES={added:"success",removed:"danger",modified:"info",renamed:"neutral"};const PROPAGATION_LOOK={converged:["check","success"],propagating:["clock","warning"],fault:["cross","danger"],unknown:["question","muted"]};const CHANGE_ICONS_SVG={copy:'<rect x="8" y="8" width="11" height="11" rx="2"/><path d="M16 8V6a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h2"/>',diff:'<path d="M4 5h7M4 9h5M4 13h7M4 17h4M13 5h7M15 9h5M13 13h7M16 17h4"/>',check:'<path d="m5 12.5 4.5 4.5L19 7.5"/>',open:'<path d="M14 4h6v6M20 4l-9 9M18 14v5a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1h5"/>',download:'<path d="M12 4v11M7.5 10.5 12 15l4.5-4.5M5 19h14"/>',compare:'<path d="M7 7h12M15 3l4 4-4 4M17 17H5M9 13l-4 4 4 4"/>',flag:'<path d="M5 21V4M5 4h11l-2 4 2 4H5"/>',cross:'<path d="M7 7l10 10M17 7 7 17"/>',clock:'<circle cx="12" cy="12" r="8"/><path d="M12 8v4.5l3 2"/>',question:'<path d="M9.5 9a2.5 2.5 0 1 1 3.5 2.3c-.6.3-1 .9-1 1.6v.6"/><circle cx="12" cy="17" r=".6"/>',chevron:'<path d="m9 6 6 6-6 6"/>',up:'<path d="M12 19V5M6 11l6-6 6 6"/>',down:'<path d="M12 5v14M6 13l6 6 6-6"/>'};function changeTips(root,signal){root.addEventListener("keydown",event=>{if(event.key==="Escape")event.target.closest?.("[data-cx-tip]")?.setAttribute("data-cx-tip-off","")},{signal});for(const type of["focusout","pointerout"])root.addEventListener(type,event=>event.target.closest?.("[data-cx-tip]")?.removeAttribute("data-cx-tip-off"),{signal})}
51
+ function changeIcon(name){const icon=svgIcon(CHANGE_ICONS_SVG[name]||CHANGE_ICONS_SVG.check);icon.classList.add("cx-chg-ico");return icon}
52
+ const DIFF_MAX_EDITS=2000,DIFF_MAX_WORD_EDITS=200,DIFF_MAX_TOKENS=1000;function diffReplace(script,n,m,from){for(let i=0;i<n;i++)script.push(["-",from+i,-1]);for(let j=0;j<m;j++)script.push(["+",-1,from+j])}
53
+ function diffMyers(a,b,offset,n,m,maxEdits){const limit=Math.min(n+m,maxEdits),v=new Int32Array(2*limit+3),zero=limit+1,trace=[];let found=-1;for(let d=0;d<=limit&&found<0;d++){trace.push(d>0?v.slice(zero -(d-1),zero+d):new Int32Array(0));for(let k=-d;k<=d;k +=2){let x=k===-d||k!==d&&v[zero+k-1]<v[zero+k+1]?v[zero+k+1]:v[zero+k-1]+ 1,y=x-k;while(x<n&&y<m&&a[offset+x]===b[offset+y]){x++;y++}
54
+ v[zero+k]=x;if(x>=n&&y>=m){found=d;break}}}
55
+ if(found<0)return null;const reversed=[];let px=n,py=m;for(let d=found;d>0;d--){const previous=trace[d],get=key=>previous[key+d-1],k=px-py;const prevK=k===-d||k!==d&&get(k-1)<get(k+1)?k+1:k-1,prevX=get(prevK),prevY=prevX-prevK;while(px>prevX&&py>prevY){reversed.push(["=",offset+px-1,offset+py-1]);px--;py--}
56
+ reversed.push(px===prevX?["+",-1,offset+py-1]:["-",offset+px-1,-1]);px=prevX;py=prevY}
57
+ while(px>0&&py>0){reversed.push(["=",offset+px-1,offset+py-1]);px--;py--}
58
+ return reversed.reverse()}
59
+ function diffScript(a,b,maxEdits){const n=a.length,m=b.length;let prefix=0,suffix=0;while(prefix<n&&prefix<m&&a[prefix]===b[prefix])prefix++;while(suffix<n-prefix&&suffix<m-prefix&&a[n-1-suffix]===b[m-1-suffix])suffix++;const script=[];for(let i=0;i<prefix;i++)script.push(["=",i,i]);const bigN=n-prefix-suffix,bigM=m-prefix-suffix;if(!bigN||!bigM)diffReplace(script,bigN,bigM,prefix);else{const middle=diffMyers(a,b,prefix,bigN,bigM,maxEdits);if(middle)script.push(...middle);else if(maxEdits<DIFF_MAX_EDITS)return null;else diffReplace(script,bigN,bigM,prefix)}
60
+ for(let i=0;i<suffix;i++)script.push(["=",n-suffix+i,m-suffix+i]);const ordered=[],added=[];for(const entry of script){if(entry[0]==="="){ordered.push(...added,entry);added.length=0}
61
+ else if(entry[0]==="-")ordered.push(entry);else added.push(entry)}
62
+ ordered.push(...added);return ordered}
63
+ function diffHunks(left,right,context){const script=diffScript(left,right,DIFF_MAX_EDITS),changes=[],hunks=[];script.forEach((entry,index)=>{if(entry[0]!=="=")changes.push(index)});for(let at=0;at<changes.length;){let last=changes[at];const first=last;while(at+1<changes.length&&changes[at+1]- last-1<=2*context)last=changes[++at];at++;const from=Math.max(0,first-context),to=Math.min(script.length-1,last+context);let before=1,after=1;for(let i=0;i<from;i++){if(script[i][0]!=="+")before++;if(script[i][0]!=="-")after++}
64
+ const lines=[];for(let i=from;i<=to;i++){const[op,b,a]=script[i];lines.push({kind:op,text:op==="+"?right[a]:left[b]})}
65
+ hunks.push({before,after,lines})}
66
+ return hunks}
67
+ const DIFF_TOKENS=/[\p{L}\p{N}_]+|[\t-\r\u0085\u2028\u2029\p{Zs}]+|[^\p{L}\p{N}_\t-\r\u0085\u2028\u2029\p{Zs}]/gu;
68
+ function diffWords(before,after){const left=before.match(DIFF_TOKENS)||[],right=after.match(DIFF_TOKENS)||[];if(left.length>DIFF_MAX_TOKENS||right.length>DIFF_MAX_TOKENS)return null;const script=diffScript(left,right,DIFF_MAX_WORD_EDITS);if(!script)return null;const removed=[],added=[],extend=(ranges,start,end)=>{const last=ranges[ranges.length-1];if(last&&last[1]===start)last[1]=end;else ranges.push([start,end])};let same=0,beforeAt=0,afterAt=0;for(const[op,b,a]of script){if(op==="="){same +=left[b].length;beforeAt +=left[b].length;afterAt +=right[a].length}
69
+ else if(op==="-"){extend(removed,beforeAt,beforeAt+left[b].length);beforeAt +=left[b].length}
70
+ else{extend(added,afterAt,afterAt+right[a].length);afterAt +=right[a].length}}
71
+ const longest=Math.max(before.length,after.length);return!longest||same*100<longest*35?null:{before:removed,after:added}}
72
+ function diffLine(kind,text,b,a){return{t:"line",kind,text,b,a,partner:null,marks:undefined}}
73
+ function diffGap(count,b,a,left,lines){if(!lines&&left){lines=[];for(let i=0;i<count;i++)lines.push(diffLine("=",left[b-1+i],b+i,a+i))}
74
+ return{t:"gap",count,b,a,lines:lines||null}}
75
+ function diffFold(lines,rows,context){for(let i=0;i<lines.length;){if(lines[i].kind!=="="){rows.push(lines[i++]);continue}
76
+ const start=i;while(i<lines.length&&lines[i].kind==="=")i++;const length=i-start,keepStart=start===0?0:context,keepEnd=i===lines.length?0:context;if(length<=keepStart+keepEnd+1){rows.push(...lines.slice(start,i));continue}
77
+ rows.push(...lines.slice(start,start+keepStart));const hidden=lines.slice(start+keepStart,i-keepEnd);rows.push(diffGap(hidden.length,hidden[0].b,hidden[0].a,null,hidden));rows.push(...lines.slice(i-keepEnd,i))}}
78
+ function diffRows(doc,hunks){const rows=[];let nextBefore=1,nextAfter=1,added=0,removed=0;for(const hunk of hunks){if(hunk.before>nextBefore)rows.push(diffGap(hunk.before-nextBefore,nextBefore,nextAfter,doc.left));const lines=[];let b=hunk.before,a=hunk.after;for(const line of hunk.lines){lines.push(diffLine(line.kind,line.text,line.kind!=="+"?b++:null,line.kind!=="-"?a++:null));if(line.kind==="+")added++;else if(line.kind==="-")removed++}
79
+ for(let i=0;i<lines.length;){if(lines[i].kind==="="){i++;continue}
80
+ const from=i;while(i<lines.length&&lines[i].kind==="-")i++;const middle=i;while(i<lines.length&&lines[i].kind==="+")i++;for(let k=0;k<Math.min(middle-from,i-middle);k++){lines[from+k].partner=lines[middle+k];lines[middle+k].partner=lines[from+k]}}
81
+ diffFold(lines,rows,doc.context);nextBefore=b;nextAfter=a}
82
+ if(doc.left&&hunks.length&&doc.left.length>=nextBefore)rows.push(diffGap(doc.left.length-nextBefore+1,nextBefore,nextAfter,doc.left));return{rows,added,removed}}
83
+ function diffMarks(row){if(row.marks===undefined){const words=row.partner?(row.kind==="-"?diffWords(row.text,row.partner.text):diffWords(row.partner.text,row.text)):null;row.marks=words?(row.kind==="-"?words.before:words.after):null;if(row.partner)row.partner.marks=words?(row.kind==="-"?words.after:words.before):null}
84
+ return row.marks}
85
+ function diffPairs(rows){const pairs=[];for(let i=0;i<rows.length;){const row=rows[i];if(row.t==="gap"||row.kind==="="){pairs.push({t:row.t==="gap"?"gap":"pair",gap:row.t==="gap"?row:null,left:row.t==="gap"?null:row,right:row.t==="gap"?null:row});i++;continue}
86
+ const removed=[],added=[];while(i<rows.length&&rows[i].t==="line"&&rows[i].kind==="-")removed.push(rows[i++]);while(i<rows.length&&rows[i].t==="line"&&rows[i].kind==="+")added.push(rows[i++]);for(let k=0;k<Math.max(removed.length,added.length);k++)pairs.push({t:"pair",gap:null,left:removed[k]||null,right:added[k]||null})}
87
+ return pairs}
88
+ function diffChanged(row){return row.t==="line"?row.kind!=="=":row.t==="pair"&&(row.left!==row.right)}
89
+ function diffUnified(doc,hunks){if(doc.unified!==null)return doc.unified;const beforeOpen=!!doc.before&&!/[\r\n]$/.test(doc.before), afterOpen = !!doc.after && !/[\r\n]$/.test(doc.after);
90
+ const last=hunks[hunks.length-1],left=doc.left,right=doc.right;if(left?.length&&right?.length&&beforeOpen!==afterOpen&&left[left.length-1]===right[right.length-1]&&(!last||
91
+ last.before+last.lines.filter(line=>line.kind!=="+").length<=left.length&&last.after+last.lines.filter(line=>line.kind!=="-").length<=right.length))
92
+ hunks=hunks.concat([{before:left.length,after:right.length,lines:[{kind:"=",text:left[left.length-1]}]}]);if(!hunks.length)return "";const name=doc.path||doc.label,range=(start,count)=>(count===0?start-1:start)+(count===1?"":","+count);let text="--- a/" +(doc.previousPath||name)+ "\n+++ b/"+name+"\n";const write=(kind,line,open)=>{text +=kind+line+"\n";if(open)text +="\\n"};for(const hunk of hunks){const before=hunk.lines.filter(line=>line.kind!=="+").length,after=hunk.lines.filter(line=>line.kind!=="-").length;text +="@@ -"+range(hunk.before,before)+ " +"+range(hunk.after,after)+ " @@\n";let b=hunk.before,a=hunk.after;for(const line of hunk.lines){const openBefore=line.kind!=="+"&&beforeOpen&&b===left.length,openAfter=line.kind!=="-"&&afterOpen&&a===right.length;if(line.kind==="="&&openBefore!==openAfter){write("-",line.text,openBefore);write("+",line.text,openAfter)}
93
+ else write(line.kind==="="?" ":line.kind,line.text,openBefore||openAfter);if(line.kind!=="+")b++;if(line.kind!=="-")a++}}
94
+ return text}
95
+ const DIFF_ROW=20,DIFF_OVERSCAN=30,DIFF_SPLIT_WIDTH=900;let changesPrinting=false;global.addEventListener("beforeprint",()=>{changesPrinting=true});global.addEventListener("afterprint",()=>{changesPrinting=false});function changesInPrint(){return changesPrinting||!!(global.matchMedia&&global.matchMedia("print").matches)}
96
+ class DiffView{constructor(host,options,doc){this.host=host;this.options=options;this.lifetime=new AbortController();this.doc=doc||diffDocument(options.view);this.labels=changeLabels(options.baseLabels,this.doc.labels,options.labels);this.lang=options.lang||host.closest("[lang]")?.getAttribute("lang")||document.documentElement.lang||"en";this.format=numberFormat(this.lang);this.mounted=new Map();this.pool=[];this.current=-1;this.chosen=this.doc.layout==="auto"?null:this.doc.layout;this.prepare();this.build()}
97
+ emit(name,detail,callback){const event=new CustomEvent("canopyx:"+name,{detail,bubbles:true});this.host.dispatchEvent(event);if(typeof this.options[callback]==="function"){try{this.options[callback](detail,event)}catch(error){this.host.dispatchEvent(new CustomEvent("canopyx:error",{detail:{error,hook:callback},bubbles:true}))}}}
98
+ prepare(){const doc=this.doc;this.tooLarge=doc.content==="text"&&!doc.hunks.length&&(doc.left.length>DIFF_CLIENT_LINES||doc.right.length>DIFF_CLIENT_LINES);this.hunks=doc.content!=="text"||this.tooLarge?[]:doc.hunks.length?doc.hunks:diffHunks(doc.left,doc.right,doc.context);const built=diffRows(doc,this.hunks);this.rows=built.rows;this.added=built.added;this.removed=built.removed}
99
+ build(){const doc=this.doc,labels=this.labels,signal=this.lifetime.signal;if(!this.host.closest("[data-cx-theme]")){this.host.setAttribute("data-cx-theme","auto");this.addedTheme=true}
100
+ const root=this.root=h("div","cx-diff");const head=h("div","cx-diff-head"),title=this.titleBox=h("div","cx-diff-title"),tools=this.tools=h("div","cx-diff-tools");head.append(title,tools);this.notes=h("div","cx-diff-notes");this.state=h("div","cx-diff-state");this.state.hidden=true;const scroll=this.scroll=h("div","cx-diff-scroll");scroll.tabIndex=0;scroll.setAttribute("role","table");this.columns=h("div","cx-diff-columns");this.columns.setAttribute("role","row");this.columns.setAttribute("aria-rowindex","1");this.canvas=h("div","cx-diff-canvas");this.canvas.setAttribute("role","rowgroup");scroll.append(this.columns,this.canvas);this.live=h("div","cx-sr-only");this.live.setAttribute("aria-live","polite");this.print=h("div","cx-diff-print");const frame=this.frameBox=h("div","cx-diff-frame");this.edgeStart=h("div","cx-diff-edge cx-diff-edge-start");this.edgeEnd=h("div","cx-diff-edge cx-diff-edge-end");this.edgeStart.hidden=this.edgeEnd.hidden=true;frame.append(scroll,this.edgeStart,this.edgeEnd);root.append(head,this.notes,this.state,frame,this.live,this.print);this.host.appendChild(root);this.probe=h("span","cx-diff-probe");setText(this.probe,"0000000000");root.appendChild(this.probe);this.buildTools();changeTips(root,signal);scroll.addEventListener("scroll",()=>{this.schedule();this.edges()},{passive:true,signal});scroll.addEventListener("keydown",event=>this.onKey(event),{signal});scroll.addEventListener("click",event=>this.onClick(event),{signal});global.addEventListener("beforeprint",()=>this.renderPrint(),{signal});this.resize=new ResizeObserver(()=>{if(!this.destroyed)this.layout()});this.resize.observe(root);this.refresh();if(changesInPrint())this.renderPrint()}
101
+ buildTools(){const labels=this.labels,tools=this.tools,signal=this.lifetime.signal;const group=this.layoutGroup=h("div","cx-diff-seg");group.setAttribute("role","radiogroup");group.setAttribute("aria-label",labels.changeLayout);this.layoutButtons=["inline","split"].map(mode=>{const button=h("button","cx-diff-segbtn");button.type="button";button.setAttribute("role","radio");button._mode=mode;setText(button,mode==="inline"?labels.changeInline:labels.changeSplit);button.addEventListener("click",()=>this.setLayout(mode),{signal});button.addEventListener("keydown",event=>{if(!["ArrowLeft","ArrowRight","ArrowUp","ArrowDown"].includes(event.key))return;event.preventDefault();const next=mode==="inline"?"split":"inline";this.setLayout(next);this.layoutButtons.find(item=>item._mode===next).focus()},{signal});group.appendChild(button);return button});const button=(label,icon,action,text)=>{const element=h("button","cx-diff-btn");element.type="button";element.appendChild(changeIcon(icon));if(text){const name=h("span");setText(name,label);element.appendChild(name)}else{element.setAttribute("aria-label",label);element.setAttribute("data-cx-tip",label)}
102
+ element.addEventListener("click",action,{signal});return element};this.prevButton=button(labels.changePrevious,"up",()=>this.move(-1));this.nextButton=button(labels.changeNext,"down",()=>this.move(1));this.copyButton=button(labels.changeCopyDiff,"copy",()=>this.copy(),true);tools.append(group,this.prevButton,this.nextButton,this.copyButton)}
103
+ refresh(){const doc=this.doc,labels=this.labels,title=this.titleBox;title.replaceChildren();const name=h("span","cx-diff-path");setText(name,doc.path?(doc.previousPath&&doc.previousPath!==doc.path?doc.previousPath+" → "+doc.path:doc.path):doc.label);title.appendChild(name);if(doc.content==="text"&&!this.tooLarge){const stats=h("span","cx-diff-stats"),plus=h("span","cx-diff-plus"),minus=h("span","cx-diff-minus");setText(plus,"+"+this.format.format(this.added));setText(minus,"−"+this.format.format(this.removed));const spoken=h("span","cx-sr-only");setText(spoken,fill(labels.changeLineCounts,{added:this.format.format(this.added),removed:this.format.format(this.removed)}));plus.setAttribute("aria-hidden","true");minus.setAttribute("aria-hidden","true");stats.append(plus,minus,spoken);title.appendChild(stats)}
104
+ const notes=[];for(const note of doc.notes)notes.push([note.text,note.tone||"info"]);if(doc.truncated)notes.push([labels.changeTruncated,"warning"]);if(doc.message&&doc.content==="text")notes.push([doc.message,"info"]);this.notes.replaceChildren(...notes.map(([text,tone])=>{const note=h("div","cx-diff-note");applyTone(note,"cx-diff-note",toneOf(tone));setText(note,text);return note}));this.notes.hidden=!notes.length;const empty=doc.content!=="text"?doc.message||(doc.content==="binary"?labels.changeBinary:doc.content==="identical"?labels.changeIdentical:labels.changeUnavailable)
105
+ :this.tooLarge?labels.changeTooLarge:!this.rows.some(row=>row.t==="line"&&row.kind!=="=")?labels.changeIdentical:null;this.state.hidden=empty===null;setText(this.state,empty||"");this.state.className="cx-diff-state cx-diff-state-" +(this.tooLarge?"unavailable":doc.content);this.scroll.hidden=!this.rows.some(row=>row.t==="line"&&row.kind!=="=");this.tools.hidden=this.scroll.hidden;this.frameBox.hidden=this.scroll.hidden;this.scroll.setAttribute("aria-label",fill(labels.changeDiffOf,{name:doc.label}));this.layout(true)}
106
+ mode(){return this.chosen||(this.root.clientWidth>=DIFF_SPLIT_WIDTH?"split":"inline")}
107
+ setLayout(mode){if(mode!=="inline"&&mode!=="split")throw new Error("CanopyX: a diff layout is \"inline\" or \"split\".");this.chosen=mode;this.layout();this.emit("diffLayout",{diff:this.doc.id,layout:mode},"onLayout")}
108
+ layout(force){const mode=this.mode();for(const button of this.layoutButtons){const on=button._mode===mode;button.setAttribute("aria-checked",String(on));button.tabIndex=on?0:-1}
109
+ if(!force&&mode===this.shown&&this.width===this.scroll.clientWidth)return;const changed=mode!==this.shown;this.shown=mode;this.width=this.scroll.clientWidth;if(changed||force)this.display();else this.size()}}
110
+ Object.assign(DiffView.prototype,{display(){const split=this.shown==="split",labels=this.labels;this.view=split?diffPairs(this.rows):this.rows;this.starts=[];this.view.forEach((row,index)=>{if(diffChanged(row)&&(index===0||!diffChanged(this.view[index-1])))this.starts.push(index)});if(this.current>=this.starts.length)this.current=this.starts.length-1;this.root.classList.toggle("cx-diff-split",split);this.columns.hidden=!split;if(split){const before=h("span","cx-diff-colhead"),after=h("span","cx-diff-colhead");before.setAttribute("role","columnheader");after.setAttribute("role","columnheader");setText(before,this.doc.beforeTitle||labels.changeBefore);setText(after,this.doc.afterTitle||labels.changeAfter);this.columns.replaceChildren(before,after)}
111
+ let digits=1,chars=1;for(const row of this.rows){for(const line of row.t==="gap"?[]:[row]){digits=Math.max(digits,String(line.b||line.a||1).length);chars=Math.max(chars,line.text.length +(line.text.match(/\t/g)?.length || 0) * 3);
112
+ }
113
+ if(row.t==="gap")digits=Math.max(digits,String(row.b+row.count).length)}
114
+ this.digits=digits;this.chars=Math.min(chars,20000);this.scroll.setAttribute("aria-rowcount",String(this.view.length+1));this.root.style.setProperty("--cx-diff-num",(digits+2)+ "ch");for(const row of this.mounted.values())this.release(row);this.mounted.clear();this.size()},size(){const cw=this.probe.getBoundingClientRect().width / 10 || 7.5, number = (this.digits + 2) * cw, code = (this.chars + 1) * cw + 32;
115
+ const needed=this.shown==="split"?2*(number+code):2*number+2*cw+code;this.canvas.style.height=this.view.length*DIFF_ROW+"px";this.canvas.style.width=Math.max(this.scroll.clientWidth,Math.ceil(needed))+ "px";this.columns.style.width=this.canvas.style.width;this.render();this.edges()},edges(){if(!this.edgeEnd)return;const scroll=this.scroll,max=scroll.scrollWidth-scroll.clientWidth,at=Math.abs(scroll.scrollLeft);const style=getComputedStyle(scroll),borders=(parseFloat(style.borderInlineStartWidth)||0)+(parseFloat(style.borderInlineEndWidth)||0);this.frameBox.style.setProperty("--cx-diff-bar-y",Math.max(0,scroll.offsetWidth-scroll.clientWidth-borders)+ "px");this.frameBox.style.setProperty("--cx-diff-bar-x",Math.max(0,scroll.offsetHeight-scroll.clientHeight -(parseFloat(style.borderTopWidth)||0)-(parseFloat(style.borderBottomWidth)||0))+ "px");this.frameBox.style.setProperty("--cx-diff-head",(this.shown==="split"?this.columns.offsetHeight:0)+ "px");this.edgeStart.hidden=scroll.hidden||at<=1;this.edgeEnd.hidden=scroll.hidden||max-at<=1},schedule(){if(this.destroyed||this.frame)return;this.frame=requestAnimationFrame(()=>{this.frame=0;this.render()})},render(){if(this.destroyed||this.scroll.hidden)return;const top=this.scroll.scrollTop-this.canvas.offsetTop,height=this.scroll.clientHeight||600;const first=Math.max(0,Math.floor(top / DIFF_ROW) - DIFF_OVERSCAN), last = Math.min(this.view.length - 1, Math.ceil((top + height) / DIFF_ROW) + DIFF_OVERSCAN);
116
+ for(const[index,element]of this.mounted)if(index<first||index>last||element._row!==this.view[index]){this.release(element);this.mounted.delete(index)}
117
+ const from=this.current>=0?this.starts[this.current]:-1;let to=from;while(to>=0&&to<this.view.length&&diffChanged(this.view[to]))to++;for(let index=first;index<=last;index++){let element=this.mounted.get(index);if(!element){element=this.pool.pop()||h("div");this.fill(element,this.view[index],index);this.canvas.appendChild(element);this.mounted.set(index,element)}
118
+ element.classList.toggle("cx-diff-current",index>=from&&index<to)}},release(element){element.remove();element._row=null;if(this.pool.length<200)this.pool.push(element)},fill(element,row,index){element._row=row;element._index=index;element.replaceChildren();element.style.transform="translateY("+index*DIFF_ROW+"px)";element.setAttribute("role","row");element.setAttribute("aria-rowindex",String(index+2));if(row.t==="gap"){element.className="cx-diff-row cx-diff-gaprow";element.appendChild(this.gapCell(row.gap||row));return}
119
+ if(row.t==="line"){element.className="cx-diff-row " +(row.kind==="-"?"cx-diff-del":row.kind==="+"?"cx-diff-add":"cx-diff-ctx");element.append(this.numberCell(row.b),this.numberCell(row.a),this.signCell(row.kind),this.codeCell(row));return}
120
+ element.className="cx-diff-row cx-diff-pairrow";for(const[line,side]of[[row.left,"b"],[row.right,"a"]]){const changed=line&&line.kind!=="="?(line.kind==="-"?" cx-diff-del":" cx-diff-add"):"";const number=this.numberCell(line?line[side]:null),code=line?this.codeCell(line):h("span","cx-diff-code cx-diff-blank");number.className +=changed;code.className +=changed;if(!line)code.setAttribute("role","cell");element.append(number,code)}},numberCell(value){const cell=h("span","cx-diff-num");cell.setAttribute("role","cell");if(value!=null)setText(cell,value);return cell},signCell(kind){const cell=h("span","cx-diff-sign");cell.setAttribute("aria-hidden","true");setText(cell,kind==="-"?"−":kind==="+"?"+":"");return cell},codeCell(line){const cell=h("span","cx-diff-code"),labels=this.labels,sr=h("span","cx-sr-only");cell.setAttribute("role","cell");setText(sr,fill(line.kind==="-"?labels.changeLineRemoved:line.kind==="+"?labels.changeLineAdded:labels.changeLineContext,{line:line.kind==="+"?line.a:line.b})+ ": ");cell.appendChild(sr);const marks=line.kind==="="?null:diffMarks(line),text=line.text;if(!marks||!marks.length){cell.appendChild(document.createTextNode(text));return cell}
121
+ let at=0;for(const[start,end]of marks){if(start>at)cell.appendChild(document.createTextNode(text.slice(at,start)));const mark=h(line.kind==="-"?"del":"ins","cx-diff-mark");setText(mark,text.slice(start,end));cell.appendChild(mark);at=end}
122
+ if(at<text.length)cell.appendChild(document.createTextNode(text.slice(at)));return cell},gapCell(gap){const cell=h("span","cx-diff-gap"),labels=this.labels,count=this.format.format(gap.count);cell.setAttribute("role","cell");cell.setAttribute("aria-colspan","4");if(gap.lines){const button=h("button","cx-diff-expand");button.type="button";button._gap=gap;setText(button,changeCount(labels.changeShowLines,gap.count,this.lang,{count}));cell.appendChild(button)}else{setText(cell,changeCount(labels.changeHiddenLines,gap.count,this.lang,{count}))}
123
+ return cell}});async function changeCopy(text){try{if(global.navigator?.clipboard?.writeText){await global.navigator.clipboard.writeText(text);return true}}catch(error){}
124
+ const area=h("textarea","cx-sr-only");area.value=text;area.setAttribute("readonly","");document.body.appendChild(area);area.select();let done=false;try{done=document.execCommand("copy")}catch(error){done=false}
125
+ area.remove();return done}
126
+ Object.assign(DiffView.prototype,{onKey(event){if(event.target!==this.scroll)return;const scroll=this.scroll,page=Math.max(DIFF_ROW,scroll.clientHeight-2*DIFF_ROW),key=event.key;const moves={ArrowDown:DIFF_ROW,ArrowUp:-DIFF_ROW,PageDown:page,PageUp:-page};if(event.altKey&&(key==="ArrowDown"||key==="ArrowUp")){event.preventDefault();this.move(key==="ArrowDown"?1:-1);return}
127
+ if(!event.ctrlKey&&!event.metaKey&&!event.altKey&&(key==="n"||key==="p")){event.preventDefault();this.move(key==="n"?1:-1);return}
128
+ if(Object.hasOwn(moves,key)){event.preventDefault();scroll.scrollTop +=moves[key]}
129
+ else if(key==="Home"||key==="End"){event.preventDefault();scroll.scrollTop=key==="Home"?0:scroll.scrollHeight}},onClick(event){const button=event.target.closest?.(".cx-diff-expand");if(!button||!this.scroll.contains(button))return;this.expand(button._gap)},expand(gap){const at=this.rows.indexOf(gap);if(at<0||!gap.lines)return;this.rows=this.rows.slice(0,at).concat(gap.lines,this.rows.slice(at+1));const current=this.current>=0?this.view[this.starts[this.current]]:null;this.display();if(current)this.current=this.starts.indexOf(this.view.indexOf(current));this.render();this.scroll.focus({preventScroll:true});setText(this.live,changeCount(this.labels.changeShowLines,gap.count,this.lang,{count:this.format.format(gap.count)}))},expandAll(){for(const gap of this.rows.filter(row=>row.t==="gap"&&row.lines))this.expand(gap)},move(step){if(!this.starts.length)return false;this.current=this.current<0?(step>0?0:this.starts.length-1):(this.current+step+this.starts.length)%this.starts.length;const top=this.canvas.offsetTop+Math.max(0,this.starts[this.current]- 2)*DIFF_ROW-this.columns.offsetHeight;this.scroll.scrollTop=Math.max(0,top);this.render();setText(this.live,fill(this.labels.changePosition,{index:this.format.format(this.current+1),count:this.format.format(this.starts.length)}));return true},async copy(){const ok=await changeCopy(diffUnified(this.doc,this.hunks));if(this.destroyed)return ok;const text=ok?this.labels.changeCopied:this.labels.changeCopyFailed;setText(this.live,text);this.copyButton.classList.toggle("cx-diff-done",ok);clearTimeout(this.copyTimer);this.copyTimer=setTimeout(()=>this.copyButton.classList.remove("cx-diff-done"),1600);this.emit("diffCopy",{diff:this.doc.id,copied:ok},"onCopy");return ok},renderPrint(){if(this.destroyed||this.scroll.hidden){this.print.replaceChildren();return}
130
+ const table=h("table","cx-diff-printtable"),body=h("tbody");let written=0;for(const row of this.rows){if(written++>=5000)break;const tr=h("tr");if(row.t==="gap"){const td=h("td","cx-diff-gap");td.colSpan=4;setText(td,"⋯ "+changeCount(this.labels.changeHiddenLines,row.count,this.lang,{count:this.format.format(row.count)}));tr.appendChild(td)}
131
+ else{tr.className=row.kind==="-"?"cx-diff-del":row.kind==="+"?"cx-diff-add":"";for(const[text,name]of[[row.b,"cx-diff-num"],[row.a,"cx-diff-num"],[row.kind==="-"?"−":row.kind==="+"?"+":"","cx-diff-sign"]]){const td=h("td",name);setText(td,text??"");tr.appendChild(td)}
132
+ const code=this.codeCell(row),td=h("td","cx-diff-code");td.append(...code.childNodes);tr.appendChild(td)}
133
+ body.appendChild(tr)}
134
+ if(written>5000){const rest=this.rows.slice(5000).reduce((sum,row)=>sum +(row.t==="gap"?row.count:1),0);if(rest>0){const tr=h("tr"),td=h("td","cx-diff-gap");td.colSpan=4;setText(td,"⋯ "+changeCount(this.labels.changePrintLimit,rest,this.lang,{count:this.format.format(rest)}));tr.appendChild(td);body.appendChild(tr)}}
135
+ table.appendChild(body);this.print.replaceChildren(table)},getHunks(){return this.hunks.map(hunk=>({before:hunk.before,after:hunk.after,lines:hunk.lines.map(line=>(line.kind==="="?" ":line.kind)+ line.text)}))},update(view){this.doc=diffDocument(view);this.labels=changeLabels(this.options.baseLabels,this.doc.labels,this.options.labels);this.source=view;this.current=-1;this.prepare();this.refresh()}});function diffCreate(host,options,doc){const view=new DiffView(host,options,doc);const impl={ready:Promise.resolve().then(()=>{if(view.destroyed)return impl;host.setAttribute("data-cx-ready","");view.emit("diffReady",{diff:view.doc.id},"onReady");return impl}),document:()=>view.source||options.view,getLayout:()=>view.shown,setLayout:mode=>view.setLayout(mode),next:()=>view.move(1),previous:()=>view.move(-1),expandAll:()=>view.expandAll(),copy:()=>view.copy(),getHunks:()=>view.getHunks(),update:next=>view.update(next),destroy:()=>{if(view.destroyed)return;view.destroyed=true;view.lifetime.abort();view.resize.disconnect();cancelAnimationFrame(view.frame);clearTimeout(view.copyTimer);view.root.remove();host.removeAttribute("data-cx-ready");if(view.addedTheme)host.removeAttribute("data-cx-theme")}};return impl}
136
+ class ChangeListView{constructor(host,options){this.host=host;this.options=options;this.lifetime=new AbortController();this.doc=changesDocument(options.view);this.labels=changeLabels(this.doc.labels,options.labels);this.lang=host.closest("[lang]")?.getAttribute("lang")||document.documentElement.lang||"en";this.format=numberFormat(this.lang);this.kinds=new Set();this.flaggedOnly=this.doc.flaggedOnly;this.limit=this.doc.pageSize;this.diffs=new Map();this.open=new Set(this.doc.expandFlagged?this.doc.items.filter(item=>item.flags.length).map(item=>item.id):[]);if(options.state)this.applyState(options.state,false);this.build()}
137
+ emit(name,detail,callback){const event=new CustomEvent("canopyx:"+name,{detail,bubbles:true});this.host.dispatchEvent(event);if(typeof this.options[callback]==="function"){try{this.options[callback](detail,event)}catch(error){this.host.dispatchEvent(new CustomEvent("canopyx:error",{detail:{error,hook:callback},bubbles:true}))}}}
138
+ build(){const signal=this.lifetime.signal;if(!this.host.closest("[data-cx-theme]")){this.host.setAttribute("data-cx-theme","auto");this.addedTheme=true}
139
+ const root=this.root=h("div","cx-changes");this.bar=h("div","cx-chg-bar");this.list=h("ul","cx-chg-list");this.list.setAttribute("aria-label",this.doc.label);this.empty=h("div","cx-chg-empty");this.empty.hidden=true;this.more=h("button","cx-chg-more");this.more.type="button";this.more.hidden=true;this.live=h("div","cx-sr-only");this.live.setAttribute("aria-live","polite");this.barPanel=h("div","cx-chg-barpanel");root.append(this.bar,this.barPanel,this.list,this.empty,this.more,this.live);this.host.appendChild(root);root.addEventListener("click",event=>this.onClick(event),{signal});root.addEventListener("keydown",event=>this.onKey(event),{signal});changeTips(root,signal);root.addEventListener("change",event=>{if(event.target===this.flagSwitch){this.applyState({flaggedOnly:event.target.checked});this.flagSwitch?.focus()}},{signal});this.more.addEventListener("click",()=>{const shown=new Set([...this.list.querySelectorAll("[data-cx-item]")].map(item=>item.getAttribute("data-cx-item")));this.limit +=this.doc.pageSize;this.render();if(this.more.hidden)[...this.list.querySelectorAll("[data-cx-item]")].find(item=>!shown.has(item.getAttribute("data-cx-item")))?.querySelector(".cx-chg-toggle")?.focus()},{signal});this.printing=changesInPrint();global.addEventListener("beforeprint",()=>{this.printing=true;this.render()},{signal});global.addEventListener("afterprint",()=>{this.printing=false;this.render()},{signal});this.render()}
140
+ visible(){return this.doc.items.filter(item=>(!this.kinds.size||this.kinds.has(item.kind))&&(!this.flaggedOnly||item.flags.length))}
141
+ folders(items){if(!this.doc.groupByFolder)return[["","",items]];const groups=new Map();for(const item of items){const folder=changePath(item.path)[0],key=folder.replace(/\\/g, "/");
142
+ if(!groups.has(key))groups.set(key,[key,folder,[]]);groups.get(key)[2].push(item)}
143
+ return[...groups.values()].sort((a,b)=>a[0]<b[0]?-1:a[0]>b[0]?1:0)}
144
+ renderBar(){const labels=this.labels,items=this.doc.items,kinds=h("div","cx-chg-kinds");kinds.setAttribute("role","group");kinds.setAttribute("aria-label",labels.changeFilter);for(const kind of CHANGE_KINDS){const count=items.filter(item=>item.kind===kind).length;if(!count)continue;const button=h("button","cx-chg-kind"),badge=h("span"),number=h("span","cx-chg-count");button.type="button";button._kind=kind;button.setAttribute("aria-pressed",String(this.kinds.has(kind)));applyTone(badge,"cx-chg-badge",toneOf(CHANGE_TONES[kind]));setText(badge,labels["change"+kind[0].toUpperCase()+ kind.slice(1)]);setText(number,this.format.format(count));button.append(badge,number);kinds.appendChild(button)}
145
+ const children=[kinds],flagged=items.filter(item=>item.flags.length).length;if(flagged){const label=h("label","cx-chg-switch"),box=this.flagSwitch=h("input"),text=h("span");box.type="checkbox";box.checked=this.flaggedOnly;setText(text,labels.changeFlaggedOnly+" · "+changeCount(labels.changeFlaggedCount,flagged,this.lang,{count:this.format.format(flagged)}));label.append(box,text);children.push(label)}
146
+ const summary=h("span","cx-chg-summary"),shown=Math.min(this.visible().length,this.shownLimit());setText(summary,shown===items.length?"":fill(labels.changeShown,{shown:this.format.format(shown),total:this.format.format(items.length)}));const filters=[...CHANGE_KINDS.filter(kind=>this.kinds.has(kind)).map(kind=>labels["change"+kind[0].toUpperCase()+ kind.slice(1)]),...(this.flaggedOnly?[labels.changeFlaggedOnly]:[])];const printed=h("span","cx-chg-printnote");setText(printed,filters.length?fill(labels.changeFiltered,{filters:filters.join(", ")}):"");children.push(summary,printed);const tools=this.tools();if(tools)children.push(tools);this.bar.replaceChildren(...children)}
147
+ shownLimit(){return this.printing?Math.max(this.limit,2000):this.limit}
148
+ render(){const focused=document.activeElement&&this.root.contains(document.activeElement)?document.activeElement.closest("[data-cx-item]")?.getAttribute("data-cx-item"):null;this.renderBar();const items=this.visible(),shown=items.slice(0,this.shownLimit()),labels=this.labels,rows=[];for(const[key,folder,members]of this.folders(shown)){if(this.doc.groupByFolder){const group=h("li","cx-chg-group"),title=h("div","cx-chg-folder"),name=h("span","cx-chg-foldername"),count=h("span","cx-chg-foldercount"),list=h("ul","cx-chg-sublist");setText(name,folder||labels.changeRoot);setText(count,changeCount(labels.changeFileCount,members.length,this.lang,{count:this.format.format(members.length)}));title.append(name,count);list.setAttribute("aria-label",folder||labels.changeRoot);for(const item of members)list.appendChild(this.row(item));group.append(title,list);rows.push(group)}else{for(const item of members)rows.push(this.row(item))}}
149
+ for(const[id,diff]of this.diffs)if(!shown.some(item=>item.id===id&&this.open.has(id))){diff.destroy();this.diffs.delete(id)}
150
+ this.list.replaceChildren(...rows);this.empty.hidden=items.length>0;setText(this.empty,labels.changeNoItems);this.more.hidden=items.length<=this.shownLimit();setText(this.more,labels.changeShowMore);if(!this.pending)this.panelLife?.abort();this.barPanel.replaceChildren(...(this.pending&&!this.pending.item?[this.panel()]:[]));if(focused)this.focusItem(focused)}
151
+ applyState(state,render=true){if(!state||typeof state!=="object")throw new Error("CanopyX: a change list state is an object.");if(state.kinds!==undefined){if(!Array.isArray(state.kinds)||state.kinds.some(kind=>!CHANGE_KINDS.includes(kind)))throw new Error("CanopyX: change list kinds are added, removed, modified and renamed.");this.kinds=new Set(state.kinds)}
152
+ if(state.flaggedOnly!==undefined)this.flaggedOnly=!!state.flaggedOnly;if(state.open!==undefined){if(!Array.isArray(state.open)||state.open.some(id=>typeof id!=="string"))throw new Error("CanopyX: open items are a list of item ids.");this.open=new Set(state.open.filter(id=>this.doc.items.some(item=>item.id===id)))}
153
+ this.limit=this.doc.pageSize;if(render){this.render();this.emit("changesState",this.getState(),"onStateChange")}}
154
+ getState(){return gridFreeze({kinds:[...this.kinds],flaggedOnly:this.flaggedOnly,open:[...this.open]})}
155
+ toggle(id,open){const item=this.doc.items.find(entry=>entry.id===id);if(!item)return false;const next=open===undefined?!this.open.has(id):!!open;if(next===this.open.has(id))return true;if(next)this.open.add(id);else this.open.delete(id);this.render();this.emit("changeToggle",{list:this.doc.id,item:id,open:next},"onToggle");return true}
156
+ focusItem(id){const toggle=[...this.list.querySelectorAll(".cx-chg-toggle")].find(button=>button.closest("[data-cx-item]").getAttribute("data-cx-item")===id);if(!toggle)return false;toggle.focus();return true}
157
+ update(view){this.doc=changesDocument(view);this.labels=changeLabels(this.doc.labels,this.options.labels);this.source=view;this.open=new Set([...this.open].filter(id=>this.doc.items.some(item=>item.id===id)));for(const diff of this.diffs.values())diff.destroy();this.diffs.clear();this.limit=this.doc.pageSize;this.pending=null;this.list.setAttribute("aria-label",this.doc.label);this.render()}}
158
+ Object.assign(ChangeListView.prototype,{row(item){const labels=this.labels,doc=this.doc,open=this.open.has(item.id),[folder,name]=changePath(item.path);const li=h("li","cx-chg-item" +(item.flags.length?" cx-chg-flagged":"")),row=h("div","cx-chg-row");li.setAttribute("data-cx-item",item.id);const toggle=h("button","cx-chg-toggle"),detailId=(this.host.id||"cx-changes")+ "-"+this.uid()+ "-detail";toggle.type="button";toggle.setAttribute("aria-expanded",String(open));toggle.setAttribute("aria-label",fill(labels.changeDetails,{name:doc.groupByFolder?name:item.path}));toggle.appendChild(changeIcon("chevron"));const badge=h("span");applyTone(badge,"cx-chg-badge",toneOf(CHANGE_TONES[item.kind]));setText(badge,item.kindLabel||labels["change"+item.kind[0].toUpperCase()+ item.kind.slice(1)]);const main=h("div","cx-chg-main"),title=h("div","cx-chg-title"),nameText=h("span","cx-chg-name");setText(nameText,doc.groupByFolder?name:item.path);nameText.title=item.path;title.appendChild(nameText);if(item.previousPath){const from=h("span","cx-chg-from");setText(from,fill(labels.changeFrom,{path:item.previousPath}));title.appendChild(from)}
159
+ main.appendChild(title);if(item.flags.length){const flags=h("div","cx-chg-flags");for(const flag of item.flags){const chip=h("span");applyTone(chip,"cx-chg-flag",toneOf(flag.tone));chip.title=flag.reason;chip.append(changeIcon("flag"),document.createTextNode(flag.label));flags.appendChild(chip)}
160
+ main.appendChild(flags)}
161
+ const second=[item.secondary,(item.after||item.before)?.time!=null?changeTimeText((item.after||item.before).time,doc.local,labels):null].filter(Boolean);if(second.length){const line=h("div","cx-chg-sub");setText(line,second.join(" · "));main.appendChild(line)}
162
+ const meta=h("div","cx-chg-meta"),size=h("span","cx-chg-size"),hash=h("span","cx-chg-hash");setText(size,this.pair(item.before?.size,item.after?.size,value=>changeSize(value,this.lang)));for(const[index,version]of[item.before,item.after].entries()){if(!version||!version.hash||index===1&&item.before?.hash===version.hash)continue;if(hash.childNodes.length){const arrow=h("span","cx-chg-arrow");setText(arrow,"→");arrow.setAttribute("aria-hidden","true");hash.appendChild(arrow)}
163
+ const button=h("button","cx-chg-hashbtn");button.type="button";button._copy=version.hash;button.title=labels.changeCopyHash+": "+version.hash;button.setAttribute("aria-label",labels.changeCopyHash+": "+version.hash);setText(button,version.hash.length>doc.hashLength?version.hash.slice(0,doc.hashLength)+ "…":version.hash);hash.appendChild(button)}
164
+ meta.append(size,hash);const actions=h("div","cx-chg-actions");actions.setAttribute("role","group");actions.setAttribute("aria-label",fill(labels.changeActions,{name}));for(const action of doc.actions){const control=this.actionControl(action,item);if(control)actions.appendChild(control)}
165
+ row.append(toggle,badge,main,meta,actions);li.appendChild(row);if(this.pending&&this.pending.item===item)li.appendChild(this.panel());if(open){const detail=this.detail(item);detail.id=detailId;toggle.setAttribute("aria-controls",detailId);li.appendChild(detail)}
166
+ return li},uid(){return(this.seq=(this.seq||0)+ 1)},pair(before,after,format){const left=before==null?null:format(before),right=after==null?null:format(after);return left===null?right??"":right===null||left===right?left:left+" → "+right},detail(item){const labels=this.labels,doc=this.doc,box=h("div","cx-chg-detail");if(item.flags.length){const heading=h("div","cx-chg-dhead"),list=h("ul","cx-chg-reasons");setText(heading,labels.changeFlags);for(const flag of item.flags){const entry=h("li"),label=h("strong");applyTone(label,"cx-chg-reason",toneOf(flag.tone));setText(label,flag.label);entry.append(label,document.createTextNode(" — "+flag.reason));list.appendChild(entry)}
167
+ box.append(heading,list)}
168
+ const rows=[],local=doc.local;if(item.before?.size!=null||item.after?.size!=null)rows.push([labels.changeSize,item.before?.size!=null?changeSize(item.before.size,this.lang):null,item.after?.size!=null?changeSize(item.after.size,this.lang):null,null,false]);if(item.before?.hash||item.after?.hash)rows.push([labels.changeHash,item.before?.hash??null,item.after?.hash??null,null,true]);if(item.before?.time!=null||item.after?.time!=null)rows.push([labels.changeTime,item.before?.time!=null?changeTimeText(item.before.time,local,labels):null,item.after?.time!=null?changeTimeText(item.after.time,local,labels):null,null,false]);for(const fact of item.facts)rows.push([fact.label,fact.before,fact.after,fact.tone,false,fact.note]);if(rows.length){const other=item.kind==="added"?0:item.kind==="removed"?1:-1;const sides=other<0||rows.some(row=>(other?row[2]:row[1])!=null)?[0,1]:[1-other];const table=h("table","cx-chg-facts" +(sides.length===1?" cx-chg-onevalue":"")),head=h("tr");for(const text of["",...sides.map(side=>side?labels.changeAfter:labels.changeBefore)]){const th=h("th");th.scope="col";setText(th,text);head.appendChild(th)}
169
+ const thead=h("thead"),tbody=h("tbody");thead.appendChild(head);for(const[label,before,after,tone,mono,note]of rows){const tr=h("tr"),th=h("th");th.scope="row";setText(th,label);tr.appendChild(th);sides.forEach((side,at)=>{const value=side?after:before;const td=h("td",(mono&&value!==null?"cx-chg-mono":"")+(side===1&&sides.length===2&&before!==null&&after!==null&&before!==after?" cx-chg-changed":""));setText(td,value??"—");if((side===1||sides.length===1)&&tone)applyTone(td,td.className,toneOf(tone));if(note&&at===sides.length-1){const small=h("div","cx-chg-factnote");setText(small,note);td.appendChild(small)}
170
+ tr.appendChild(td)});tbody.appendChild(tr)}
171
+ table.append(thead,tbody);box.appendChild(table)}
172
+ if(item.diff){let diff=this.diffs.get(item.id);if(!diff){const holder=h("div","cx-chg-diff");diff=diffCreate(holder,{labels:this.options.labels,baseLabels:this.doc.labels,lang:this.lang},item.diff);diff.holder=holder;this.diffs.set(item.id,diff)}
173
+ box.appendChild(diff.holder)}
174
+ return box},onClick(event){const target=event.target.closest?.("button, a.cx-chg-action");if(!target||!this.root.contains(target))return;if(target._export){this.exportItems(target._export);return}
175
+ if(target.classList.contains("cx-chg-kind")){const kinds=new Set(this.kinds);if(kinds.has(target._kind))kinds.delete(target._kind);else kinds.add(target._kind);this.applyState({kinds:[...kinds]});[...this.bar.querySelectorAll(".cx-chg-kind")].find(button=>button._kind===target._kind)?.focus();return}
176
+ if(target.classList.contains("cx-chg-toggle")){const id=target.closest("[data-cx-item]").getAttribute("data-cx-item");this.toggle(id);this.focusItem(id);return}
177
+ if(target.classList.contains("cx-chg-hashbtn")){this.copyText(target._copy);return}
178
+ if(target._action)this.act(target._action,target._item,target)},async copyText(text){const ok=await changeCopy(text);if(!this.destroyed)setText(this.live,ok?this.labels.changeCopied:this.labels.changeCopyFailed);return ok},onKey(event){if(!event.target.classList?.contains("cx-chg-toggle")||event.key!=="ArrowDown"&&event.key!=="ArrowUp"&&event.key!=="Home"&&event.key!=="End")return;const toggles=[...this.list.querySelectorAll(".cx-chg-toggle")],at=toggles.indexOf(event.target);const next=event.key==="Home"?0:event.key==="End"?toggles.length-1:at +(event.key==="ArrowDown"?1:-1);if(next<0||next>=toggles.length)return;event.preventDefault();toggles[next].focus()}});function changeListCreate(host,options){const view=new ChangeListView(host,options);const impl={ready:Promise.resolve().then(()=>{if(view.destroyed)return impl;host.setAttribute("data-cx-ready","");view.emit("changesReady",{list:view.doc.id},"onReady");return impl}),document:()=>view.source||options.view,getState:()=>view.getState(),setState:state=>view.applyState(state),toggle:(id,open)=>view.toggle(id,open),focusItem:id=>view.focusItem(id),update:next=>view.update(next),destroy:()=>{if(view.destroyed)return;view.destroyed=true;view.lifetime.abort();view.panelLife?.abort();for(const diff of view.diffs.values())diff.destroy();view.root.remove();host.removeAttribute("data-cx-ready");if(view.addedTheme)host.removeAttribute("data-cx-theme")}};return impl}
179
+ const CHANGE_LINK=/^(?:#|\/(?!\/)|\.{1,2}\/|https?:\/\/|mailto:)/i;
180
+ function changeHref(value){if(typeof value!=="string")return null;const href=value.trim();return CHANGE_LINK.test(href)&&!/[\\\u0000-\u001f\u007f]/.test(href) ? href : null;
181
+ }
182
+ function changeDownload(name,type,text){if(typeof Blob!=="function"||!global.URL||typeof URL.createObjectURL!=="function")return false;const url=URL.createObjectURL(new Blob([text],{type})),link=h("a");link.href=url;link.download=name;link.hidden=true;document.body.appendChild(link);link.click();link.remove();setTimeout(()=>URL.revokeObjectURL(url),1000);return true}
183
+ function changeCsvField(value){let text=value==null?"":String(value);if(/^[=+\-@\t\r]/.test(text)) text = "'" + text;
184
+ return /[",\r\n]/.test(text) ? "\"" + text.replace(/"/g, "\"\"") + "\"" : text;
185
+ }
186
+ function changeIso(time){return time==null?null:new Date(time).toISOString()}
187
+ function changeExportDiff(diff){if(!diff)return null;let hunks=diff.hunks;const canCompare=diff.left&&diff.left.length<=DIFF_CLIENT_LINES&&diff.right.length<=DIFF_CLIENT_LINES;if(diff.content==="text"&&!hunks.length&&canCompare)hunks=diffHunks(diff.left,diff.right,diff.context);const unified=diff.content==="text"&&(diff.unified!==null||hunks.length||canCompare)?diffUnified(diff,hunks):null;return{content:diff.content,message:diff.message,unified,truncated:diff.truncated}}
188
+ function changeExportVersion(version){return version?{size:version.size,hash:version.hash,time:changeIso(version.time)}:null}
189
+ function changeExportItem(item){return{id:item.id,kind:item.kind,kindLabel:item.kindLabel,path:item.path,previousPath:item.previousPath,secondary:item.secondary,before:changeExportVersion(item.before),after:changeExportVersion(item.after),flags:item.flags.map(flag=>({id:flag.id,label:flag.label,reason:flag.reason,tone:flag.tone})),facts:item.facts.map(fact=>({label:fact.label,before:fact.before,after:fact.after,note:fact.note})),values:{...item.values},diff:changeExportDiff(item.diff)}}
190
+ function changeCsv(items){const keys=[...new Set(items.flatMap(item=>Object.keys(item.values)))].sort();const head=["kind","kindLabel","path","previousPath","secondary","sizeBefore","sizeAfter","hashBefore","hashAfter","timeBefore","timeAfter","flags","facts"].concat(keys.map(key=>"values."+key));const rows=[head].concat(items.map(item=>[item.kind,item.kindLabel,item.path,item.previousPath,item.secondary,item.before?.size,item.after?.size,item.before?.hash,item.after?.hash,changeIso(item.before?.time),changeIso(item.after?.time),item.flags.map(flag=>flag.label+": "+flag.reason).join(" | "),item.facts.map(fact=>fact.label+": " +(fact.before??"")+(fact.before!=null&&fact.after!=null?" → ":"")+(fact.after??"")+(fact.note?" ("+fact.note+")":"")).join(" | ")].concat(keys.map(key=>item.values[key]))));return rows.map(row=>row.map(changeCsvField).join(",")).join("\r\n")+ "\r\n"}
191
+ Object.assign(ChangeListView.prototype,{actionControl(action,item){if(item&&item.actions.length&&!item.actions.includes(action.id))return null;const values=item?item.values:this.doc.values;const value=key=>key===null?null:item&&key==="path"?item.path:item&&key==="previousPath"?item.previousPath:values[key]??null;if(action.copy!==null&&value(action.copy)===null)return null;const href=action.link===null?null:changeHref(value(action.link));if(action.link!==null&&href===null)return null;if(action.reveal==="diff"&&!item.diff)return null;const control=h(href?"a":"button","cx-chg-action" +(action.icon==="none"||!item?" cx-chg-textaction":""));if(href)control.href=href;else control.type="button";control._action=action;control._item=item;control.setAttribute("data-cx-action",action.id);if(action.icon!=="none")control.appendChild(changeIcon(action.icon));if(action.icon==="none"||!item)control.appendChild(document.createTextNode(action.label));else{control.setAttribute("aria-label",action.label);control.setAttribute("data-cx-tip",action.label)}
192
+ if(action.reason)control.setAttribute("aria-expanded",String(!!(this.pending&&this.pending.mode==="ask"&&this.pending.action===action&&this.pending.item===item)));return control},tools(){const tools=h("div","cx-chg-tools");tools.setAttribute("role","group");tools.setAttribute("aria-label",fill(this.labels.changeActions,{name:this.doc.label}));for(const action of this.doc.toolbar){const control=this.actionControl(action,null);if(control)tools.appendChild(control)}
193
+ for(const format of this.doc.exports){const button=h("button","cx-chg-action cx-chg-textaction cx-chg-export");button.type="button";button._export=format;button.append(changeIcon("download"),document.createTextNode(format==="csv"?this.labels.changeExportCsv:this.labels.changeExportJson));tools.appendChild(button)}
194
+ return tools.childNodes.length?tools:null},detailOf(action,item,extra){return Object.assign({list:this.doc.id,action:action.id,item:item?item.id:null,kind:item?item.kind:null,path:item?item.path:null,previousPath:item?item.previousPath:null,values:{...(item?item.values:this.doc.values)},copied:null},extra)},request(detail){const event=new CustomEvent("canopyx:changeAction",{detail,bubbles:true,cancelable:true});this.host.dispatchEvent(event);let claimed=event.defaultPrevented;if(typeof this.options.onAction==="function"){try{this.options.onAction(detail,event);claimed=true}catch(error){this.host.dispatchEvent(new CustomEvent("canopyx:error",{detail:{error,hook:"onAction"},bubbles:true}))}}
195
+ return claimed},async act(action,item,control){if(action.copy!==null){const key=action.copy,copied=await this.copyText(item&&key==="path"?item.path:item&&key==="previousPath"?item.previousPath:(item?item.values:this.doc.values)[key]);if(!this.destroyed)this.emit("changeAction",this.detailOf(action,item,{copied}),"onAction");return}
196
+ if(action.link!==null){this.emit("changeAction",this.detailOf(action,item,{href:control.getAttribute("href")}),"onAction");return}
197
+ if(action.reveal!=="none"){this.reveal(item.id,action.reveal);this.emit("changeAction",this.detailOf(action,item,{reveal:action.reveal}),"onAction");return}
198
+ if(action.reason){this.pending={mode:"ask",action,item,text:"",error:false};this.render();this.focusPanel();return}
199
+ this.send(action,item,null)},send(action,item,reason){const detail=this.detailOf(action,item,reason===null?{}:{reason});const claimed=this.request(detail);if(this.destroyed)return;if(item?!this.doc.items.includes(item):!this.doc.toolbar.includes(action)){this.pending=null;this.render();return}
200
+ this.pending=claimed?null:{mode:"note",action,item,detail};this.render();if(this.pending)this.focusPanel();else this.focusAction(action,item)},reveal(id,what){this.toggle(id,true);const box=this.list.querySelector("[data-cx-item=\""+CSS.escape(id)+ "\"] > .cx-chg-detail");if(!box)return;const target=what==="diff"?box.querySelector(".cx-chg-diff .cx-diff-scroll")||box:box;if(target===box)box.tabIndex=-1;target.scrollIntoView?.({block:"nearest"});target.focus({preventScroll:true})},panel(){this.panelLife?.abort();this.panelLife=new AbortController();const pending=this.pending,labels=this.labels,signal=this.panelLife.signal,box=h("div","cx-chg-panel");box.setAttribute("role","group");box.setAttribute("aria-label",pending.action.label);const cancel=h("button","cx-chg-action cx-chg-textaction");cancel.type="button";setText(cancel,labels.changeCancel);cancel.addEventListener("click",()=>{const{action,item}=pending;this.pending=null;this.render();this.focusAction(action,item)},{signal});if(pending.mode==="note"){const text=h("p","cx-chg-note"),copy=h("button","cx-chg-action cx-chg-textaction");copy.type="button";setText(text,fill(labels.changeUnclaimed,{action:pending.action.label}));copy.append(changeIcon("copy"),document.createTextNode(labels.changeCopyRequest));copy.addEventListener("click",()=>this.copyText(JSON.stringify(pending.detail,null,2)),{signal});const buttons=h("div","cx-chg-askbtns");buttons.append(copy,cancel);box.append(text,buttons);return box}
201
+ const id=(this.host.id||"cx-changes")+ "-reason-"+this.uid(),field=h("textarea","cx-chg-reasonbox"),label=h("label","cx-chg-asklabel"),hint=h("div","cx-chg-hint");field.id=id;field.rows=2;field.maxLength=1000;field.value=pending.text;field.setAttribute("aria-describedby",id+"-hint");label.htmlFor=id;setText(label,labels.changeReason);hint.id=id+"-hint";setText(hint,fill(labels.changeReasonHint,{action:pending.action.label}));const error=h("div","cx-chg-askerror");error.setAttribute("role","alert");setText(error,pending.error?labels.changeReasonRequired:"");if(pending.error)field.setAttribute("aria-invalid","true");const confirm=h("button","cx-chg-action cx-chg-textaction cx-chg-primary");confirm.type="button";setText(confirm,pending.action.label);const submit=()=>{const reason=field.value.trim();if(!reason){pending.error=true;pending.text=field.value;this.render();this.focusPanel();return}
202
+ this.send(pending.action,pending.item,reason)};confirm.addEventListener("click",submit,{signal});field.addEventListener("input",()=>{pending.text=field.value},{signal});field.addEventListener("keydown",event=>{if(event.key==="Escape"){event.preventDefault();event.stopPropagation();cancel.click()}
203
+ else if(event.key==="Enter"&&(event.ctrlKey||event.metaKey)){event.preventDefault();submit()}},{signal});const buttons=h("div","cx-chg-askbtns");buttons.append(confirm,cancel);box.append(label,field,hint,error,buttons);return box},focusPanel(){const target=this.root.querySelector(".cx-chg-panel :is(textarea, button)");if(target)target.focus()},focusAction(action,item){const scope=item?this.list.querySelector("[data-cx-item=\""+CSS.escape(item.id)+ "\"] > .cx-chg-row"):this.bar;scope?.querySelector("[data-cx-action=\""+CSS.escape(action.id)+ "\"]")?.focus()},exportItems(format){const items=this.folders(this.visible()).flatMap(group=>group[2]),name=(this.doc.id.replace(/[^\w.-]+/g, "-") || "changes") + "." + format;
204
+ const text=format==="csv"?""+changeCsv(items)
205
+ :JSON.stringify({list:this.doc.id,label:this.doc.label,exported:new Date().toISOString(),filters:{kinds:[...this.kinds],flaggedOnly:this.flaggedOnly},items:items.map(changeExportItem)},null,2);const saved=changeDownload(name,format==="csv"?"text/csv;charset=utf-8":"application/json",text);this.emit("changeExport",{list:this.doc.id,format,count:items.length,saved},"onExport");return saved}});const PROPAGATION_RANK={fault:3,propagating:2,unknown:1,converged:0};class PropagationView{constructor(host,options){this.host=host;this.options=options;this.lifetime=new AbortController();this.doc=propagationDocument(options.view);this.labels=changeLabels(this.doc.labels,options.labels);this.lang=host.closest("[lang]")?.getAttribute("lang")||document.documentElement.lang||"en";this.format=numberFormat(this.lang);if(!host.closest("[data-cx-theme]")){host.setAttribute("data-cx-theme","auto");this.addedTheme=true}
206
+ this.root=h("div","cx-prop");host.appendChild(this.root);this.render()}
207
+ emit(name,detail,callback){const event=new CustomEvent("canopyx:"+name,{detail,bubbles:true});this.host.dispatchEvent(event);if(typeof this.options[callback]==="function"){try{this.options[callback](detail,event)}catch(error){this.host.dispatchEvent(new CustomEvent("canopyx:error",{detail:{error,hook:callback},bubbles:true}))}}}
208
+ stateText(state){return this.labels["change"+state[0].toUpperCase()+ state.slice(1)]}
209
+ badge(state,className){const badge=h("span"),[icon,tone]=PROPAGATION_LOOK[state];applyTone(badge,className +(state==="fault"||state==="propagating"?" cx-prop-attn":""),toneOf(tone));const text=h("span");setText(text,this.stateText(state));badge.append(changeIcon(icon),text);return badge}
210
+ render(){const doc=this.doc,labels=this.labels,witnesses=doc.witnesses;const seen=witnesses.map(witness=>witness.firstSeen).filter(time=>time!==null),reports=witnesses.flatMap(witness=>[witness.firstSeen,witness.lastSeen]).filter(time=>time!==null);const start=doc.start??(seen.length?Math.min(...seen):null),end=doc.asOf??(reports.length?Math.max(...reports):null),span=start!==null&&end!==null?Math.max(1,end-start):0;const overall=doc.state||witnesses.reduce((worst,witness)=>PROPAGATION_RANK[witness.state]>PROPAGATION_RANK[worst]?witness.state:worst,"converged");const converged=witnesses.filter(witness=>witness.state==="converged").length;const head=h("div","cx-prop-head"),summary=h("span","cx-prop-summary"),counts=h("ul","cx-prop-counts");setText(summary,doc.summary||fill(labels.changeProgress,{converged:this.format.format(converged),total:this.format.format(witnesses.length)}));head.append(this.badge(overall,"cx-prop-state"),summary);for(const state of PROPAGATION_STATES){const count=witnesses.filter(witness=>witness.state===state).length;if(!count)continue;const item=h("li","cx-prop-count"),badge=this.badge(state,"cx-prop-key"),number=h("span","cx-prop-number");setText(number,this.format.format(count));item.append(badge,number);counts.appendChild(item)}
211
+ counts.setAttribute("aria-label",labels.changeState);const table=h("table","cx-prop-table"),thead=h("thead"),heads=h("tr");table.setAttribute("aria-label",doc.label);for(const text of[doc.title||labels.changeReplica,labels.changeState,labels.changeFirstSeen,labels.changeLastSeen,labels.changeTimeline]){const th=h("th");th.scope="col";setText(th,text);heads.appendChild(th)}
212
+ thead.appendChild(heads);table.appendChild(thead);const groups=new Map();for(const witness of witnesses){if(!groups.has(witness.group))groups.set(witness.group,[]);groups.get(witness.group).push(witness)}
213
+ for(const[group,members]of groups){const body=h("tbody");if(group!==null){const tr=h("tr","cx-prop-group"),th=h("th"),count=h("span","cx-prop-groupcount");th.scope="rowgroup";th.colSpan=5;setText(th,group);setText(count," · "+changeCount(doc.groupCount||"{count}",members.length,this.lang,{count:this.format.format(members.length)}));th.appendChild(count);tr.appendChild(th);body.appendChild(tr)}
214
+ for(const witness of members)body.appendChild(this.row(witness,start,span));table.appendChild(body)}
215
+ this.root.replaceChildren(head,counts,table)}
216
+ row(witness,start,span){const labels=this.labels,tr=h("tr","cx-prop-row cx-prop-"+witness.state),name=h("th"),state=h("td","cx-prop-statecell"),first=h("td","cx-prop-first"),last=h("td","cx-prop-last"),lane=h("td","cx-prop-lanecell");name.scope="row";const label=h("span","cx-prop-name");setText(label,witness.label);name.appendChild(label);if(witness.source){const mark=h("span","cx-prop-source");setText(mark,labels.changeSource);name.appendChild(mark)}
217
+ if(witness.secondary){const sub=h("span","cx-prop-sub");setText(sub,witness.secondary);name.appendChild(sub)}
218
+ state.appendChild(this.badge(witness.state,"cx-prop-badge"));if(witness.note){const note=h("span","cx-prop-sub");setText(note,witness.note);state.appendChild(note)}
219
+ let delay=null;if(witness.firstSeen!==null){const time=h("span");setText(time,changeTimeText(witness.firstSeen,this.doc.local,labels));first.appendChild(time);if(start!==null&&!witness.source){delay=fill(labels.changeDelay,{duration:changeDuration(witness.firstSeen-start,labels,this.format)});const sub=h("span","cx-prop-delay");setText(sub,delay);first.appendChild(sub)}}else{const none=h("span","cx-prop-none");setText(none,labels.changeNotSeen);first.appendChild(none)}
220
+ setText(last,witness.lastSeen!==null?changeTimeText(witness.lastSeen,this.doc.local,labels):"");const track=h("div","cx-prop-lane"),fillBar=h("div","cx-prop-fill");track.setAttribute("aria-hidden","true");const reached=witness.firstSeen!==null&&span?Math.min(1,Math.max(0,(witness.firstSeen-start)/ span)) : witness.state === "unknown" || !span ? 0 : 1;
221
+ fillBar.style.width=(reached*100).toFixed(1)+ "%";applyTone(fillBar,"cx-prop-fill" +(witness.firstSeen===null&&witness.state!=="unknown"?" cx-prop-open":""),toneOf(PROPAGATION_LOOK[witness.state][1]));track.appendChild(fillBar);if(delay)track.title=delay;lane.appendChild(track);tr.append(name,state,first,last,lane);return tr}
222
+ update(view){this.doc=propagationDocument(view);this.labels=changeLabels(this.doc.labels,this.options.labels);this.source=view;this.render()}}
223
+ function propagationCreate(host,options){const view=new PropagationView(host,options);const impl={ready:Promise.resolve().then(()=>{if(view.destroyed)return impl;host.setAttribute("data-cx-ready","");view.emit("propagationReady",{propagation:view.doc.id},"onReady");return impl}),document:()=>view.source||options.view,update:next=>view.update(next),destroy:()=>{if(view.destroyed)return;view.destroyed=true;view.lifetime.abort();view.root.remove();host.removeAttribute("data-cx-ready");if(view.addedTheme)host.removeAttribute("data-cx-theme")}};return impl}
224
+ Object.assign(CHANGES_IMPL,{diff:diffCreate,changes:changeListCreate,propagation:propagationCreate});installed.add(context.CHANGES_IMPL)}};(global.CanopyXParts||=[]).push(["changes",part])})(typeof window!=="undefined"?window:globalThis);
@@ -62,7 +62,7 @@ function fill(template,values){return template.replace(/\{(\w+)\}/g, (match, nam
62
62
  const pluralRules=new Map();function plural(template,count,lang){if(!template.startsWith("["))return template;const forms={};for(const match of template.matchAll(/\[(zero|one|two|few|many|other)\]([^[]*)/g)) forms[match[1]] = match[2];
63
63
  let rules=pluralRules.get(lang);if(!rules){try{rules=new Intl.PluralRules(lang||"en")}catch(_){rules=new Intl.PluralRules("en")}pluralRules.set(lang,rules)}
64
64
  return forms[rules.select(Number(count))]??forms.other??template}
65
- let DEFAULT_LABELS=Object.freeze({searchPlaceholder:"Search",searchResults:"Search results",searchResultCount:"{count} results",searchResultCountOfTotal:"{count} of {total} results",searchResultCountOfMany:"{count} of many results",searchNoResults:"No results",searchTooShort:"Type at least {min} characters",searchWordTooShort:"Type a word of at least {min} characters",searchFailed:"Search failed: {error}",columns:"Columns",hiddenColumns:"[one]+{count} column[other]+{count} columns",visibleColumns:"Visible columns",resizeColumn:"Resize {column}",columnWidth:"{width} pixels",loading:"Loading…",loadError:"Could not load: {error} (Enter to retry)",queryFailed:"Could not update the view: {error}",showMore:"Show more ({loaded} of {total} loaded)",showMoreUnknownTotal:"Show more ({loaded} loaded)",pageLoaded:"{loaded} of {total} loaded",itemsLoaded:"{count} items",yes:"Yes",no:"No",utcTime:"{time} UTC",localTime:"{time} local time",mountError:"This view could not be displayed: {error}"});function labelsFor(...sources){const labels=Object.assign({},DEFAULT_LABELS);for(const source of sources){if(source==null)continue;if(typeof source!=="object"||Array.isArray(source))throw new Error("CanopyX: labels must be an object of strings.");Object.keys(source).forEach((name)=>{if(!Object.hasOwn(DEFAULT_LABELS,name)&&!/^(?:gridEdit|format|calendar|flow|matrix)[A-Z]\w{0,63}$/.test(name)) throw new Error("CanopyX: there is no label '" + name + "'.");
65
+ let DEFAULT_LABELS=Object.freeze({searchPlaceholder:"Search",searchResults:"Search results",searchResultCount:"{count} results",searchResultCountOfTotal:"{count} of {total} results",searchResultCountOfMany:"{count} of many results",searchNoResults:"No results",searchTooShort:"Type at least {min} characters",searchWordTooShort:"Type a word of at least {min} characters",searchFailed:"Search failed: {error}",columns:"Columns",hiddenColumns:"[one]+{count} column[other]+{count} columns",visibleColumns:"Visible columns",resizeColumn:"Resize {column}",columnWidth:"{width} pixels",loading:"Loading…",loadError:"Could not load: {error} (Enter to retry)",queryFailed:"Could not update the view: {error}",showMore:"Show more ({loaded} of {total} loaded)",showMoreUnknownTotal:"Show more ({loaded} loaded)",pageLoaded:"{loaded} of {total} loaded",itemsLoaded:"{count} items",yes:"Yes",no:"No",utcTime:"{time} UTC",localTime:"{time} local time",mountError:"This view could not be displayed: {error}"});function labelsFor(...sources){const labels=Object.assign({},DEFAULT_LABELS);for(const source of sources){if(source==null)continue;if(typeof source!=="object"||Array.isArray(source))throw new Error("CanopyX: labels must be an object of strings.");Object.keys(source).forEach((name)=>{if(!Object.hasOwn(DEFAULT_LABELS,name)&&!/^(?:gridEdit|format|calendar|flow|matrix|change)[A-Z]\w{0,63}$/.test(name)) throw new Error("CanopyX: there is no label '" + name + "'.");
66
66
  if(typeof source[name]!=="string"||!/\S/.test(source[name])) throw new Error("CanopyX: label '" + name + "' must be a string with visible text.");
67
67
  if((name==="utcTime"||name==="localTime")&&source[name].indexOf("{time}")<0)throw new Error("CanopyX: label '"+name+"' must hold {time}.");labels[name]=source[name]})}
68
68
  return Object.freeze(labels)}
@@ -506,7 +506,7 @@ if(active>=0){list.children[active].setAttribute("aria-selected","false")}
506
506
  active=active<0?(delta<0?hits.length-1:0):(active+delta+hits.length)%hits.length;const item=list.children[active];item.setAttribute("aria-selected","true");input.setAttribute("aria-activedescendant",item.id);item.scrollIntoView({block:"nearest"})};input.addEventListener("input",()=>{cancelNavigation();sequence++;clearTimeout(timer);hits=[];close();timer=setTimeout(run,150)});input.addEventListener("keydown",(e)=>{if(e.defaultPrevented||e.isComposing)return;if(e.key==="ArrowDown"||e.key==="ArrowUp"){move(e.key==="ArrowDown"?1:-1)}else if(e.key==="Enter"){if(!list.hidden){choose(active>=0?active:0)}}else if(e.key==="Escape"){cancelNavigation();sequence++;clearTimeout(timer);if(list.hidden){input.value=""}
507
507
  close()}else{return}
508
508
  e.preventDefault()});input.addEventListener("blur",()=>{cancelNavigation();sequence++;clearTimeout(timer);clearTimeout(blurTimer);blurTimer=setTimeout(close,150)});this.cancelSearch=()=>{cancelNavigation();sequence++;clearTimeout(timer);clearTimeout(blurTimer);hits=[];close()};box.appendChild(input);box.appendChild(list);box.appendChild(status);bar.appendChild(box);this.searchInput=input}});function watchFlyout(anchorElement,panel,isOpen){const position=()=>{if(!isOpen())return;const anchor=anchorElement.getBoundingClientRect(),fixed=getComputedStyle(panel).position==="fixed";const below=Math.max(0,global.innerHeight-anchor.bottom-8);const above=Math.max(0,anchor.top-8);const opensAbove=below<120&&above>below,room=opensAbove?above:below;panel.style.maxHeight=(panel.classList.contains("cx-fb")?room:Math.min(320,room))+ "px";panel.style.top=opensAbove?"auto":fixed?anchor.bottom+"px":"100%";panel.style.bottom=opensAbove?fixed?global.innerHeight-anchor.top+"px":"100%":"auto";const width=panel.getBoundingClientRect().width;panel.style.left=Math.max(8,Math.min(anchor.left,global.innerWidth-width-8))-(fixed?0:anchor.left)+ "px"};global.addEventListener("resize",position);global.addEventListener("scroll",position,true);return{position,dispose:()=>{global.removeEventListener("resize",position);global.removeEventListener("scroll",position,true)}}}
509
- const PART_URL=typeof document!=="undefined"&&document.currentScript?.src;const PARTS={"tree-tools":["4c6c4101089c4444","canopyx-tree-tools.510207e8f47014fd.js","canopyx-tree-tools.ef7a3fb9c9115a82.css"],"filter-editor":["8a058a87bbba3cbe","canopyx-filter-editor.15e355b04c7138cc.js","canopyx-filter-editor.01b7bb6820dc9edb.css"],"export":["8b99c0b7b0ec37c5","canopyx-export.6989ea36f5d6da7b.js"],"history":["7d9901d2fe04df72","canopyx-history.9432e724bf14c9ed.js"],"pager":["8f804f70b96409f2","canopyx-pager.ac543250cf2f6e8b.js","canopyx-pager.08ad67c69026852e.css"],"enhance":["31162b1ffa622bf8","canopyx-enhance.65868addbc5c4a1a.js"],"data":["f07aebe37f3d1bb4","canopyx-data.401d09bc34c61d2f.js"],"forms":["a8c60f205d9babca","canopyx-forms.16987de1cdf9bda9.js","canopyx-forms.29058ce3c88af96e.css"],"flow":["842e1f1956f6213f","canopyx-flow.efc6250968e8b10a.js","canopyx-flow.3152e616c5557452.css"],"flow-host":["de4f9b11ab881eca","canopyx-flow-host.aea5ed35816df2af.js","canopyx-flow-host.1301fcf3454a723a.css"],"flow-checks":["ec2451183901867b","canopyx-flow-checks.29c4586a80f04b6a.js","canopyx-flow-checks.4502d6f7f7487b73.css"],"flow-options":["60450342239ded26","canopyx-flow-options.d31458a54aa4b1e7.js"],"flow-pick":["426718e2fa1f3cd0","canopyx-flow-pick.379205e09325bca3.js","canopyx-flow-pick.771b06ac50c141a1.css"],"flow-records":["f3c09661bdffb8d0","canopyx-flow-records.fddf130ace0764b1.js","canopyx-flow-records.e362111fb5f85732.css"],"flow-drafts":["4f112f64736ca8e3","canopyx-flow-drafts.09b88e275c7bc3b9.js","canopyx-flow-drafts.2dd86fb31fc10458.css"],"flow-preview":["f4fb5c42743873c0","canopyx-flow-preview.5778d5fce9ae0d78.js","canopyx-flow-preview.7928e22901da2088.css"],"flow-command":["39472118ca80ef02","canopyx-flow-command.78f4f931c52b1bfc.js","canopyx-flow-command.0c1247a371b7e775.css"],"flow-tasks":["147dd31638a9cb94","canopyx-flow-tasks.f012a521560f7075.js","canopyx-flow-tasks.524df90f908f81dd.css"],"flow-dialog":["ebb432cd6e34aba6","canopyx-flow-dialog.b00addd6a176198e.js","canopyx-flow-dialog.e6192e827eca25d2.css"],"flow-links":["2c14b19c9236e90d","canopyx-flow-links.7b6cfe42d28b6067.js"],"flow-endpoint":["f5cddfc9545e0920","canopyx-flow-endpoint.b31146202bd76aac.js"],"flow-consent":["22121778bc0d07c5","canopyx-flow-consent.749b97f13039bc71.js","canopyx-flow-consent.9f6580d3673ae8bf.css"],"matrix":["b7324bd75b73b9a3","canopyx-matrix.c8e156769fe3eb9d.js","canopyx-matrix.b6ea56044183ca82.css"],"editing":["c82648dfaa7e5ee5","canopyx-editing.4fe83feec389c2ce.js","canopyx-editing.67c43d3b80f20232.css"],"column-groups":["b42238fd108a8614","canopyx-column-groups.d8ad6322f769b915.js","canopyx-column-groups.37e177d74cebb516.css"],"cell-range":["e99a39e7cccc57b1","canopyx-cell-range.647da96e57924f3e.js","canopyx-cell-range.23f3222652588441.css"],"links":["f1ab7e32b501ce37","canopyx-links.d96addeefb3eccf5.js"],"search":["521eebd6e1bbbd35","canopyx-search.14c69f4551c69f23.js"],"formatting":["1a28068597f435de","canopyx-formatting.68c501f97b71f1ec.js","canopyx-formatting.a59507e4e8334891.css"],"format-editor":["24f176a39c02944a","canopyx-format-editor.3b1dec2527e53988.js","canopyx-format-editor.1ba153c82aa87b04.css"],"link":["fa0c9e888463553b","canopyx-link.4ec39bc39fa6cf50.js","canopyx-link.8435c892613926ac.css"],"detail-rows":["4908d1d2b7f67aa8","canopyx-detail-rows.fe29b1759c962565.js","canopyx-detail-rows.8e38a8ad01fded76.css"],"grouping":["0970f2edbcb32de8","canopyx-grouping.84660cd1a0ea5f8f.js","canopyx-grouping.6c93259b4b89e523.css"],"highlight":["f39423961654c1e2","canopyx-highlight.a6f93893d36ab343.js","canopyx-highlight.5b6d2e0d979f9f9b.css"],"layouts":["fa8c686f21626884","canopyx-layouts.e7250c82a6abf52e.js","canopyx-layouts.0b75738924533ee8.css"],"facets":["f9219d35a87635f8","canopyx-facets.8bf477b7915bdcd6.js","canopyx-facets.648a88d2f36d4999.css"],"live":["a27c818b731c22e3","canopyx-live.d19df9edc92e7d09.js","canopyx-live.cc4b94a2dec6f064.css"],"record-panel":["bcc06276c7933af6","canopyx-record-panel.a374fb4254f9115c.js","canopyx-record-panel.f3691d54cefaab04.css"],"calendar":["762f4b4b62ecb4d8","canopyx-calendar.dc45ebdeb704d9c3.js","canopyx-calendar.1099197a45632f63.css"],"calendar-recurrence":["64bcc24d785bd1af","canopyx-calendar-recurrence.4b7f430c2a64de23.js"],"calendar-edit":["bdde26d4b7de2096","canopyx-calendar-edit.4ab2cf55e84f310f.js"],"calendar-ics":["feb3ed2cd67d057f","canopyx-calendar-ics.2a3af9eb3bf444fc.js"],"timeline":["3f04ba99401e9ebd","canopyx-timeline.76928f089774b3de.js","canopyx-timeline.0393f92746f8fa09.css"]},PART_CODE=new Map(),partName=name=>typeof name==="string"&&/^[a-z][a-z\d-]{0,31}$/.test(name);
509
+ const PART_URL=typeof document!=="undefined"&&document.currentScript?.src;const PARTS={"tree-tools":["4c6c4101089c4444","canopyx-tree-tools.510207e8f47014fd.js","canopyx-tree-tools.ef7a3fb9c9115a82.css"],"filter-editor":["8a058a87bbba3cbe","canopyx-filter-editor.15e355b04c7138cc.js","canopyx-filter-editor.01b7bb6820dc9edb.css"],"export":["8b99c0b7b0ec37c5","canopyx-export.6989ea36f5d6da7b.js"],"history":["7d9901d2fe04df72","canopyx-history.9432e724bf14c9ed.js"],"pager":["8f804f70b96409f2","canopyx-pager.ac543250cf2f6e8b.js","canopyx-pager.08ad67c69026852e.css"],"enhance":["31162b1ffa622bf8","canopyx-enhance.65868addbc5c4a1a.js"],"data":["f07aebe37f3d1bb4","canopyx-data.401d09bc34c61d2f.js"],"forms":["a8c60f205d9babca","canopyx-forms.16987de1cdf9bda9.js","canopyx-forms.29058ce3c88af96e.css"],"flow":["842e1f1956f6213f","canopyx-flow.efc6250968e8b10a.js","canopyx-flow.3152e616c5557452.css"],"flow-host":["de4f9b11ab881eca","canopyx-flow-host.aea5ed35816df2af.js","canopyx-flow-host.1301fcf3454a723a.css"],"flow-checks":["ec2451183901867b","canopyx-flow-checks.29c4586a80f04b6a.js","canopyx-flow-checks.4502d6f7f7487b73.css"],"flow-options":["60450342239ded26","canopyx-flow-options.d31458a54aa4b1e7.js"],"flow-pick":["426718e2fa1f3cd0","canopyx-flow-pick.379205e09325bca3.js","canopyx-flow-pick.771b06ac50c141a1.css"],"flow-records":["f3c09661bdffb8d0","canopyx-flow-records.fddf130ace0764b1.js","canopyx-flow-records.e362111fb5f85732.css"],"flow-drafts":["4f112f64736ca8e3","canopyx-flow-drafts.09b88e275c7bc3b9.js","canopyx-flow-drafts.2dd86fb31fc10458.css"],"flow-preview":["f4fb5c42743873c0","canopyx-flow-preview.5778d5fce9ae0d78.js","canopyx-flow-preview.7928e22901da2088.css"],"flow-command":["39472118ca80ef02","canopyx-flow-command.78f4f931c52b1bfc.js","canopyx-flow-command.0c1247a371b7e775.css"],"flow-tasks":["147dd31638a9cb94","canopyx-flow-tasks.f012a521560f7075.js","canopyx-flow-tasks.524df90f908f81dd.css"],"flow-dialog":["ebb432cd6e34aba6","canopyx-flow-dialog.b00addd6a176198e.js","canopyx-flow-dialog.e6192e827eca25d2.css"],"flow-links":["2c14b19c9236e90d","canopyx-flow-links.7b6cfe42d28b6067.js"],"flow-endpoint":["f5cddfc9545e0920","canopyx-flow-endpoint.b31146202bd76aac.js"],"flow-consent":["22121778bc0d07c5","canopyx-flow-consent.749b97f13039bc71.js","canopyx-flow-consent.9f6580d3673ae8bf.css"],"matrix":["16ab7faa98c96b46","canopyx-matrix.4d417eaeba54ca4f.js","canopyx-matrix.9b3f57c1d0f090b6.css"],"changes":["2db8e138da4b5658","canopyx-changes.053104f8f56b0a87.js","canopyx-changes.dfbdb3d6c468ccb1.css"],"editing":["c82648dfaa7e5ee5","canopyx-editing.4fe83feec389c2ce.js","canopyx-editing.67c43d3b80f20232.css"],"column-groups":["b42238fd108a8614","canopyx-column-groups.d8ad6322f769b915.js","canopyx-column-groups.37e177d74cebb516.css"],"cell-range":["e99a39e7cccc57b1","canopyx-cell-range.647da96e57924f3e.js","canopyx-cell-range.23f3222652588441.css"],"links":["f1ab7e32b501ce37","canopyx-links.d96addeefb3eccf5.js"],"search":["521eebd6e1bbbd35","canopyx-search.14c69f4551c69f23.js"],"formatting":["1a28068597f435de","canopyx-formatting.68c501f97b71f1ec.js","canopyx-formatting.a59507e4e8334891.css"],"format-editor":["24f176a39c02944a","canopyx-format-editor.3b1dec2527e53988.js","canopyx-format-editor.1ba153c82aa87b04.css"],"link":["fa0c9e888463553b","canopyx-link.4ec39bc39fa6cf50.js","canopyx-link.8435c892613926ac.css"],"detail-rows":["4908d1d2b7f67aa8","canopyx-detail-rows.fe29b1759c962565.js","canopyx-detail-rows.8e38a8ad01fded76.css"],"grouping":["0970f2edbcb32de8","canopyx-grouping.84660cd1a0ea5f8f.js","canopyx-grouping.6c93259b4b89e523.css"],"highlight":["f39423961654c1e2","canopyx-highlight.a6f93893d36ab343.js","canopyx-highlight.5b6d2e0d979f9f9b.css"],"layouts":["fa8c686f21626884","canopyx-layouts.e7250c82a6abf52e.js","canopyx-layouts.0b75738924533ee8.css"],"facets":["f9219d35a87635f8","canopyx-facets.8bf477b7915bdcd6.js","canopyx-facets.648a88d2f36d4999.css"],"live":["a27c818b731c22e3","canopyx-live.d19df9edc92e7d09.js","canopyx-live.cc4b94a2dec6f064.css"],"record-panel":["bcc06276c7933af6","canopyx-record-panel.a374fb4254f9115c.js","canopyx-record-panel.f3691d54cefaab04.css"],"calendar":["762f4b4b62ecb4d8","canopyx-calendar.dc45ebdeb704d9c3.js","canopyx-calendar.1099197a45632f63.css"],"calendar-recurrence":["64bcc24d785bd1af","canopyx-calendar-recurrence.4b7f430c2a64de23.js"],"calendar-edit":["bdde26d4b7de2096","canopyx-calendar-edit.4ab2cf55e84f310f.js"],"calendar-ics":["feb3ed2cd67d057f","canopyx-calendar-ics.2a3af9eb3bf444fc.js"],"timeline":["3f04ba99401e9ebd","canopyx-timeline.76928f089774b3de.js","canopyx-timeline.0393f92746f8fa09.css"]},PART_CODE=new Map(),partName=name=>typeof name==="string"&&/^[a-z][a-z\d-]{0,31}$/.test(name);
510
510
  function registerPart(name,part){if(!partName(name)||!part||typeof part.version!=="string"||typeof part.install!=="function"||
511
511
  part.requires!==undefined&&!(Array.isArray(part.requires)&&part.requires.every(partName)))throw new Error("CanopyX: invalid part.");PART_CODE.set(name,part)}
512
512
  function definePart(View,name,label,context){const parts=View.prototype.parts||={};return parts[name]={name,label,context,parts}}
@@ -973,9 +973,14 @@ return applied},initializeState(){if(this.initialState?.facets)this.facetOpen=th
973
973
  !!row&&Object.values(row.cells).some(cell=>isCellObject(cell)&&RECORD_STRUCTURE.some(name=>cell[name]!==undefined))};const GRID_RECORD_INIT=DataGrid.prototype.initializeState;DataGrid.prototype.initializeState=function(){const ready=GRID_RECORD_INIT.call(this);return this.columns.some(column=>column.presenter?.kind==="structured")?Promise.all([ready,this.loadRecordPanel()]).then(([result])=>result):ready};const GRID_DATA_IMPL={},GRID_DATA=definePart(DataGrid,"data","row data",()=>({GRID_DATA_IMPL,PROTOCOL,validateGridRows,inlineGridSource,loadSidecar,gridIdOrder}));function rowDataSource(grid){let inner=null;const source={reset:()=>{inner=null},query:(query,options)=>
974
974
  (inner||=loadPart(GRID_DATA).then(()=>GRID_DATA_IMPL.source(grid,source)).catch(error=>{inner=null;throw error})).then(rows=>rows.query(query,options))};return source}
975
975
  class MatrixHost{}
976
- const MATRIX_IMPL={},activeMatrices=new WeakMap();const MATRIX_PART=definePart(MatrixHost,"matrix","matrix view",()=>({MATRIX_IMPL,h,setText,fill,foldText,numberFormat,gridFreeze,toneOf,applyTone,toneColors,AxisWindow,PROTOCOL}));const MATRIX_METHODS=["sort","group","filter","setSearch","setAttentionOnly","getState","setState","getVisible","focusCell","update"];function createMatrix(host,options){if(!host||!options||!options.view||typeof options.view!=="object")throw new Error("CanopyX: createMatrix needs a host element and a matrix document.");const existing=activeMatrices.get(host);if(existing)return existing;const addedClasses=["cx-host","cx-matrix-host"].filter(name=>!host.classList.contains(name)),hadClass=host.hasAttribute("class");const previousError=host.getAttribute("data-cx-error");let impl=null,destroyed=false,notice=null,errorText=null;const cancelled=()=>new DOMException("Matrix was destroyed.","AbortError");const ready=loadPart(MATRIX_PART).then(()=>{if(destroyed)throw cancelled();impl=MATRIX_IMPL.create(host,options);return impl.ready});const api={element:host};Object.defineProperty(api,"view",{enumerable:true,get:()=>impl?impl.document():options.view});for(const name of MATRIX_METHODS)api[name]=(...args)=>{const invoke=()=>{if(destroyed)throw cancelled();return impl[name](...args)};if(destroyed)throw cancelled();return impl?invoke():ready.then(invoke)};api.ready=ready.then(()=>{if(destroyed)throw cancelled();return api});api.ready.catch(()=>{});api.destroy=()=>{if(destroyed)return;destroyed=true;impl?.destroy();if(activeMatrices.get(host)===api)activeMatrices.delete(host);if(host.canopyx===api)delete host.canopyx;host.removeAttribute("data-canopyx-mounted");notice?.remove();if(errorText!==null&&host.getAttribute("data-cx-error")===errorText){if(previousError===null)host.removeAttribute("data-cx-error");else host.setAttribute("data-cx-error",previousError)}
976
+ const MATRIX_IMPL={},activeMatrices=new WeakMap();const MATRIX_PART=definePart(MatrixHost,"matrix","matrix view",()=>({MATRIX_IMPL,h,setText,fill,plural,foldText,numberFormat,gridFreeze,toneOf,applyTone,toneColors,AxisWindow,PROTOCOL}));const MATRIX_METHODS=["sort","group","filter","setSearch","setAttentionOnly","getState","setState","getVisible","focusCell","update"];function createMatrix(host,options){if(!host||!options||!options.view||typeof options.view!=="object")throw new Error("CanopyX: createMatrix needs a host element and a matrix document.");const existing=activeMatrices.get(host);if(existing)return existing;const addedClasses=["cx-host","cx-matrix-host"].filter(name=>!host.classList.contains(name)),hadClass=host.hasAttribute("class");const previousError=host.getAttribute("data-cx-error");let impl=null,destroyed=false,notice=null,errorText=null;const cancelled=()=>new DOMException("Matrix was destroyed.","AbortError");const ready=loadPart(MATRIX_PART).then(()=>{if(destroyed)throw cancelled();impl=MATRIX_IMPL.create(host,options);return impl.ready});const api={element:host};Object.defineProperty(api,"view",{enumerable:true,get:()=>impl?impl.document():options.view});for(const name of MATRIX_METHODS)api[name]=(...args)=>{const invoke=()=>{if(destroyed)throw cancelled();return impl[name](...args)};if(destroyed)throw cancelled();return impl?invoke():ready.then(invoke)};api.ready=ready.then(()=>{if(destroyed)throw cancelled();return api});api.ready.catch(()=>{});api.destroy=()=>{if(destroyed)return;destroyed=true;impl?.destroy();if(activeMatrices.get(host)===api)activeMatrices.delete(host);if(host.canopyx===api)delete host.canopyx;host.removeAttribute("data-canopyx-mounted");notice?.remove();if(errorText!==null&&host.getAttribute("data-cx-error")===errorText){if(previousError===null)host.removeAttribute("data-cx-error");else host.setAttribute("data-cx-error",previousError)}
977
977
  for(const name of addedClasses)host.classList.remove(name);if(!hadClass&&!host.classList.length)host.removeAttribute("class")};ready.catch(error=>{if(destroyed)return;notice=h("div","cx-mount-error");notice.setAttribute("role","alert");setText(notice,error&&error.message?error.message:String(error));host.appendChild(notice);errorText=notice.textContent;host.setAttribute("data-cx-error",errorText);host.dispatchEvent(new CustomEvent("canopyx:error",{detail:{error},bubbles:true}))});Object.freeze(api);activeMatrices.set(host,api);host.classList.add("cx-host","cx-matrix-host");host.setAttribute("data-canopyx-mounted","true");host.canopyx=api;return api}
978
- const MATRIX_API={createMatrix};DEFAULT_LABELS=Object.freeze(Object.assign({},DEFAULT_LABELS,{gridCellRangeSelected:"{rows} rows × {columns} columns selected",gridCellRangesSelected:"{ranges} ranges selected, {cells} cells",gridCellRangeCleared:"Cell selection cleared",gridCopyCells:"Copy cells",gridCopyCellsWithHeaders:"Copy cells with headers",gridPasteCells:"Paste",gridSelectAllCells:"Select all cells",gridCellsCopied:"Copied {rows} rows × {columns} columns",gridCellsPasted:"Pasted {count} cells",gridPasteRefused:"{count} cells were not pasted",gridPasteReadOnly:"This cell cannot be changed.",gridPasteType:"“{value}” is not a valid value for {column}.",gridPasteReport:"Paste report",gridPasteCell:"Row {row}, {column}: {reason}",gridFillHandle:"Drag to fill the cells below",gridCellsFilled:"Filled {count} cells",gridCellMenu:"Cell actions",gridClearCellRanges:"Clear cell selection"}));const GRID_PART_CONTEXT=()=>({DataGrid,h,setText,fill,columnWidth,foldText,gridButton,gridIcon,gridDecorateButton,cellText,cellValue,isCellObject,numberFormat,gridFreeze,gridMomentFromParts,requireIdentifier,loadGridFilterEditor,CELL_CONTROLS,reachable,gridExportField});const GRID_COLUMN_GROUPS=definePart(DataGrid,"column-groups","column groups",GRID_PART_CONTEXT);const GRID_CELL_RANGE=definePart(DataGrid,"cell-range","cell ranges",GRID_PART_CONTEXT);const GRID_PART_API=[["column-groups","getColumnGroups","setColumnGroupVisible"],["cell-range","getCellRanges","setCellRanges","clearCellRanges","selectAllCells","copyCellRanges","getCellRangeText","pasteCells"]];const gridPartLoader=(part,start)=>function(){return(this.partLoads||={})[start]||=loadPart(part).then(()=>this.destroyed||this["start"+start]()).catch(error=>{this.partLoads[start]=null;throw error})};DataGrid.prototype.loadColumnGroups=gridPartLoader(GRID_COLUMN_GROUPS,"ColumnGroups");DataGrid.prototype.loadCellRanges=gridPartLoader(GRID_CELL_RANGE,"CellRanges");for(const[part,read,...actions]of GRID_PART_API){const load=part==="cell-range"?"loadCellRanges":"loadColumnGroups";DataGrid.prototype[read]=()=>[];for(const name of actions){const stub=DataGrid.prototype[name]=function(...args){return this[load]().then(()=>{if(DataGrid.prototype[name]===stub)throw new Error("CanopyX: the "+part+" part did not install "+name+".");return this[name](...args)})}}}
978
+ const MATRIX_API={createMatrix};class ChangesHost{}
979
+ const CHANGES_IMPL={},activeChangeViews=new WeakMap();const CHANGES_PART=definePart(ChangesHost,"changes","change views",()=>({CHANGES_IMPL,h,setText,fill,plural,numberFormat,gridFreeze,toneOf,applyTone,svgIcon,PROTOCOL}));const CHANGE_VIEW_METHODS={diff:["getLayout","setLayout","next","previous","expandAll","copy","getHunks","update"],changes:["getState","setState","toggle","focusItem","update"],propagation:["update"]};function changeViewFactory(kind,name){return function(host,options){if(!host||!options||!options.view||typeof options.view!=="object")throw new Error("CanopyX: "+name+" needs a host element and a "+kind+" document.");const existing=activeChangeViews.get(host);if(existing)return existing;const addedClasses=["cx-host","cx-change-host"].filter(className=>!host.classList.contains(className)),hadClass=host.hasAttribute("class"),previousError=host.getAttribute("data-cx-error");let impl=null,destroyed=false,notice=null,errorText=null,failure=null,loaded=false,currentView=options.view;const cancelled=()=>new DOMException("Change view was destroyed.","AbortError");const clearError=()=>{notice?.remove();notice=null;if(errorText!==null&&host.getAttribute("data-cx-error")===errorText){if(previousError===null)host.removeAttribute("data-cx-error");else host.setAttribute("data-cx-error",previousError)}
980
+ errorText=null};const showError=error=>{if(destroyed)return;impl?.destroy();impl=null;failure=error;clearError();notice=h("div","cx-mount-error");notice.setAttribute("role","alert");setText(notice,error&&error.message?error.message:String(error));host.appendChild(notice);errorText=notice.textContent;host.setAttribute("data-cx-error",errorText);host.dispatchEvent(new CustomEvent("canopyx:error",{detail:{error},bubbles:true}))};const mount=view=>{clearError();impl=CHANGES_IMPL[kind](host,{...options,view});failure=null;return impl.ready};const ready=loadPart(CHANGES_PART).then(()=>{if(destroyed)throw cancelled();loaded=true;return mount(currentView)});const api={element:host};Object.defineProperty(api,"view",{enumerable:true,get:()=>impl?impl.document():currentView});for(const method of CHANGE_VIEW_METHODS[kind])api[method]=(...args)=>{const invoke=()=>{if(destroyed)throw cancelled();if(method==="update"){currentView=args[0];try{return impl?impl.update(...args):mount(currentView)}
981
+ catch(error){showError(error);throw error}}
982
+ if(failure)throw failure;return impl[method](...args)};if(destroyed)throw cancelled();return loaded?invoke():ready.then(invoke)};api.ready=ready.then(()=>{if(destroyed)throw cancelled();return api});api.ready.catch(()=>{});api.destroy=()=>{if(destroyed)return;destroyed=true;impl?.destroy();if(activeChangeViews.get(host)===api)activeChangeViews.delete(host);if(host.canopyx===api)delete host.canopyx;host.removeAttribute("data-canopyx-mounted");clearError();for(const className of addedClasses)host.classList.remove(className);if(!hadClass&&!host.classList.length)host.removeAttribute("class")};ready.catch(showError);Object.freeze(api);activeChangeViews.set(host,api);host.classList.add("cx-host","cx-change-host");host.setAttribute("data-canopyx-mounted","true");host.canopyx=api;return api}}
983
+ const CHANGES_API={createDiff:changeViewFactory("diff","createDiff"),createChangeList:changeViewFactory("changes","createChangeList"),createPropagation:changeViewFactory("propagation","createPropagation")};const CHANGE_VIEW_TYPES={diff:CHANGES_API.createDiff,changes:CHANGES_API.createChangeList,propagation:CHANGES_API.createPropagation};DEFAULT_LABELS=Object.freeze(Object.assign({},DEFAULT_LABELS,{gridCellRangeSelected:"{rows} rows × {columns} columns selected",gridCellRangesSelected:"{ranges} ranges selected, {cells} cells",gridCellRangeCleared:"Cell selection cleared",gridCopyCells:"Copy cells",gridCopyCellsWithHeaders:"Copy cells with headers",gridPasteCells:"Paste",gridSelectAllCells:"Select all cells",gridCellsCopied:"Copied {rows} rows × {columns} columns",gridCellsPasted:"Pasted {count} cells",gridPasteRefused:"{count} cells were not pasted",gridPasteReadOnly:"This cell cannot be changed.",gridPasteType:"“{value}” is not a valid value for {column}.",gridPasteReport:"Paste report",gridPasteCell:"Row {row}, {column}: {reason}",gridFillHandle:"Drag to fill the cells below",gridCellsFilled:"Filled {count} cells",gridCellMenu:"Cell actions",gridClearCellRanges:"Clear cell selection"}));const GRID_PART_CONTEXT=()=>({DataGrid,h,setText,fill,columnWidth,foldText,gridButton,gridIcon,gridDecorateButton,cellText,cellValue,isCellObject,numberFormat,gridFreeze,gridMomentFromParts,requireIdentifier,loadGridFilterEditor,CELL_CONTROLS,reachable,gridExportField});const GRID_COLUMN_GROUPS=definePart(DataGrid,"column-groups","column groups",GRID_PART_CONTEXT);const GRID_CELL_RANGE=definePart(DataGrid,"cell-range","cell ranges",GRID_PART_CONTEXT);const GRID_PART_API=[["column-groups","getColumnGroups","setColumnGroupVisible"],["cell-range","getCellRanges","setCellRanges","clearCellRanges","selectAllCells","copyCellRanges","getCellRangeText","pasteCells"]];const gridPartLoader=(part,start)=>function(){return(this.partLoads||={})[start]||=loadPart(part).then(()=>this.destroyed||this["start"+start]()).catch(error=>{this.partLoads[start]=null;throw error})};DataGrid.prototype.loadColumnGroups=gridPartLoader(GRID_COLUMN_GROUPS,"ColumnGroups");DataGrid.prototype.loadCellRanges=gridPartLoader(GRID_CELL_RANGE,"CellRanges");for(const[part,read,...actions]of GRID_PART_API){const load=part==="cell-range"?"loadCellRanges":"loadColumnGroups";DataGrid.prototype[read]=()=>[];for(const name of actions){const stub=DataGrid.prototype[name]=function(...args){return this[load]().then(()=>{if(DataGrid.prototype[name]===stub)throw new Error("CanopyX: the "+part+" part did not install "+name+".");return this[name](...args)})}}}
979
984
  const GRID_PARTS_INIT=DataGrid.prototype.initializeState;DataGrid.prototype.initializeState=function(){const ready=GRID_PARTS_INIT.call(this),range=this.opts.rangeSelection??this.view.rangeSelection;const parts=[this.view.columnGroups?.length&&this.loadColumnGroups(),range&&range.enabled!==false&&this.loadCellRanges()].filter(Boolean);return parts.length?Promise.all([ready,...parts.map(part=>part.catch(error=>this.emit("error",{error})))]).then(([result])=>result):ready};function gridButton(text,run){const button=h("button","cx-button");button.type="button";setText(button,text);button.onclick=event=>{button.focus({preventScroll:true});run(event)};return button}
980
985
  let keyboardInput=false;if(typeof document!=="undefined"){document.addEventListener("keydown",()=>{keyboardInput=true},true);document.addEventListener("pointerdown",()=>{keyboardInput=false},true)}
981
986
  const focusDrawerTitle=title=>{if(keyboardInput&&title===document.activeElement&&!title.matches(":focus-visible"))title.blur();title.toggleAttribute("data-cx-pointer-focus",!keyboardInput);if(!keyboardInput)title.addEventListener("blur",()=>title.removeAttribute("data-cx-pointer-focus"),{once:true});title.focus({preventScroll:true,focusVisible:keyboardInput})};function gridInputLabel(text,input){const label=h("label","cx-grid-field");label.appendChild(document.createTextNode(text));label.appendChild(input);return label}
@@ -1184,5 +1189,5 @@ watcher.observe(host)}
1184
1189
  function mount(root,automatic){const scope=root||document;const created=[];const showError=(host,err,api,view)=>{if(api){api.destroy();if(host.canopyx===api)delete host.canopyx}
1185
1190
  const message=err&&err.message?err.message:String(err);let labels=DEFAULT_LABELS;for(const attempt of[()=>labelsFor(view&&view.labels,view&&configured.has(view.id)?configured.get(view.id).labels:null),()=>labelsFor(view&&view.labels)]){try{labels=attempt();break}catch(e){labels=DEFAULT_LABELS}}
1186
1191
  const notice=document.createElement("div");notice.className="cx-mount-error";notice.setAttribute("role","alert");setText(notice,fill(labels.mountError,{error:message}));host.classList.add("cx-host");host.setAttribute("data-canopyx-mounted","true");host.setAttribute("data-cx-error",message);host.appendChild(notice);if(global.console)global.console.error(err)};const selector="[data-canopyx-host]:not([data-canopyx-mounted])";(scope.matches?.(selector)?[scope]:scope.querySelectorAll(selector)).forEach((host)=>{const config=host.querySelector("script.cx-config"),mode=host.getAttribute("data-cx-mount");if(!config||(automatic===true&&mode==="manual")){return}
1187
- if(mode==="visible"&&host!==scope&&global.IntersectionObserver)return mountWhenVisible(host);watcher?.unobserve(host);host.setAttribute("data-canopyx-mounted","true");let view=null;try{view=JSON.parse(config.textContent);if(view===Object(view))parsedViews.add(view);const flow=view&&view.kind==="flow"&&typeof FLOW_API==="object",matrix=view&&view.type==="matrix"&&typeof MATRIX_API==="object";const create=flow?FLOW_API.createFlow:matrix?MATRIX_API.createMatrix:view&&view.type==="grid"&&typeof createDataGrid==="function"?createDataGrid:createTreeGrid;const api=create(host,Object.assign({},view&&configured.get(view.id),flow?{flow:view}:{view}));created.push(api);api.ready.catch((err)=>{if(view.type!=="grid"&&!matrix&&activeGrids.get(host)===api&&host.canopyx===api)showError(host,err,api,view)})}catch(err){showError(host,err,null,view)}});return created}
1188
- global.CanopyX=Object.freeze(Object.assign({protocol:PROTOCOL,createTreeGrid,configure,unconfigure,mount:(root)=>mount(root,false),sources:Object.freeze({inline:inlineSource,sidecar:sidecarSource,remote:remoteSource}),badge:hostBadge,labels:DEFAULT_LABELS,registerExportFormat,use,registerPart,setNonce},typeof GRID_API==="object"?GRID_API:{},typeof FLOW_API==="object"?FLOW_API:{},typeof MATRIX_API==="object"?MATRIX_API:{}));if(typeof document!=="undefined"){const navigation=global.performance&&performance.getEntriesByType?performance.getEntriesByType("navigation")[0]:null;if(document.readyState==="loading"||(document.readyState==="interactive"&&navigation&&navigation.domContentLoadedEventStart===0)){document.addEventListener("DOMContentLoaded",()=>mount(document,true))}else{mount(document,true)}}})(runtimeHost);const CanopyX=runtimeHost.CanopyX;const{protocol,createTreeGrid,createDataGrid,createCalendar,createFlow,createMatrix,enhance,grid,configure,unconfigure,mount,sources,badge,labels,registerExportFormat,use,registerPart,setNonce}=CanopyX;export{protocol,createTreeGrid,createDataGrid,createCalendar,createFlow,createMatrix,enhance,grid,configure,unconfigure,mount,sources,badge,labels,registerExportFormat,use,registerPart,setNonce};export default CanopyX;
1192
+ if(mode==="visible"&&host!==scope&&global.IntersectionObserver)return mountWhenVisible(host);watcher?.unobserve(host);host.setAttribute("data-canopyx-mounted","true");let view=null;try{view=JSON.parse(config.textContent);if(view===Object(view))parsedViews.add(view);const flow=view&&view.kind==="flow"&&typeof FLOW_API==="object",matrix=view&&view.type==="matrix"&&typeof MATRIX_API==="object";const change=view&&typeof CHANGE_VIEW_TYPES==="object"&&Object.hasOwn(CHANGE_VIEW_TYPES,view.type)?CHANGE_VIEW_TYPES[view.type]:null;const create=flow?FLOW_API.createFlow:matrix?MATRIX_API.createMatrix:change?change:view&&view.type==="grid"&&typeof createDataGrid==="function"?createDataGrid:createTreeGrid;const api=create(host,Object.assign({},view&&configured.get(view.id),flow?{flow:view}:{view}));created.push(api);api.ready.catch((err)=>{if(view.type!=="grid"&&!matrix&&!change&&activeGrids.get(host)===api&&host.canopyx===api)showError(host,err,api,view)})}catch(err){showError(host,err,null,view)}});return created}
1193
+ global.CanopyX=Object.freeze(Object.assign({protocol:PROTOCOL,createTreeGrid,configure,unconfigure,mount:(root)=>mount(root,false),sources:Object.freeze({inline:inlineSource,sidecar:sidecarSource,remote:remoteSource}),badge:hostBadge,labels:DEFAULT_LABELS,registerExportFormat,use,registerPart,setNonce},typeof GRID_API==="object"?GRID_API:{},typeof FLOW_API==="object"?FLOW_API:{},typeof MATRIX_API==="object"?MATRIX_API:{},typeof CHANGES_API==="object"?CHANGES_API:{}));if(typeof document!=="undefined"){const navigation=global.performance&&performance.getEntriesByType?performance.getEntriesByType("navigation")[0]:null;if(document.readyState==="loading"||(document.readyState==="interactive"&&navigation&&navigation.domContentLoadedEventStart===0)){document.addEventListener("DOMContentLoaded",()=>mount(document,true))}else{mount(document,true)}}})(runtimeHost);const CanopyX=runtimeHost.CanopyX;const{protocol,createTreeGrid,createDataGrid,createCalendar,createFlow,createMatrix,createDiff,createChangeList,createPropagation,enhance,grid,configure,unconfigure,mount,sources,badge,labels,registerExportFormat,use,registerPart,setNonce}=CanopyX;export{protocol,createTreeGrid,createDataGrid,createCalendar,createFlow,createMatrix,createDiff,createChangeList,createPropagation,enhance,grid,configure,unconfigure,mount,sources,badge,labels,registerExportFormat,use,registerPart,setNonce};export default CanopyX;
@@ -596,6 +596,8 @@ body .cx-host.cx-chrome{padding:0;}
596
596
  .cx-data-grid.cx-phone .cx-phone-primary > .cx-content{flex:1;}
597
597
  .cx-data-grid.cx-phone:not([data-cx-layout="scroll"]) .cx-phone-primary :is(.cx-text,.p-mono,.cx-presenter-text){display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;line-clamp:2;white-space:normal;overflow-wrap:anywhere;line-height:1.25;max-height:2.5em;}
598
598
  .cx-data-grid.cx-phone .cx-phone-fields > .cx-hidden-fields{margin-left:auto;flex:none;}
599
+ .cx-host.cx-change-host{min-height:0;display:block;}
600
+ .cx-host:where(.cx-change-host:not([data-cx-height])){height:auto;}
599
601
  .cx-host.cx-flow-host{min-height:0;display:block;background:transparent;}
600
602
  .cx-host:where(.cx-flow-host:not([data-cx-height])){height:auto;}
601
603
  body.cx-page > .cx-host.cx-flow-host:not([data-cx-height]),body.cx-page > .cx-main > .cx-host.cx-flow-host:not([data-cx-height]){height:auto;padding:0;}