@mohsinonxrm/pptb-commandbar-studio 0.7.0-beta.2 → 0.7.0-beta.3

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 (28) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/README.md +1 -1
  3. package/dist/assets/{BulkPublishModal-dGiT6nNQ.js → BulkPublishModal-B0Ecmiq1.js} +1 -1
  4. package/dist/assets/{CommandEditorPanel-BfcpfNps.js → CommandEditorPanel-FdBZ2zNj.js} +1 -1
  5. package/dist/assets/{CommandPalette-C4NOIuA3.js → CommandPalette-BL4yGqtx.js} +1 -1
  6. package/dist/assets/{ConflictDrawer-CW1NLdAD.js → ConflictDrawer-DAQi31Y8.js} +1 -1
  7. package/dist/assets/{DiffViewer-R0g0dxLc.js → DiffViewer-DVIsuwXy.js} +1 -1
  8. package/dist/assets/{HideInsteadOfDeleteModal-DbfHSKmm.js → HideInsteadOfDeleteModal-BBfhx5dO.js} +1 -1
  9. package/dist/assets/{IconPickerModal-B1AgGWq0.js → IconPickerModal-D97ew6l_.js} +1 -1
  10. package/dist/assets/{ImportXmlModal-DImDI43_.js → ImportXmlModal-2-0E4Ib_.js} +1 -1
  11. package/dist/assets/{LocalizationEditor-CeAgqq85.js → LocalizationEditor-7sYCHKMx.js} +1 -1
  12. package/dist/assets/{MonacoEditor-BRCY30rO.js → MonacoEditor-DGzdcWNE.js} +1 -1
  13. package/dist/assets/{NewButtonModal-DqdSshXe.js → NewButtonModal-CKKbFpLd.js} +1 -1
  14. package/dist/assets/{NewGroupModal-VysrEsbU.js → NewGroupModal-BpXuxZKo.js} +1 -1
  15. package/dist/assets/{NewTabModal-ChEmOjmh.js → NewTabModal-CoBSto1f.js} +1 -1
  16. package/dist/assets/RuleEditorPanel-Dg2vnxrc.js +1 -0
  17. package/dist/assets/{ScalingEditorPanel-f2Rmk9AG.js → ScalingEditorPanel-L_j0ox4f.js} +1 -1
  18. package/dist/assets/{TabDisplayRulesPanel-DR-c2-Km.js → TabDisplayRulesPanel-W1SMJTlj.js} +1 -1
  19. package/dist/assets/{UrlComposerModal-DFwPOYL1.js → UrlComposerModal-BhcG5L1S.js} +1 -1
  20. package/dist/assets/{WebResourceBrowser-D2RMaFeZ.js → WebResourceBrowser-CQemLOXD.js} +1 -1
  21. package/dist/assets/{XmlDrawer-DXyRtGs1.js → XmlDrawer-BOOdH0HN.js} +1 -1
  22. package/dist/assets/{index-CKs2IGe0.js → index-DUEHURiA.js} +11 -11
  23. package/dist/assets/{useReplaceWebResourceConfirm-C4vg9hxg.js → useReplaceWebResourceConfirm-Mb36NTI7.js} +1 -1
  24. package/dist/assets/xmlValidator-BB2o3QUU.js +3 -0
  25. package/dist/index.html +1 -1
  26. package/package.json +1 -1
  27. package/dist/assets/RuleEditorPanel-C26e1gMH.js +0 -1
  28. package/dist/assets/xmlValidator-TOuV4jko.js +0 -3
@@ -1 +1 @@
1
- import{y as e}from"./fluent-icons-DUgjkNjy.js";import{a as r}from"./monaco-BIO15LCS.js";import{Y as u,E as g}from"./index-CKs2IGe0.js";import{D as x,i as h,k as j,j as f,l as y,t as b,b as l,m as k,d as o}from"./fluent-ui-Dde22_ZN.js";function E(){const[n,t]=r.useState(null),i=r.useRef(null),d=u(a=>a.connectionVersion),s=r.useCallback(a=>{i.current?.(a),i.current=null,t(null)},[]),p=r.useCallback(a=>new Promise(c=>{i.current?.("cancel"),i.current=c,t({name:a.name,managed:a.managed})}),[]);r.useEffect(()=>()=>s("cancel"),[d,s]);const m=e.jsx(x,{open:n!==null,onOpenChange:(a,c)=>!c.open&&s("cancel"),children:e.jsx(h,{style:{maxWidth:520},children:e.jsxs(j,{children:[e.jsx(f,{children:n?.managed?"Use the existing web resource?":"Replace the existing web resource?"}),e.jsxs(y,{style:{display:"flex",flexDirection:"column",gap:b.spacingVerticalS},children:[n?.managed?e.jsxs(l,{children:["A managed web resource named ",e.jsx("strong",{children:n.name})," already exists. It can't be replaced: keep it as it is, or rename the file and upload it again."]}):e.jsxs(l,{children:["A web resource named ",e.jsx("strong",{children:n?.name})," already exists. Replacing it changes its content everywhere it is used (other ribbons, forms, apps)."]}),e.jsx(g,{prefix:"In"})]}),e.jsxs(k,{children:[e.jsx(o,{appearance:"secondary",onClick:()=>s("cancel"),children:"Cancel"}),e.jsx(o,{appearance:n?.managed?"primary":"secondary",onClick:()=>s("keep"),children:"Keep existing"}),!n?.managed&&e.jsx(o,{appearance:"primary",onClick:()=>s("replace"),children:"Replace"})]})]})})});return{onExisting:p,dialog:m}}export{E as u};
1
+ import{y as e}from"./fluent-icons-DUgjkNjy.js";import{a as r}from"./monaco-BIO15LCS.js";import{Y as u,E as g}from"./index-DUEHURiA.js";import{D as x,i as h,k as j,j as f,l as y,t as b,b as l,m as k,d as o}from"./fluent-ui-Dde22_ZN.js";function E(){const[n,t]=r.useState(null),i=r.useRef(null),d=u(a=>a.connectionVersion),s=r.useCallback(a=>{i.current?.(a),i.current=null,t(null)},[]),p=r.useCallback(a=>new Promise(c=>{i.current?.("cancel"),i.current=c,t({name:a.name,managed:a.managed})}),[]);r.useEffect(()=>()=>s("cancel"),[d,s]);const m=e.jsx(x,{open:n!==null,onOpenChange:(a,c)=>!c.open&&s("cancel"),children:e.jsx(h,{style:{maxWidth:520},children:e.jsxs(j,{children:[e.jsx(f,{children:n?.managed?"Use the existing web resource?":"Replace the existing web resource?"}),e.jsxs(y,{style:{display:"flex",flexDirection:"column",gap:b.spacingVerticalS},children:[n?.managed?e.jsxs(l,{children:["A managed web resource named ",e.jsx("strong",{children:n.name})," already exists. It can't be replaced: keep it as it is, or rename the file and upload it again."]}):e.jsxs(l,{children:["A web resource named ",e.jsx("strong",{children:n?.name})," already exists. Replacing it changes its content everywhere it is used (other ribbons, forms, apps)."]}),e.jsx(g,{prefix:"In"})]}),e.jsxs(k,{children:[e.jsx(o,{appearance:"secondary",onClick:()=>s("cancel"),children:"Cancel"}),e.jsx(o,{appearance:n?.managed?"primary":"secondary",onClick:()=>s("keep"),children:"Keep existing"}),!n?.managed&&e.jsx(o,{appearance:"primary",onClick:()=>s("replace"),children:"Replace"})]})]})})});return{onExisting:p,dialog:m}}export{E as u};
@@ -0,0 +1,3 @@
1
+ import{a5 as T,a6 as ee,a7 as te,a8 as B,a9 as ne,p as oe}from"./index-DUEHURiA.js";import{D as se,E as ae}from"./ruleKinds-B9hACGaq.js";import{C as ie,i as re}from"./crmParameters-rgfZhhzN.js";const le=["Mscrm.HideOnModern","Mscrm.ShowOnlyOnModern"];function R(c,l){const b=c.indexOf(l);return b<0?void 0:`Line ${c.slice(0,b).split(/\r?\n/).length}`}function we(c,l,b){const d=[],y=new DOMParser().parseFromString(c,"application/xml"),$=y.querySelector("parsererror");if($){const i=$.textContent?.trim()??"Unknown",u=i.match(/line\s+(\d+)/i);return d.push({message:`XML parse error: ${i}`,lineHint:u?`Line ${u[1]}`:void 0}),d}const f=new Set([]);return y.querySelectorAll("[Id]").forEach(i=>{const u=i.getAttribute("Id")??"";if(/\s/.test(u)){const A=R(c,`Id="${u}"`);d.push({message:`Element ID "${u}" contains invalid whitespace characters`,elementId:u,lineHint:A})}}),y.querySelectorAll("[LabelText]").forEach(i=>{const u=i.getAttribute("LabelText")??"",A=u.match(/^\$LocLabels:(.+)$/);if(A&&f.size>0&&!f.has(A[1])){const g=i.getAttribute("Id")??"",I=R(c,g?`Id="${g}"`:`LabelText="${u}"`);d.push({message:`Button label reference "$LocLabels:${A[1]}" has no matching LocLabel entry`,elementId:g||void 0,lineHint:I})}}),(c.includes("& ")||c.includes(`&
2
+ `))&&d.push({message:'URL may contain an unescaped "&" — all ampersands in XML must be written as &amp;',lineHint:R(c,"& ")??R(c,`&
3
+ `)}),y.querySelectorAll("Group").forEach(i=>{const u=[];i.querySelectorAll(":scope > Controls > Button, :scope > Controls > FlyoutAnchor, :scope > Controls > SplitButton").forEach(A=>{const g=parseInt(A.getAttribute("Sequence")??"0",10);if(u.includes(g)){const I=i.getAttribute("Id")??"?";d.push({message:`Two elements share Sequence "${g}" in group "${I}"`,elementId:I,lineHint:R(c,`Sequence="${g}"`)})}u.push(g)})}),d.push(...ce(y,c)),d}const H=new Set(["BoolParameter","CrmParameter","DecimalParameter","IntParameter","StringParameter"]);function p(c){return Array.from(c.children)}function X(c,l){return l==="enable"?se.has(c):ae.has(c)}function ce(c,l){const b=[],d=(f,i,u)=>b.push({message:f,elementId:u,lineHint:R(l,i)}),w=(f,i,u)=>b.push({message:f,elementId:u,lineHint:R(l,i),severity:"warning"}),y=c.querySelector("RibbonDiffXml > RuleDefinitions"),$=[{tag:"EnableRule",label:"Enable",container:"enable"},{tag:"DisplayRule",label:"Display",container:"display"}];for(const{tag:f,label:i,container:u}of $)y?.querySelectorAll(`:scope > ${f}s > ${f}`).forEach(A=>{const g=A.getAttribute("Id")??"",I=p(A);I.length===0&&d(`${i} rule "${g}" has no steps — the schema requires at least one`,`Id="${g}"`,g);const D=S=>{X(S.tagName,u)&&w(`${i} rule "${g}" contains ${S.tagName}, which RibbonTypes.xsd only allows in ${u==="enable"?"display":"enable"} rules`,`<${S.tagName}`,g)};for(const S of I){if(S.tagName!=="OrRule"){D(S);continue}const L=p(S);L.filter(N=>N.tagName==="Or").length<2&&d(`OrRule in ${i.toLowerCase()} rule "${g}" needs at least 2 <Or> children (found ${L.filter(N=>N.tagName==="Or").length})`,"<OrRule",g);for(const N of L){const E=p(N);E.length===0&&d(`Empty <Or> in rule "${g}"`,"<Or",g);for(const O of E)O.tagName==="OrRule"?d(`Rule "${g}": an <Or> cannot contain another OrRule`,"<OrRule",g):D(O)}}});return y||c.querySelectorAll("OrRule").forEach(f=>{const i=f.querySelectorAll(":scope > Or").length;i<2&&d(`OrRule requires at least 2 <Or> children (found ${i})`,"<OrRule")}),c.querySelectorAll("JavaScriptFunction, CustomRule").forEach(f=>{for(const i of p(f)){if(!H.has(i.tagName))continue;i.hasAttribute("Name")&&d(`${i.tagName} inside <${f.tagName}> must not have a Name attribute (only <Url> parameters are named)`,`<${i.tagName} Name=`);const u=i.getAttribute("Value")??"";i.tagName==="CrmParameter"&&!ie.has(u)&&d(`CrmParameter Value "${u}" is not a valid CrmParameterValue`,`Value="${u}"`)}}),c.querySelectorAll("Url").forEach(f=>{for(const i of p(f)){if(!H.has(i.tagName))continue;i.getAttribute("Name")||d(`${i.tagName} inside <Url> requires a Name (the query-string key)`,`<${i.tagName}`);const u=i.getAttribute("Value")??"";i.tagName==="CrmParameter"&&!re(u)&&d(`CrmParameter Value "${u}" is not allowed in a <Url> action (CrmNamedParameterValue)`,`Value="${u}"`)}}),c.querySelector("RibbonDiffXml > Scalings")&&d('<Scalings> is not allowed in RibbonDiffXml — scaling changes are CustomActions with Location="{tabId}.Scaling._children"',"<Scalings"),c.querySelectorAll("CustomAction > CommandUIDefinition > Tab").forEach(f=>{const i=f.getAttribute("Id")??"",u=p(f).map(I=>I.tagName),A=u.indexOf("Scaling"),g=u.indexOf("Groups");(A<0||g>=0&&A>g)&&d(`Tab "${i}" needs a <Scaling Id> element before <Groups>`,`Id="${i}"`,i)}),c.querySelectorAll("TabDisplayRule > DisplayRules").forEach(()=>{d("TabDisplayRule rules must be direct children — remove the <DisplayRules> wrapper","<TabDisplayRule")}),b}const ue=["LabelText","ToolTipTitle","ToolTipDescription","Alt","Title","Description"],de=new Set(["powerFx","customApi"]),fe={powerFx:"Power Fx",customApi:"Custom API"};function me(c){return[...new Set(c.actions.map(l=>l.kind).filter(l=>de.has(l)))]}const x=/^\$LocLabels:(.+)$/,Y=/^\$webresource:([^?#]+)/i,he=new Set(["ModernImage","Image16by16","Image32by32"]),F=new Set(["Tab","Group"]),Q=new Set(["main_system_library.js"]),ge=new Set(["FlyoutAnchor","SplitButton"]),pe=new Set(["Button","FlyoutAnchor","SplitButton","TextBox","ComboBox","CheckBox"]),V=/^(.+)\.Scaling\._children$/,W=c=>/^(Mscrm|Microsoft)\./.test(c);function J(c){const l=[],b=[],d=new DOMParser;for(const w of Object.keys(c))for(const[y,$]of c[w]){const f=d.parseFromString($,"application/xml");if(f.querySelector("parsererror")){b.push({severity:"error",code:"xml-parse",message:`${w} element ${y} is not well-formed XML`,elementId:y});continue}l.push({part:w,key:`${w}:${y}`,el:f.documentElement})}return{elements:l,errors:b}}function C(c){return[c,...Array.from(c.getElementsByTagName("*"))]}function Z(c){return typeof c=="string"?oe(c):c}function $e(c){let l;try{l=Z(c)}catch{return[]}const b=new Map;for(const{el:d}of J(l).elements)for(const w of C(d))for(const y of Array.from(w.attributes)){const $=Y.exec(y.value.trim())?.[1],f=$?.toLowerCase();$&&f&&!Q.has(f)&&!b.has(f)&&b.set(f,$)}return[...b.values()]}const K=c=>{const l=c.trim();return l&&!l.includes(".")?`Mscrm.Templates.${l}`:l};function Ie(c,l={}){let b;try{b=Z(c)}catch(e){return[{severity:"error",code:"xml-parse",message:e instanceof Error?e.message:String(e)}]}const{elements:d,errors:w}=J(b),y=l.changedKeys?new Set(l.changedKeys):null,$=e=>!y||y.has(e),f=new Set(l.newKeys??[]),i=(e,o,t,a,n=!1)=>{const s=(Array.isArray(o)?o:[o]).some($);w.push({severity:n||!s?"warning":"error",code:e,message:s?t:`${t} (already in the published customizations)`,elementId:a})},u=d.filter(e=>e.part==="customActions"),A=d.filter(e=>e.part==="commandDefinitions"),g=new Map((l.commands??[]).map(e=>[e.id,e]));for(const{key:e,el:o}of A){const t=o.getAttribute("Id")??"",a=g.get(t);if(!a||!$(e))continue;const n=me(a);n.length!==0&&i("dropped-action",e,`Command ${t} has a ${n.map(s=>fe[s]??s).join(" / ")} action — classic ribbon commands can't run it, so it would be dropped; remove the action`,t)}const I=new Set;for(const{el:e}of u)if(e.tagName==="CustomAction")for(const o of Array.from(e.getElementsByTagName("*")))o.getAttribute("Command")===T&&I.add(o.getAttribute("Id")??"");for(const{key:e,el:o}of u){if(o.tagName!=="HideCustomAction")continue;const t=o.getAttribute("Location")??"";!I.has(t)&&!ee({id:t,commandId:T})||i("open-record-hidden",e,`${t} is hidden, but it carries the record open override (${T}) — Unified Interface may then open records as usual or fail to open them; unhide it, or turn the override off`,t,!0)}for(const{key:e,el:o}of A){if(o.getAttribute("Id")!==T)continue;const t=p(o).filter(n=>n.tagName==="DisplayRules").flatMap(n=>p(n).map(s=>s.getAttribute("Id")??s.tagName));if(t.length===0)continue;const a=t.includes("Mscrm.HideOnModern")?le.filter(n=>t.includes(n)):[];i("open-record-hidden",e,`${T} has display rules (${t.join(", ")})${a.length>0?` — ${a.join(" + ")} hide its button`:""}: where they hide the "Open record" button, the record open override may stop working; the button must stay shown`,T,!0)}for(const{key:e,el:o}of u)for(const t of Array.from(o.getElementsByTagName("*"))){if(!ge.has(t.tagName)||p(t).some(n=>n.tagName==="Menu"))continue;const a=t.getAttribute("Id")??"";if(te(t.getAttribute("PopulateDynamically"))){t.getAttribute("PopulateQueryCommand")?.trim()||i("menu-missing",e,`${t.tagName} ${a} is populated dynamically but has no PopulateQueryCommand — its drop-down may stay empty`,a,!0);continue}i("menu-missing",e,`${t.tagName} ${a} has no <Menu> — it would publish as an empty drop-down`,a)}const D=new Set(l.menuControlsIds??[]);for(const{key:e,el:o}of u){const t=o.getAttribute("Location")??"",a=t.endsWith("._children")&&D.has(t.slice(0,-10));for(const n of Array.from(o.getElementsByTagName("*"))){const s=a||!!n.parentElement?.closest("Menu");for(const r of ue){const h=n.getAttribute(r);if(!h?.startsWith("$LocLabels:"))continue;const m=h.slice(11);m.length<=B||i("label-id-length",e,`${n.tagName} ${n.getAttribute("Id")??""} uses label ${m} (${m.length} characters)`+(s?`: a menu item's label id may have at most ${B} characters, or the publish fails after the import — give the control a shorter id`:`: label ids over ${B} characters fail inside menus; consider a shorter control id`),n.getAttribute("Id")??void 0,!s)}}}if(l.known?.commandIds){const e=new Set([...b.commandDefinitions.keys(),...l.known.commandIds]);for(const{key:o,el:t}of u)for(const a of Array.from(t.getElementsByTagName("*"))){const n=a.getAttribute("Command");if(!n||e.has(n)||F.has(a.tagName))continue;const s=a.getAttribute("Id")??"";i("undefined-command",o,`${a.tagName} ${s} uses command ${n}, which is defined neither in the customizations nor in the loaded ribbon`,s,W(n))}}const S={EnableRule:l.known?.enableRuleIds?new Set([...b.enableRules.keys(),...l.known.enableRuleIds]):null,DisplayRule:l.known?.displayRuleIds?new Set([...b.displayRules.keys(),...l.known.displayRuleIds]):null};for(const{key:e,el:o}of A){const t=o.getAttribute("Id")??"",a=p(o).filter(n=>n.tagName==="EnableRules"||n.tagName==="DisplayRules").flatMap(p);for(const n of a){const s=S[n.tagName],r=n.getAttribute("Id");!s||!r||s.has(r)||i("undefined-rule",e,`Command ${t} uses ${n.tagName==="EnableRule"?"enable":"display"} rule ${r}, which is defined neither in the customizations nor in the loaded ribbon`,t,W(r))}}for(const{part:e,key:o,el:t}of d){if(e!=="enableRules"&&e!=="displayRules")continue;const a=e==="enableRules"?"enable":"display",n=t.getAttribute("Id")??"",s=p(t).flatMap(r=>r.tagName==="OrRule"?p(r).filter(h=>h.tagName==="Or").flatMap(p):[r]);for(const r of s)X(r.tagName,a)&&i("rule-placement",o,`${a==="enable"?"Enable":"Display"} rule ${n} contains ${r.tagName}, which the ribbon schema only allows in ${a==="enable"?"display":"enable"} rules`,n,!0)}const L=new Set;for(const e of Object.values(l.loadedAttributes??{}))for(const o of Object.values(e))x.test(o)&&L.add(o);for(const{key:e,el:o}of d)if(o.tagName!=="LocLabel")for(const t of C(o))for(const a of Array.from(t.attributes)){const n=x.exec(a.value)?.[1];if(!n||b.locLabels.has(n))continue;const s=t.getAttribute("Id")??void 0;s&&l.loadedAttributes?.[s]?.[a.name]===a.value||L.has(a.value)||i("loclabel-missing",e,`${[t.tagName,s,a.name].filter(Boolean).join(" ")} references $LocLabels:${n}, which has no <LocLabel> in these customizations — the text won't resolve unless another solution defines the label`,s,!0)}if(l.webResources){const e=new Set;for(const{el:o}of d)for(const t of C(o))for(const a of Array.from(t.attributes)){const n=Y.exec(a.value.trim())?.[1],s=n?.toLowerCase();if(!n||!s||Q.has(s)||e.has(s))continue;const r=l.webResources[s];if(!r)continue;const h=t.getAttribute("Id")??void 0;r.exists?!r.inSolution&&!r.managed&&(e.add(s),w.push({severity:"warning",code:"webresource-not-in-solution",message:`Web resource ${n} isn't in the selected solution — the ribbon won't import into another environment without it`,elementId:h})):(e.add(s),w.push({severity:"warning",code:"webresource-missing",message:`Web resource ${n} (${t.tagName} ${a.name}) doesn't exist in this environment — ${he.has(a.name)?"the button will show no icon":"the action or rule that loads it will fail when it runs"}`,elementId:h}))}}const N=new Map,E=e=>{let o=N.get(e);return o||N.set(e,o=new Map),o};for(const[e,o]of Object.entries(l.tabScaling??{})){for(const t of o.maxSizes)E(e).set(t.id,{...t,tag:"MaxSize"});for(const t of o.scales)E(e).set(t.id,{...t,tag:"Scale"})}const O=new Map(Object.entries(l.groupTemplates??{}).map(([e,o])=>[e,K(o)])),U=new Map(Object.entries(l.templateLayouts??{}));for(const{part:e,el:o}of d)if(e==="templates")for(const t of C(o).filter(a=>a.tagName==="GroupTemplate")){const a=p(t).filter(n=>n.tagName==="Layout").map(n=>n.getAttribute("Title")??"");U.set(t.getAttribute("Id")??"",a)}const z=[];for(const{key:e,el:o}of u){for(const s of Array.from(o.getElementsByTagName("Group"))){const r=s.getAttribute("Template");r&&O.set(s.getAttribute("Id")??"",K(r))}const t=[],a=V.exec(o.getAttribute("Location")??"")?.[1],n=p(o).find(s=>s.tagName==="CommandUIDefinition");if(a&&n)for(const s of p(n))t.push([a,s]);for(const s of Array.from(o.getElementsByTagName("Tab")))for(const r of p(s).filter(h=>h.tagName==="Scaling"))for(const h of p(r))t.push([s.getAttribute("Id")??"",h]);for(const[s,r]of t){if(r.tagName!=="MaxSize"&&r.tagName!=="Scale")continue;const h={id:r.getAttribute("Id")??"",tag:r.tagName,groupId:r.getAttribute("GroupId")??"",size:r.getAttribute("Size")??"",sequence:parseInt(r.getAttribute("Sequence")??"",10),key:e};E(s).set(h.id,h),z.push(h)}}for(const[e,o]of N){const t=new Map;for(const a of o.values())Number.isNaN(a.sequence)||t.set(a.sequence,[...t.get(a.sequence)??[],a]);for(const[a,n]of t){const s=[...new Set(n.flatMap(m=>m.key?[m.key]:[]))];if(n.length<2||s.length===0)continue;const r=n.find(m=>m.key&&m.tag==="MaxSize"&&m.id===`${m.groupId}.MaxSize`),h=r?`move group ${r.groupId} to another position (its MaxSize takes the group's Sequence; #66 picks a free one automatically)`:"give each step its own Sequence";i("scaling-collision",s,`Scaling of tab ${e}: ${n.map(m=>`${m.tag} ${m.id}`).join(" and ")} have the same Sequence ${a} — Unified Interface then drops every custom control of the table; ${h}`,n.find(m=>m.key)?.id)}}for(const e of z){const o=O.get(e.groupId),t=o?U.get(o):void 0;!t?.length||t.includes(e.size)||i("scaling-size",e.key??[],`${e.tag} ${e.id} has Size "${e.size}", which isn't a layout of template ${o} used by group ${e.groupId} (${t.join(", ")})`,e.id,!0)}const G=new Set(l.known?.elementIds??[]),k=new Map;for(const{key:e,el:o}of u){if(!(o.getAttribute("Location")??"").endsWith("._children"))continue;const t=p(o).find(n=>n.tagName==="CommandUIDefinition"),a=t?p(t)[0]:void 0;if(!(!a||a.tagName==="MaxSize"||a.tagName==="Scale"))for(const n of C(a)){const s=n.getAttribute("Id");if(!s||n.closest("Scaling"))continue;const r=k.get(s)??[];r.includes(e)||k.set(s,[...r,e])}}for(const[e,o]of k)o.length>1?i("duplicate-id",o,`Id ${e} is added by more than one CustomAction (${o.map(t=>t.replace(/^customActions:ca:/,"")).join(", ")})`,e):f.has(o[0])&&G.has(e)&&i("duplicate-id",o,`Id ${e} is added as a new element, but the ribbon already has an element with this id`,e);if(l.places){const{entityLogicalName:e,tabByCollection:o}=l.places,t=new Set(["Mscrm.Tabs",`Mscrm.SubGrid.${e}.ContextualTabs`]);for(const{key:a,el:n}of u){const s=n.getAttribute("Location")??"",r=/^(.+)\._children$/.exec(s)?.[1];if(!r||V.test(s)||!$(a))continue;const h=p(n).find(M=>M.tagName==="CommandUIDefinition"),m=h?p(h)[0]:void 0,v=m?.getAttribute("Id")??"";if(!m||!v||!(pe.has(m.tagName)||F.has(m.tagName)))continue;const P=r.split("{!EntityLogicalName}").join(e),q=o[P];if(q!==void 0){ne(q)&&i("orphan-location",a,`${m.tagName} ${v} is added to ${r}, on the tab ${q}: Unified Interface doesn't show that tab and the ribbon doesn't return what is added there, so it won't show anywhere — add it to the table's main tab instead`,v,!0);continue}const j=M=>M.replace(/\.(Controls|Groups)$/,"");t.has(P)||[r,P,j(r),j(P)].some(M=>k.has(M))||i("orphan-location",a,`${m.tagName} ${v} is added at ${s}, but this table's ribbon has no ${r} — it won't show anywhere (check the Location, or move it to an existing group)`,v,!0)}}const _=l.publisherPrefix?.trim().toLowerCase();if(_&&f.size>0){const e=new Set([...G,...l.known?.commandIds??[],...l.known?.enableRuleIds??[],...l.known?.displayRuleIds??[]]),o=n=>{const s=n.split(".");for(let r=s.length;r>0;r--)if(e.has(s.slice(0,r).join(".")))return!0;return!1},t=n=>{const s=n.toLowerCase();return s.startsWith(`${_}.`)||s.startsWith(`${_}_`)},a=new Set;for(const{part:n,key:s,el:r}of d)if(!(!f.has(s)||n==="templates"||n==="tabDisplayRules"))for(const h of C(r)){if(h!==r&&h.closest("Actions, EnableRules, DisplayRules, EnableRule, DisplayRule"))continue;const m=h.getAttribute("Id")??h.getAttribute("HideActionId");!m||a.has(m)||t(m)||o(m)||(a.add(m),w.push({severity:"warning",code:"id-prefix",message:`${h.tagName} id ${m} doesn't start with the publisher prefix "${_}." — ids without it can collide with other solutions`,elementId:m}))}}return w}export{we as a,Ie as v,$e as w};
package/dist/index.html CHANGED
@@ -4,7 +4,7 @@
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <title>Command Bar Studio</title>
7
- <script type="module" crossorigin src="./assets/index-CKs2IGe0.js"></script>
7
+ <script type="module" crossorigin src="./assets/index-DUEHURiA.js"></script>
8
8
  <link rel="modulepreload" crossorigin href="./assets/monaco-BIO15LCS.js">
9
9
  <link rel="modulepreload" crossorigin href="./assets/fluent-icons-DUgjkNjy.js">
10
10
  <link rel="modulepreload" crossorigin href="./assets/fluent-ui-Dde22_ZN.js">
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mohsinonxrm/pptb-commandbar-studio",
3
- "version": "0.7.0-beta.2",
3
+ "version": "0.7.0-beta.3",
4
4
  "displayName": "Command Bar Studio",
5
5
  "description": "Visual designer for Dataverse classic ribbon / command bar customizations — the spiritual successor to Ribbon Workbench, built for Power Platform ToolBox",
6
6
  "author": "Mohsin On xRM",
@@ -1 +0,0 @@
1
- import{y as e,z as L,h as X,cq as K}from"./fluent-icons-DUgjkNjy.js";import{a as I,R as te}from"./monaco-BIO15LCS.js";import{a as _,u as $}from"./index-CKs2IGe0.js";import{R as se,i as oe,a as V}from"./ruleKinds-B9hACGaq.js";import{b as E,t as o,I as y,d as O,B as q,F as s,S as h,an as w,P as Q,c as Y,e as J,ai as ce,aj as ue,ak as de,al as pe,M as he,f as me,a7 as H}from"./fluent-ui-Dde22_ZN.js";import{C as xe}from"./CrmParameterEditor-CQD0gNNH.js";import{f as Z,D as ye,c as ve,S as je,v as ge,g as Re,C as be}from"./dnd-BmqIZ1-i.js";import"./crmParameters-rgfZhhzN.js";const fe=L({root:{display:"flex",flexDirection:"column",gap:o.spacingVerticalXS},search:{marginBottom:o.spacingVerticalXS},group:{display:"flex",flexDirection:"column",gap:o.spacingVerticalXXS},groupLabel:{color:o.colorNeutralForeground3,paddingTop:o.spacingVerticalXS},kindButton:{justifyContent:"flex-start",textAlign:"left"}});function Ce(l){switch(l){case"CommandClientTypeRule":return{kind:l,type:"Refresh"};case"CrmClientTypeRule":return{kind:l,type:"Web"};case"FormStateRule":return{kind:l,state:"Existing"};case"FormTypeRule":return{kind:l,type:"Main"};case"SelectionCountRule":return{kind:l,minimum:1};case"EntityRule":return{kind:l};case"CustomRule":return{kind:l,library:"$webresource:",functionName:""};case"ValueRule":return{kind:l,field:"",value:""};case"SkuRule":return{kind:l,sku:"Online"};case"OrRule":return{kind:l,groups:[[{kind:"FormStateRule",state:"Existing"}],[{kind:"FormStateRule",state:"Create"}]]};case"FeatureControlRule":return{kind:l,featureControlBit:"",expectedValue:!0};case"OrganizationSettingRule":return{kind:l,setting:"IsSharepointEnabled"};case"EntityPrivilegeRule":return{kind:l,privilegeType:"Read",privilegeDepth:"Basic"};case"EntityPropertyRule":return{kind:l,propertyName:"IsCustomizable",propertyValue:!0};case"RecordPrivilegeRule":return{kind:l,privilegeType:"Read"};case"RelationshipTypeRule":return{kind:l,appliesTo:"SelectedEntity"};case"MiscellaneousPrivilegeRule":return{kind:l,privilegeName:"ExportToExcel"};case"FormEntityContextRule":return{kind:l,entityName:""};case"PageRule":return{kind:l,address:""};case"CrmOfflineAccessStateRule":return{kind:l,state:"Online"};case"CrmOutlookClientTypeRule":return{kind:l,type:"CrmForOutlook"};case"CrmOutlookClientVersionRule":return{kind:l,major:15,minor:0,build:0,revision:0};case"DeviceTypeRule":return{kind:l,type:"Web"};case"OptionSetRule":return{kind:l,optionSet:"",stateCode:"",objectTypeCode:""};case"OutlookItemTrackingRule":return{kind:l,trackedInCrm:!0};case"OutlookRenderTypeRule":return{kind:l,type:"Web"};case"OutlookVersionRule":return{kind:l,version:"2010"};case"HideForTabletExperienceRule":return{kind:l};case"ReferencingAttributeRequiredRule":return{kind:l};default:return{kind:l}}}const ee=({onSelect:l,container:t,insideOr:d=!1})=>{const p=fe(),[c,f]=I.useState(""),R=se.map(x=>({...x,kinds:x.kinds.filter(m=>{if(t&&!oe(m,t,d)||d&&m==="OrRule")return!1;const C=V[m]??m;return!c||C.toLowerCase().includes(c.toLowerCase())||m.toLowerCase().includes(c.toLowerCase())})})).filter(x=>x.kinds.length>0);return e.jsxs("div",{className:p.root,children:[e.jsx(E,{size:100,style:{color:o.colorNeutralForeground3},children:"Quick action rules are OOB references and should be bound by ID (for example, Mscrm.ShowOnQuickAction), not created as custom step kinds."}),e.jsx(y,{className:p.search,placeholder:"Search rule kinds…",value:c,onChange:(x,m)=>f(m.value)}),R.map(x=>e.jsxs("div",{className:p.group,children:[e.jsx(E,{size:100,weight:"semibold",className:p.groupLabel,children:x.label}),x.kinds.map(m=>e.jsx(O,{appearance:"subtle",className:p.kindButton,onClick:()=>l(Ce(m)),children:V[m]??m},m))]},x.label))]})},Se=L({step:{display:"flex",flexDirection:"column",gap:o.spacingVerticalXS,padding:o.spacingVerticalS,border:`1px solid ${o.colorNeutralStroke2}`,borderRadius:o.borderRadiusMedium,backgroundColor:o.colorNeutralBackground2},header:{display:"flex",alignItems:"center",justifyContent:"space-between"},kindBadge:{flexShrink:0},fields:{display:"flex",flexDirection:"column",gap:o.spacingVerticalXS},row:{display:"flex",gap:o.spacingHorizontalS,alignItems:"flex-end",flexWrap:"wrap"},nested:{display:"flex",flexDirection:"column",gap:o.spacingVerticalXS,paddingLeft:o.spacingHorizontalM,borderLeft:`2px solid ${o.colorBrandStroke2}`},nestedToolbar:{display:"flex",alignItems:"center",gap:o.spacingHorizontalXS},pickerSurface:{minWidth:"300px",maxHeight:"50vh",overflowY:"auto",padding:o.spacingVerticalS}}),G=["Create","Read","Write","Delete","Assign","Share","Append","AppendTo"],W=["None","Basic","Local","Deep","Global"],Ee=["DuplicateDetectionEnabled","GridFiltersEnabled","HasStateCode","IsConnectionsEnabled","MailMergeEnabled","WorksWithQueue","HasActivities","IsActivity","HasNotes","IsCustomizable","IsActivityParty","HasEmailAddresses","IsChildEntity","IsImportable","IsEnabledForCharts","IsBusinessProcessEnabled","HasFeedback","IsBPFEntity"],Te=["ExportToExcel","MailMerge","GoOffline","OfflineSynchronize","PublishReports"],le=({step:l,onChange:t,onRemove:d,container:p})=>{const c=Se(),[f,R]=te.useState(null),x=l.kind!=="OrRule"&&l.kind!=="RawRule",m=x?l.invertResult:void 0,C=x?l.default:void 0,r=a=>{t({...l,...a})},k=a=>{if(!a.trim())return;const n=parseInt(a,10);return Number.isNaN(n)?void 0:n},A=()=>{switch(l.kind){case"CommandClientTypeRule":return e.jsx(s,{label:"Type",children:e.jsxs(h,{value:l.type,onChange:(a,n)=>r({type:n.value}),children:[e.jsx("option",{value:"Refresh",children:"Refresh (Unified Interface / modern web)"}),e.jsx("option",{value:"Modern",children:"Modern (Dynamics 365 for tablets)"}),e.jsx("option",{value:"Legacy",children:"Legacy (classic ribbon + Outlook list views)"})]})});case"CrmClientTypeRule":return e.jsx(s,{label:"Type",children:e.jsxs(h,{value:l.type,onChange:(a,n)=>r({type:n.value}),children:[e.jsx("option",{value:"Web",children:"Web"}),e.jsx("option",{value:"Outlook",children:"Outlook"})]})});case"FormStateRule":return e.jsx(s,{label:"State",children:e.jsx(h,{value:l.state,onChange:(a,n)=>r({state:n.value}),children:["Create","Existing","ReadOnly","Disabled","BulkEdit"].map(a=>e.jsx("option",{value:a,children:a},a))})});case"FormTypeRule":return e.jsx(s,{label:"Type",children:e.jsx(h,{value:l.type,onChange:(a,n)=>r({type:n.value}),children:["Main","Preview","AppointmentBook","Dashboard","Quick","QuickCreate","Card","MainInteractionCentric"].map(a=>e.jsx("option",{value:a,children:a},a))})});case"SelectionCountRule":return e.jsxs("div",{className:c.row,children:[e.jsx(s,{label:"Minimum",children:e.jsx(y,{value:String(l.minimum??""),onChange:(a,n)=>r({minimum:n.value?parseInt(n.value):void 0}),style:{width:"80px"}})}),e.jsx(s,{label:"Maximum",children:e.jsx(y,{type:"number",value:String(l.maximum??""),onChange:(a,n)=>r({maximum:n.value?parseInt(n.value):void 0}),style:{width:"80px"}})}),e.jsx(s,{label:"Applies To",children:e.jsxs(h,{value:l.appliesTo??"",onChange:(a,n)=>r({appliesTo:n.value}),children:[e.jsx("option",{value:"",children:"Any"}),e.jsx("option",{value:"PrimaryEntity",children:"PrimaryEntity"}),e.jsx("option",{value:"SelectedEntity",children:"SelectedEntity"})]})})]});case"EntityRule":return e.jsxs("div",{className:c.row,children:[e.jsx(s,{label:"Entity name",children:e.jsx(y,{value:l.entityName??"",onChange:(a,n)=>r({entityName:n.value||void 0})})}),e.jsx(s,{label:"Applies To",children:e.jsxs(h,{value:l.appliesTo??"",onChange:(a,n)=>r({appliesTo:n.value}),children:[e.jsx("option",{value:"",children:"Any"}),e.jsx("option",{value:"PrimaryEntity",children:"PrimaryEntity"}),e.jsx("option",{value:"SelectedEntity",children:"SelectedEntity"})]})}),e.jsx(s,{label:"Context",children:e.jsxs(h,{value:l.context??"",onChange:(a,n)=>r({context:n.value||void 0}),children:[e.jsx("option",{value:"",children:"Any"}),e.jsx("option",{value:"Form",children:"Form"}),e.jsx("option",{value:"HomePageGrid",children:"HomePageGrid"}),e.jsx("option",{value:"SubGridStandard",children:"SubGridStandard"}),e.jsx("option",{value:"SubGridAssociated",children:"SubGridAssociated"})]})})]});case"FormEntityContextRule":return e.jsx(s,{label:"Entity name",children:e.jsx(y,{value:l.entityName,onChange:(a,n)=>r({entityName:n.value})})});case"CustomRule":return e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:c.row,children:[e.jsx(s,{label:"Library ($webresource:)",children:e.jsx(y,{value:l.library,onChange:(a,n)=>r({library:n.value}),placeholder:"$webresource:..."})}),e.jsx(s,{label:"Function name",children:e.jsx(y,{value:l.functionName,onChange:(a,n)=>r({functionName:n.value})})})]}),e.jsx(s,{label:"Parameters",children:e.jsx(xe,{params:l.params??[],onChange:a=>r({params:a.length?a:void 0})})}),e.jsx(E,{size:100,style:{color:o.colorNeutralForeground3},children:"Async support: on Unified Interface, custom rules can return Promise<boolean>; unresolved promises default to false after 10 seconds."})]});case"CrmOfflineAccessStateRule":return e.jsx(s,{label:"State",children:e.jsxs(h,{value:l.state,onChange:(a,n)=>r({state:n.value}),children:[e.jsx("option",{value:"Online",children:"Online"}),e.jsx("option",{value:"Offline",children:"Offline"})]})});case"CrmOutlookClientTypeRule":return e.jsx(s,{label:"Type",children:e.jsxs(h,{value:l.type,onChange:(a,n)=>r({type:n.value}),children:[e.jsx("option",{value:"CrmForOutlook",children:"CrmForOutlook"}),e.jsx("option",{value:"CrmForOutlookOfflineAccess",children:"CrmForOutlookOfflineAccess"})]})});case"CrmOutlookClientVersionRule":return e.jsxs("div",{className:c.row,children:[e.jsx(s,{label:"Major",children:e.jsx(y,{type:"number",value:String(l.major),onChange:(a,n)=>r({major:k(n.value)??l.major}),style:{width:"84px"}})}),e.jsx(s,{label:"Minor",children:e.jsx(y,{type:"number",value:String(l.minor??""),onChange:(a,n)=>r({minor:k(n.value)}),style:{width:"84px"}})}),e.jsx(s,{label:"Build",children:e.jsx(y,{type:"number",value:String(l.build??""),onChange:(a,n)=>r({build:k(n.value)}),style:{width:"84px"}})}),e.jsx(s,{label:"Revision",children:e.jsx(y,{type:"number",value:String(l.revision??""),onChange:(a,n)=>r({revision:k(n.value)}),style:{width:"84px"}})})]});case"DeviceTypeRule":return e.jsx(s,{label:"Device type",children:e.jsxs(h,{value:l.type,onChange:(a,n)=>r({type:n.value}),children:[e.jsx("option",{value:"None",children:"None"}),e.jsx("option",{value:"Phone",children:"Phone"}),e.jsx("option",{value:"Tablet",children:"Tablet"}),e.jsx("option",{value:"Web",children:"Web"}),e.jsx("option",{value:"Outlook",children:"Outlook"}),e.jsx("option",{value:"InteractionCentric",children:"InteractionCentric"})]})});case"EntityPrivilegeRule":return e.jsxs("div",{className:c.row,children:[e.jsx(s,{label:"Entity (optional)",children:e.jsx(y,{value:l.entityName??"",onChange:(a,n)=>r({entityName:n.value||void 0})})}),e.jsx(s,{label:"Applies To",children:e.jsxs(h,{value:l.appliesTo??"",onChange:(a,n)=>r({appliesTo:n.value||void 0}),children:[e.jsx("option",{value:"",children:"Any"}),e.jsx("option",{value:"PrimaryEntity",children:"PrimaryEntity"}),e.jsx("option",{value:"SelectedEntity",children:"SelectedEntity"})]})}),e.jsx(s,{label:"Privilege",children:e.jsx(h,{value:l.privilegeType,onChange:(a,n)=>r({privilegeType:n.value}),children:G.map(a=>e.jsx("option",{value:a,children:a},a))})}),e.jsx(s,{label:"Depth",children:e.jsx(h,{value:l.privilegeDepth,onChange:(a,n)=>r({privilegeDepth:n.value}),children:W.map(a=>e.jsx("option",{value:a,children:a},a))})})]});case"EntityPropertyRule":return e.jsxs("div",{className:c.row,children:[e.jsx(s,{label:"Entity (optional)",children:e.jsx(y,{value:l.entityName??"",onChange:(a,n)=>r({entityName:n.value||void 0})})}),e.jsx(s,{label:"Applies To",children:e.jsxs(h,{value:l.appliesTo??"",onChange:(a,n)=>r({appliesTo:n.value||void 0}),children:[e.jsx("option",{value:"",children:"Any"}),e.jsx("option",{value:"PrimaryEntity",children:"PrimaryEntity"}),e.jsx("option",{value:"SelectedEntity",children:"SelectedEntity"})]})}),e.jsx(s,{label:"Property",children:e.jsx(h,{value:l.propertyName,onChange:(a,n)=>r({propertyName:n.value}),children:Ee.map(a=>e.jsx("option",{value:a,children:a},a))})}),e.jsx(w,{label:"Property value",checked:l.propertyValue,onChange:(a,n)=>r({propertyValue:n.checked===!0})})]});case"ValueRule":return e.jsxs("div",{className:c.row,children:[e.jsx(s,{label:"Field",children:e.jsx(y,{value:l.field,onChange:(a,n)=>r({field:n.value})})}),e.jsx(s,{label:"Value",children:e.jsx(y,{value:l.value,onChange:(a,n)=>r({value:n.value})})})]});case"OrRule":{const a=g=>t({...l,groups:g.filter(b=>b.length>0)}),n=(g,b)=>e.jsxs(Q,{open:f===g,onOpenChange:(v,u)=>R(u.open?g:null),children:[e.jsx(Y,{children:e.jsx(O,{icon:e.jsx(K,{}),appearance:"secondary",size:"small",children:b})}),e.jsx(J,{className:c.pickerSurface,children:e.jsx(ee,{container:p,insideOr:!0,onSelect:v=>{a(g==="new"?[...l.groups,[v]]:l.groups.map((u,S)=>S===g?[...u,v]:u)),R(null)}})})]});return e.jsxs("div",{className:c.nested,children:[l.groups.map((g,b)=>e.jsxs("div",{className:c.nested,children:[e.jsxs(E,{size:100,weight:"semibold",children:[b===0?"Either":"Or",g.length>1?" (all of)":""]}),g.map((v,u)=>e.jsx(le,{step:v,container:p,onChange:S=>a(l.groups.map((T,D)=>D===b?T.map((P,M)=>M===u?S:P):T)),onRemove:()=>a(l.groups.map((S,T)=>T===b?S.filter((D,P)=>P!==u):S))},`${b}-${u}-${v.kind}`)),e.jsx("div",{className:c.nestedToolbar,children:n(b,"And…")})]},b)),e.jsxs("div",{className:c.nestedToolbar,children:[n("new","Add Or branch"),e.jsx(E,{size:100,style:{color:o.colorNeutralForeground3},children:"An OrRule needs at least two branches."})]})]})}case"FeatureControlRule":return e.jsxs("div",{className:c.row,children:[e.jsx(s,{label:"Feature control bit",children:e.jsx(y,{value:l.featureControlBit,onChange:(a,n)=>r({featureControlBit:n.value})})}),e.jsx(w,{label:"Expected value",checked:l.expectedValue,onChange:(a,n)=>r({expectedValue:n.checked===!0})})]});case"RawRule":return e.jsxs(e.Fragment,{children:[e.jsx(E,{size:100,style:{color:o.colorNeutralForeground3},children:"Command Bar Studio doesn't edit this rule; it is kept exactly as loaded."}),e.jsx("pre",{style:{margin:0,whiteSpace:"pre-wrap",fontSize:"11px"},children:l.xml})]});case"SkuRule":return e.jsx(s,{label:"SKU",children:e.jsxs(h,{value:l.sku,onChange:(a,n)=>r({sku:n.value}),children:[e.jsx("option",{value:"Online",children:"Online"}),e.jsx("option",{value:"OnPremise",children:"OnPremise"}),e.jsx("option",{value:"Spla",children:"Spla"})]})});case"OrganizationSettingRule":return e.jsx(s,{label:"Setting",children:e.jsx(h,{value:l.setting,onChange:(a,n)=>r({setting:n.value}),children:["IsSharepointEnabled","IsSOPIntegrationEnabled","IsFiscalCalendarDefined","IsReadFormModeDefined","IsBPFEntityCustomizationFeatureEnabled"].map(a=>e.jsx("option",{value:a,children:a},a))})});case"MiscellaneousPrivilegeRule":return e.jsxs("div",{className:c.row,children:[e.jsxs(s,{label:"Privilege name",children:[e.jsx(y,{value:l.privilegeName,onChange:(a,n)=>r({privilegeName:n.value}),placeholder:"ExportToExcel",list:"misc-privilege-suggestions"}),e.jsx("datalist",{id:"misc-privilege-suggestions",children:Te.map(a=>e.jsx("option",{value:a},a))})]}),e.jsx(s,{label:"Depth (optional)",children:e.jsxs(h,{value:l.privilegeDepth??"",onChange:(a,n)=>r({privilegeDepth:n.value||void 0}),children:[e.jsx("option",{value:"",children:"Any"}),W.map(a=>e.jsx("option",{value:a,children:a},a))]})})]});case"OptionSetRule":return e.jsxs("div",{className:c.row,children:[e.jsx(s,{label:"Option set",children:e.jsx(y,{value:l.optionSet,onChange:(a,n)=>r({optionSet:n.value})})}),e.jsx(s,{label:"State code",children:e.jsx(y,{value:l.stateCode,onChange:(a,n)=>r({stateCode:n.value})})}),e.jsx(s,{label:"Object type code",children:e.jsx(y,{value:l.objectTypeCode,onChange:(a,n)=>r({objectTypeCode:n.value})})})]});case"OutlookItemTrackingRule":return e.jsxs("div",{className:c.row,children:[e.jsx(w,{label:"Tracked in CRM",checked:l.trackedInCrm,onChange:(a,n)=>r({trackedInCrm:n.checked===!0})}),e.jsx(s,{label:"Applies To",children:e.jsx(h,{value:l.appliesTo??"PrimaryEntity",onChange:(a,n)=>r({appliesTo:n.value}),children:e.jsx("option",{value:"PrimaryEntity",children:"PrimaryEntity"})})})]});case"OutlookRenderTypeRule":return e.jsx(s,{label:"Render type",children:e.jsxs(h,{value:l.type,onChange:(a,n)=>r({type:n.value}),children:[e.jsx("option",{value:"Web",children:"Web"}),e.jsx("option",{value:"Outlook",children:"Outlook"})]})});case"OutlookVersionRule":return e.jsx(s,{label:"Outlook version",children:e.jsxs(h,{value:l.version,onChange:(a,n)=>r({version:n.value}),children:[e.jsx("option",{value:"2003",children:"2003"}),e.jsx("option",{value:"2007",children:"2007"}),e.jsx("option",{value:"2010",children:"2010"})]})});case"PageRule":return e.jsx(s,{label:"Address pattern",children:e.jsx(y,{value:l.address,onChange:(a,n)=>r({address:n.value}),placeholder:"main.aspx"})});case"RecordPrivilegeRule":return e.jsxs("div",{className:c.row,children:[e.jsx(s,{label:"Privilege",children:e.jsx(h,{value:l.privilegeType,onChange:(a,n)=>r({privilegeType:n.value}),children:G.map(a=>e.jsx("option",{value:a,children:a},a))})}),e.jsx(s,{label:"Applies To",children:e.jsxs(h,{value:l.appliesTo??"",onChange:(a,n)=>r({appliesTo:n.value||void 0}),children:[e.jsx("option",{value:"",children:"Default"}),e.jsx("option",{value:"PrimaryEntity",children:"PrimaryEntity"})]})})]});case"RelationshipTypeRule":return e.jsxs("div",{className:c.row,children:[e.jsx(s,{label:"Applies To",children:e.jsx(h,{value:l.appliesTo,disabled:!0,children:e.jsx("option",{value:"SelectedEntity",children:"SelectedEntity"})})}),e.jsx(s,{label:"Relationship type",children:e.jsxs(h,{value:l.relationshipType??"",onChange:(a,n)=>r({relationshipType:n.value||void 0}),children:[e.jsx("option",{value:"",children:"Any"}),e.jsx("option",{value:"OneToMany",children:"OneToMany"}),e.jsx("option",{value:"ManyToMany",children:"ManyToMany"}),e.jsx("option",{value:"NoRelationship",children:"NoRelationship"})]})}),e.jsx(w,{label:"Allow custom relationship",checked:l.allowCustomRelationship===!0,onChange:(a,n)=>r({allowCustomRelationship:n.checked===!0})}),e.jsx(w,{label:"Allow system relationship",checked:l.allowSystemRelationship===!0,onChange:(a,n)=>r({allowSystemRelationship:n.checked===!0})})]});default:return e.jsxs(E,{size:200,italic:!0,style:{color:o.colorNeutralForeground3},children:[l.kind," — no additional parameters"]})}};return e.jsxs("div",{className:c.step,role:"listitem",children:[e.jsxs("div",{className:c.header,children:[e.jsx(q,{appearance:"outline",size:"small",className:c.kindBadge,children:V[l.kind]??l.kind}),e.jsx(O,{appearance:"subtle",icon:e.jsx(X,{}),size:"small",onClick:d,"aria-label":`Remove ${l.kind} step`})]}),e.jsxs("div",{className:c.fields,children:[A(),e.jsx("div",{className:c.row,children:x&&e.jsxs(e.Fragment,{children:[e.jsx(s,{label:"Default",title:"Value the rule returns when it cannot be evaluated.",children:e.jsxs(h,{value:C===void 0?"":String(C),onChange:(a,n)=>r({default:n.value===""?void 0:n.value==="true"}),children:[e.jsx("option",{value:"",children:"(not set)"}),e.jsx("option",{value:"true",children:"true"}),e.jsx("option",{value:"false",children:"false"})]})}),e.jsx(w,{label:"Invert result",checked:m===!0,title:"Reverse the evaluated result.",onChange:(a,n)=>r({invertResult:n.checked===!0?!0:void 0})})]})})]})]})};function Ne(l){return l.length===0?"No steps defined — rule always passes.":l.map(B).join(" AND ")}function B(l){const t="invertResult"in l&&l.invertResult?"NOT ":"";switch(l.kind){case"SelectionCountRule":{const d=l.minimum!==void 0?`at least ${l.minimum}`:null,p=l.maximum!==void 0?`at most ${l.maximum}`:null,c=[d,p].filter(Boolean).join(" and "),f=l.appliesTo?` (${l.appliesTo})`:"";return`${t}Selection count is ${c}${f}`}case"EntityRule":{const d=l.entityName??"any entity",p=l.context?` in ${l.context} context`:"",c=l.appliesTo?` (${l.appliesTo})`:"";return`${t}Entity is "${d}"${p}${c}`}case"FormStateRule":return`${t}Form state is "${l.state}"`;case"CommandClientTypeRule":return`${t}Client type is "${l.type}" (${$e(l.type)})`;case"CrmClientTypeRule":return`${t}CRM client type is "${l.type}"`;case"CustomRule":return`${t}Custom rule: ${l.functionName}() from ${l.library}`;case"OrRule":return`(${l.groups.map(p=>p.length===1?B(p[0]):`(${p.map(B).join(" AND ")})`).join(" OR ")})`;case"FeatureControlRule":return`${t}Feature "${l.featureControlBit}" is ${l.expectedValue?"on":"off"}`;case"RawRule":return`${l.tagName} (kept as XML)`;case"MiscellaneousPrivilegeRule":{const d=l.privilegeDepth?` at ${l.privilegeDepth} depth`:"";return`${t}User has "${l.privilegeName}" privilege${d}`}case"EntityPrivilegeRule":{const d=l.entityName?` on "${l.entityName}"`:"",p=l.appliesTo?` (${l.appliesTo})`:"";return`${t}User has ${l.privilegeType} privilege${d} at ${l.privilegeDepth} depth${p}`}case"EntityPropertyRule":{const d=l.entityName?` for "${l.entityName}"`:"";return`${t}Entity property "${l.propertyName}" is ${l.propertyValue}${d}`}case"FormTypeRule":return`${t}Form type is "${l.type}"`;case"FormEntityContextRule":return`${t}Form entity context is "${l.entityName}"`;case"ValueRule":return`${t}Field "${l.field}" equals "${l.value}"`;case"SkuRule":return`${t}SKU is "${l.sku}"`;case"HideForTabletExperienceRule":return`${t}Hide for tablet experience`;case"OrganizationSettingRule":return`${t}Organization setting "${l.setting}" is enabled`;case"PageRule":return`${t}Page address matches "${l.address}"`;case"RecordPrivilegeRule":{const d=l.appliesTo?` (${l.appliesTo})`:"";return`${t}User has ${l.privilegeType} privilege on current record${d}`}case"RelationshipTypeRule":{const d=l.relationshipType??"any";return`${t}Relationship type is "${d}" (${l.appliesTo})`}case"ReferencingAttributeRequiredRule":return`${t}Referencing attribute is required`;case"OutlookItemTrackingRule":return`${t}Outlook item is ${l.trackedInCrm?"":"not "}tracked in CRM`;case"OutlookRenderTypeRule":return`${t}Outlook render type is "${l.type}"`;case"OutlookVersionRule":return`${t}Outlook version is "${l.version}"`;case"CrmOfflineAccessStateRule":return`${t}CRM offline state is "${l.state}"`;case"CrmOutlookClientTypeRule":return`${t}CRM Outlook client type is "${l.type}"`;case"CrmOutlookClientVersionRule":return`${t}CRM Outlook client version is ${l.major}.${l.minor??0}+`;case"DeviceTypeRule":return`${t}Device type is "${l.type}"`;case"OptionSetRule":return`${t}Option set "${l.optionSet}" state code is "${l.stateCode}"`;default:return"Unknown rule step"}}function $e(l){switch(l){case"Modern":return"Dynamics 365 tablets";case"Refresh":return"Unified Interface / modern web";case"Legacy":return"Classic ribbon / Outlook list views";default:return l}}let Oe=0;const U=(l,t)=>Array.from({length:t},()=>`${l}#${++Oe}`);function ke(l,t){const[d,p]=I.useState(null),c=I.useMemo(()=>U(l,t.length),[l,t]),f=!!d&&d.scope===l&&d.keys.length===t.length&&(d.items===t||d.pending);d?.pending&&p({...d,items:f?t:d.items,pending:!1});const R=f?d.keys:c,x=m=>p({scope:l,items:t,pending:!0,keys:m});return{keys:R,add:()=>x([...R,...U(l,1)]),remove:m=>x(R.filter((C,r)=>r!==m)),move:(m,C)=>x(Z(R,m,C)),update:()=>x(R)}}const we=L({drawer:{width:"560px",maxWidth:"90vw"},body:{display:"flex",flexDirection:"column",gap:o.spacingVerticalS,padding:o.spacingVerticalM,overflowY:"auto"},header:{display:"flex",alignItems:"center",gap:o.spacingHorizontalS},stepsList:{display:"flex",flexDirection:"column",gap:o.spacingVerticalXS,listStyle:"none",padding:0,margin:0},summary:{padding:o.spacingVerticalS,backgroundColor:o.colorNeutralBackground3,borderRadius:o.borderRadiusMedium,fontStyle:"italic",color:o.colorNeutralForeground2,fontSize:o.fontSizeBase200},addRow:{display:"flex",gap:o.spacingHorizontalS},pickerPopover:{maxHeight:"60vh",overflowY:"auto",minWidth:"320px",padding:o.spacingVerticalM},usedBy:{color:o.colorNeutralForeground3,fontSize:o.fontSizeBase200},ruleIdRow:{display:"flex",alignItems:"flex-end",gap:o.spacingHorizontalS},ruleIdInput:{fontFamily:o.fontFamilyMonospace},error:{color:o.colorPaletteRedForeground1,fontSize:o.fontSizeBase100},sortableItem:{listStyle:"none"},sortableInner:{display:"flex",alignItems:"flex-start",gap:o.spacingHorizontalS},dragHandle:{marginTop:o.spacingVerticalS,cursor:"grab"}}),Ie=[],Le=()=>{const l=we(),t=_(i=>i.ruleEditorOpen),d=_(i=>i.ruleEditorId),p=_(i=>i.ruleEditorKind),c=_(i=>i.closeRuleEditor),f=_(i=>i.openRuleEditor),R=$(i=>i.enableRules),x=$(i=>i.displayRules),m=$(i=>i.commands),C=$(i=>i.upsertEnableRule),r=$(i=>i.upsertDisplayRule),k=$(i=>i.renameEnableRuleId),A=$(i=>i.renameDisplayRuleId),[a,n]=I.useState(!1),[g,b]=I.useState(""),v=p==="enable",u=v?R.find(i=>i.id===d):x.find(i=>i.id===d);I.useEffect(()=>{u&&b(u.id)},[u]);const S=ke(`${p}:${u?.id??""}`,u?.steps??Ie),T=S.keys,D=m.filter(i=>v?i.enableRules.includes(d??""):i.displayRules.includes(d??"")).length,P=(i,j)=>{if(!u)return;const N=[...u.steps];N[i]=j,S.update(),v?C({...u,steps:N}):r({...u,steps:N})},M=i=>{if(!u)return;const j=u.steps.filter((N,F)=>F!==i);S.remove(i),v?C({...u,steps:j}):r({...u,steps:j})},ne=i=>{if(!u)return;const j=[...u.steps,i];S.add(),v?C({...u,steps:j}):r({...u,steps:j}),n(!1)},ae=i=>{if(!u||!i.over||i.active.id===i.over.id)return;const j=T.indexOf(String(i.active.id)),N=T.indexOf(String(i.over.id));if(j<0||N<0)return;const F=Z(u.steps,j,N);S.move(j,N),v?C({...u,steps:F}):r({...u,steps:F})};if(!u)return null;const re=Ne(u.steps),ie=(v?R:x).some(i=>i.id===g.trim()&&i.id!==u.id),z=g.trim()?/\s/.test(g)?"Rule ID cannot contain whitespace.":ie?"A rule with this ID already exists.":"":"Rule ID is required.";return e.jsxs(ce,{position:"end",open:t,onOpenChange:()=>c(),className:l.drawer,children:[e.jsx(ue,{children:e.jsx(de,{action:e.jsx(O,{appearance:"subtle",icon:e.jsx(X,{}),onClick:c,"aria-label":"Close rule editor"}),children:e.jsxs("div",{className:l.header,children:[e.jsx(q,{appearance:"filled",color:v?"success":"informative",size:"small",children:v?"Enable":"Display"}),e.jsx(E,{size:400,weight:"semibold",style:{fontFamily:o.fontFamilyMonospace},children:u.id})]})})}),e.jsxs(pe,{className:l.body,children:[e.jsxs(E,{className:l.usedBy,children:["Used by ",D," command(s)"]}),e.jsxs("div",{className:l.ruleIdRow,children:[e.jsx(y,{className:l.ruleIdInput,value:g,onChange:(i,j)=>b(j.value),"aria-label":"Rule ID"}),e.jsx(O,{appearance:"secondary",size:"small",disabled:!g.trim()||!!z||g===u.id,onClick:()=>{const i=g.trim();(v?k(u.id,i):A(u.id,i))&&f(i,v?"enable":"display")},children:"Apply ID change"})]}),z&&e.jsx(E,{className:l.error,children:z}),v&&e.jsx(he,{intent:"warning",children:e.jsx(me,{children:"Enable rules have a restricted set of allowed step types. Display-only rule kinds are disabled."})}),e.jsx(H,{children:"Steps (ALL must pass)"}),e.jsx(ye,{collisionDetection:ve,onDragEnd:ae,children:e.jsx(je,{items:T,strategy:ge,children:e.jsxs("ol",{className:l.stepsList,role:"list","aria-label":"Rule steps",children:[u.steps.map((i,j)=>e.jsx(Pe,{id:T[j],styles:l,children:e.jsx(le,{step:i,onChange:N=>P(j,N),onRemove:()=>M(j),container:v?"enable":"display"})},T[j])),u.steps.length===0&&e.jsx(E,{italic:!0,style:{color:o.colorNeutralForeground3},children:"No steps yet — all buttons with this rule will be visible/enabled by default."})]})})}),e.jsxs(Q,{open:a,onOpenChange:(i,j)=>n(j.open),children:[e.jsx(Y,{children:e.jsx(O,{icon:e.jsx(K,{}),appearance:"secondary",onClick:()=>n(!0),children:"Add step"})}),e.jsx(J,{className:l.pickerPopover,children:e.jsx(ee,{onSelect:ne,container:v?"enable":"display"})})]}),e.jsx(H,{children:"Plain-English summary"}),e.jsx("div",{className:l.summary,role:"note","aria-label":"Rule summary",children:re})]})]})};function Pe({id:l,children:t,styles:d}){const{attributes:p,listeners:c,setNodeRef:f,setActivatorNodeRef:R,transform:x,transition:m}=Re({id:l});return e.jsx("li",{ref:f,className:d.sortableItem,style:{transform:be.Transform.toString(x),transition:m},children:e.jsxs("div",{className:d.sortableInner,children:[e.jsx(O,{ref:R,appearance:"subtle",size:"small",className:d.dragHandle,...p,...c,"aria-label":"Drag to reorder step",children:"Drag"}),e.jsx("div",{style:{flex:1},children:t})]})})}export{Le as RuleEditorPanel,Le as default};
@@ -1,3 +0,0 @@
1
- import{a5 as T,a6 as ee,a7 as te,a8 as B,a9 as ne,p as oe}from"./index-CKs2IGe0.js";import{D as se,E as ae}from"./ruleKinds-B9hACGaq.js";import{C as ie,i as re}from"./crmParameters-rgfZhhzN.js";const le=["Mscrm.HideOnModern","Mscrm.ShowOnlyOnModern"];function R(c,l){const b=c.indexOf(l);return b<0?void 0:`Line ${c.slice(0,b).split(/\r?\n/).length}`}function we(c,l,b){const d=[],y=new DOMParser().parseFromString(c,"application/xml"),$=y.querySelector("parsererror");if($){const i=$.textContent?.trim()??"Unknown",u=i.match(/line\s+(\d+)/i);return d.push({message:`XML parse error: ${i}`,lineHint:u?`Line ${u[1]}`:void 0}),d}const f=new Set([]);return y.querySelectorAll("[Id]").forEach(i=>{const u=i.getAttribute("Id")??"";if(/\s/.test(u)){const A=R(c,`Id="${u}"`);d.push({message:`Element ID "${u}" contains invalid whitespace characters`,elementId:u,lineHint:A})}}),y.querySelectorAll("[LabelText]").forEach(i=>{const u=i.getAttribute("LabelText")??"",A=u.match(/^\$LocLabels:(.+)$/);if(A&&f.size>0&&!f.has(A[1])){const g=i.getAttribute("Id")??"",I=R(c,g?`Id="${g}"`:`LabelText="${u}"`);d.push({message:`Button label reference "$LocLabels:${A[1]}" has no matching LocLabel entry`,elementId:g||void 0,lineHint:I})}}),(c.includes("& ")||c.includes(`&
2
- `))&&d.push({message:'URL may contain an unescaped "&" — all ampersands in XML must be written as &amp;',lineHint:R(c,"& ")??R(c,`&
3
- `)}),y.querySelectorAll("Group").forEach(i=>{const u=[];i.querySelectorAll(":scope > Controls > Button, :scope > Controls > FlyoutAnchor, :scope > Controls > SplitButton").forEach(A=>{const g=parseInt(A.getAttribute("Sequence")??"0",10);if(u.includes(g)){const I=i.getAttribute("Id")??"?";d.push({message:`Two elements share Sequence "${g}" in group "${I}"`,elementId:I,lineHint:R(c,`Sequence="${g}"`)})}u.push(g)})}),d.push(...ce(y,c)),d}const H=new Set(["BoolParameter","CrmParameter","DecimalParameter","IntParameter","StringParameter"]);function p(c){return Array.from(c.children)}function X(c,l){return l==="enable"?se.has(c):ae.has(c)}function ce(c,l){const b=[],d=(f,i,u)=>b.push({message:f,elementId:u,lineHint:R(l,i)}),w=(f,i,u)=>b.push({message:f,elementId:u,lineHint:R(l,i),severity:"warning"}),y=c.querySelector("RibbonDiffXml > RuleDefinitions"),$=[{tag:"EnableRule",label:"Enable",container:"enable"},{tag:"DisplayRule",label:"Display",container:"display"}];for(const{tag:f,label:i,container:u}of $)y?.querySelectorAll(`:scope > ${f}s > ${f}`).forEach(A=>{const g=A.getAttribute("Id")??"",I=p(A);I.length===0&&d(`${i} rule "${g}" has no steps — the schema requires at least one`,`Id="${g}"`,g);const M=S=>{X(S.tagName,u)&&w(`${i} rule "${g}" contains ${S.tagName}, which RibbonTypes.xsd only allows in ${u==="enable"?"display":"enable"} rules`,`<${S.tagName}`,g)};for(const S of I){if(S.tagName!=="OrRule"){M(S);continue}const L=p(S);L.filter(N=>N.tagName==="Or").length<2&&d(`OrRule in ${i.toLowerCase()} rule "${g}" needs at least 2 <Or> children (found ${L.filter(N=>N.tagName==="Or").length})`,"<OrRule",g);for(const N of L){const E=p(N);E.length===0&&d(`Empty <Or> in rule "${g}"`,"<Or",g);for(const v of E)v.tagName==="OrRule"?d(`Rule "${g}": an <Or> cannot contain another OrRule`,"<OrRule",g):M(v)}}});return y||c.querySelectorAll("OrRule").forEach(f=>{const i=f.querySelectorAll(":scope > Or").length;i<2&&d(`OrRule requires at least 2 <Or> children (found ${i})`,"<OrRule")}),c.querySelectorAll("JavaScriptFunction, CustomRule").forEach(f=>{for(const i of p(f)){if(!H.has(i.tagName))continue;i.hasAttribute("Name")&&d(`${i.tagName} inside <${f.tagName}> must not have a Name attribute (only <Url> parameters are named)`,`<${i.tagName} Name=`);const u=i.getAttribute("Value")??"";i.tagName==="CrmParameter"&&!ie.has(u)&&d(`CrmParameter Value "${u}" is not a valid CrmParameterValue`,`Value="${u}"`)}}),c.querySelectorAll("Url").forEach(f=>{for(const i of p(f)){if(!H.has(i.tagName))continue;i.getAttribute("Name")||d(`${i.tagName} inside <Url> requires a Name (the query-string key)`,`<${i.tagName}`);const u=i.getAttribute("Value")??"";i.tagName==="CrmParameter"&&!re(u)&&d(`CrmParameter Value "${u}" is not allowed in a <Url> action (CrmNamedParameterValue)`,`Value="${u}"`)}}),c.querySelector("RibbonDiffXml > Scalings")&&d('<Scalings> is not allowed in RibbonDiffXml — scaling changes are CustomActions with Location="{tabId}.Scaling._children"',"<Scalings"),c.querySelectorAll("CustomAction > CommandUIDefinition > Tab").forEach(f=>{const i=f.getAttribute("Id")??"",u=p(f).map(I=>I.tagName),A=u.indexOf("Scaling"),g=u.indexOf("Groups");(A<0||g>=0&&A>g)&&d(`Tab "${i}" needs a <Scaling Id> element before <Groups>`,`Id="${i}"`,i)}),c.querySelectorAll("TabDisplayRule > DisplayRules").forEach(()=>{d("TabDisplayRule rules must be direct children — remove the <DisplayRules> wrapper","<TabDisplayRule")}),b}const ue=["LabelText","ToolTipTitle","ToolTipDescription","Alt","Title","Description"],de=new Set(["powerFx","customApi"]),fe={powerFx:"Power Fx",customApi:"Custom API"};function me(c){return[...new Set(c.actions.map(l=>l.kind).filter(l=>de.has(l)))]}const x=/^\$LocLabels:(.+)$/,Y=/^\$webresource:([^?#]+)/i,he=new Set(["ModernImage","Image16by16","Image32by32"]),F=new Set(["Tab","Group"]),Q=new Set(["main_system_library.js"]),ge=new Set(["FlyoutAnchor","SplitButton"]),pe=new Set(["Button","FlyoutAnchor","SplitButton","TextBox","ComboBox","CheckBox"]),V=/^(.+)\.Scaling\._children$/,W=c=>/^(Mscrm|Microsoft)\./.test(c);function J(c){const l=[],b=[],d=new DOMParser;for(const w of Object.keys(c))for(const[y,$]of c[w]){const f=d.parseFromString($,"application/xml");if(f.querySelector("parsererror")){b.push({severity:"error",code:"xml-parse",message:`${w} element ${y} is not well-formed XML`,elementId:y});continue}l.push({part:w,key:`${w}:${y}`,el:f.documentElement})}return{elements:l,errors:b}}function C(c){return[c,...Array.from(c.getElementsByTagName("*"))]}function Z(c){return typeof c=="string"?oe(c):c}function $e(c){let l;try{l=Z(c)}catch{return[]}const b=new Map;for(const{el:d}of J(l).elements)for(const w of C(d))for(const y of Array.from(w.attributes)){const $=Y.exec(y.value.trim())?.[1],f=$?.toLowerCase();$&&f&&!Q.has(f)&&!b.has(f)&&b.set(f,$)}return[...b.values()]}const K=c=>{const l=c.trim();return l&&!l.includes(".")?`Mscrm.Templates.${l}`:l};function Ie(c,l={}){let b;try{b=Z(c)}catch(e){return[{severity:"error",code:"xml-parse",message:e instanceof Error?e.message:String(e)}]}const{elements:d,errors:w}=J(b),y=l.changedKeys?new Set(l.changedKeys):null,$=e=>!y||y.has(e),f=new Set(l.newKeys??[]),i=(e,o,t,a,n=!1)=>{const s=(Array.isArray(o)?o:[o]).some($);w.push({severity:n||!s?"warning":"error",code:e,message:s?t:`${t} (already in the published customizations)`,elementId:a})},u=d.filter(e=>e.part==="customActions"),A=d.filter(e=>e.part==="commandDefinitions"),g=new Map((l.commands??[]).map(e=>[e.id,e]));for(const{key:e,el:o}of A){const t=o.getAttribute("Id")??"",a=g.get(t);if(!a||!$(e))continue;const n=me(a);n.length!==0&&i("dropped-action",e,`Command ${t} has a ${n.map(s=>fe[s]??s).join(" / ")} action — classic ribbon commands can't run it, so it would be dropped; remove the action`,t)}const I=new Set;for(const{el:e}of u)if(e.tagName==="CustomAction")for(const o of Array.from(e.getElementsByTagName("*")))o.getAttribute("Command")===T&&I.add(o.getAttribute("Id")??"");for(const{key:e,el:o}of u){if(o.tagName!=="HideCustomAction")continue;const t=o.getAttribute("Location")??"";!I.has(t)&&!ee({id:t,commandId:T})||i("open-record-hidden",e,`${t} is hidden, but it carries the record open override (${T}) — Unified Interface may then open records as usual or fail to open them; unhide it, or turn the override off`,t,!0)}for(const{key:e,el:o}of A){if(o.getAttribute("Id")!==T)continue;const t=p(o).filter(n=>n.tagName==="DisplayRules").flatMap(n=>p(n).map(s=>s.getAttribute("Id")??s.tagName));if(t.length===0)continue;const a=le.filter(n=>t.includes(n));i("open-record-hidden",e,`${T} has display rules (${t.join(", ")})${a.length>0?` — ${a.join(" + ")} hide its button`:""}: where they hide the "Open record" button, the record open override may stop working; the button must stay shown`,T,!0)}for(const{key:e,el:o}of u)for(const t of Array.from(o.getElementsByTagName("*"))){if(!ge.has(t.tagName)||p(t).some(n=>n.tagName==="Menu"))continue;const a=t.getAttribute("Id")??"";if(te(t.getAttribute("PopulateDynamically"))){t.getAttribute("PopulateQueryCommand")?.trim()||i("menu-missing",e,`${t.tagName} ${a} is populated dynamically but has no PopulateQueryCommand — its drop-down may stay empty`,a,!0);continue}i("menu-missing",e,`${t.tagName} ${a} has no <Menu> — it would publish as an empty drop-down`,a)}const M=new Set(l.menuControlsIds??[]);for(const{key:e,el:o}of u){const t=o.getAttribute("Location")??"",a=t.endsWith("._children")&&M.has(t.slice(0,-10));for(const n of Array.from(o.getElementsByTagName("*"))){const s=a||!!n.parentElement?.closest("Menu");for(const r of ue){const h=n.getAttribute(r);if(!h?.startsWith("$LocLabels:"))continue;const m=h.slice(11);m.length<=B||i("label-id-length",e,`${n.tagName} ${n.getAttribute("Id")??""} uses label ${m} (${m.length} characters)`+(s?`: a menu item's label id may have at most ${B} characters, or the publish fails after the import — give the control a shorter id`:`: label ids over ${B} characters fail inside menus; consider a shorter control id`),n.getAttribute("Id")??void 0,!s)}}}if(l.known?.commandIds){const e=new Set([...b.commandDefinitions.keys(),...l.known.commandIds]);for(const{key:o,el:t}of u)for(const a of Array.from(t.getElementsByTagName("*"))){const n=a.getAttribute("Command");if(!n||e.has(n)||F.has(a.tagName))continue;const s=a.getAttribute("Id")??"";i("undefined-command",o,`${a.tagName} ${s} uses command ${n}, which is defined neither in the customizations nor in the loaded ribbon`,s,W(n))}}const S={EnableRule:l.known?.enableRuleIds?new Set([...b.enableRules.keys(),...l.known.enableRuleIds]):null,DisplayRule:l.known?.displayRuleIds?new Set([...b.displayRules.keys(),...l.known.displayRuleIds]):null};for(const{key:e,el:o}of A){const t=o.getAttribute("Id")??"",a=p(o).filter(n=>n.tagName==="EnableRules"||n.tagName==="DisplayRules").flatMap(p);for(const n of a){const s=S[n.tagName],r=n.getAttribute("Id");!s||!r||s.has(r)||i("undefined-rule",e,`Command ${t} uses ${n.tagName==="EnableRule"?"enable":"display"} rule ${r}, which is defined neither in the customizations nor in the loaded ribbon`,t,W(r))}}for(const{part:e,key:o,el:t}of d){if(e!=="enableRules"&&e!=="displayRules")continue;const a=e==="enableRules"?"enable":"display",n=t.getAttribute("Id")??"",s=p(t).flatMap(r=>r.tagName==="OrRule"?p(r).filter(h=>h.tagName==="Or").flatMap(p):[r]);for(const r of s)X(r.tagName,a)&&i("rule-placement",o,`${a==="enable"?"Enable":"Display"} rule ${n} contains ${r.tagName}, which the ribbon schema only allows in ${a==="enable"?"display":"enable"} rules`,n,!0)}const L=new Set;for(const e of Object.values(l.loadedAttributes??{}))for(const o of Object.values(e))x.test(o)&&L.add(o);for(const{key:e,el:o}of d)if(o.tagName!=="LocLabel")for(const t of C(o))for(const a of Array.from(t.attributes)){const n=x.exec(a.value)?.[1];if(!n||b.locLabels.has(n))continue;const s=t.getAttribute("Id")??void 0;s&&l.loadedAttributes?.[s]?.[a.name]===a.value||L.has(a.value)||i("loclabel-missing",e,`${[t.tagName,s,a.name].filter(Boolean).join(" ")} references $LocLabels:${n}, which has no <LocLabel> in these customizations — the text won't resolve unless another solution defines the label`,s,!0)}if(l.webResources){const e=new Set;for(const{el:o}of d)for(const t of C(o))for(const a of Array.from(t.attributes)){const n=Y.exec(a.value.trim())?.[1],s=n?.toLowerCase();if(!n||!s||Q.has(s)||e.has(s))continue;const r=l.webResources[s];if(!r)continue;const h=t.getAttribute("Id")??void 0;r.exists?!r.inSolution&&!r.managed&&(e.add(s),w.push({severity:"warning",code:"webresource-not-in-solution",message:`Web resource ${n} isn't in the selected solution — the ribbon won't import into another environment without it`,elementId:h})):(e.add(s),w.push({severity:"warning",code:"webresource-missing",message:`Web resource ${n} (${t.tagName} ${a.name}) doesn't exist in this environment — ${he.has(a.name)?"the button will show no icon":"the action or rule that loads it will fail when it runs"}`,elementId:h}))}}const N=new Map,E=e=>{let o=N.get(e);return o||N.set(e,o=new Map),o};for(const[e,o]of Object.entries(l.tabScaling??{})){for(const t of o.maxSizes)E(e).set(t.id,{...t,tag:"MaxSize"});for(const t of o.scales)E(e).set(t.id,{...t,tag:"Scale"})}const v=new Map(Object.entries(l.groupTemplates??{}).map(([e,o])=>[e,K(o)])),U=new Map(Object.entries(l.templateLayouts??{}));for(const{part:e,el:o}of d)if(e==="templates")for(const t of C(o).filter(a=>a.tagName==="GroupTemplate")){const a=p(t).filter(n=>n.tagName==="Layout").map(n=>n.getAttribute("Title")??"");U.set(t.getAttribute("Id")??"",a)}const z=[];for(const{key:e,el:o}of u){for(const s of Array.from(o.getElementsByTagName("Group"))){const r=s.getAttribute("Template");r&&v.set(s.getAttribute("Id")??"",K(r))}const t=[],a=V.exec(o.getAttribute("Location")??"")?.[1],n=p(o).find(s=>s.tagName==="CommandUIDefinition");if(a&&n)for(const s of p(n))t.push([a,s]);for(const s of Array.from(o.getElementsByTagName("Tab")))for(const r of p(s).filter(h=>h.tagName==="Scaling"))for(const h of p(r))t.push([s.getAttribute("Id")??"",h]);for(const[s,r]of t){if(r.tagName!=="MaxSize"&&r.tagName!=="Scale")continue;const h={id:r.getAttribute("Id")??"",tag:r.tagName,groupId:r.getAttribute("GroupId")??"",size:r.getAttribute("Size")??"",sequence:parseInt(r.getAttribute("Sequence")??"",10),key:e};E(s).set(h.id,h),z.push(h)}}for(const[e,o]of N){const t=new Map;for(const a of o.values())Number.isNaN(a.sequence)||t.set(a.sequence,[...t.get(a.sequence)??[],a]);for(const[a,n]of t){const s=[...new Set(n.flatMap(m=>m.key?[m.key]:[]))];if(n.length<2||s.length===0)continue;const r=n.find(m=>m.key&&m.tag==="MaxSize"&&m.id===`${m.groupId}.MaxSize`),h=r?`move group ${r.groupId} to another position (its MaxSize takes the group's Sequence; #66 picks a free one automatically)`:"give each step its own Sequence";i("scaling-collision",s,`Scaling of tab ${e}: ${n.map(m=>`${m.tag} ${m.id}`).join(" and ")} have the same Sequence ${a} — Unified Interface then drops every custom control of the table; ${h}`,n.find(m=>m.key)?.id)}}for(const e of z){const o=v.get(e.groupId),t=o?U.get(o):void 0;!t?.length||t.includes(e.size)||i("scaling-size",e.key??[],`${e.tag} ${e.id} has Size "${e.size}", which isn't a layout of template ${o} used by group ${e.groupId} (${t.join(", ")})`,e.id,!0)}const G=new Set(l.known?.elementIds??[]),k=new Map;for(const{key:e,el:o}of u){if(!(o.getAttribute("Location")??"").endsWith("._children"))continue;const t=p(o).find(n=>n.tagName==="CommandUIDefinition"),a=t?p(t)[0]:void 0;if(!(!a||a.tagName==="MaxSize"||a.tagName==="Scale"))for(const n of C(a)){const s=n.getAttribute("Id");if(!s||n.closest("Scaling"))continue;const r=k.get(s)??[];r.includes(e)||k.set(s,[...r,e])}}for(const[e,o]of k)o.length>1?i("duplicate-id",o,`Id ${e} is added by more than one CustomAction (${o.map(t=>t.replace(/^customActions:ca:/,"")).join(", ")})`,e):f.has(o[0])&&G.has(e)&&i("duplicate-id",o,`Id ${e} is added as a new element, but the ribbon already has an element with this id`,e);if(l.places){const{entityLogicalName:e,tabByCollection:o}=l.places,t=new Set(["Mscrm.Tabs",`Mscrm.SubGrid.${e}.ContextualTabs`]);for(const{key:a,el:n}of u){const s=n.getAttribute("Location")??"",r=/^(.+)\._children$/.exec(s)?.[1];if(!r||V.test(s)||!$(a))continue;const h=p(n).find(D=>D.tagName==="CommandUIDefinition"),m=h?p(h)[0]:void 0,O=m?.getAttribute("Id")??"";if(!m||!O||!(pe.has(m.tagName)||F.has(m.tagName)))continue;const P=r.split("{!EntityLogicalName}").join(e),q=o[P];if(q!==void 0){ne(q)&&i("orphan-location",a,`${m.tagName} ${O} is added to ${r}, on the tab ${q}: Unified Interface doesn't show that tab and the ribbon doesn't return what is added there, so it won't show anywhere — add it to the table's main tab instead`,O,!0);continue}const j=D=>D.replace(/\.(Controls|Groups)$/,"");t.has(P)||[r,P,j(r),j(P)].some(D=>k.has(D))||i("orphan-location",a,`${m.tagName} ${O} is added at ${s}, but this table's ribbon has no ${r} — it won't show anywhere (check the Location, or move it to an existing group)`,O,!0)}}const _=l.publisherPrefix?.trim().toLowerCase();if(_&&f.size>0){const e=new Set([...G,...l.known?.commandIds??[],...l.known?.enableRuleIds??[],...l.known?.displayRuleIds??[]]),o=n=>{const s=n.split(".");for(let r=s.length;r>0;r--)if(e.has(s.slice(0,r).join(".")))return!0;return!1},t=n=>{const s=n.toLowerCase();return s.startsWith(`${_}.`)||s.startsWith(`${_}_`)},a=new Set;for(const{part:n,key:s,el:r}of d)if(!(!f.has(s)||n==="templates"||n==="tabDisplayRules"))for(const h of C(r)){if(h!==r&&h.closest("Actions, EnableRules, DisplayRules, EnableRule, DisplayRule"))continue;const m=h.getAttribute("Id")??h.getAttribute("HideActionId");!m||a.has(m)||t(m)||o(m)||(a.add(m),w.push({severity:"warning",code:"id-prefix",message:`${h.tagName} id ${m} doesn't start with the publisher prefix "${_}." — ids without it can collide with other solutions`,elementId:m}))}}return w}export{we as a,Ie as v,$e as w};