@pure-ds/core 0.6.8 → 0.6.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,7 +4,7 @@ var Ge=Object.defineProperty;var Ve=(e,t)=>()=>(e&&(t=e(e=0)),t);var xe=(e,t)=>{
4
4
  ${this.handleImageOrIcon(o)}
5
5
  <span class="text">${this.formatResultItem(o,n,i)}</span>
6
6
  ${this.settings.hideCategory?"":`<span class="category">${o.category||""}</span>`}
7
- </div>`;t.forEach(i=>{let o=n.categories[i.category]||{};i.element?this.resultsDiv.appendChild(i.element):(i=typeof i=="string"?{text:i}:i,this.resultsDiv.appendChild(ke(s(o,i))[0])),r++}),t.length?(this.acItems=this.resultsDiv.querySelectorAll(".ac-itm"),this.controller().show()):n.search.length&&this.controller().empty()}handleImageOrIcon(t){return t.image?`<img src="${t.image}"/>`:typeof this.settings.iconHandler=="function"?this.settings.iconHandler(t):`<svg-icon icon="${t.icon}"></svg-icon>`}formatResultItem(t,n,r){let s=typeof t=="string"?{text:t}:t,i=s.text;return n.search&&(i=i.replace("%search%",n.search),s.description=s.description?.replace("%search%",n.search)),i=this.highlight(i,n.search),s.description&&(i=`<div>${i}</div><small>${s.description}</small>`),r.format&&(i=r.format({item:s,result:i,options:n})),i}highlight(t,n){var r=new RegExp("("+n+")","gi");return t.replace(r,'<span class="txt-hl">$1</span>')}async getItems(t,n){this.aborter&&this.aborter.abort();let r=this.caches.get(t.search);if(r)return r;let s=this.settings.map,i=p=>(typeof p=="string"&&(p={text:p}),p),o=p=>s?p.map(l=>({text:l[s]})):p.map(l=>i(l)),d=p=>(this.settings.max&&this.settings.max>0&&(p.length=this.settings.max),p);return this.aborter=new AbortController,this.aborterSignal=this.aborter.signal,new Promise(p=>{let l=u=>{u=this.sort(u,t),this.settings.cache!==!1&&this.caches.set(t.search,u),p(u)};if(Ee(this.items)){if(this.settings.minlength>0&&(!t.search||t.search.length<this.settings.minlength)){l([]);return}let u=this.formatSearch(this.items,t);fetch(u).then(a=>{if(a.status===200){a.json().then(h=>{h=o(h),l(d(h.filter(f=>this.isMatch(t,f))))});return}throw Error(`HTTP error ${a.status} - ${u}`)})}else if(Array.isArray(this.items)){let u=!0;this.items=this.items.map(a=>typeof a=="string"?{text:a}:(u=!1,a)),u&&this.container.classList.add("simple"),l(d(o(this.items)))}else if(typeof this.items=="function")t.control=this.container,Promise.resolve(this.items(t,n)).then(a=>{a=a.map(h=>i(h)),a=o(a),l(a)});else return l(Promise.resolve(this.items.apply(this,t)))})}async items(t){let n=[];t.results=[],t.signal=this.aborterSignal;for(var r in t.categories){let s=t.categories[r];if(s.trigger=s.trigger??(()=>!0),t.results=n,s.trigger(t)){let i=[];try{i=await s.getItems(t)}catch(o){console.warn(`Error loading items for omniBox category '${r}'.`,o)}n=n.concat(i.map(o=>(o.category=r,o)))}}return n}formatSearch(t,n){return t.indexOf("%search%")?t.replace("%search%",n.search||""):t+"?"+this.createQueryParam(n)}createQueryParam(t){let n=t.suggest?"&suggest=true":"";return`q=${t.text}${n}`}isMatch(t,n){return n.text?.indexOf("%search%")>=0?!0:t.search?n.text?.toLowerCase().indexOf(t.search.toLowerCase())>=0:t.suggest}static textFilter(t,n){return function(r){if(!t.search)return!0;if(r.hidden)return!1;let i=(n?r[n]:r).match(new RegExp(t.search,"gi"));if(i)return i;if(r.config?.tags)return r.config.tags.some(o=>o.match(new RegExp(t.search,"gi")))}}};var ee=class{constructor(){this._mode="static",this._staticPaths={tokens:"/assets/pds/styles/pds-tokens.css.js",primitives:"/assets/pds/styles/pds-primitives.css.js",components:"/assets/pds/styles/pds-components.css.js",utilities:"/assets/pds/styles/pds-utilities.css.js",styles:"/assets/pds/styles/pds-styles.css.js"}}setLiveMode(){this._mode="live"}setStaticMode(t={}){this._mode="static",this._staticPaths={...this._staticPaths,...t}}async getStylesheet(t){if(this._mode==="live")return null;try{return(await import(this._staticPaths[t]))[t]}catch(n){console.error(`[PDS Registry] Failed to load static ${t}:`,n),console.error(`[PDS Registry] Looking for: ${this._staticPaths[t]}`),console.error("[PDS Registry] Make sure you've run 'npm run pds:build' and configured PDS.start() with the correct static.root path");let r=new CSSStyleSheet;return r.replaceSync("/* Failed to load "+t+" */"),r}}get mode(){return this._mode}get isLive(){return this._mode==="live"}},F=new ee;async function Le(e,t=[],n=null){try{let r=n?.primitivesStylesheet?n.primitivesStylesheet:await F.getStylesheet("primitives");e.adoptedStyleSheets=[r,...t]}catch(r){let s=e.host?.tagName?.toLowerCase()||"unknown";console.error(`[PDS Adopter] <${s}> failed to adopt primitives:`,r),e.adoptedStyleSheets=t}}async function Re(e,t=["primitives"],n=[],r=null){try{let i=(await Promise.all(t.map(async o=>{if(r)switch(o){case"tokens":return r.tokensStylesheet;case"primitives":return r.primitivesStylesheet;case"components":return r.componentsStylesheet;case"utilities":return r.utilitiesStylesheet;default:break}return F.getStylesheet(o)}))).filter(o=>o!==null);e.adoptedStyleSheets=[...i,...n]}catch(s){let i=e.host?.tagName?.toLowerCase()||"unknown";console.error(`[PDS Adopter] <${i}> failed to adopt layers:`,s),e.adoptedStyleSheets=n}}function Me(e){let t=new CSSStyleSheet;return t.replaceSync(e),t}var c={FontWeights:{light:300,normal:400,medium:500,semibold:600,bold:700},LineHeights:{tight:1.25,normal:1.5,relaxed:1.75},BorderWidths:{hairline:.5,thin:1,medium:2,thick:3},RadiusSizes:{none:0,small:4,medium:8,large:16,xlarge:24,xxlarge:32},ShadowDepths:{none:"none",light:"0 1px 2px 0 rgba(0, 0, 0, 0.05)",medium:"0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)",deep:"0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)",extreme:"0 25px 50px -12px rgba(0, 0, 0, 0.25)"},TransitionSpeeds:{fast:150,normal:250,slow:350},AnimationEasings:{linear:"linear",ease:"ease","ease-in":"ease-in","ease-out":"ease-out","ease-in-out":"ease-in-out",bounce:"cubic-bezier(0.68, -0.55, 0.265, 1.55)"},TouchTargetSizes:{compact:36,standard:44,comfortable:48,spacious:56},LinkStyles:{inline:"inline",block:"block",button:"button"},FocusStyles:{ring:"ring",outline:"outline",border:"border",glow:"glow"},TabSizes:{compact:2,standard:4,wide:8},SelectIcons:{chevron:"chevron",arrow:"arrow",caret:"caret",none:"none"},IconSizes:{xs:16,sm:20,md:24,lg:32,xl:48,"2xl":64,"3xl":96}};var Ce={mode:"live",preset:"default",autoDefine:{predefine:["pds-icon","pds-drawer","pds-toaster"]},log(e,t,...n){console[e](t,...n)}};var re={};xe(re,{deepMerge:()=>Te,fragmentFromTemplateLike:()=>te,isObject:()=>V,parseHTML:()=>ne});function V(e){return e&&typeof e=="object"&&!Array.isArray(e)}function Te(e,t){let n={...e};return V(e)&&V(t)&&Object.keys(t).forEach(r=>{V(t[r])?r in e?n[r]=Te(e[r],t[r]):Object.assign(n,{[r]:t[r]}):Object.assign(n,{[r]:t[r]})}),n}function te(e){let t=Array.isArray(e?.strings)?e.strings:[],n=Array.isArray(e?.values)?e.values:[],r=new Set,s=[],i=/(\s)(\.[\w-]+)=\s*$/;for(let a=0;a<t.length;a+=1){let h=t[a]??"",f=h.match(i);if(f&&a<n.length){let S=f[2].slice(1),m=`pds-val-${a}`;h=h.replace(i,`$1data-pds-prop="${S}:${m}"`),r.add(a)}s.push(h),a<n.length&&!r.has(a)&&s.push(`<!--pds-val-${a}-->`)}let o=document.createElement("template");o.innerHTML=s.join("");let d=(a,h)=>{let f=a.parentNode;if(!f)return;if(h==null){f.removeChild(a);return}let w=S=>{if(S!=null){if(S instanceof Node){f.insertBefore(S,a);return}if(Array.isArray(S)){S.forEach(m=>w(m));return}f.insertBefore(document.createTextNode(String(S)),a)}};w(h),f.removeChild(a)},p=document.createTreeWalker(o.content,NodeFilter.SHOW_COMMENT),l=[];for(;p.nextNode();){let a=p.currentNode;a?.nodeValue?.startsWith("pds-val-")&&l.push(a)}return l.forEach(a=>{let h=Number(a.nodeValue.replace("pds-val-",""));d(a,n[h])}),o.content.querySelectorAll("*").forEach(a=>{let h=a.getAttribute("data-pds-prop");if(!h)return;let[f,w]=h.split(":"),S=Number(String(w).replace("pds-val-",""));f&&Number.isInteger(S)&&(a[f]=n[S]),a.removeAttribute("data-pds-prop")}),o.content}function ne(e){return new DOMParser().parseFromString(e,"text/html").body.childNodes}function se(e,t){if(t==null)return;if(typeof t=="object"&&Array.isArray(t.strings)&&Array.isArray(t.values)){e.appendChild(te(t));return}if(t instanceof Node){e.appendChild(t);return}if(Array.isArray(t)){t.forEach(r=>se(e,r));return}let n=typeof t=="string"?t:String(t);e.appendChild(document.createTextNode(n))}function Ke(){let e=navigator.userAgent,t=/Safari/i.test(e),n=/(Chrome|Chromium|CriOS|FxiOS|EdgiOS|OPiOS|Opera)/i.test(e);return t&&!n}function Qe(e){if(window.matchMedia?.("(prefers-reduced-motion: reduce)").matches)return;let t=window.matchMedia?.("(max-width: 639px)").matches,n=e.classList.contains("dialog-no-scale-animation")?"pds-dialog-fade-enter":t?"pds-dialog-enter-mobile":"pds-dialog-enter";e.style.animation="none",e.offsetWidth,e.style.animation=`${n} var(--transition-normal) ease`,e.addEventListener("animationend",()=>{e.style.animation=""},{once:!0})}async function De(e,t={}){return t={...{title:"Confirm",type:"confirm",buttons:{ok:{name:"OK",primary:!0},cancel:{name:"Cancel",cancel:!0}}},...t},new Promise(r=>{let s=document.createElement("dialog");Ke()&&s.classList.add("dialog-no-scale-animation"),Ce.options?.liquidGlassEffects&&s.classList.add("liquid-glass"),t.size&&s.classList.add(`dialog-${t.size}`),t.type&&s.classList.add(`dialog-${t.type}`),t.class&&(Array.isArray(t.class)?s.classList.add(...t.class):s.classList.add(t.class)),t.maxHeight&&s.style.setProperty("--dialog-max-height",t.maxHeight);let i=Object.entries(t.buttons).map(([d,p])=>{let l=p.primary?"btn-primary btn-sm":"btn-outline btn-sm";return`<button type="${p.cancel?"button":"submit"}" class="${l}" value="${d}">${p.name}</button>`});if(t.useForm){let d=document.createElement("div");se(d,e);let p=d.querySelector("form");if(p){s.innerHTML=`
7
+ </div>`;t.forEach(i=>{let o=n.categories[i.category]||{};i.element?this.resultsDiv.appendChild(i.element):(i=typeof i=="string"?{text:i}:i,this.resultsDiv.appendChild(ke(s(o,i))[0])),r++}),t.length?(this.acItems=this.resultsDiv.querySelectorAll(".ac-itm"),this.controller().show()):n.search.length&&this.controller().empty()}handleImageOrIcon(t){return t.image?`<img src="${t.image}"/>`:typeof this.settings.iconHandler=="function"?this.settings.iconHandler(t):`<svg-icon icon="${t.icon}"></svg-icon>`}formatResultItem(t,n,r){let s=typeof t=="string"?{text:t}:t,i=s.text;return n.search&&(i=i.replace("%search%",n.search),s.description=s.description?.replace("%search%",n.search)),i=this.highlight(i,n.search),s.description&&(i=`<div>${i}</div><small>${s.description}</small>`),r.format&&(i=r.format({item:s,result:i,options:n})),i}highlight(t,n){var r=new RegExp("("+n+")","gi");return t.replace(r,'<span class="txt-hl">$1</span>')}async getItems(t,n){this.aborter&&this.aborter.abort();let r=this.caches.get(t.search);if(r)return r;let s=this.settings.map,i=p=>(typeof p=="string"&&(p={text:p}),p),o=p=>s?p.map(l=>({text:l[s]})):p.map(l=>i(l)),d=p=>(this.settings.max&&this.settings.max>0&&(p.length=this.settings.max),p);return this.aborter=new AbortController,this.aborterSignal=this.aborter.signal,new Promise(p=>{let l=u=>{u=this.sort(u,t),this.settings.cache!==!1&&this.caches.set(t.search,u),p(u)};if(Ee(this.items)){if(this.settings.minlength>0&&(!t.search||t.search.length<this.settings.minlength)){l([]);return}let u=this.formatSearch(this.items,t);fetch(u).then(a=>{if(a.status===200){a.json().then(h=>{h=o(h),l(d(h.filter(f=>this.isMatch(t,f))))});return}throw Error(`HTTP error ${a.status} - ${u}`)})}else if(Array.isArray(this.items)){let u=!0;this.items=this.items.map(a=>typeof a=="string"?{text:a}:(u=!1,a)),u&&this.container.classList.add("simple"),l(d(o(this.items)))}else if(typeof this.items=="function")t.control=this.container,Promise.resolve(this.items(t,n)).then(a=>{a=a.map(h=>i(h)),a=o(a),l(a)});else return l(Promise.resolve(this.items.apply(this,t)))})}async items(t){let n=[];t.results=[],t.signal=this.aborterSignal;for(var r in t.categories){let s=t.categories[r];if(s.trigger=s.trigger??(()=>!0),t.results=n,s.trigger(t)){let i=[];try{i=await s.getItems(t)}catch(o){console.warn(`Error loading items for omniBox category '${r}'.`,o)}n=n.concat(i.map(o=>(o.category=r,o)))}}return n}formatSearch(t,n){return t.indexOf("%search%")?t.replace("%search%",n.search||""):t+"?"+this.createQueryParam(n)}createQueryParam(t){let n=t.suggest?"&suggest=true":"";return`q=${t.text}${n}`}isMatch(t,n){return n.text?.indexOf("%search%")>=0?!0:t.search?n.text?.toLowerCase().indexOf(t.search.toLowerCase())>=0:t.suggest}static textFilter(t,n){return function(r){if(!t.search)return!0;if(r.hidden)return!1;let i=(n?r[n]:r).match(new RegExp(t.search,"gi"));if(i)return i;if(r.config?.tags)return r.config.tags.some(o=>o.match(new RegExp(t.search,"gi")))}}};var ee=class{constructor(){this._mode="static",this._staticPaths={tokens:"/assets/pds/styles/pds-tokens.css.js",primitives:"/assets/pds/styles/pds-primitives.css.js",components:"/assets/pds/styles/pds-components.css.js",utilities:"/assets/pds/styles/pds-utilities.css.js",styles:"/assets/pds/styles/pds-styles.css.js"}}setLiveMode(){this._mode="live"}setStaticMode(t={}){this._mode="static",this._staticPaths={...this._staticPaths,...t}}async getStylesheet(t){if(this._mode==="live")return null;try{return(await import(this._staticPaths[t]))[t]}catch(n){console.error(`[PDS Registry] Failed to load static ${t}:`,n),console.error(`[PDS Registry] Looking for: ${this._staticPaths[t]}`),console.error("[PDS Registry] Make sure you've run 'npm run pds:build' and configured PDS.start() with the correct static.root path");let r=new CSSStyleSheet;return r.replaceSync("/* Failed to load "+t+" */"),r}}get mode(){return this._mode}get isLive(){return this._mode==="live"}},F=new ee;async function Le(e,t=[],n=null){try{let r=n?.primitivesStylesheet?n.primitivesStylesheet:await F.getStylesheet("primitives");e.adoptedStyleSheets=[r,...t]}catch(r){let s=e.host?.tagName?.toLowerCase()||"unknown";console.error(`[PDS Adopter] <${s}> failed to adopt primitives:`,r),e.adoptedStyleSheets=t}}async function Re(e,t=["primitives"],n=[],r=null){try{let i=(await Promise.all(t.map(async o=>{if(r)switch(o){case"tokens":return r.tokensStylesheet;case"primitives":return r.primitivesStylesheet;case"components":return r.componentsStylesheet;case"utilities":return r.utilitiesStylesheet;default:break}return F.getStylesheet(o)}))).filter(o=>o!==null);e.adoptedStyleSheets=[...i,...n]}catch(s){let i=e.host?.tagName?.toLowerCase()||"unknown";console.error(`[PDS Adopter] <${i}> failed to adopt layers:`,s),e.adoptedStyleSheets=n}}function Me(e){let t=new CSSStyleSheet;return t.replaceSync(e),t}var c={FontWeights:{light:300,normal:400,medium:500,semibold:600,bold:700},LineHeights:{tight:1.25,normal:1.5,relaxed:1.75},BorderWidths:{hairline:.5,thin:1,medium:2,thick:3},RadiusSizes:{none:0,small:4,medium:8,large:16,xlarge:24,xxlarge:32},ShadowDepths:{none:"none",light:"0 1px 2px 0 rgba(0, 0, 0, 0.05)",medium:"0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)",deep:"0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)",extreme:"0 25px 50px -12px rgba(0, 0, 0, 0.25)"},TransitionSpeeds:{fast:150,normal:250,slow:350},AnimationEasings:{linear:"linear",ease:"ease","ease-in":"ease-in","ease-out":"ease-out","ease-in-out":"ease-in-out",bounce:"cubic-bezier(0.68, -0.55, 0.265, 1.55)"},TouchTargetSizes:{compact:36,standard:44,comfortable:48,spacious:56},LinkStyles:{inline:"inline",block:"block",button:"button"},FocusStyles:{ring:"ring",outline:"outline",border:"border",glow:"glow"},TabSizes:{compact:2,standard:4,wide:8},SelectIcons:{chevron:"chevron",arrow:"arrow",caret:"caret",none:"none"},IconSizes:{xs:16,sm:20,md:24,lg:32,xl:48,"2xl":64,"3xl":96}};var Ce={mode:"live",liveEdit:!0,preset:"default",autoDefine:{predefine:["pds-icon","pds-drawer","pds-toaster"]},log(e,t,...n){console[e](t,...n)}};var re={};xe(re,{deepMerge:()=>Te,fragmentFromTemplateLike:()=>te,isObject:()=>V,parseHTML:()=>ne});function V(e){return e&&typeof e=="object"&&!Array.isArray(e)}function Te(e,t){let n={...e};return V(e)&&V(t)&&Object.keys(t).forEach(r=>{V(t[r])?r in e?n[r]=Te(e[r],t[r]):Object.assign(n,{[r]:t[r]}):Object.assign(n,{[r]:t[r]})}),n}function te(e){let t=Array.isArray(e?.strings)?e.strings:[],n=Array.isArray(e?.values)?e.values:[],r=new Set,s=[],i=/(\s)(\.[\w-]+)=\s*$/;for(let a=0;a<t.length;a+=1){let h=t[a]??"",f=h.match(i);if(f&&a<n.length){let S=f[2].slice(1),m=`pds-val-${a}`;h=h.replace(i,`$1data-pds-prop="${S}:${m}"`),r.add(a)}s.push(h),a<n.length&&!r.has(a)&&s.push(`<!--pds-val-${a}-->`)}let o=document.createElement("template");o.innerHTML=s.join("");let d=(a,h)=>{let f=a.parentNode;if(!f)return;if(h==null){f.removeChild(a);return}let w=S=>{if(S!=null){if(S instanceof Node){f.insertBefore(S,a);return}if(Array.isArray(S)){S.forEach(m=>w(m));return}f.insertBefore(document.createTextNode(String(S)),a)}};w(h),f.removeChild(a)},p=document.createTreeWalker(o.content,NodeFilter.SHOW_COMMENT),l=[];for(;p.nextNode();){let a=p.currentNode;a?.nodeValue?.startsWith("pds-val-")&&l.push(a)}return l.forEach(a=>{let h=Number(a.nodeValue.replace("pds-val-",""));d(a,n[h])}),o.content.querySelectorAll("*").forEach(a=>{let h=a.getAttribute("data-pds-prop");if(!h)return;let[f,w]=h.split(":"),S=Number(String(w).replace("pds-val-",""));f&&Number.isInteger(S)&&(a[f]=n[S]),a.removeAttribute("data-pds-prop")}),o.content}function ne(e){return new DOMParser().parseFromString(e,"text/html").body.childNodes}function se(e,t){if(t==null)return;if(typeof t=="object"&&Array.isArray(t.strings)&&Array.isArray(t.values)){e.appendChild(te(t));return}if(t instanceof Node){e.appendChild(t);return}if(Array.isArray(t)){t.forEach(r=>se(e,r));return}let n=typeof t=="string"?t:String(t);e.appendChild(document.createTextNode(n))}function Ke(){let e=navigator.userAgent,t=/Safari/i.test(e),n=/(Chrome|Chromium|CriOS|FxiOS|EdgiOS|OPiOS|Opera)/i.test(e);return t&&!n}function Qe(e){if(window.matchMedia?.("(prefers-reduced-motion: reduce)").matches)return;let t=window.matchMedia?.("(max-width: 639px)").matches,n=e.classList.contains("dialog-no-scale-animation")?"pds-dialog-fade-enter":t?"pds-dialog-enter-mobile":"pds-dialog-enter";e.style.animation="none",e.offsetWidth,e.style.animation=`${n} var(--transition-normal) ease`,e.addEventListener("animationend",()=>{e.style.animation=""},{once:!0})}async function De(e,t={}){return t={...{title:"Confirm",type:"confirm",buttons:{ok:{name:"OK",primary:!0},cancel:{name:"Cancel",cancel:!0}}},...t},new Promise(r=>{let s=document.createElement("dialog");Ke()&&s.classList.add("dialog-no-scale-animation"),Ce.options?.liquidGlassEffects&&s.classList.add("liquid-glass"),t.size&&s.classList.add(`dialog-${t.size}`),t.type&&s.classList.add(`dialog-${t.type}`),t.class&&(Array.isArray(t.class)?s.classList.add(...t.class):s.classList.add(t.class)),t.maxHeight&&s.style.setProperty("--dialog-max-height",t.maxHeight);let i=Object.entries(t.buttons).map(([d,p])=>{let l=p.primary?"btn-primary btn-sm":"btn-outline btn-sm";return`<button type="${p.cancel?"button":"submit"}" class="${l}" value="${d}">${p.name}</button>`});if(t.useForm){let d=document.createElement("div");se(d,e);let p=d.querySelector("form");if(p){s.innerHTML=`
8
8
  <header>
9
9
  <h2>${t.title}</h2>
10
10
  </header>
@@ -137,6 +137,7 @@ const SURFACE_CONTEXT_PATHS = new Set([
137
137
  const DARK_MODE_PATH_MARKER = ".darkMode.";
138
138
  const QUICK_EDIT_LIMIT = 4;
139
139
  const DROPDOWN_VIEWPORT_PADDING = 8;
140
+ const FONT_FAMILY_PATH_REGEX = /^typography\.fontFamily/i;
140
141
 
141
142
  function isHoverCapable() {
142
143
  if (typeof window === "undefined" || !window.matchMedia) return false;
@@ -443,6 +444,33 @@ function collectQuickRulePaths(target) {
443
444
  }).flatMap((rule) => rule.paths);
444
445
  }
445
446
 
447
+ function isHeadingElement(target) {
448
+ if (!target || !(target instanceof Element)) return false;
449
+ const tag = target.tagName?.toLowerCase?.() || "";
450
+ if (/^h[1-6]$/.test(tag)) return true;
451
+ if (target.getAttribute("role") === "heading") return true;
452
+ return false;
453
+ }
454
+
455
+ function hasMeaningfulText(target) {
456
+ if (!target || !(target instanceof Element)) return false;
457
+ const tag = target.tagName?.toLowerCase?.() || "";
458
+ if (["script", "style", "svg", "path", "defs", "symbol"].includes(tag)) return false;
459
+ const text = target.textContent || "";
460
+ return text.trim().length > 0;
461
+ }
462
+
463
+ function collectTypographyPathsForTarget(target) {
464
+ if (!target || !(target instanceof Element)) return [];
465
+ if (isHeadingElement(target)) {
466
+ return ["typography.fontFamilyHeadings"];
467
+ }
468
+ if (hasMeaningfulText(target)) {
469
+ return ["typography.fontFamilyBody"];
470
+ }
471
+ return [];
472
+ }
473
+
446
474
  function pathExistsInDesign(path, design) {
447
475
  if (!path || !design) return false;
448
476
  const segments = path.split(".");
@@ -455,7 +483,7 @@ function pathExistsInDesign(path, design) {
455
483
  return true;
456
484
  }
457
485
 
458
- function filterPathsByContext(target, paths) {
486
+ function filterPathsByContext(target, paths, alwaysAllow = new Set()) {
459
487
  if (!target || !paths.length) return paths;
460
488
  const isGlobal = target.matches("body, main");
461
489
  const isInForm = Boolean(target.closest("form, pds-form"));
@@ -468,6 +496,7 @@ function filterPathsByContext(target, paths) {
468
496
  const design = PDS?.currentConfig?.design || {};
469
497
 
470
498
  return paths.filter((path) => {
499
+ if (alwaysAllow.has(path)) return true;
471
500
  if (!theme.isDark && path.includes(DARK_MODE_PATH_MARKER)) return false;
472
501
  if (path.startsWith("typography.") && !isGlobal) return false;
473
502
  if (GLOBAL_LAYOUT_PATHS.has(path) && !isGlobal) return false;
@@ -768,7 +797,10 @@ function collectPathsFromComputedStyles(target) {
768
797
  const trimmed = value.trim();
769
798
 
770
799
  // Extract var refs from the value (handles var() with fallbacks)
771
- addVarSet(extractAllVarRefs(trimmed));
800
+ const directVarRefs = extractAllVarRefs(trimmed);
801
+ addVarSet(directVarRefs);
802
+ const hasDirectVarRefs = directVarRefs.size > 0;
803
+ if (hasDirectVarRefs) return;
772
804
 
773
805
  if (trimmed && valueToVars.has(trimmed)) {
774
806
  valueToVars.get(trimmed).forEach((varName) => addVarName(varName));
@@ -829,15 +861,18 @@ function collectQuickContext(target) {
829
861
  const byComputed = computed?.paths || [];
830
862
  const byRelations = collectPathsFromRelations(target);
831
863
  const byQuickRules = collectQuickRulePaths(target);
864
+ const byTypographyContext = collectTypographyPathsForTarget(target);
832
865
  const hints = computed?.hints || {};
833
866
  const debug = computed?.debug || { vars: [], paths: [] };
867
+ const alwaysAllow = new Set(byTypographyContext);
834
868
 
835
869
  // Prioritize quick rule paths first (selector-based), then computed/relations
836
870
  const filtered = filterPathsByContext(target, [
871
+ ...byTypographyContext,
837
872
  ...byQuickRules,
838
873
  ...byComputed,
839
874
  ...byRelations,
840
- ]);
875
+ ], alwaysAllow);
841
876
  if (!filtered.length) {
842
877
  return {
843
878
  paths: normalizePaths(DEFAULT_QUICK_PATHS),
@@ -860,6 +895,105 @@ function collectDrawerPaths(quickPaths) {
860
895
  return normalizePaths([...quickPaths, ...expanded]);
861
896
  }
862
897
 
898
+ function splitFontFamilyStack(value) {
899
+ if (typeof value !== "string") return [];
900
+ const input = value.trim();
901
+ if (!input) return [];
902
+ const parts = [];
903
+ let buffer = "";
904
+ let quote = null;
905
+ for (let i = 0; i < input.length; i += 1) {
906
+ const char = input[i];
907
+ if (quote) {
908
+ buffer += char;
909
+ if (char === quote && input[i - 1] !== "\\") {
910
+ quote = null;
911
+ }
912
+ continue;
913
+ }
914
+ if (char === '"' || char === "'") {
915
+ quote = char;
916
+ buffer += char;
917
+ continue;
918
+ }
919
+ if (char === ",") {
920
+ const token = buffer.trim();
921
+ if (token) parts.push(token);
922
+ buffer = "";
923
+ continue;
924
+ }
925
+ buffer += char;
926
+ }
927
+ const last = buffer.trim();
928
+ if (last) parts.push(last);
929
+ return parts;
930
+ }
931
+
932
+ function getPresetFontFamilyVariations() {
933
+ const presets = Object.values(PDS?.presets || {});
934
+ const seen = new Set();
935
+ const items = [];
936
+ const addItem = (fontFamily) => {
937
+ const normalized = String(fontFamily || "").trim().replace(/\s+/g, " ");
938
+ if (!normalized || seen.has(normalized)) return;
939
+ seen.add(normalized);
940
+ items.push({
941
+ id: normalized,
942
+ text: normalized,
943
+ style: `font-family: ${normalized}`,
944
+ });
945
+ };
946
+
947
+ presets.forEach((preset) => {
948
+ const typography = preset?.typography || {};
949
+ ["fontFamilyHeadings", "fontFamilyBody"].forEach((key) => {
950
+ const stack = typography[key];
951
+ if (typeof stack !== "string" || !stack.trim()) return;
952
+
953
+ addItem(stack);
954
+ const parts = splitFontFamilyStack(stack);
955
+ parts.forEach((part) => addItem(part));
956
+ for (let i = 1; i < parts.length; i += 1) {
957
+ addItem(parts.slice(i).join(", "));
958
+ }
959
+ });
960
+ });
961
+
962
+ return items;
963
+ }
964
+
965
+ function buildFontFamilyOmniboxSettings() {
966
+ const allItems = getPresetFontFamilyVariations();
967
+ const filterItems = (search) => {
968
+ const query = String(search || "").trim().toLowerCase();
969
+ if (!query) return allItems;
970
+ return allItems.filter((item) => item.text.toLowerCase().includes(query));
971
+ };
972
+
973
+ return {
974
+ hideCategory: true,
975
+ iconHandler: (item) => {
976
+
977
+ return "";
978
+ },
979
+ categories: {
980
+ FontFamilies: {
981
+ trigger: () => true,
982
+ getItems: (options) => filterItems(options?.search),
983
+ action: (options, ev) => {
984
+ const input = document.querySelector("[name='/typography/fontFamilyHeadings']");
985
+
986
+ if (input && input.tagName === "PDS-OMNIBOX") {
987
+ input.value = options?.text || "";
988
+
989
+ }
990
+ return options?.text || options?.id;
991
+ },
992
+ },
993
+ },
994
+ };
995
+ }
996
+
863
997
  function buildSchemaFromPaths(paths, design, hints = {}) {
864
998
  const schema = { type: "object", properties: {} };
865
999
  const uiSchema = {};
@@ -914,6 +1048,13 @@ function buildSchemaFromPaths(paths, design, hints = {}) {
914
1048
  if (!parent) {
915
1049
  parent = { type: "object", title: titleize(category), properties: {} };
916
1050
  schema.properties[category] = parent;
1051
+
1052
+ if (category === "colors") {
1053
+ uiSchema[`/${category}`] = {
1054
+ "ui:layout": "flex",
1055
+ "ui:layoutOptions": { wrap: true, gap: "sm" },
1056
+ };
1057
+ }
917
1058
  }
918
1059
 
919
1060
  let current = parent;
@@ -973,6 +1114,8 @@ function buildSchemaFromPaths(paths, design, hints = {}) {
973
1114
  uiEntry["ui:step"] = bounds.step;
974
1115
  } else if (isColorValue(value, path)) {
975
1116
  uiEntry["ui:widget"] = "input-color";
1117
+ } else if (FONT_FAMILY_PATH_REGEX.test(path) && schemaType === "string") {
1118
+ uiEntry["ui:widget"] = "font-family-omnibox";
976
1119
  }
977
1120
 
978
1121
  const isTextOrNumberInput =
@@ -1173,6 +1316,155 @@ async function applyPresetSelection(presetId) {
1173
1316
  await applyDesignPatch({});
1174
1317
  }
1175
1318
 
1319
+ function figmafyTokens(rawTokens) {
1320
+ const isPlainObject = (value) =>
1321
+ value !== null && typeof value === "object" && !Array.isArray(value);
1322
+
1323
+ const detectType = (path, key, value) => {
1324
+ const root = path[0];
1325
+
1326
+ if (root === "colors") {
1327
+ if (key === "scheme") return "string";
1328
+ return "color";
1329
+ }
1330
+
1331
+ if (root === "spacing" || root === "radius" || root === "borderWidths") {
1332
+ return "dimension";
1333
+ }
1334
+
1335
+ if (root === "typography") {
1336
+ const group = path[1];
1337
+ if (group === "fontFamily") return "fontFamily";
1338
+ if (group === "fontSize") return "fontSize";
1339
+ if (group === "fontWeight") return "fontWeight";
1340
+ if (group === "lineHeight") return "lineHeight";
1341
+ return "string";
1342
+ }
1343
+
1344
+ if (root === "shadows") return "shadow";
1345
+ if (root === "layout") return "dimension";
1346
+ if (root === "transitions") return "duration";
1347
+ if (root === "zIndex") return "number";
1348
+
1349
+ if (root === "icons") {
1350
+ if (key === "defaultSize" || path.includes("sizes")) {
1351
+ return "dimension";
1352
+ }
1353
+ return "string";
1354
+ }
1355
+
1356
+ if (typeof value === "number") {
1357
+ return "number";
1358
+ }
1359
+
1360
+ if (typeof value === "string") {
1361
+ if (/^\d+(\.\d+)?ms$/.test(value)) return "duration";
1362
+ if (/^\d+(\.\d+)?(px|rem|em|vh|vw|%)$/.test(value)) return "dimension";
1363
+
1364
+ if (
1365
+ /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/.test(value) ||
1366
+ /^(rgb|rgba|hsl|hsla|oklab|lab)\(/.test(value)
1367
+ ) {
1368
+ return "color";
1369
+ }
1370
+ }
1371
+
1372
+ return undefined;
1373
+ };
1374
+
1375
+ const walk = (node, path = []) => {
1376
+ if (node == null) return node;
1377
+
1378
+ if (Array.isArray(node)) {
1379
+ return node.map((item, index) => walk(item, path.concat(String(index))));
1380
+ }
1381
+
1382
+ if (isPlainObject(node)) {
1383
+ if (
1384
+ Object.prototype.hasOwnProperty.call(node, "value") &&
1385
+ (Object.prototype.hasOwnProperty.call(node, "type") ||
1386
+ Object.keys(node).length === 1)
1387
+ ) {
1388
+ return node;
1389
+ }
1390
+
1391
+ const result = {};
1392
+ for (const [key, value] of Object.entries(node)) {
1393
+ result[key] = walk(value, path.concat(key));
1394
+ }
1395
+ return result;
1396
+ }
1397
+
1398
+ const key = path[path.length - 1] ?? "";
1399
+ const type = detectType(path, key, node);
1400
+ let value = node;
1401
+
1402
+ if (type === "number" && typeof value === "string") {
1403
+ const num = Number(value);
1404
+ if (!Number.isNaN(num)) value = num;
1405
+ }
1406
+
1407
+ return type ? { value, type } : { value };
1408
+ };
1409
+
1410
+ return walk(rawTokens, []);
1411
+ }
1412
+
1413
+ function downloadTextFile(content, filename, mimeType) {
1414
+ if (typeof document === "undefined") return;
1415
+ const blob = new Blob([content], { type: mimeType });
1416
+ const url = URL.createObjectURL(blob);
1417
+ const anchor = document.createElement("a");
1418
+ anchor.href = url;
1419
+ anchor.download = filename;
1420
+ anchor.click();
1421
+ URL.revokeObjectURL(url);
1422
+ }
1423
+
1424
+ function getLiveEditExportConfig() {
1425
+ const stored = getStoredConfig();
1426
+ const design = shallowClone(PDS?.currentConfig?.design || stored?.design || {});
1427
+ const preset = stored?.preset || PDS?.currentConfig?.preset || PDS?.currentPreset || null;
1428
+ return {
1429
+ preset,
1430
+ design,
1431
+ };
1432
+ }
1433
+
1434
+ function buildConfigModuleContent(config) {
1435
+ return `export const pdsConfig = ${JSON.stringify(config, null, 2)};\n\nexport default pdsConfig;\n`;
1436
+ }
1437
+
1438
+ async function exportFromLiveEdit(format) {
1439
+ try {
1440
+ if (format === "config") {
1441
+ const config = getLiveEditExportConfig();
1442
+ const content = buildConfigModuleContent(config);
1443
+ downloadTextFile(content, "pds.config.js", "text/javascript");
1444
+ await PDS?.toast?.("Exported config file", { type: "success" });
1445
+ return;
1446
+ }
1447
+
1448
+ if (format === "figma") {
1449
+ const Generator = await getGeneratorClass();
1450
+ const generator = Generator?.instance;
1451
+ if (!generator || typeof generator.generateTokens !== "function") {
1452
+ throw new Error("Token generator unavailable");
1453
+ }
1454
+
1455
+ const rawTokens = generator.generateTokens();
1456
+ const figmaTokens = figmafyTokens(rawTokens);
1457
+ const content = JSON.stringify(figmaTokens, null, 2);
1458
+ downloadTextFile(content, "design-tokens.figma.json", "application/json");
1459
+ await PDS?.toast?.("Exported Figma tokens", { type: "success" });
1460
+ return;
1461
+ }
1462
+ } catch (error) {
1463
+ console.warn("[pds-live-edit] Export failed", error);
1464
+ await PDS?.toast?.("Export failed", { type: "error" });
1465
+ }
1466
+ }
1467
+
1176
1468
  function setFormSchemas(form, schema, uiSchema, design) {
1177
1469
  form.jsonSchema = schema;
1178
1470
  form.uiSchema = uiSchema;
@@ -1181,7 +1473,13 @@ function setFormSchemas(form, schema, uiSchema, design) {
1181
1473
 
1182
1474
  async function buildForm(paths, design, onSubmit, onUndo, hints = {}) {
1183
1475
  const { schema, uiSchema } = buildSchemaFromPaths(paths, design, hints);
1476
+
1477
+ if (!customElements.get("pds-form")) {
1478
+ await customElements.whenDefined("pds-form");
1479
+ }
1480
+
1184
1481
  const form = document.createElement("pds-form");
1482
+ const fontFamilyOmniboxSettings = buildFontFamilyOmniboxSettings();
1185
1483
  form.setAttribute("hide-actions", "");
1186
1484
  form.options = {
1187
1485
  layouts: {
@@ -1191,6 +1489,62 @@ async function buildForm(paths, design, onSubmit, onUndo, hints = {}) {
1191
1489
  rangeOutput: true,
1192
1490
  },
1193
1491
  };
1492
+ form.defineRenderer(
1493
+ "font-family-omnibox",
1494
+ ({ id, path, value, attrs, set }) => {
1495
+ const resolveSelectedValue = (options, actionResult) => {
1496
+ if (typeof actionResult === "string" && actionResult.trim()) {
1497
+ return actionResult;
1498
+ }
1499
+ const fromText = String(options?.text || "").trim();
1500
+ if (fromText) return fromText;
1501
+ return String(options?.id || "").trim();
1502
+ };
1503
+
1504
+ const categories = Object.fromEntries(
1505
+ Object.entries(fontFamilyOmniboxSettings.categories || {}).map(
1506
+ ([categoryName, categoryConfig]) => {
1507
+ const originalAction = categoryConfig?.action;
1508
+ return [
1509
+ categoryName,
1510
+ {
1511
+ ...categoryConfig,
1512
+ action: (options) => {
1513
+ const actionResult =
1514
+ typeof originalAction === "function"
1515
+ ? originalAction(options)
1516
+ : undefined;
1517
+ const selected = resolveSelectedValue(options, actionResult);
1518
+ if (selected) {
1519
+ set(selected);
1520
+ }
1521
+ return actionResult;
1522
+ },
1523
+ },
1524
+ ];
1525
+ }
1526
+ )
1527
+ );
1528
+
1529
+ const omnibox = document.createElement("pds-omnibox");
1530
+ omnibox.id = id;
1531
+ omnibox.setAttribute("name", path);
1532
+ omnibox.setAttribute("item-grid", "0 1fr");
1533
+ omnibox.setAttribute("placeholder", attrs?.placeholder || "Select a font family");
1534
+ omnibox.value = value ?? "";
1535
+ omnibox.settings = {
1536
+ ...fontFamilyOmniboxSettings,
1537
+ categories,
1538
+ };
1539
+ omnibox.addEventListener("input", (event) => {
1540
+ set(event?.target?.value ?? omnibox.value ?? "");
1541
+ });
1542
+ omnibox.addEventListener("change", (event) => {
1543
+ set(event?.target?.value ?? omnibox.value ?? "");
1544
+ });
1545
+ return omnibox;
1546
+ }
1547
+ );
1194
1548
  form.addEventListener("pw:submit", onSubmit);
1195
1549
  const values = shallowClone(design || {});
1196
1550
  Object.keys(ENUM_FIELD_OPTIONS).forEach((path) => {
@@ -1208,12 +1562,6 @@ async function buildForm(paths, design, onSubmit, onUndo, hints = {}) {
1208
1562
  });
1209
1563
  setFormSchemas(form, schema, uiSchema, values);
1210
1564
 
1211
- if (!customElements.get("pds-form")) {
1212
- customElements.whenDefined("pds-form").then(() => {
1213
- setFormSchemas(form, schema, uiSchema, values);
1214
- });
1215
- }
1216
-
1217
1565
  // Apply button (will trigger form submit programmatically)
1218
1566
  const applyBtn = document.createElement("button");
1219
1567
  applyBtn.className = "btn-primary btn-sm";
@@ -1821,6 +2169,65 @@ class PdsLiveEdit extends HTMLElement {
1821
2169
  const themeToggle = document.createElement("pds-theme");
1822
2170
  themeCard.appendChild(themeToggle);
1823
2171
 
2172
+ const exportCard = document.createElement("section");
2173
+ exportCard.className = "card surface-elevated stack-sm";
2174
+
2175
+ const exportTitle = document.createElement("h4");
2176
+ exportTitle.textContent = "Export";
2177
+ exportCard.appendChild(exportTitle);
2178
+
2179
+ const exportNav = document.createElement("nav");
2180
+ exportNav.setAttribute("data-dropdown", "");
2181
+ exportNav.setAttribute("data-mode", "auto");
2182
+
2183
+ const exportButton = document.createElement("button");
2184
+ exportButton.className = "btn-primary";
2185
+ const exportIcon = document.createElement("pds-icon");
2186
+ exportIcon.setAttribute("icon", "download");
2187
+ exportIcon.setAttribute("size", "sm");
2188
+ const exportLabel = document.createElement("span");
2189
+ exportLabel.textContent = "Download";
2190
+ const exportCaret = document.createElement("pds-icon");
2191
+ exportCaret.setAttribute("icon", "caret-down");
2192
+ exportCaret.setAttribute("size", "sm");
2193
+ exportButton.append(exportIcon, exportLabel, exportCaret);
2194
+
2195
+ const exportMenu = document.createElement("menu");
2196
+
2197
+ const configItem = document.createElement("li");
2198
+ const configLink = document.createElement("a");
2199
+ configLink.href = "#";
2200
+ configLink.addEventListener("click", async (event) => {
2201
+ event.preventDefault();
2202
+ await this._handleExport("config");
2203
+ });
2204
+ const configIcon = document.createElement("pds-icon");
2205
+ configIcon.setAttribute("icon", "file-js");
2206
+ configIcon.setAttribute("size", "sm");
2207
+ const configLabel = document.createElement("span");
2208
+ configLabel.textContent = "Config File";
2209
+ configLink.append(configIcon, configLabel);
2210
+ configItem.appendChild(configLink);
2211
+
2212
+ const figmaItem = document.createElement("li");
2213
+ const figmaLink = document.createElement("a");
2214
+ figmaLink.href = "#";
2215
+ figmaLink.addEventListener("click", async (event) => {
2216
+ event.preventDefault();
2217
+ await this._handleExport("figma");
2218
+ });
2219
+ const figmaIcon = document.createElement("pds-icon");
2220
+ figmaIcon.setAttribute("icon", "brackets-curly");
2221
+ figmaIcon.setAttribute("size", "sm");
2222
+ const figmaLabel = document.createElement("span");
2223
+ figmaLabel.textContent = "Figma Tokens (JSON)";
2224
+ figmaLink.append(figmaIcon, figmaLabel);
2225
+ figmaItem.appendChild(figmaLink);
2226
+
2227
+ exportMenu.append(configItem, figmaItem);
2228
+ exportNav.append(exportButton, exportMenu);
2229
+ exportCard.appendChild(exportNav);
2230
+
1824
2231
  const searchCard = document.createElement("section");
1825
2232
  searchCard.className = "card surface-elevated stack-sm";
1826
2233
 
@@ -1867,6 +2274,7 @@ class PdsLiveEdit extends HTMLElement {
1867
2274
 
1868
2275
  content.appendChild(presetCard);
1869
2276
  content.appendChild(themeCard);
2277
+ content.appendChild(exportCard);
1870
2278
  content.appendChild(searchCard);
1871
2279
 
1872
2280
  this._drawer.replaceChildren(header, content);
@@ -1878,6 +2286,10 @@ class PdsLiveEdit extends HTMLElement {
1878
2286
  }
1879
2287
  }
1880
2288
 
2289
+ async _handleExport(format) {
2290
+ await exportFromLiveEdit(format);
2291
+ }
2292
+
1881
2293
  async _handleFormSubmit(event, form) {
1882
2294
  if (!form || typeof form.getValuesFlat !== "function") return;
1883
2295