@evotecit/canopyx 0.3.6 → 0.3.8

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.
Files changed (35) hide show
  1. package/CanopyX.Html/Assets/canopyx-calendar.js +4 -3
  2. package/CanopyX.Html/Assets/canopyx-changes.css +240 -0
  3. package/CanopyX.Html/Assets/canopyx-changes.js +224 -0
  4. package/CanopyX.Html/Assets/canopyx-detail-rows.js +2 -2
  5. package/CanopyX.Html/Assets/canopyx-filter-editor.js +2 -1
  6. package/CanopyX.Html/Assets/canopyx-formatting.js +7 -6
  7. package/CanopyX.Html/Assets/canopyx-grid-module.mjs +71 -44
  8. package/CanopyX.Html/Assets/canopyx-grid.css +24 -0
  9. package/CanopyX.Html/Assets/canopyx-grid.js +71 -44
  10. package/CanopyX.Html/Assets/canopyx-interactive.js +6 -6
  11. package/CanopyX.Html/Assets/canopyx-labels-de.js +76 -2
  12. package/CanopyX.Html/Assets/canopyx-labels-es.js +76 -2
  13. package/CanopyX.Html/Assets/canopyx-labels-fr.js +76 -2
  14. package/CanopyX.Html/Assets/canopyx-labels-it.js +76 -2
  15. package/CanopyX.Html/Assets/canopyx-labels-pl.js +76 -2
  16. package/CanopyX.Html/Assets/canopyx-live.js +4 -3
  17. package/CanopyX.Html/Assets/canopyx-matrix.css +16 -3
  18. package/CanopyX.Html/Assets/canopyx-matrix.js +22 -14
  19. package/CanopyX.Html/Assets/canopyx-module.mjs +6 -6
  20. package/CanopyX.Html/Assets/canopyx-reporting-module.mjs +73 -46
  21. package/CanopyX.Html/Assets/canopyx-reporting.css +24 -0
  22. package/CanopyX.Html/Assets/canopyx-reporting.js +73 -46
  23. package/CanopyX.Html/Assets/canopyx-tree-tools.js +7 -6
  24. package/CanopyX.Html/Assets/canopyx.js +6 -6
  25. package/JavaScript/changes.d.ts +252 -0
  26. package/JavaScript/grid.d.ts +15 -5
  27. package/JavaScript/locales/de.mjs +76 -2
  28. package/JavaScript/locales/es.mjs +76 -2
  29. package/JavaScript/locales/fr.mjs +76 -2
  30. package/JavaScript/locales/it.mjs +76 -2
  31. package/JavaScript/locales/pl.mjs +76 -2
  32. package/JavaScript/matrix.d.ts +2 -0
  33. package/JavaScript/protocol.d.ts +1 -1
  34. package/README.md +7 -2
  35. package/package.json +5 -1
@@ -1,4 +1,4 @@
1
- (function(global){"use strict";const installed=new WeakSet();const part={version:"1e7e68d65a0c7aeb",install(context){if(installed.has(context.TreeGrid.prototype))return;const{TreeGrid,h,setText,fill,numberFormat,watchFlyout,columnWidth,prepareWidth,cellText,cellValue,isCellObject,foldText,formatting,sortKey,compareKeys,EXPORT_FORMATS,exportFormat,exportTimeout,exportFormatsOf,INDENT,MAX_PROTOCOL_INTEGER,hostControl,detailCell,HostCellContext,disposeHostSlot,loadPart,definePart,pluginItems,applyTone,toneOf,badgeElement,RECORD_IMPL,svgIcon,gridExportField}=context;const EXPORT_IMPL={formatsOf:exportFormatsOf};const GRID_ICONS={search:'<circle cx="11" cy="11" r="7"/><path d="m20 20-3.5-3.5"/>',filter:'<path d="M4 5h16l-6 7.5V19l-4-2v-4.5z"/>',columns:'<rect x="3.5" y="4.5" width="17" height="15" rx="2"/><path d="M9.5 4.5v15M14.5 4.5v15"/>',views:'<path d="M6 4h12v16l-6-4-6 4z"/>',export:'<path d="M12 4v11M7.5 10.5 12 15l4.5-4.5M5 19h14"/>',more:'<circle cx="5" cy="12" r="1.2"/><circle cx="12" cy="12" r="1.2"/><circle cx="19" cy="12" r="1.2"/>',close:'<path d="M6 6l12 12M18 6 6 18"/>',check:'<path d="m5 12.5 4.5 4.5L19 7.5"/>',chart:'<path d="M4 19h16M7 16V10M12 16V6M17 16v-4"/>',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"/>',up:'<path d="M12 19V5M6 11l6-6 6 6"/>',down:'<path d="M12 5v14M6 13l6 6 6-6"/>',retry:'<path d="M4 12a8 8 0 1 0 2.4-5.7M4 4v4.5h4.5"/>',previous:'<path d="m14 7-5 5 5 5"/>',next:'<path d="m10 7 5 5-5 5"/>',first:'<path d="m13 7-5 5 5 5M17 7l-5 5 5 5"/>',last:'<path d="m11 7 5 5-5 5M7 7l5 5-5 5"/>'};function gridIcon(name,shapes){return svgIcon(shapes||GRID_ICONS[name]||GRID_ICONS.more)}
1
+ (function(global){"use strict";const installed=new WeakSet();const part={version:"4c6c4101089c4444",install(context){if(installed.has(context.TreeGrid.prototype))return;const{TreeGrid,h,setText,fill,numberFormat,watchFlyout,columnWidth,prepareWidth,cellText,cellValue,isCellObject,foldText,formatting,sortKey,compareKeys,EXPORT_FORMATS,exportFormat,exportTimeout,exportFormatsOf,INDENT,MAX_PROTOCOL_INTEGER,hostControl,detailCell,HostCellContext,disposeHostSlot,loadPart,definePart,pluginItems,applyTone,toneOf,badgeElement,RECORD_IMPL,svgIcon,gridExportField}=context;const EXPORT_IMPL={formatsOf:exportFormatsOf};const GRID_ICONS={search:'<circle cx="11" cy="11" r="7"/><path d="m20 20-3.5-3.5"/>',filter:'<path d="M4 5h16l-6 7.5V19l-4-2v-4.5z"/>',columns:'<rect x="3.5" y="4.5" width="17" height="15" rx="2"/><path d="M9.5 4.5v15M14.5 4.5v15"/>',views:'<path d="M6 4h12v16l-6-4-6 4z"/>',export:'<path d="M12 4v11M7.5 10.5 12 15l4.5-4.5M5 19h14"/>',more:'<circle cx="5" cy="12" r="1.2"/><circle cx="12" cy="12" r="1.2"/><circle cx="19" cy="12" r="1.2"/>',close:'<path d="M6 6l12 12M18 6 6 18"/>',check:'<path d="m5 12.5 4.5 4.5L19 7.5"/>',chart:'<path d="M4 19h16M7 16V10M12 16V6M17 16v-4"/>',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"/>',up:'<path d="M12 19V5M6 11l6-6 6 6"/>',down:'<path d="M12 5v14M6 13l6 6 6-6"/>',retry:'<path d="M4 12a8 8 0 1 0 2.4-5.7M4 4v4.5h4.5"/>',previous:'<path d="m14 7-5 5 5 5"/>',next:'<path d="m10 7 5 5-5 5"/>',first:'<path d="m13 7-5 5 5 5M17 7l-5 5 5 5"/>',last:'<path d="m11 7 5 5-5 5M7 7l5 5-5 5"/>'};function gridIcon(name,shapes){return svgIcon(shapes||GRID_ICONS[name]||GRID_ICONS.more)}
2
2
  function gridDecorateButton(button,icon,ghost){button.classList.add("cx-btn");if(ghost)button.classList.add("ghost");if(icon)button.prepend(gridIcon(icon));return button}
3
3
  function gridFilterCount(query){const count=group=>group?group.filters.length+group.groups.reduce((sum,child)=>sum+count(child),0):0;return(query?.filters?.length||0)+ count(query?.filterGroup)}
4
4
  function gridPriority(value){return["essential","normal","optional"].includes(value)||Number.isInteger(value)&&value>=0&&value<=MAX_PROTOCOL_INTEGER}
@@ -8,7 +8,8 @@ function gridOptions(column){return column.presenter||{}}
8
8
  const COLUMN_DIGITS=new Map(),COLUMN_NUMERIC_CHARS="0123456789 .,:;+-−/()[]\u00a0\u202f",COLUMN_HAS_NUMERIC=/[0-9\s.,:;+\-−/()\[\]]/;
9
9
  function gridNumericVariant(grid){let sample=grid.querySelector(".cx-cell"),owned=!sample;if(owned){sample=document.createElement("span");sample.className="cx-cell";sample.style.display="none";grid.appendChild(sample)}
10
10
  const variant=getComputedStyle(sample).fontVariantNumeric;if(owned)sample.remove();return variant}
11
- function gridClassFont(grid,className){const probe=document.createElement("span");probe.className=className;probe.style.display="none";grid.appendChild(probe);const style=getComputedStyle(probe),font=style.fontWeight+" "+style.fontSize+" "+style.fontFamily;probe.remove();return font}
11
+ function gridClassFont(grid,className,chrome){const probe=h("span",className);probe.style.cssText="position:absolute;visibility:hidden";grid.appendChild(probe);const style=getComputedStyle(probe),font=[style.fontWeight,style.fontSize,style.fontFamily].join(" ");if(chrome){chrome.extra=probe.getBoundingClientRect().width;chrome.spacing=parseFloat(style.letterSpacing)||0}
12
+ probe.remove();return font}
12
13
  function gridMeasureText(context,value,variant="normal"){const text=String(value).slice(0,512),measured=context.measureText(text).width;if(!variant.includes("tabular-nums")||!COLUMN_HAS_NUMERIC.test(text))return measured;const font=context.font;let variants=COLUMN_DIGITS.get(font);if(!variants){if(COLUMN_DIGITS.size===32)COLUMN_DIGITS.delete(COLUMN_DIGITS.keys().next().value);COLUMN_DIGITS.set(font,variants=new Map())}
13
14
  if(!variants.has(variant)){const probe=document.createElement("span"),glyphs=[];probe.style.cssText="all:initial;position:absolute;visibility:hidden;display:flex;white-space:pre;padding:0;border:0;max-width:none;";probe.style.font=font;probe.style.fontVariantNumeric=variant;for(const character of COLUMN_NUMERIC_CHARS)for(const numeric of[true,false]){const glyph=document.createElement("span");glyph.textContent=(numeric?"00"+character+"00":character).repeat(10);glyph.style.cssText="flex:none;white-space:pre;";probe.appendChild(glyph);glyphs.push(glyph)}
14
15
  document.body.appendChild(probe);const zero=glyphs[0].getBoundingClientRect().width / 50, numeric = new Map(), plain = new Map();
@@ -30,10 +31,10 @@ return excess},fitSpare(columns,spare){let target,flex=columns.filter(column=>co
30
31
  flex=flex.filter(column=>column.maxW-column.renderWidth>0.5)}
31
32
  const identity=columns.find(column=>column.identity),widest=columns.filter(column=>!column.identity&&
32
33
  gridTextual(column)&&gridLevel(column)===1).sort((a,b)=>b.need-a.need)[0];grow(identity,420);if(identity&&widest)grow(widest,widest.renderWidth+spare*widest.need / (widest.need + identity.need));
33
- grow(identity,Infinity);grow(widest,Infinity);return target},fitHide(shown,width,phone){let total=0,remaining=shown.length;const order=(a,b)=>gridLevel(b)- gridLevel(a)||gridRank(b)- gridRank(a)||shown.indexOf(b)- shown.indexOf(a),identity=phone&&shown.find(column=>column.identity);const hide=(column,pinned)=>!column.sized&&!column.alwaysVisible&&(column.pin==="none")!==pinned&&!(phone&&column.identity)&&gridLevel(column)>!phone;for(const column of this.columns)column.fitHidden=false;if(this.layoutMode&&this.layoutMode!=="fit")return;for(const column of shown)total +=this.fitSize(column,1);for(const column of shown.filter(column=>hide(column,false)).sort(order))
34
- if(total>width&&remaining>1){column.fitHidden=true;total -=this.fitSize(column,1);remaining--}
35
- if(identity&&!identity.fixed&&!identity.sized)total +=Math.min(identity.need??identity.width,(identity.indentNeed||0)+ 2*(this.fitCh||88))+(identity.reserve||0)- this.fitSize(identity,1);if(identity)for(const column of shown.filter(column=>hide(column,true)).sort(order))
36
- if(total>width){column.fitHidden=true;total -=this.fitSize(column,1)}}};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}
34
+ grow(identity,Infinity);grow(widest,Infinity);return target},fitHide(shown,width,phone,size=column=>this.fitSize(column,1)){let total=0,remaining=shown.length;const order=(a,b)=>gridLevel(b)- gridLevel(a)||gridRank(b)- gridRank(a)||shown.indexOf(b)- shown.indexOf(a),identity=phone&&shown.find(column=>column.identity);const hide=(column,pinned)=>!column.sized&&!column.alwaysVisible&&(column.pin==="none")!==pinned&&!(phone&&column.identity)&&gridLevel(column)>!phone;for(const column of this.columns)column.fitHidden=false;if(this.layoutMode&&this.layoutMode!=="fit")return;for(const column of shown)total +=size(column);for(const column of shown.filter(column=>hide(column,false)).sort(order))
35
+ if(total>width&&remaining>1){column.fitHidden=true;total -=size(column);remaining--}
36
+ if(identity&&!identity.fixed&&!identity.sized)total +=Math.min(identity.need??identity.width,(identity.indentNeed||0)+ 2*(this.fitCh||88))+(identity.reserve||0)- size(identity);if(identity)for(const column of shown.filter(column=>hide(column,true)).sort(order))
37
+ if(total>width){column.fitHidden=true;total -=size(column)}}};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}
37
38
  let keyboardInput=false;if(typeof document!=="undefined"){document.addEventListener("keydown",()=>{keyboardInput=true},true);document.addEventListener("pointerdown",()=>{keyboardInput=false},true)}
38
39
  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}
39
40
  function gridOption(value,text){const option=h("option");option.value=value;setText(option,text);return option}
@@ -60,7 +60,7 @@ function fill(template,values){return template.replace(/\{(\w+)\}/g, (match, nam
60
60
  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];
61
61
  let rules=pluralRules.get(lang);if(!rules){try{rules=new Intl.PluralRules(lang||"en")}catch(_){rules=new Intl.PluralRules("en")}pluralRules.set(lang,rules)}
62
62
  return forms[rules.select(Number(count))]??forms.other??template}
63
- 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",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 + "'.");
63
+ 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 + "'.");
64
64
  if(typeof source[name]!=="string"||!/\S/.test(source[name])) throw new Error("CanopyX: label '" + name + "' must be a string with visible text.");
65
65
  if((name==="utcTime"||name==="localTime")&&source[name].indexOf("{time}")<0)throw new Error("CanopyX: label '"+name+"' must hold {time}.");labels[name]=source[name]})}
66
66
  return Object.freeze(labels)}
@@ -74,7 +74,7 @@ function trusted(kind,value){if(trustPolicy===undefined){try{trustPolicy=global.
74
74
  catch(_){trustPolicy=null}}
75
75
  return trustPolicy?trustPolicy["create"+kind](value):value}
76
76
  function hostHtml(element,html,sanitize,kind){const text=String(html??""),value=sanitize?sanitize(text,{kind}):text;if(value?.nodeType)element.replaceChildren(value);else if(typeof value==="string")element.innerHTML=trusted("HTML",value);else if(global.trustedTypes?.isHTML?.(value))element.innerHTML=value;else throw new Error("CanopyX: invalid sanitizeHtml result.")}
77
- DEFAULT_LABELS=Object.freeze(Object.assign({},DEFAULT_LABELS,{gridViews:"Views",gridViewName:"View name",gridSaveView:"Save view",gridRemoveView:"Remove view",gridResetView:"Reset view",gridChooseView:"Choose a saved view",gridApply:"Apply",gridFindColumn:"Find a column…",gridShowAllColumns:"Show all",gridResetColumns:"Reset columns",gridReorderColumn:"{column}: drag, or press Alt+Up or Alt+Down to move",gridPinnedLeft:"Pinned left",gridPinnedRight:"Pinned right",gridResponsiveHidden:"Hidden at this width",gridExport:"Export",gridVisibleRows:"Visible rows",gridSelectedRows:"Selected rows",gridAllRows:"All rows",gridDownloadCsv:"Download CSV",gridCopy:"Copy",gridExportProgress:"{completed} of {total} records",gridExportReading:"{completed} records",gridExportCancelled:"Export cancelled",gridCancelExport:"Cancel export",gridMore:"More",gridPrint:"Print",gridDetails:"Record details",gridDetailsSection:"Details",gridPreviousRecord:"Previous record",gridNextRecord:"Next record",gridCloseRecord:"Close record details"}));DEFAULT_LABELS=Object.freeze(Object.assign({},DEFAULT_LABELS,{recordBack:"Back",recordBackTo:"Back to {record}",recordTrail:"Record trail",recordShowing:"Showing {record}",recordTabs:"Record views",recordSections:"Jump to a section",recordOtherFields:"Other fields",recordShowEmpty:"Show empty fields ({count})",recordHideEmpty:"Hide empty fields",recordActions:"Record actions",recordCopyValues:"Copy values",recordCopyJson:"Copy as JSON",recordCopyLink:"Copy link",recordCopied:"Copied to the clipboard",recordPrint:"Print record",recordExportCsv:"Export CSV",recordExportJson:"Export JSON",recordOpenAll:"Open all in grid",recordLoadMore:"Load more",recordCount:"{shown} of {total}",recordItems:"{count} items",recordMore:"+{count}",recordShowAll:"Show all {count}",recordRetry:"Retry",recordLoadFailed:"Could not load the record details: {error}",recordUnavailable:"Record {id} is not available here.",recordField:"Field",recordValue:"Value",recordSection:"Section",recordBefore:"Before",recordAfter:"After",recordAdded:"Added",recordRemoved:"Removed",recordChanged:"{count} changed fields"}));function boundedPattern(source,exact=true,whole=false){const bad=()=>{throw new Error("CanopyX: pattern requires literals, character classes and at most one variable repetition anchored at the start; groups, alternatives and backreferences are unsupported.")};if(typeof source!=="string"||source.length>256)bad();let width=0,variable=0;for(let i=0;i<source.length;){const unit=source[i++];if(unit==="^"&&i===1||unit==="$"&&i===source.length)continue;if(unit==="\\"){const escape=source[i++];if(!escape||!/^[dDsSwWbBnrtfv\\.^$*+?{}\[\]()|/]$/.test(escape)) bad();
77
+ DEFAULT_LABELS=Object.freeze(Object.assign({},DEFAULT_LABELS,{gridViews:"Views",gridViewName:"View name",gridSaveView:"Save view",gridRemoveView:"Remove view",gridResetView:"Reset view",gridChooseView:"Choose a saved view",gridApply:"Apply",gridFindColumn:"Find a column…",gridShowAllColumns:"Show all",gridResetColumns:"Reset columns",gridReorderColumn:"{column}: drag, or press Alt+Up or Alt+Down to move",gridPinnedLeft:"Pinned left",gridPinnedRight:"Pinned right",gridResponsiveHidden:"Hidden at this width",gridExport:"Export",gridVisibleRows:"Visible rows",gridSelectedRows:"Selected rows",gridAllRows:"All rows",gridDownloadCsv:"Download CSV",gridCopy:"Copy",gridExportProgress:"{completed} of {total} records",gridExportReading:"{completed} records",gridExportCancelled:"Export cancelled",gridCancelExport:"Cancel export",gridMore:"More",gridPrint:"Print",gridDetails:"Record details",gridDetailsSection:"Details",gridPreviousRecord:"Previous record",gridNextRecord:"Next record",gridCloseRecord:"Close"}));DEFAULT_LABELS=Object.freeze(Object.assign({},DEFAULT_LABELS,{recordBack:"Back",recordBackTo:"Back to {record}",recordTrail:"Record trail",recordShowing:"Showing {record}",recordTabs:"Record views",recordSections:"Jump to a section",recordOtherFields:"Other fields",recordShowEmpty:"Show empty fields ({count})",recordHideEmpty:"Hide empty fields",recordActions:"Record actions",recordCopyValues:"Copy values",recordCopyJson:"Copy as JSON",recordCopyLink:"Copy link",recordCopied:"Copied to the clipboard",recordPrint:"Print record",recordExportCsv:"Export CSV",recordExportJson:"Export JSON",recordOpenAll:"Open all in grid",recordLoadMore:"Load more",recordCount:"{shown} of {total}",recordItems:"{count} items",recordMore:"+{count}",recordShowAll:"Show all {count}",recordRetry:"Retry",recordLoadFailed:"Could not load the record details: {error}",recordUnavailable:"Record {id} is not available here.",recordField:"Field",recordValue:"Value",recordSection:"Section",recordBefore:"Before",recordAfter:"After",recordAdded:"Added",recordRemoved:"Removed",recordChanged:"{count} changed fields"}));function boundedPattern(source,exact=true,whole=false){const bad=()=>{throw new Error("CanopyX: pattern requires literals, character classes and at most one variable repetition anchored at the start; groups, alternatives and backreferences are unsupported.")};if(typeof source!=="string"||source.length>256)bad();let width=0,variable=0;for(let i=0;i<source.length;){const unit=source[i++];if(unit==="^"&&i===1||unit==="$"&&i===source.length)continue;if(unit==="\\"){const escape=source[i++];if(!escape||!/^[dDsSwWbBnrtfv\\.^$*+?{}\[\]()|/]$/.test(escape)) bad();
78
78
  }else if(unit==="["){let closed=false;while(i<source.length){const c=source[i++];if(c==="\\"){const escape=source[i++];if(!escape||!/^[dDsSwWbnrtfv\\.^$*+?{}\[\]()|/-]$/.test(escape)) bad(); }
79
79
  else if(c==="]"){closed=true;break}}
80
80
  if(!closed)bad()}else if("()|^$*+?{}]".includes(unit))bad();let count=1;if(i<source.length&&"*+?".includes(source[i])){variable++;i++}
@@ -334,7 +334,7 @@ function hostRenderers(renderers,columns,plugins){if(![Object.prototype,null].in
334
334
  Object.keys(renderers).some(id=>typeof renderers[id]!=="function"||!columns.some(column=>column.id===id)))
335
335
  throw new Error("CanopyX: invalid column renderers.");const named=Object.assign(Object.create(null),...plugins.map(item=>item.cellRenderers));return Object.assign(Object.create(null),...columns.filter(column=>typeof column.renderer==="string"&&named[column.renderer])
336
336
  .map(column=>({[column.id]:named[column.renderer]})),renderers)}
337
- function disposeHostSlot(slot){const lifetime=slot.hostLifetime;if(!lifetime)return;delete slot.hostLifetime;lifetime.dispose()}
337
+ function disposeHostSlot(slot){slot.childSlots?.forEach(disposeHostSlot);const lifetime=slot.hostLifetime;if(!lifetime)return;delete slot.hostLifetime;lifetime.dispose()}
338
338
  function disposeRowHostCells(row){row._cells?.forEach(disposeHostSlot)}
339
339
  class HostCellContext{#controller;#cleanups;constructor(slot,column,level,expanded,index){slot.hostLifetime=this;this.column=column;this.level=level;this.expanded=expanded;this.index=index}
340
340
  get signal(){this.#controller||=new AbortController();if(this.#cleanups===null)this.#controller.abort();return this.#controller.signal}
@@ -504,7 +504,7 @@ if(active>=0){list.children[active].setAttribute("aria-selected","false")}
504
504
  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=""}
505
505
  close()}else{return}
506
506
  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)}}}
507
- const PART_URL=typeof document!=="undefined"&&document.currentScript?.src;const PARTS={"tree-tools":["1e7e68d65a0c7aeb","canopyx-tree-tools.6f6db0b476335e64.js","canopyx-tree-tools.ef7a3fb9c9115a82.css"],"export":["8b99c0b7b0ec37c5","canopyx-export.6989ea36f5d6da7b.js"],"links":["f1ab7e32b501ce37","canopyx-links.d96addeefb3eccf5.js"],"search":["521eebd6e1bbbd35","canopyx-search.14c69f4551c69f23.js"],"link":["fa0c9e888463553b","canopyx-link.4ec39bc39fa6cf50.js","canopyx-link.8435c892613926ac.css"],"highlight":["f39423961654c1e2","canopyx-highlight.a6f93893d36ab343.js","canopyx-highlight.5b6d2e0d979f9f9b.css"],"layouts":["fa8c686f21626884","canopyx-layouts.e7250c82a6abf52e.js","canopyx-layouts.0b75738924533ee8.css"],"record-panel":["bcc06276c7933af6","canopyx-record-panel.a374fb4254f9115c.js","canopyx-record-panel.f3691d54cefaab04.css"]},PART_CODE=new Map(),partName=name=>typeof name==="string"&&/^[a-z][a-z\d-]{0,31}$/.test(name);
507
+ const PART_URL=typeof document!=="undefined"&&document.currentScript?.src;const PARTS={"tree-tools":["4c6c4101089c4444","canopyx-tree-tools.510207e8f47014fd.js","canopyx-tree-tools.ef7a3fb9c9115a82.css"],"export":["8b99c0b7b0ec37c5","canopyx-export.6989ea36f5d6da7b.js"],"links":["f1ab7e32b501ce37","canopyx-links.d96addeefb3eccf5.js"],"search":["521eebd6e1bbbd35","canopyx-search.14c69f4551c69f23.js"],"link":["fa0c9e888463553b","canopyx-link.4ec39bc39fa6cf50.js","canopyx-link.8435c892613926ac.css"],"highlight":["f39423961654c1e2","canopyx-highlight.a6f93893d36ab343.js","canopyx-highlight.5b6d2e0d979f9f9b.css"],"layouts":["fa8c686f21626884","canopyx-layouts.e7250c82a6abf52e.js","canopyx-layouts.0b75738924533ee8.css"],"record-panel":["bcc06276c7933af6","canopyx-record-panel.a374fb4254f9115c.js","canopyx-record-panel.f3691d54cefaab04.css"]},PART_CODE=new Map(),partName=name=>typeof name==="string"&&/^[a-z][a-z\d-]{0,31}$/.test(name);
508
508
  function registerPart(name,part){if(!partName(name)||!part||typeof part.version!=="string"||typeof part.install!=="function"||
509
509
  part.requires!==undefined&&!(Array.isArray(part.requires)&&part.requires.every(partName)))throw new Error("CanopyX: invalid part.");PART_CODE.set(name,part)}
510
510
  function definePart(View,name,label,context){const parts=View.prototype.parts||={};return parts[name]={name,label,context,parts}}
@@ -610,5 +610,5 @@ watcher.observe(host)}
610
610
  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}
611
611
  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}}
612
612
  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}
613
- 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}
614
- 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:{}));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)}})(typeof window!=="undefined"?window:this);
613
+ 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}
614
+ 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:{}));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)}})(typeof window!=="undefined"?window:this);
@@ -0,0 +1,252 @@
1
+ // Type declarations for the change views (docs/changes.md): a text diff, a change list and a propagation view.
2
+
3
+ /** A note about a change that the lines cannot show, such as changed line endings. */
4
+ export interface ChangeNote {
5
+ text: string;
6
+ /** A CanopyX tone or a host tone; info when left out. */
7
+ tone?: string;
8
+ }
9
+
10
+ /** A run of lines: where it starts on each side and its lines, each beginning with " " (unchanged), "-" (removed) or "+" (added). */
11
+ export interface DiffHunk {
12
+ before: number;
13
+ after: number;
14
+ lines: string[];
15
+ }
16
+
17
+ /** The properties of a diff, as a diff document carries them and as a change list item's diff. */
18
+ export interface DiffBody {
19
+ id: string;
20
+ label: string;
21
+ path?: string;
22
+ previousPath?: string;
23
+ /** Text by default; the other states have no lines and show their message. */
24
+ content?: "binary" | "unavailable" | "identical";
25
+ message?: string;
26
+ beforeTitle?: string;
27
+ afterTitle?: string;
28
+ /** The whole earlier text: the browser compares the texts when there are no hunks (up to 5,000 lines each) and opens folded regions. */
29
+ before?: string;
30
+ after?: string;
31
+ hunks?: DiffHunk[];
32
+ /** Unchanged lines around each change, 0 to 20; 3 by default. */
33
+ context?: number;
34
+ /** Side by side when there is room, by default. */
35
+ layout?: "inline" | "split";
36
+ notes?: ChangeNote[];
37
+ truncated?: boolean;
38
+ /** The host's copy-ready unified diff. */
39
+ unified?: string;
40
+ labels?: Record<string, string>;
41
+ }
42
+
43
+ /** The diff document: `{ protocol: "canopyx/2", type: "diff" }`. */
44
+ export interface DiffDocument extends DiffBody {
45
+ protocol: "canopyx/2";
46
+ type: "diff";
47
+ }
48
+
49
+ /** Options of createDiff and CanopyX.configure for a diff fragment. */
50
+ export interface DiffOptions {
51
+ view: DiffDocument;
52
+ labels?: Record<string, string>;
53
+ onReady?: (detail: { diff: string }) => void;
54
+ onLayout?: (detail: { diff: string; layout: "inline" | "split" }) => void;
55
+ onCopy?: (detail: { diff: string; copied: boolean }) => void;
56
+ }
57
+
58
+ /** The API of a diff. A call made before the changes part is in waits for it and returns a promise instead. */
59
+ export interface DiffApi {
60
+ readonly element: HTMLElement;
61
+ readonly view: DiffDocument;
62
+ readonly ready: Promise<DiffApi>;
63
+ getLayout(): "inline" | "split";
64
+ setLayout(layout: "inline" | "split"): void;
65
+ /** Moves to the next or previous change; false when there is none. */
66
+ next(): boolean;
67
+ previous(): boolean;
68
+ /** Opens every folded region whose lines the diff has. */
69
+ expandAll(): void;
70
+ /** Copies the unified diff; resolves whether the clipboard took it. */
71
+ copy(): Promise<boolean>;
72
+ /** The hunks in effect: the document's, or those the browser compared. */
73
+ getHunks(): DiffHunk[];
74
+ update(view: DiffDocument): void;
75
+ destroy(): void;
76
+ }
77
+
78
+ /** One side of a change: size in bytes, content hash and time. */
79
+ export interface ChangeVersion {
80
+ size?: number;
81
+ hash?: string;
82
+ /** ISO 8601. */
83
+ time?: string;
84
+ }
85
+
86
+ /** A changed file or item. */
87
+ export interface ChangeItem {
88
+ id: string;
89
+ kind: "added" | "removed" | "modified" | "renamed";
90
+ path: string;
91
+ previousPath?: string;
92
+ kindLabel?: string;
93
+ secondary?: string;
94
+ before?: ChangeVersion;
95
+ after?: ChangeVersion;
96
+ /** Shown side by side; an added item shows only its after, a removed one only its before. `note` is quiet text under the values. */
97
+ facts?: { label: string; before?: string; after?: string; tone?: string; note?: string }[];
98
+ flags?: { id: string; label: string; reason: string; tone?: string }[];
99
+ /** A diff that opens in the item's details. */
100
+ diff?: DiffBody;
101
+ /** The ids of the list's actions this item offers; all of them when left out. */
102
+ actions?: string[];
103
+ /** Values that copy and link actions use by key. */
104
+ values?: Record<string, string>;
105
+ }
106
+
107
+ /**
108
+ * An action the items (or the list's bar) offer: a host action, one that asks for a reason first, a copy action ("path",
109
+ * "previousPath" or a value key), a link that opens a value (#, /, ./, ../, http(s):// or mailto: addresses), or one that reveals the
110
+ * item's details or diff. One of copy, link, reveal and reason at most.
111
+ */
112
+ export interface ChangeAction {
113
+ id: string;
114
+ label: string;
115
+ icon?: "none" | "copy" | "diff" | "check" | "open" | "download" | "compare" | "flag";
116
+ copy?: string;
117
+ link?: string;
118
+ reveal?: "none" | "details" | "diff";
119
+ reason?: boolean;
120
+ }
121
+
122
+ /** The change list document: `{ protocol: "canopyx/2", type: "changes" }`. */
123
+ export interface ChangeListDocument {
124
+ protocol: "canopyx/2";
125
+ type: "changes";
126
+ id: string;
127
+ label: string;
128
+ actions?: ChangeAction[];
129
+ /** The bar's actions: host actions, and copy and link actions on the list's values. */
130
+ toolbar?: ChangeAction[];
131
+ values?: Record<string, string>;
132
+ /** The files the bar offers to download: the items the filters keep. */
133
+ exports?: ("csv" | "json")[];
134
+ items: ChangeItem[];
135
+ groupByFolder?: boolean;
136
+ flaggedOnly?: boolean;
137
+ expandFlagged?: boolean;
138
+ hashLength?: number;
139
+ pageSize?: number;
140
+ timeZone?: "utc" | "local";
141
+ labels?: Record<string, string>;
142
+ }
143
+
144
+ /** The reader's filters and open items. */
145
+ export interface ChangeListState {
146
+ kinds: ChangeItem["kind"][];
147
+ flaggedOnly: boolean;
148
+ open: string[];
149
+ }
150
+
151
+ /** What canopyx:changeAction carries. */
152
+ export interface ChangeActionDetail {
153
+ list: string;
154
+ action: string;
155
+ /** The item, or null for an action of the list's bar. */
156
+ item: string | null;
157
+ kind: ChangeItem["kind"] | null;
158
+ path: string | null;
159
+ previousPath: string | null;
160
+ /** The item's values, or the list's for a bar action. */
161
+ values: Record<string, string>;
162
+ /** For a copy action, whether the clipboard took the value; null otherwise. */
163
+ copied: boolean | null;
164
+ /** The reason the reader gave, for an action that asks for one. */
165
+ reason?: string;
166
+ /** The address a link action opened. */
167
+ href?: string;
168
+ /** What a reveal action opened. */
169
+ reveal?: "details" | "diff";
170
+ }
171
+
172
+ /** What canopyx:changeExport carries. */
173
+ export interface ChangeExportDetail {
174
+ list: string;
175
+ format: "csv" | "json";
176
+ count: number;
177
+ /** Whether the browser was given the file. */
178
+ saved: boolean;
179
+ }
180
+
181
+ /** Options of createChangeList and CanopyX.configure for a change list fragment. */
182
+ export interface ChangeListOptions {
183
+ view: ChangeListDocument;
184
+ labels?: Record<string, string>;
185
+ state?: Partial<ChangeListState>;
186
+ onReady?: (detail: { list: string }) => void;
187
+ /** Receives every action; for a host action, having it claims the action (as cancelling canopyx:changeAction does). */
188
+ onAction?: (detail: ChangeActionDetail, event: CustomEvent<ChangeActionDetail>) => void;
189
+ onExport?: (detail: ChangeExportDetail) => void;
190
+ onToggle?: (detail: { list: string; item: string; open: boolean }) => void;
191
+ onStateChange?: (state: ChangeListState) => void;
192
+ }
193
+
194
+ /** The API of a change list. */
195
+ export interface ChangeListApi {
196
+ readonly element: HTMLElement;
197
+ readonly view: ChangeListDocument;
198
+ readonly ready: Promise<ChangeListApi>;
199
+ getState(): Readonly<ChangeListState>;
200
+ setState(state: Partial<ChangeListState>): void;
201
+ /** Opens, closes or switches an item's details; false for an unknown item. */
202
+ toggle(id: string, open?: boolean): boolean;
203
+ focusItem(id: string): boolean;
204
+ update(view: ChangeListDocument): void;
205
+ destroy(): void;
206
+ }
207
+
208
+ /** Where a change stands at a replica. */
209
+ export type PropagationState = "converged" | "propagating" | "fault" | "unknown";
210
+
211
+ /** The propagation document: `{ protocol: "canopyx/2", type: "propagation" }`. */
212
+ export interface PropagationDocument {
213
+ protocol: "canopyx/2";
214
+ type: "propagation";
215
+ id: string;
216
+ label: string;
217
+ /** ISO 8601: when the change happened; the delays count from it. */
218
+ start?: string;
219
+ /** ISO 8601: when the evidence is from. */
220
+ asOf?: string;
221
+ state?: PropagationState;
222
+ summary?: string;
223
+ title?: string;
224
+ /** How a group heading counts its replicas, with {count} and optional plural forms. */
225
+ groupCount?: string;
226
+ witnesses: { id: string; label: string; secondary?: string; state: PropagationState; firstSeen?: string; lastSeen?: string; note?: string; source?: boolean; group?: string }[];
227
+ timeZone?: "utc" | "local";
228
+ labels?: Record<string, string>;
229
+ }
230
+
231
+ /** Options of createPropagation and CanopyX.configure for a propagation fragment. */
232
+ export interface PropagationOptions {
233
+ view: PropagationDocument;
234
+ labels?: Record<string, string>;
235
+ onReady?: (detail: { propagation: string }) => void;
236
+ }
237
+
238
+ /** The API of a propagation view. */
239
+ export interface PropagationApi {
240
+ readonly element: HTMLElement;
241
+ readonly view: PropagationDocument;
242
+ readonly ready: Promise<PropagationApi>;
243
+ update(view: PropagationDocument): void;
244
+ destroy(): void;
245
+ }
246
+
247
+ /** Creates a text diff in a host element (docs/changes.md). Ships with the grid and reporting entry points. */
248
+ export declare function createDiff(host: HTMLElement, options: DiffOptions): DiffApi;
249
+ /** Creates a change list in a host element (docs/changes.md). Ships with the grid and reporting entry points. */
250
+ export declare function createChangeList(host: HTMLElement, options: ChangeListOptions): ChangeListApi;
251
+ /** Creates a propagation view in a host element (docs/changes.md). Ships with the grid and reporting entry points. */
252
+ export declare function createPropagation(host: HTMLElement, options: PropagationOptions): PropagationApi;
@@ -354,6 +354,8 @@ interface GridDefinition<Q extends AcceptedGridQuery = GridQuery, Row extends Gr
354
354
  /** Pager settings of a paged view, read by the pager part (canopyx-pager.js); absent keeps the built-in pager. */
355
355
  pager?: GridPager;
356
356
  selection: "none" | "single" | "multiple";
357
+ /** Ordinal IDs are source positions in one snapshot; saved selection replay and keyed live updates are unavailable. Default stableKey. */
358
+ rowIdentity?: "stableKey" | "ordinal";
357
359
  search: boolean;
358
360
  /** Search-input debounce (0..5000). Absent uses 0 for inline and 300 for host sources. */
359
361
  searchDelayMs?: number;
@@ -378,6 +380,8 @@ interface GridDefinition<Q extends AcceptedGridQuery = GridQuery, Row extends Gr
378
380
  dragRows?: boolean;
379
381
  stateKey?: string;
380
382
  timeZone?: TimeZone;
383
+ /** Space-separated host styling classes copied onto body-appended record, text and column overlays when opened. */
384
+ overlayClass?: string;
381
385
  labels?: Partial<CanopyLabels>;
382
386
  /** Sanitizes trusted host HTML details before insertion; shares the tree host's sanitizer contract. */
383
387
  sanitizeHtml?: HostOptions["sanitizeHtml"];
@@ -695,6 +699,8 @@ interface GridHostBehavior<Q extends AcceptedGridQuery = GridQuery, Row extends
695
699
  /** Overrides the view's search-input debounce (0..5000); Enter always applies immediately. */
696
700
  searchDelayMs?: number;
697
701
  source?: Q extends GridCursorQuery ? GridCursorSource<Row> : GridSource<Row>;
702
+ /** Space-separated host styling classes copied onto body-appended record, text and column overlays when opened. */
703
+ overlayClass?: string;
698
704
  labels?: Partial<CanopyLabels>;
699
705
  renderers?: Record<string, (row: Row, element: HTMLElement, context: GridCellContext) => boolean | void>;
700
706
  cachePageLimit?: number;
@@ -933,14 +939,15 @@ export function createDataGrid<Row extends GridRow = GridRow>(host: HTMLElement,
933
939
  export function createDataGrid<Row extends GridRow = GridRow>(host: HTMLElement, options: CursorGridOptions<GridNamedRow<Row>>): DataGrid<GridCursorQuery, Row>;
934
940
  /** A calendar from a list of events, with no grid schema to write: a preset (basic by default) and options over it (calendar.d.ts). */
935
941
  export function createCalendar(host: HTMLElement, options?: CalendarOptions): DataGrid<GridQuery, GridRow>;
936
- /** Registers options before a tree, flat-grid, flow or matrix fragment mounts. */
937
- export function configure(viewId: string, options: HostOptions | GridHostOptions | GridHostOptions<GridCursorQuery> | Partial<Omit<FlowOptions, "flow">> | Partial<Omit<MatrixOptions, "view">>): () => void;
942
+ /** Registers options before a tree, flat-grid, flow, matrix or change view fragment mounts. */
943
+ export function configure(viewId: string, options: HostOptions | GridHostOptions | GridHostOptions<GridCursorQuery> | Partial<Omit<FlowOptions, "flow">> | Partial<Omit<MatrixOptions, "view">>
944
+ | Partial<Omit<DiffOptions, "view">> | Partial<Omit<ChangeListOptions, "view">> | Partial<Omit<PropagationOptions, "view">>): () => void;
938
945
  /**
939
- * Mounts tree, flat-grid, flow and matrix fragments below root, or root itself when it is a fragment. Fragments marked
946
+ * Mounts tree, flat-grid, flow, matrix and change view fragments below root, or root itself when it is a fragment. Fragments marked
940
947
  * `data-cx-mount="visible"` below root wait until they come near the viewport; passing such a fragment as root mounts it at once.
941
948
  * Narrow a controller before using view-specific methods.
942
949
  */
943
- export function mount(root?: ParentNode): Array<TreeGrid | DataGrid<AcceptedGridQuery> | FlowApi | MatrixApi>;
950
+ export function mount(root?: ParentNode): Array<TreeGrid | DataGrid<AcceptedGridQuery> | FlowApi | MatrixApi | DiffApi | ChangeListApi | PropagationApi>;
944
951
 
945
952
  import type { createFlow, FlowApi, FlowOptions } from "./flow.js";
946
953
  export { createFlow } from "./flow.js";
@@ -948,5 +955,8 @@ export type { FlowDocument, FlowField, FlowStep, FlowSection, FlowOption, FlowCo
948
955
  import type { createMatrix, MatrixOptions, MatrixApi } from "./matrix.js";
949
956
  export { createMatrix } from "./matrix.js";
950
957
  export type { MatrixDocument, MatrixAxis, MatrixItem, MatrixAttribute, MatrixState, MatrixSort, MatrixNote, MatrixQueryState, MatrixStateInput, MatrixCellDetail, MatrixOptions, MatrixApi } from "./matrix.js";
951
- declare const gridRuntime: Omit<typeof CanopyX, "configure" | "mount"> & { createDataGrid: typeof createDataGrid; createCalendar: typeof createCalendar; createFlow: typeof createFlow; createMatrix: typeof createMatrix; enhance: typeof enhance; grid: typeof grid; configure: typeof configure; mount: typeof mount };
958
+ import type { createDiff, createChangeList, createPropagation, DiffOptions, DiffApi, ChangeListOptions, ChangeListApi, PropagationOptions, PropagationApi } from "./changes.js";
959
+ export { createDiff, createChangeList, createPropagation } from "./changes.js";
960
+ export type { ChangeNote, DiffHunk, DiffBody, DiffDocument, DiffOptions, DiffApi, ChangeVersion, ChangeItem, ChangeAction, ChangeListDocument, ChangeListState, ChangeActionDetail, ChangeExportDetail, ChangeListOptions, ChangeListApi, PropagationState, PropagationDocument, PropagationOptions, PropagationApi } from "./changes.js";
961
+ declare const gridRuntime: Omit<typeof CanopyX, "configure" | "mount"> & { createDataGrid: typeof createDataGrid; createCalendar: typeof createCalendar; createFlow: typeof createFlow; createMatrix: typeof createMatrix; createDiff: typeof createDiff; createChangeList: typeof createChangeList; createPropagation: typeof createPropagation; enhance: typeof enhance; grid: typeof grid; configure: typeof configure; mount: typeof mount };
952
962
  export default gridRuntime;
@@ -11,6 +11,7 @@ const labels = Object.freeze({
11
11
  searchWordTooShort: "Ein Wort mit mindestens {min} Zeichen eingeben",
12
12
  searchFailed: "Suche fehlgeschlagen: {error}",
13
13
  columns: "Spalten",
14
+ hiddenColumns: "[one]+{count} Spalte[other]+{count} Spalten",
14
15
  gridFilters: "Filter",
15
16
  gridFitContent: "An Inhalt anpassen",
16
17
  gridUnpin: "Fixierung aufheben",
@@ -243,7 +244,7 @@ const labels = Object.freeze({
243
244
  gridExportCancelled: "Export abgebrochen",
244
245
  gridCancelExport: "Export abbrechen",
245
246
  gridPrint: "Drucken",
246
- gridCloseRecord: "Datensatzdetails schließen",
247
+ gridCloseRecord: "Schließen",
247
248
  gridContains: "enthält",
248
249
  gridStartsWith: "beginnt mit",
249
250
  gridIn: "ist einer der Werte",
@@ -688,13 +689,86 @@ const labels = Object.freeze({
688
689
  matrixItemTotal: "{item}: {count}",
689
690
  matrixExpandGroup: "{group} aufklappen",
690
691
  matrixCollapseGroup: "{group} zuklappen",
691
- matrixGroupCount: "Elemente: {count}",
692
+ matrixGroupCount: "{count}",
693
+ matrixCellCount: "Zellen: {count}",
692
694
  matrixDetails: "Details",
693
695
  matrixClose: "Schließen",
694
696
  matrixValue: "Wert",
695
697
  matrixState: "Zustand",
696
698
  matrixAllClear: "Nichts erfordert Aufmerksamkeit",
697
699
  matrixShowMore: "Mehr anzeigen",
700
+ changeInline: "Inline",
701
+ changeSplit: "Nebeneinander",
702
+ changeLayout: "Ansicht",
703
+ changeCopyDiff: "Diff kopieren",
704
+ changeCopied: "Kopiert",
705
+ changeCopyFailed: "Kopieren nicht möglich",
706
+ changeNext: "Nächste Änderung",
707
+ changePrevious: "Vorherige Änderung",
708
+ changePosition: "Änderung {index} von {count}",
709
+ changeShowLines: "[one]{count} weitere Zeile anzeigen[other]{count} weitere Zeilen anzeigen",
710
+ changeHiddenLines: "[one]{count} unveränderte Zeile nicht enthalten[other]{count} unveränderte Zeilen nicht enthalten",
711
+ changeLineCounts: "Hinzugefügte Zeilen: {added}, entfernte: {removed}",
712
+ changeLineAdded: "Hinzugefügte Zeile {line}",
713
+ changeLineRemoved: "Entfernte Zeile {line}",
714
+ changeLineContext: "Zeile {line}",
715
+ changeBefore: "Vorher",
716
+ changeAfter: "Nachher",
717
+ changeBinary: "Binärdatei: nach Größe und Hash verglichen, nicht zeilenweise.",
718
+ changeUnavailable: "Der Inhalt dieser Datei ist nicht verfügbar.",
719
+ changeIdentical: "Der Text hat sich nicht geändert.",
720
+ changeTruncated: "Einige Änderungen fehlen: Der Vergleich war zu groß.",
721
+ changeTooLarge: "Diese Texte sind zu lang, um sie hier zu vergleichen.",
722
+ changeDiffOf: "Änderungen in {name}",
723
+ changeAdded: "Hinzugefügt",
724
+ changeRemoved: "Entfernt",
725
+ changeModified: "Geändert",
726
+ changeRenamed: "Umbenannt",
727
+ changeFlaggedOnly: "Nur markierte",
728
+ changeFlaggedCount: "{count} markiert",
729
+ changeNoItems: "Keine Änderungen anzuzeigen.",
730
+ changeSize: "Größe",
731
+ changeHash: "Hash",
732
+ changeTime: "Zeit",
733
+ changeCopyHash: "Vollständigen Hash kopieren",
734
+ changeDetails: "Details zu {name}",
735
+ changeFileCount: "[one]{count} Datei[other]{count} Dateien",
736
+ changeFrom: "von {path}",
737
+ changeShowMore: "Mehr anzeigen",
738
+ changeFlags: "Markierungen",
739
+ changeFacts: "Details",
740
+ changeRoot: "Oberste Ebene",
741
+ changeShown: "{shown} von {total} angezeigt",
742
+ changeFilter: "Änderungen anzeigen",
743
+ changeActions: "Aktionen für {name}",
744
+ changeConverged: "Konvergiert",
745
+ changePropagating: "Wird repliziert",
746
+ changeFault: "Fehler",
747
+ changeUnknown: "Unbekannt",
748
+ changeReplica: "Replikat",
749
+ changeState: "Zustand",
750
+ changeFirstSeen: "Zuerst gesehen",
751
+ changeLastSeen: "Zuletzt gesehen",
752
+ changeTimeline: "Verzögerung",
753
+ changeNotSeen: "Noch nicht gesehen",
754
+ changeDelay: "+{duration}",
755
+ changeSource: "Quelle",
756
+ changeProgress: "{converged} von {total} haben die Änderung",
757
+ changeSeconds: "{count} s",
758
+ changeMinutes: "{count} Min.",
759
+ changeHours: "{count} Std.",
760
+ changeDays: "{count} T.",
761
+ changeUtcTime: "{time} UTC",
762
+ changeExportCsv: "CSV exportieren",
763
+ changeExportJson: "JSON exportieren",
764
+ changeReason: "Grund",
765
+ changeReasonHint: "{action}: Begründen Sie die Aktion. Der Grund wird mit ihr gespeichert.",
766
+ changeReasonRequired: "Geben Sie zuerst einen Grund ein.",
767
+ changeCancel: "Abbrechen",
768
+ changeUnclaimed: "Diese Seite kann „{action}“ nicht selbst ausführen. Kopieren Sie die Anfrage, um sie weiterzugeben.",
769
+ changeCopyRequest: "Anfrage kopieren",
770
+ changeFiltered: "Gefiltert: {filters}",
771
+ changePrintLimit: "{count} weitere Zeilen werden nicht gedruckt.",
698
772
  });
699
773
  const staticLabels = Object.freeze({
700
774
  overflowNote: "{shown} Zeilen angezeigt. Gesamtzahl: {total}.",