@adia-ai/web-modules 0.6.34 → 0.6.36

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.
@@ -1,10 +1,10 @@
1
- var S=Symbol("signal"),f=null,h=null,_=!1;function P(t){for(let e of t){if(e.disposed){t.delete(e);continue}e.dirty=!0,h||(h=new Set,_||queueMicrotask(q)),h.add(e)}}function q(){let t,e=0;for(;t=h;){if(++e>100){h=null,console.error("signals: drain loop exceeded 100 iterations");break}h=null;for(let n of t)if(!n.disposed&&n.dirty){n.dirty=!1;try{n.run()}catch(r){n.onError?n.onError(r):(n.disposed=!0,queueMicrotask(()=>{throw r}))}}}}function K(t){if(_){t();return}_=!0;try{t()}finally{_=!1,h&&q()}}function k(t){let e=f;f=null;try{return t()}finally{f=e}}function m(t){let e=new Set;return{[S]:!0,get value(){return f&&e.add(f),t},set value(n){Object.is(t,n)||(t=n,P(e))},peek(){return t}}}function O(t){let e=new Set,n,r=!0,i={dirty:!1,disposed:!1,run(){r=!0,P(e)}};return{[S]:!0,get value(){if(f&&e.add(f),r){let o=f;f=i;try{n=t(),r=!1}finally{f=o}}return n},peek(){return n}}}function p(t,e){let n={dirty:!1,disposed:!1,host:e?.host??null,onError:e?.onError??null,run(){let r=f;f=n;try{t()}finally{f=r}}};return n.run(),()=>{n.disposed=!0}}function v(t){return t!=null&&typeof t=="object"&&t[S]===!0}var[$,y]=[Symbol(),Symbol()];function V(t,...e){return{strings:t,values:e}}function X(t,...e){let n="";for(let i=0;i<t.length;i++)n+=t[i],i<e.length&&(n+=e[i]);let r=new CSSStyleSheet;return r.replaceSync(n),r}var I=new WeakMap;function Y(t){let e=0,n=0,r=0;for(;r<t.length;){if(e===0&&t.charCodeAt(r)===60&&t.charCodeAt(r+1)===33&&t.charCodeAt(r+2)===45&&t.charCodeAt(r+3)===45){let o=t.indexOf("-->",r+4);if(o===-1){n=0;break}r=o+3,n=0;continue}let i=t.charCodeAt(r);e?i===(e===1?39:34)&&(e=0):i===39?e=1:i===34?e=2:i===60?n=1:i===62&&(n=0),r++}return n===1}function J(t){let e=I.get(t);if(e)return e;let n="";for(let r=0;r<t.length;r++)n+=t[r],r<t.length-1&&(n+=Y(n)?`{{p:${r}}}`:`<!--p:${r}-->`);return e=document.createElement("template"),e.innerHTML=n,I.set(t,e),e}var B={classname:"className",innertext:"innerText",innerhtml:"innerHTML",outerhtml:"outerHTML",textcontent:"textContent",tabindex:"tabIndex",arialabel:"ariaLabel",ariadescribedby:"ariaDescribedBy",arialabelledby:"ariaLabelledBy",ariarole:"role",contenteditable:"contentEditable",readonly:"readOnly",maxlength:"maxLength",minlength:"minLength",colspan:"colSpan",rowspan:"rowSpan",cellpadding:"cellPadding",cellspacing:"cellSpacing",usemap:"useMap",ismap:"isMap",accesskey:"accessKey",defaultchecked:"defaultChecked",defaultvalue:"defaultValue",defaultselected:"defaultSelected"};function H(t,e){if(B[e])return B[e];let n=Object.getOwnPropertyNames(t);for(let i=0;i<n.length;i++){let o=n[i];if(o!==e&&o.toLowerCase()===e)return o}let r=Object.getPrototypeOf(t);for(;r&&r!==Object.prototype;){let i=Object.getOwnPropertyNames(r);for(let o=0;o<i.length;o++){let a=i[o];if(a!==e&&a.toLowerCase()===e)return a}r=Object.getPrototypeOf(r)}return e}function g(t,e){let n=e._i;(!n||n.s!==t.strings)&&(n&&C(n.p),n=Q(t,e),e._i=n),ne(n.p,t.values)}function Q(t,e){let{strings:n}=t,i=J(n).content.cloneNode(!0),o=Z(e);o&&(i=ee(i,o));let a=te(i,t.values.length);return e.replaceChildren(i),{s:n,p:a}}var A="http://www.w3.org/2000/svg",b="http://www.w3.org/1998/Math/MathML",D="http://www.w3.org/1999/xhtml";function Z(t){if(!t||t.nodeType!==1)return null;if(t.namespaceURI===A)return A;if(t.namespaceURI===b)return b;if(typeof t.closest!="function")return null;let e=t.closest("svg, math, foreignObject, annotation-xml");if(!e)return null;let n=e.localName;if(n==="foreignobject"||n==="foreignObject")return null;if(n==="annotation-xml"){let r=(e.getAttribute("encoding")||"").toLowerCase();return r==="text/html"||r==="application/xhtml+xml"?null:b}return n==="svg"?A:n==="math"?b:null}function ee(t,e){let n=document.createDocumentFragment();return R(t,n,e),n}function R(t,e,n){for(let r of[...t.childNodes])if(r.nodeType===1){let o=document.createElementNS(n,r.localName);for(let s of r.attributes)s.namespaceURI?o.setAttributeNS(s.namespaceURI,s.name,s.value):o.setAttribute(s.name,s.value);e.appendChild(o);let a=r.localName,c=n;if(n===A&&(a==="foreignObject"||a==="foreignobject"))c=D;else if(n===b&&a==="annotation-xml"){let s=(r.getAttribute("encoding")||"").toLowerCase();(s==="text/html"||s==="application/xhtml+xml")&&(c=D)}R(r,o,c)}else e.appendChild(r.cloneNode(!0))}function te(t,e){let n=new Array(e),r=document.createTreeWalker(t,129),i;for(;i=r.nextNode();)if(i.nodeType===8){let o=i.data.match(/^p:(\d+)$/);if(o){let a=document.createTextNode("");i.replaceWith(a),r.currentNode=a,n[+o[1]]={t:"n",n:a,c:void 0,_fx:null}}}else if(i.nodeType===1)for(let o of[...i.attributes]){let a=o.value.match(/^\{\{p:(\d+)\}\}$/);if(!a){if(o.value.includes("{{p:")){let l=o.name,u=l==="class"?' class="${expression}" \u2190 full replacement (whole class string is the expression)\n .className=${expression} \u2190 writes to the className property (resolved camelCase since FB-55 fix)\n':l==="style"?' style="${expression}" \u2190 full replacement of the style string\n .style.cssText=${expression} \u2190 write CSS text via the style.cssText accessor\n':` ${l}="\${expression}" \u2190 full replacement (whole attr is the placeholder)
1
+ var S=Symbol("signal"),f=null,h=null,_=!1;function P(t){for(let e of t){if(e.disposed){t.delete(e);continue}e.dirty=!0,h||(h=new Set,_||queueMicrotask(q)),h.add(e)}}function q(){let t,e=0;for(;t=h;){if(++e>100){h=null,console.error("signals: drain loop exceeded 100 iterations");break}h=null;for(let n of t)if(!n.disposed&&n.dirty){n.dirty=!1;try{n.run()}catch(r){n.onError?n.onError(r):(n.disposed=!0,queueMicrotask(()=>{throw r}))}}}}function K(t){if(_){t();return}_=!0;try{t()}finally{_=!1,h&&q()}}function T(t){let e=f;f=null;try{return t()}finally{f=e}}function p(t){let e=new Set;return{[S]:!0,get value(){return f&&e.add(f),t},set value(n){Object.is(t,n)||(t=n,P(e))},peek(){return t}}}function k(t){let e=new Set,n,r=!0,i={dirty:!1,disposed:!1,run(){r=!0,P(e)}};return{[S]:!0,get value(){if(f&&e.add(f),r){let s=f;f=i;try{n=t(),r=!1}finally{f=s}}return n},peek(){return n}}}function m(t,e){let n={dirty:!1,disposed:!1,host:e?.host??null,onError:e?.onError??null,run(){let r=f;f=n;try{t()}finally{f=r}}};return n.run(),()=>{n.disposed=!0}}function O(t){return t!=null&&typeof t=="object"&&t[S]===!0}var[v,b]=[Symbol(),Symbol()];function V(t,...e){return{strings:t,values:e}}function X(t,...e){let n="";for(let i=0;i<t.length;i++)n+=t[i],i<e.length&&(n+=e[i]);let r=new CSSStyleSheet;return r.replaceSync(n),r}var I=new WeakMap;function Y(t){let e=0,n=0,r=0;for(;r<t.length;){if(e===0&&t.charCodeAt(r)===60&&t.charCodeAt(r+1)===33&&t.charCodeAt(r+2)===45&&t.charCodeAt(r+3)===45){let s=t.indexOf("-->",r+4);if(s===-1){n=0;break}r=s+3,n=0;continue}let i=t.charCodeAt(r);e?i===(e===1?39:34)&&(e=0):i===39?e=1:i===34?e=2:i===60?n=1:i===62&&(n=0),r++}return n===1}function J(t){let e=I.get(t);if(e)return e;let n="";for(let r=0;r<t.length;r++)n+=t[r],r<t.length-1&&(n+=Y(n)?`{{p:${r}}}`:`<!--p:${r}-->`);return e=document.createElement("template"),e.innerHTML=n,I.set(t,e),e}var R={classname:"className",innertext:"innerText",innerhtml:"innerHTML",outerhtml:"outerHTML",textcontent:"textContent",tabindex:"tabIndex",arialabel:"ariaLabel",ariadescribedby:"ariaDescribedBy",arialabelledby:"ariaLabelledBy",ariarole:"role",contenteditable:"contentEditable",readonly:"readOnly",maxlength:"maxLength",minlength:"minLength",colspan:"colSpan",rowspan:"rowSpan",cellpadding:"cellPadding",cellspacing:"cellSpacing",usemap:"useMap",ismap:"isMap",accesskey:"accessKey",defaultchecked:"defaultChecked",defaultvalue:"defaultValue",defaultselected:"defaultSelected"};function B(t,e){if(R[e])return R[e];let n=Object.getOwnPropertyNames(t);for(let i=0;i<n.length;i++){let s=n[i];if(s!==e&&s.toLowerCase()===e)return s}let r=Object.getPrototypeOf(t);for(;r&&r!==Object.prototype;){let i=Object.getOwnPropertyNames(r);for(let s=0;s<i.length;s++){let a=i[s];if(a!==e&&a.toLowerCase()===e)return a}r=Object.getPrototypeOf(r)}return e}function g(t,e){let n=e._i;(!n||n.s!==t.strings)&&(n&&A(n.p),n=Q(t,e),e._i=n),ne(n.p,t.values)}function Q(t,e){let{strings:n}=t,i=J(n).content.cloneNode(!0),s=Z(e);s&&(i=ee(i,s));let a=te(i,t.values.length);return e.replaceChildren(i),{s:n,p:a}}var C="http://www.w3.org/2000/svg",y="http://www.w3.org/1998/Math/MathML",D="http://www.w3.org/1999/xhtml";function Z(t){if(!t||t.nodeType!==1)return null;if(t.namespaceURI===C)return C;if(t.namespaceURI===y)return y;if(typeof t.closest!="function")return null;let e=t.closest("svg, math, foreignObject, annotation-xml");if(!e)return null;let n=e.localName;if(n==="foreignobject"||n==="foreignObject")return null;if(n==="annotation-xml"){let r=(e.getAttribute("encoding")||"").toLowerCase();return r==="text/html"||r==="application/xhtml+xml"?null:y}return n==="svg"?C:n==="math"?y:null}function ee(t,e){let n=document.createDocumentFragment();return H(t,n,e),n}function H(t,e,n){for(let r of[...t.childNodes])if(r.nodeType===1){let s=document.createElementNS(n,r.localName);for(let o of r.attributes)o.namespaceURI?s.setAttributeNS(o.namespaceURI,o.name,o.value):s.setAttribute(o.name,o.value);e.appendChild(s);let a=r.localName,c=n;if(n===C&&(a==="foreignObject"||a==="foreignobject"))c=D;else if(n===y&&a==="annotation-xml"){let o=(r.getAttribute("encoding")||"").toLowerCase();(o==="text/html"||o==="application/xhtml+xml")&&(c=D)}H(r,s,c)}else e.appendChild(r.cloneNode(!0))}function te(t,e){let n=new Array(e),r=document.createTreeWalker(t,129),i;for(;i=r.nextNode();)if(i.nodeType===8){let s=i.data.match(/^p:(\d+)$/);if(s){let a=document.createTextNode("");i.replaceWith(a),r.currentNode=a,n[+s[1]]={t:"n",n:a,c:void 0,_fx:null}}}else if(i.nodeType===1)for(let s of[...i.attributes]){let a=s.value.match(/^\{\{p:(\d+)\}\}$/);if(!a){if(s.value.includes("{{p:")){let l=s.name,u=l==="class"?' class="${expression}" \u2190 full replacement (whole class string is the expression)\n .className=${expression} \u2190 writes to the className property (resolved camelCase since FB-55 fix)\n':l==="style"?' style="${expression}" \u2190 full replacement of the style string\n .style.cssText=${expression} \u2190 write CSS text via the style.cssText accessor\n':` ${l}="\${expression}" \u2190 full replacement (whole attr is the placeholder)
2
2
  .${l}=\${expression} \u2190 property assignment (preferred for objects/functions)
3
3
  `;console.warn(`[template] Partial attribute interpolation is not supported.
4
4
  Element: <${i.tagName.toLowerCase()}>
5
- Attribute: ${l}="${o.value.slice(0,80)}${o.value.length>80?"\u2026":""}"
5
+ Attribute: ${l}="${s.value.slice(0,80)}${s.value.length>80?"\u2026":""}"
6
6
  Use one of:
7
- `+u+" Compute the full attr value as a single expression and interpolate the whole.")}continue}let c=+a[1],s=o.name;s[0]==="@"?(i.removeAttribute(s),n[c]={t:"e",n:i,name:s.slice(1),c:void 0,_fx:null}):s[0]==="."?(i.removeAttribute(s),n[c]={t:"p",n:i,name:H(i,s.slice(1)),c:void 0,_fx:null,_resolved:!1}):s[0]==="?"?(i.removeAttribute(s),console.warn(`[template] Lit-style boolean attribute "${s}=" is not supported.
7
+ `+u+" Compute the full attr value as a single expression and interpolate the whole.")}continue}let c=+a[1],o=s.name;o[0]==="@"?(i.removeAttribute(o),n[c]={t:"e",n:i,name:o.slice(1),c:void 0,_fx:null}):o[0]==="."?(i.removeAttribute(o),n[c]={t:"p",n:i,name:B(i,o.slice(1)),c:void 0,_fx:null,_resolved:!1}):o[0]==="?"?(i.removeAttribute(o),console.warn(`[template] Lit-style boolean attribute "${o}=" is not supported.
8
8
  Element: <${i.tagName.toLowerCase()}>
9
- Use .${s.slice(1)}=\${value} (property binding) instead \u2014 the primitive reflects the property to the DOM attribute for you.
10
- See USAGE.md \xA7 Template parser \u2014 invariants + unsupported syntaxes.`),n[c]={t:"n",n:document.createTextNode(""),c:void 0,_fx:null}):n[c]={t:"a",n:i,name:s,c:void 0,_fx:null}}return n}function C(t){for(let e of t)e&&e._fx&&(e._fx(),e._fx=null)}function M(t){if(t.n.nodeType===1&&t.n[$])return t.n;let e=document.createElement("span");return e.style.display="contents",e[$]=!0,t.n.replaceWith(e),t.n=e,e}var U=t=>t!=null&&typeof t=="object"&&t.strings,W=t=>typeof t=="function"||t!=null&&typeof t.handleEvent=="function";function L(t,e){if(e!==t.c){if(t.t==="n")if(e!=null&&e._directive)e._commit(M(t));else if(U(e))g(e,M(t));else if(Array.isArray(e)){let n=M(t);n.replaceChildren();for(let r of e)if(U(r)){let i=document.createElement("span");i.style.display="contents",n.appendChild(i),g(r,i)}else n.appendChild(document.createTextNode(r??""))}else if(e instanceof Node)t.n.replaceWith(e),t.n=e;else if(t.n[$]){let n=document.createTextNode(e??"");t.n.replaceWith(n),t.n=n}else t.n.textContent=e??"";else if(t.t==="a")e==null||e===!1?t.n.removeAttribute(t.name):t.n.setAttribute(t.name,e===!0?"":e);else if(t.t==="p"){if(!t._resolved){let n=H(t.n,t.name);n!==t.name&&(t.name=n),t._resolved=!0}t.n[t.name]=e}else t.t==="e"&&(W(t.c)&&t.n.removeEventListener(t.name,t.c),W(e)&&t.n.addEventListener(t.name,e));t.c=e}}function ne(t,e){for(let n=0;n<t.length;n++){let r=t[n];if(!r)continue;let i=e[n],o=r.t!=="e"&&typeof i=="function",a=!o&&r.t!=="e"&&v(i);if(r._fx&&!o&&!a&&(r._fx(),r._fx=null),o){r._fx||(r._fx=p(()=>L(r,i())));continue}if(a){r._fx||(r._fx=p(()=>L(r,i.value)));continue}L(r,i)}}function re(t,e,n){return{_directive:!0,_commit(r){let i=r[y]||new Map,o=new Map,a=[];for(let s=0;s<t.length;s++){let l=e(t[s],s);if(o.has(l))continue;a.push(l);let u=n(t[s],s);if(i.has(l)){let d=i.get(l);g(u,d),o.set(l,d)}else{let d=document.createElement("span");d.style.display="contents",g(u,d),o.set(l,d)}}for(let[s,l]of i)o.has(s)||(l._i&&C(l._i.p),l.remove());let c=null;for(let s=a.length-1;s>=0;s--){let l=o.get(a[s]);(l.nextSibling!==c||l.parentNode!==r)&&r.insertBefore(l,c),c=l}r[y]=o}}}var E=new Map,z=new Set(["input-interaction","keyboard-navigation","forms-data","layout-measurement","motion-positioning","animation-feedback","visual-dynamics","interaction-delight","audio-haptics-sensory"]);function de(t){if(!t.name)throw new Error("Trait requires a name");if(!t.setup)throw new Error(`Trait "${t.name}" requires a setup function`);if(!t.category)throw new Error(`Trait "${t.name}" requires a category`);if(!z.has(t.category))throw new Error(`Trait "${t.name}" has unknown category "${t.category}". Known: ${[...z].join(", ")}`);if(!t.description)throw new Error(`Trait "${t.name}" requires a description`);let e=Object.freeze({name:t.name,category:t.category,description:t.description,attributes:Object.freeze(t.attributes||[]),events:Object.freeze(t.events||[]),config:Object.freeze(t.config||[])});function n(){let r=null;return{schema:e,connect(i,o){r=t.setup({host:i,...o||{}})},disconnect(i){r&&(r(),r=null);for(let o of e.attributes)i.hasAttribute(o)&&(console.warn(`Trait "${e.name}": attribute "${o}" still present after disconnect`),i.removeAttribute(o))}}}return n.schema=e,E.set(t.name,n),n}function F(t){return E.get(t)||null}function he(t){return E.get(t)?.schema||null}function pe(){return[...E.keys()]}var j=Symbol(),x=Symbol();function ie(t,e){t[x]=new Map,t[j]=new Map;for(let[n,r]of Object.entries(e)){let i=r.attribute??n.toLowerCase(),o=r.type??String,a=m(r.default??void 0);t[j].set(n,a),Object.defineProperty(t,n,{get(){return a.value},set(c){let s=a.peek();Object.is(s,c)||(t[x].set(n,s),a.value=c,r.reflect&&oe(t,i,c,o))},configurable:!0})}}function oe(t,e,n,r){r===Boolean?n?t.setAttribute(e,""):t.removeAttribute(e):n==null?t.removeAttribute(e):t.setAttribute(e,String(n))}var se=(t,e)=>e===Boolean?t!==null:e===Number?t===null?null:+t:t;function ae(t){if(Object.hasOwn(t,"_sa"))return;t._sa=!0;let e=t.styles;if(!e)return;let n=Array.isArray(e)?e:[e];document.adoptedStyleSheets=[...document.adoptedStyleSheets,...n.filter(r=>!document.adoptedStyleSheets.includes(r))]}function G(t){if(Object.hasOwn(t,"_pp"))return;t._pp={};let e=t.parts;if(e)for(let[n,r]of Object.entries(e)){let i=document.createElement("template");i.innerHTML=r,t._pp[n]=i.content.firstElementChild}}var T=class extends HTMLElement{static get properties(){return{}}static get traits(){return[]}static get observedAttributes(){let e=Object.entries(this.properties).map(([n,r])=>r.attribute??n.toLowerCase());return e.includes("traits")?e:[...e,"traits"]}#r=[];#t=[];#i=[];#n=null;#o=null;#e=null;#a=m(0);constructor(){super(),this.internals=this.attachInternals(),ie(this,this.constructor.properties)}connectedCallback(){let e=this.constructor;e._tag||(e._tag=this.localName),ae(e),G(e),k(()=>this.connected()),this.#r.push(p(()=>{for(let r of this[j].values())r.value;this.#a.value;let n=e.template(this);if(n&&g(n,this),this.render(),this[x].size){let r=new Map(this[x]);this[x].clear(),this.updated(r)}},{host:this.localName,onError:this.onError?n=>this.onError(n):null})),this.#e||(this.#e=new Set);for(let n of e.traits)this.#s(n);this.#c(),this.#n&&this.#l()}disconnectedCallback(){this._i&&C(this._i.p);for(let e of this.#r)e();this.#r.length=0;for(let e of this.#i)e.value=void 0;this.#i.length=0;for(let e of this.#t)e.disconnect(this);this.#t.length=0,this.#e?.clear(),this.#o?.(),this.#o=null,this.#n?.disconnect?.(this),this.disconnected()}#l(){let e=this.#n;e.connect?.(this),this.#o=e.subscribe?.(()=>{this.#a.value++})}get controller(){return this.#n}set controller(e){this.#o?.(),this.#o=null,this.#n?.disconnect?.(this),this.#n=e,e&&this.isConnected&&this.#l()}#s(e,{declarative:n=!1}={}){this.#e.add(e);let r=e();r.connect(this,{host:this,signal:m,computed:O,effect:p}),n&&(r._declarative=!0),this.#t.push(r)}addTrait(e){return this.#e||(this.#e=new Set),this.#e.has(e)?this:(this.#s(e),this)}#c(){let e=this.getAttribute("traits");if(e)for(let n of e.split(/\s+/).filter(Boolean)){let r=F(n);if(!r){console.warn(`<${this.localName}> traits="${n}" \u2014 trait not found. Did you forget to import it?`);continue}this.#e.has(r)||this.#s(r,{declarative:!0})}}#f(){if(!this.#e)return;let e=[];for(let n of this.#t)if(n._declarative){n.disconnect(this);for(let r of this.#e)if(r.schema?.name&&n.schema?.name===r.schema.name){this.#e.delete(r);break}}else e.push(n);this.#t.length=0,this.#t.push(...e),this.#c()}attributeChangedCallback(e,n,r){if(e==="traits"){this.isConnected&&this.#f();return}for(let[i,o]of Object.entries(this.constructor.properties))if((o.attribute??i.toLowerCase())===e){this[i]=se(r,o.type??String);break}}static template=()=>null;static create(e={}){let n=this._tag??customElements.getName?.(this);if(!n)throw new Error("Component not registered");let r=document.createElement(n);for(let[i,o]of Object.entries(e))r[i]=o;return r}signal(e){let n=m(e);return this.#i.push(n),n}ensure(e){for(let o of this.children)if(o.getAttribute("slot")===e)return o;let n=this.#u(e);if(n)return n;G(this.constructor);let r=this.constructor._pp?.[e];if(!r)return null;let i=r.cloneNode(!0);return i._uiPart=!0,this.appendChild(i),i}drop(e){for(let r of this.children)if(r.getAttribute("slot")===e){r.remove();return}let n=this.#u(e);n&&n.remove()}#u(e){for(let n of this.querySelectorAll(`[slot="${e}"]`)){let r=n.parentElement,i=!0;for(;r&&r!==this;){if(r.localName.includes("-")){i=!1;break}r=r.parentElement}if(i&&r===this)return n}return null}reconcile(e,n,r,i){let o=e[y]||new Map,a=new Map,c=[];for(let l=0;l<n.length;l++){let u=n[l],d=r(u,l);if(a.has(d))continue;let w=o.get(d);w?i(u,l,w):w=i(u,l,null),a.set(d,w),c.push(d)}for(let[l,u]of o)a.has(l)||u.remove();let s=null;for(let l=c.length-1;l>=0;l--){let u=a.get(c[l]);(u.nextSibling!==s||u.parentNode!==e)&&e.insertBefore(u,s),s=u}e[y]=a}connected(){}render(){}disconnected(){}updated(e){}};var le="rounded borderless",N=class extends T{static properties={mode:{type:String,default:"",reflect:!0}};static template=()=>null;connected(){this.hasAttribute("mode")||(this.mode=le),this.#t(),this.#i()}#r(e){return this.querySelector(`admin-sidebar[slot="${e}"], admin-sidebar[name="${e}"]`)}#t(){for(let e of this.querySelectorAll("[data-sidebar-toggle]")){let n=e.getAttribute("data-sidebar-toggle");e.addEventListener("click",()=>{this.#r(n)?.toggle?.()})}}#i(){let e=this.querySelector("admin-command");if(!e)return;for(let r of this.querySelectorAll("[data-command-trigger]"))r.addEventListener("click",i=>{i.stopPropagation(),e.show()});let n=this.querySelector("nav-ui");n&&e.addEventListener("command-select",r=>{let i=n.querySelector(`nav-item-ui[value="${r.detail.value}"]`);i&&n.select(i)})}};customElements.define("admin-shell",N);export{N as AdminShell};
9
+ Use .${o.slice(1)}=\${value} (property binding) instead \u2014 the primitive reflects the property to the DOM attribute for you.
10
+ See USAGE.md \xA7 Template parser \u2014 invariants + unsupported syntaxes.`),n[c]={t:"n",n:document.createTextNode(""),c:void 0,_fx:null}):n[c]={t:"a",n:i,name:o,c:void 0,_fx:null}}return n}function A(t){for(let e of t)e&&e._fx&&(e._fx(),e._fx=null)}function M(t){if(t.n.nodeType===1&&t.n[v])return t.n;let e=document.createElement("span");return e.style.display="contents",e[v]=!0,t.n.replaceWith(e),t.n=e,e}var U=t=>t!=null&&typeof t=="object"&&t.strings,W=t=>typeof t=="function"||t!=null&&typeof t.handleEvent=="function";function j(t,e){if(e!==t.c){if(t.t==="n")if(e!=null&&e._directive)e._commit(M(t));else if(U(e))g(e,M(t));else if(Array.isArray(e)){let n=M(t);n.replaceChildren();for(let r of e)if(U(r)){let i=document.createElement("span");i.style.display="contents",n.appendChild(i),g(r,i)}else n.appendChild(document.createTextNode(r??""))}else if(e instanceof Node)t.n.replaceWith(e),t.n=e;else if(t.n[v]){let n=document.createTextNode(e??"");t.n.replaceWith(n),t.n=n}else t.n.textContent=e??"";else if(t.t==="a")e==null||e===!1?t.n.removeAttribute(t.name):t.n.setAttribute(t.name,e===!0?"":e);else if(t.t==="p"){if(!t._resolved){let n=B(t.n,t.name);n!==t.name&&(t.name=n),t._resolved=!0}t.n[t.name]=e}else t.t==="e"&&(W(t.c)&&t.n.removeEventListener(t.name,t.c),W(e)&&t.n.addEventListener(t.name,e));t.c=e}}function ne(t,e){for(let n=0;n<t.length;n++){let r=t[n];if(!r)continue;let i=e[n],s=r.t!=="e"&&typeof i=="function",a=!s&&r.t!=="e"&&O(i);if(r._fx&&!s&&!a&&(r._fx(),r._fx=null),s){r._fx||(r._fx=m(()=>j(r,i())));continue}if(a){r._fx||(r._fx=m(()=>j(r,i.value)));continue}j(r,i)}}function re(t,e,n){return{_directive:!0,_commit(r){let i=r[b]||new Map,s=new Map,a=[];for(let o=0;o<t.length;o++){let l=e(t[o],o);if(s.has(l))continue;a.push(l);let u=n(t[o],o);if(i.has(l)){let d=i.get(l);g(u,d),s.set(l,d)}else{let d=document.createElement("span");d.style.display="contents",g(u,d),s.set(l,d)}}for(let[o,l]of i)s.has(o)||(l._i&&A(l._i.p),l.remove());let c=null;for(let o=a.length-1;o>=0;o--){let l=s.get(a[o]);(l.nextSibling!==c||l.parentNode!==r)&&r.insertBefore(l,c),c=l}r[b]=s}}}var $=new Map,F=new Set(["input-interaction","keyboard-navigation","forms-data","layout-measurement","motion-positioning","animation-feedback","visual-dynamics","interaction-delight","audio-haptics-sensory"]);function de(t){if(!t.name)throw new Error("Trait requires a name");if(!t.setup)throw new Error(`Trait "${t.name}" requires a setup function`);if(!t.category)throw new Error(`Trait "${t.name}" requires a category`);if(!F.has(t.category))throw new Error(`Trait "${t.name}" has unknown category "${t.category}". Known: ${[...F].join(", ")}`);if(!t.description)throw new Error(`Trait "${t.name}" requires a description`);let e=Object.freeze({name:t.name,category:t.category,description:t.description,attributes:Object.freeze(t.attributes||[]),events:Object.freeze(t.events||[]),config:Object.freeze(t.config||[])});function n(){let r=null;return{schema:e,connect(i,s){r=t.setup({host:i,...s||{}})},disconnect(i){r&&(r(),r=null);for(let s of e.attributes)i.hasAttribute(s)&&(console.warn(`Trait "${e.name}": attribute "${s}" still present after disconnect`),i.removeAttribute(s))}}}return n.schema=e,$.set(t.name,n),n}function z(t){return $.get(t)||null}function he(t){return $.get(t)?.schema||null}function me(){return[...$.keys()]}var L=Symbol(),w=Symbol();function ie(t,e){t[w]=new Map,t[L]=new Map;for(let[n,r]of Object.entries(e)){let i=r.attribute??n.toLowerCase(),s=r.type??String,a=p(r.default??void 0);t[L].set(n,a),Object.defineProperty(t,n,{get(){return a.value},set(c){let o=a.peek();Object.is(o,c)||(t[w].set(n,o),a.value=c,r.reflect&&se(t,i,c,s))},configurable:!0})}}function se(t,e,n,r){r===Boolean?n?t.setAttribute(e,""):t.removeAttribute(e):n==null?t.removeAttribute(e):t.setAttribute(e,String(n))}var oe=(t,e)=>e===Boolean?t!==null:e===Number?t===null?null:+t:t;function ae(t){if(Object.hasOwn(t,"_sa"))return;t._sa=!0;let e=t.styles;if(!e)return;let n=Array.isArray(e)?e:[e];document.adoptedStyleSheets=[...document.adoptedStyleSheets,...n.filter(r=>!document.adoptedStyleSheets.includes(r))]}function G(t){if(Object.hasOwn(t,"_pp"))return;t._pp={};let e=t.parts;if(e)for(let[n,r]of Object.entries(e)){let i=document.createElement("template");i.innerHTML=r,t._pp[n]=i.content.firstElementChild}}var E=class extends HTMLElement{static get properties(){return{}}static get traits(){return[]}static get observedAttributes(){let e=Object.entries(this.properties).map(([n,r])=>r.attribute??n.toLowerCase());return e.includes("traits")?e:[...e,"traits"]}#i=[];#n=[];#s=[];#t=null;#r=null;#e=null;#a=p(0);constructor(){super(),this.internals=this.attachInternals(),ie(this,this.constructor.properties)}connectedCallback(){let e=this.constructor;e._tag||(e._tag=this.localName),ae(e),G(e),T(()=>this.connected()),this.#i.push(m(()=>{for(let r of this[L].values())r.value;this.#a.value;let n=e.template(this);if(n&&g(n,this),this.render(),this[w].size){let r=new Map(this[w]);this[w].clear(),this.updated(r)}},{host:this.localName,onError:this.onError?n=>this.onError(n):null})),this.#e||(this.#e=new Set);for(let n of e.traits)this.#o(n);this.#c(),this.#t&&this.#l()}disconnectedCallback(){this._i&&A(this._i.p);for(let e of this.#i)e();this.#i.length=0;for(let e of this.#s)e.value=void 0;this.#s.length=0;for(let e of this.#n)e.disconnect(this);this.#n.length=0,this.#e?.clear(),this.#r?.(),this.#r=null,this.#t?.disconnect?.(this),this.disconnected()}#l(){let e=this.#t;e.connect?.(this),this.#r=e.subscribe?.(()=>{this.#a.value++})}get controller(){return this.#t}set controller(e){this.#r?.(),this.#r=null,this.#t?.disconnect?.(this),this.#t=e,e&&this.isConnected&&this.#l()}#o(e,{declarative:n=!1}={}){this.#e.add(e);let r=e();r.connect(this,{host:this,signal:p,computed:k,effect:m}),n&&(r._declarative=!0),this.#n.push(r)}addTrait(e){return this.#e||(this.#e=new Set),this.#e.has(e)?this:(this.#o(e),this)}#c(){let e=this.getAttribute("traits");if(e)for(let n of e.split(/\s+/).filter(Boolean)){let r=z(n);if(!r){console.warn(`<${this.localName}> traits="${n}" \u2014 trait not found. Did you forget to import it?`);continue}this.#e.has(r)||this.#o(r,{declarative:!0})}}#f(){if(!this.#e)return;let e=[];for(let n of this.#n)if(n._declarative){n.disconnect(this);for(let r of this.#e)if(r.schema?.name&&n.schema?.name===r.schema.name){this.#e.delete(r);break}}else e.push(n);this.#n.length=0,this.#n.push(...e),this.#c()}attributeChangedCallback(e,n,r){if(e==="traits"){this.isConnected&&this.#f();return}for(let[i,s]of Object.entries(this.constructor.properties))if((s.attribute??i.toLowerCase())===e){this[i]=oe(r,s.type??String);break}}static template=()=>null;static create(e={}){let n=this._tag??customElements.getName?.(this);if(!n)throw new Error("Component not registered");let r=document.createElement(n);for(let[i,s]of Object.entries(e))r[i]=s;return r}signal(e){let n=p(e);return this.#s.push(n),n}ensure(e){for(let s of this.children)if(s.getAttribute("slot")===e)return s;let n=this.#u(e);if(n)return n;G(this.constructor);let r=this.constructor._pp?.[e];if(!r)return null;let i=r.cloneNode(!0);return i._uiPart=!0,this.appendChild(i),i}drop(e){for(let r of this.children)if(r.getAttribute("slot")===e){r.remove();return}let n=this.#u(e);n&&n.remove()}#u(e){for(let n of this.querySelectorAll(`[slot="${e}"]`)){let r=n.parentElement,i=!0;for(;r&&r!==this;){if(r.localName.includes("-")){i=!1;break}r=r.parentElement}if(i&&r===this)return n}return null}reconcile(e,n,r,i){let s=e[b]||new Map,a=new Map,c=[];for(let l=0;l<n.length;l++){let u=n[l],d=r(u,l);if(a.has(d))continue;let x=s.get(d);x?i(u,l,x):x=i(u,l,null),a.set(d,x),c.push(d)}for(let[l,u]of s)a.has(l)||u.remove();let o=null;for(let l=c.length-1;l>=0;l--){let u=a.get(c[l]);(u.nextSibling!==o||u.parentNode!==e)&&e.insertBefore(u,o),o=u}e[b]=a}connected(){}render(){}disconnected(){}updated(e){}};var le="rounded borderless",N=class t extends E{static properties={mode:{type:String,default:"",reflect:!0}};static template=()=>null;connected(){this.hasAttribute("mode")||(this.mode=le),this.addEventListener("click",this.#n),this.#s()}#i(e){return this.querySelector(`admin-sidebar[slot="${e}"], admin-sidebar[name="${e}"]`)}#n=e=>{let n=e.target.closest("[data-sidebar-toggle]");if(n&&this.contains(n)){let i=n.getAttribute("data-sidebar-toggle"),s=this.#i(i);s&&typeof s.toggle!="function"&&this.#r("admin-sidebar",`slot="${i}"`,"Sidebar toggle"),s?.toggle?.();return}let r=e.target.closest("[data-command-trigger]");if(r&&this.contains(r)){e.stopPropagation();let i=this.querySelector("admin-command");if(!i)return;if(typeof i.show!="function"){this.#r("admin-command","","Command palette");return}i.show()}};#s(){let e=this.querySelector("admin-command"),n=this.querySelector("nav-ui");!e||!n||e.addEventListener("command-select",r=>{let i=n.querySelector(`nav-item-ui[value="${r.detail.value}"]`);i&&n.select(i)})}static#t=new WeakSet;#r(e,n,r){let i=n?this.querySelector(`${e}[${n}], ${e}[${n.replace("slot=","name=")}]`):this.querySelector(e);if(!i||t.#t.has(i))return;t.#t.add(i);let s=e==="admin-sidebar"?"shell/admin-sidebar":"shell/admin-command";console.warn(`[admin-shell] <${e}${n?" "+n:""}> is in the DOM but ${e==="admin-sidebar"?"AdminSidebar":"AdminCommand"} is NOT registered \u2014 ${r} will not work. Import the cluster barrel \`@adia-ai/web-modules/shell\` (which registers all shell elements), OR add \`@adia-ai/web-modules/${s}\` alongside \`@adia-ai/web-modules/shell/admin-shell\` on the per-component path. Closes FB-55 silent-failure trap.`)}};customElements.define("admin-shell",N);export{N as AdminShell};
@@ -18,6 +18,11 @@
18
18
  "type": "boolean",
19
19
  "default": false
20
20
  },
21
+ "collapsible": {
22
+ "description": "Reflected CSS-state marker — declares this sidebar's collapse\naffordance is exposed to authors (matching the <admin-sidebar>\nconvention). The .toggle() / .collapse() / .expand() public API\nis always callable; [collapsible] is the attribute consumers\nkey CSS rules off (e.g. showing a toggle button when present).\nSet on the canonical demos in editor-sidebar.examples.html.\n",
23
+ "type": "boolean",
24
+ "default": false
25
+ },
21
26
  "component": {
22
27
  "const": "EditorSidebar"
23
28
  },
@@ -38,6 +38,14 @@ export type EditorSidebarSidebarToggleEvent = CustomEvent<EditorSidebarSidebarTo
38
38
  export class EditorSidebar extends UIElement {
39
39
  /** Reflected — set when inner <pane-ui> width is at or below 96px snap threshold. */
40
40
  collapsed: boolean;
41
+ /** Reflected CSS-state marker — declares this sidebar's collapse
42
+ affordance is exposed to authors (matching the <admin-sidebar>
43
+ convention). The .toggle() / .collapse() / .expand() public API
44
+ is always callable; [collapsible] is the attribute consumers
45
+ key CSS rules off (e.g. showing a toggle button when present).
46
+ Set on the canonical demos in editor-sidebar.examples.html.
47
+ */
48
+ collapsible: boolean;
41
49
  /** Alias for [persist]; kept for v0.4.x back-compat. Deprecated —
42
50
  new code should use [persist]. If both are set, [persist] wins.
43
51
  */
@@ -58,6 +58,11 @@ class EditorSidebar extends UIElement {
58
58
  static properties = {
59
59
  collapsed: { type: Boolean, default: false, reflect: true },
60
60
  resizing: { type: Boolean, default: false, reflect: true },
61
+ // FB-61: CSS-state marker, mirrors <admin-sidebar>'s convention.
62
+ // The .toggle()/.collapse()/.expand() API is always callable; this
63
+ // attribute exists so consumers can key CSS rules off "this sidebar
64
+ // exposes a collapse affordance" (e.g. showing a toggle button).
65
+ collapsible: { type: Boolean, default: false, reflect: true },
61
66
  };
62
67
 
63
68
  static template = () => null;
@@ -38,6 +38,17 @@ props:
38
38
  type: boolean
39
39
  default: false
40
40
  reflect: true
41
+ collapsible:
42
+ description: |
43
+ Reflected CSS-state marker — declares this sidebar's collapse
44
+ affordance is exposed to authors (matching the <admin-sidebar>
45
+ convention). The .toggle() / .collapse() / .expand() public API
46
+ is always callable; [collapsible] is the attribute consumers
47
+ key CSS rules off (e.g. showing a toggle button when present).
48
+ Set on the canonical demos in editor-sidebar.examples.html.
49
+ type: boolean
50
+ default: false
51
+ reflect: true
41
52
  persist:
42
53
  description: |
43
54
  localStorage key suffix. When set, the sidebar persists its pane
package/index.js CHANGED
@@ -17,3 +17,5 @@ export * from './theme/index.js';
17
17
  export * from './onboarding/index.js';
18
18
  export * from './dashboard/index.js';
19
19
  export * from './feedback/index.js';
20
+ export * from './billing/index.js';
21
+ export * from './settings/index.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adia-ai/web-modules",
3
- "version": "0.6.34",
3
+ "version": "0.6.36",
4
4
  "description": "AdiaUI composite custom elements \u2014 shell, chat, editor, runtime clusters built from @adia-ai/web-components primitives. Subpath exports per cluster.",
5
5
  "type": "module",
6
6
  "exports": {
@@ -46,8 +46,14 @@ class AdminShell extends UIElement {
46
46
  // true for `mode=""` so explicit empty-string authoring (the flat
47
47
  // legacy chrome opt-out) is preserved.
48
48
  if (!this.hasAttribute('mode')) this.mode = DEFAULT_MODE;
49
- this.#wireToggleButtons();
50
- this.#wireCommandTriggers();
49
+ // Delegated click handler — re-resolves children on every click so
50
+ // late-attached buttons (router swaps, deferred innerHTML, etc.)
51
+ // and parse-order quirks (happy-dom appends children AFTER firing
52
+ // the parent's connectedCallback when `document.body.innerHTML =`
53
+ // is used) don't end up with dead wiring. The host listener auto-
54
+ // cleans when the element disconnects.
55
+ this.addEventListener('click', this.#onClick);
56
+ this.#wireCommandSelectRouting();
51
57
  }
52
58
 
53
59
  // No #disconnected — children own their own cleanup; the host registers
@@ -60,41 +66,91 @@ class AdminShell extends UIElement {
60
66
  );
61
67
  }
62
68
 
63
- // ── 1. Toggle buttons — forward clicks to <admin-sidebar>.toggle() ──
69
+ // ── Delegated click dispatch ────────────────────────────────────────
70
+ //
71
+ // FB-55: when a consumer imports the per-component path
72
+ // (`@adia-ai/web-modules/shell/admin-shell`) instead of the cluster
73
+ // barrel (`@adia-ai/web-modules/shell`), AdminSidebar / AdminCommand
74
+ // are NOT registered. The elements still render (CSS targets by tag),
75
+ // so the layout looks right — but they are unupgraded HTMLElements
76
+ // with no `.toggle()` / `.show()` method. The previous optional-chain
77
+ // form `sidebar?.toggle?.()` silently swallowed the no-op: zero
78
+ // console output, zero diagnostic. Toggle buttons appeared broken
79
+ // with no signal. Documented in adia-ui-kit skill v2.27.1's setup
80
+ // checklist but the substrate now warns at first click so consumers
81
+ // who don't read the checklist still catch the error.
82
+ //
83
+ // Delegation (vs per-button listeners at connect-time) makes this
84
+ // resilient to late-attached buttons (router swaps, deferred
85
+ // innerHTML) and to happy-dom's parse-order quirk where children
86
+ // land AFTER the parent's connectedCallback fires.
64
87
 
65
- #wireToggleButtons() {
66
- for (const btn of this.querySelectorAll('[data-sidebar-toggle]')) {
67
- const name = btn.getAttribute('data-sidebar-toggle');
68
- btn.addEventListener('click', () => {
69
- const sidebar = this.#findSidebar(name);
70
- sidebar?.toggle?.();
71
- });
88
+ #onClick = (e) => {
89
+ const toggle = e.target.closest('[data-sidebar-toggle]');
90
+ if (toggle && this.contains(toggle)) {
91
+ const name = toggle.getAttribute('data-sidebar-toggle');
92
+ const sidebar = this.#findSidebar(name);
93
+ if (sidebar && typeof sidebar.toggle !== 'function') {
94
+ this.#warnUnregistered('admin-sidebar', `slot="${name}"`, 'Sidebar toggle');
95
+ }
96
+ sidebar?.toggle?.();
97
+ return;
72
98
  }
73
- }
99
+ const trigger = e.target.closest('[data-command-trigger]');
100
+ if (trigger && this.contains(trigger)) {
101
+ e.stopPropagation();
102
+ const command = this.querySelector('admin-command');
103
+ if (!command) return;
104
+ if (typeof command.show !== 'function') {
105
+ this.#warnUnregistered('admin-command', '', 'Command palette');
106
+ return;
107
+ }
108
+ command.show();
109
+ }
110
+ };
74
111
 
75
- // ── 2. Command triggers forward clicks to <admin-command>.show() ──
112
+ // <admin-command> owns its own Cmd+K listener. The host only wires
113
+ // command-select → nav routing if a <nav-ui> is present (so clicking
114
+ // a result in the palette navigates to that route). Re-runs on each
115
+ // connect — the listener is auto-cleaned with the command element.
76
116
 
77
- #wireCommandTriggers() {
117
+ #wireCommandSelectRouting() {
78
118
  const command = this.querySelector('admin-command');
79
- if (!command) return;
80
-
81
- for (const trigger of this.querySelectorAll('[data-command-trigger]')) {
82
- trigger.addEventListener('click', (e) => {
83
- e.stopPropagation();
84
- command.show();
85
- });
86
- }
87
-
88
- // <admin-command> owns its own Cmd+K listener. The host only wires
89
- // command-select → nav routing if a <nav-ui> is present (so clicking
90
- // a result in the palette navigates to that route).
91
119
  const nav = this.querySelector('nav-ui');
92
- if (!nav) return;
120
+ if (!command || !nav) return;
93
121
  command.addEventListener('command-select', (e) => {
94
122
  const item = nav.querySelector(`nav-item-ui[value="${e.detail.value}"]`);
95
123
  if (item) nav.select(item);
96
124
  });
97
125
  }
126
+
127
+ // ── Helper — one-shot warn per unregistered-element instance ────────
128
+ //
129
+ // FB-55: dedupe via a WeakSet keyed on the element so repeated clicks
130
+ // on the same broken toggle don't spam the console. Re-warns ONLY
131
+ // if a new offender element appears (e.g., consumer dynamically
132
+ // inserts another <admin-sidebar> later).
133
+
134
+ static #warned = new WeakSet();
135
+
136
+ #warnUnregistered(tag, qualifier, feature) {
137
+ // Find the offender (re-query to avoid stale refs from earlier paths).
138
+ const el = qualifier
139
+ ? this.querySelector(`${tag}[${qualifier}], ${tag}[${qualifier.replace('slot=', 'name=')}]`)
140
+ : this.querySelector(tag);
141
+ if (!el || AdminShell.#warned.has(el)) return;
142
+ AdminShell.#warned.add(el);
143
+ const path = tag === 'admin-sidebar' ? 'shell/admin-sidebar' : 'shell/admin-command';
144
+ console.warn(
145
+ `[admin-shell] <${tag}${qualifier ? ' ' + qualifier : ''}> is in the DOM ` +
146
+ `but ${tag === 'admin-sidebar' ? 'AdminSidebar' : 'AdminCommand'} is NOT registered — ` +
147
+ `${feature} will not work. Import the cluster barrel ` +
148
+ `\`@adia-ai/web-modules/shell\` (which registers all shell elements), ` +
149
+ `OR add \`@adia-ai/web-modules/${path}\` alongside ` +
150
+ `\`@adia-ai/web-modules/shell/admin-shell\` on the per-component path. ` +
151
+ `Closes FB-55 silent-failure trap.`
152
+ );
153
+ }
98
154
  }
99
155
 
100
156
  customElements.define('admin-shell', AdminShell);
@@ -115,3 +115,71 @@ describe('admin-shell.collapsed.css — vanilla HTML fallback (v0.6.17)', () =>
115
115
  expect(inside).toMatch(/text-indent:\s*-9999px/);
116
116
  });
117
117
  });
118
+
119
+ // ── FB-55 — runtime warn when AdminSidebar / AdminCommand unregistered ──
120
+ //
121
+ // Per-component import path (`@adia-ai/web-modules/shell/admin-shell`)
122
+ // registers ONLY AdminShell. <admin-sidebar> renders (CSS targets by
123
+ // tag) but is an unupgraded HTMLElement with no .toggle(). The
124
+ // optional chain `sidebar?.toggle?.()` previously swallowed the no-op
125
+ // silently — zero diagnostic. v0.6.35 adds a one-shot console.warn at
126
+ // toggle-time when the sidebar is found but not upgraded.
127
+ //
128
+ // These tests import ONLY AdminShell (not AdminSidebar), simulate the
129
+ // per-component-path failure mode, and assert the warn fires + dedups
130
+ // per element.
131
+
132
+ describe('admin-shell — FB-55 unregistered-element warn', () => {
133
+ it('logs warn when [data-sidebar-toggle] clicked and admin-sidebar not registered', async () => {
134
+ // AdminShell only — AdminSidebar deliberately NOT imported.
135
+ await import('./admin-shell.js');
136
+ const warns = [];
137
+ const origWarn = console.warn;
138
+ console.warn = (...args) => warns.push(args.join(' '));
139
+ try {
140
+ const shell = mount(
141
+ '<admin-shell>' +
142
+ '<admin-sidebar slot="leading"></admin-sidebar>' +
143
+ '<button data-sidebar-toggle="leading">☰</button>' +
144
+ '</admin-shell>'
145
+ );
146
+ // Allow the connect-time wiring to settle.
147
+ await new Promise((r) => queueMicrotask(r));
148
+ const btn = shell.querySelector('[data-sidebar-toggle]');
149
+ btn.click();
150
+ expect(warns.length).toBe(1);
151
+ expect(warns[0]).toMatch(/admin-sidebar.*slot="leading".*not registered/i);
152
+ expect(warns[0]).toMatch(/Sidebar toggle will not work/);
153
+ expect(warns[0]).toMatch(/cluster barrel/);
154
+ // Second click — dedup, no additional warn for the same offender.
155
+ btn.click();
156
+ expect(warns.length).toBe(1);
157
+ } finally {
158
+ console.warn = origWarn;
159
+ }
160
+ });
161
+
162
+ it('does NOT log when admin-sidebar IS registered (upgraded element)', async () => {
163
+ // Import the sidebar class — element is now upgraded with .toggle().
164
+ await import('../admin-sidebar/admin-sidebar.js');
165
+ await import('./admin-shell.js');
166
+ const warns = [];
167
+ const origWarn = console.warn;
168
+ console.warn = (...args) => warns.push(args.join(' '));
169
+ try {
170
+ const shell = mount(
171
+ '<admin-shell>' +
172
+ '<admin-sidebar slot="leading"></admin-sidebar>' +
173
+ '<button data-sidebar-toggle="leading">☰</button>' +
174
+ '</admin-shell>'
175
+ );
176
+ await new Promise((r) => queueMicrotask(r));
177
+ shell.querySelector('[data-sidebar-toggle]').click();
178
+ // No warn — sidebar.toggle() exists and runs cleanly.
179
+ const fb55Warns = warns.filter((w) => /FB-55|not registered/.test(w));
180
+ expect(fb55Warns.length).toBe(0);
181
+ } finally {
182
+ console.warn = origWarn;
183
+ }
184
+ });
185
+ });