@adia-ai/web-modules 0.6.34 → 0.6.35
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.
- package/CHANGELOG.md +41 -0
- package/chat/chat-composer/chat-composer.a2ui.json +1 -1
- package/chat/chat-composer/chat-composer.js +1 -1
- package/chat/chat-composer/chat-composer.yaml +6 -3
- package/chat/chat-shell/chat-shell.a2ui.json +1 -1
- package/chat/chat-shell/chat-shell.yaml +5 -5
- package/dist/everything.min.js +200 -104
- package/dist/shell/admin-shell.min.js +5 -5
- package/editor/editor-sidebar/editor-sidebar.a2ui.json +5 -0
- package/editor/editor-sidebar/editor-sidebar.d.ts +8 -0
- package/editor/editor-sidebar/editor-sidebar.js +5 -0
- package/editor/editor-sidebar/editor-sidebar.yaml +11 -0
- package/index.js +2 -0
- package/package.json +1 -1
- package/shell/admin-shell/admin-shell.js +82 -26
- package/shell/admin-shell/admin-shell.test.js +68 -0
|
@@ -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
|
|
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}="${
|
|
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
|
|
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 .${
|
|
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:
|
|
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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adia-ai/web-modules",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.35",
|
|
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
|
-
|
|
50
|
-
|
|
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
|
-
// ──
|
|
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
|
-
#
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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
|
-
//
|
|
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
|
-
#
|
|
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
|
+
});
|