bluedither 1.0.14 → 1.0.16

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,4 +1,4 @@
1
- (()=>{var p=structuredClone(window.__BD_TOKENS__||JSON.parse(document.querySelector('script[type="application/json"][data-bluedither-tokens]')?.textContent||"{}")),N=structuredClone(window.__BD_DEFAULTS__||JSON.parse(document.querySelector('script[type="application/json"][data-bluedither-defaults]')?.textContent||"{}")),j=!!(window.__BD_TOKENS__&&window.__BD_DEFAULTS__)||!!window.__BD_TUNER_SERVER_MODE__,$=["Arial","Arial Black","Bebas Neue Pro","Consolas","Courier New","Georgia","Helvetica","Impact","Lucida Console","Segoe UI","Tahoma","Times New Roman","Trebuchet MS","Verdana","SF Pro Display","SF Mono","Cascadia Code","Menlo","Monaco"],H=["Bebas Neue","Space Mono","Inter","Roboto","Roboto Mono","Roboto Condensed","Open Sans","Montserrat","Lato","Oswald","Raleway","Poppins","Nunito","Playfair Display","Merriweather","Source Sans 3","Source Code Pro","PT Sans","PT Serif","PT Mono","Ubuntu","Ubuntu Mono","Fira Sans","Fira Code","Fira Mono","Work Sans","Noto Sans","Noto Serif","DM Sans","DM Serif Display","DM Mono","IBM Plex Sans","IBM Plex Mono","IBM Plex Serif","JetBrains Mono","Inconsolata","Space Grotesk","Archivo","Archivo Black","Barlow","Barlow Condensed","Lexend","Outfit","Sora","Manrope","Bitter","Crimson Text","Libre Baskerville","Abril Fatface","Anton","Permanent Marker","Righteous","Orbitron","Teko","Rubik","Quicksand","Cabin","Karla","Josefin Sans","Comfortaa","Fredoka","Geologica","Instrument Sans","Instrument Serif"],A=[...new Set([...$,...H])].sort();function v(n,t,e){let i=t.split("."),o=n;for(let d=0;d<i.length-1;d++)o=o[i[d]];o[i[i.length-1]]=e}function E(n,t){return t.split(".").reduce((e,i)=>e?.[i],n)}function k(n,t){document.documentElement.style.setProperty(n,t)}function S(n,t){let e=window.__BD_SHADER__;e&&e.updateParams({[n]:t})}function U(n){let t=p.layout.designWidth,e=n/16,i=n/t*100;return`clamp(${(e*.55).toFixed(4)}rem, ${i.toFixed(4)}vw, ${e.toFixed(4)}rem)`}function R(n){let t="bd-gf-"+n.replace(/\s+/g,"-").toLowerCase();if(document.getElementById(t))return;let e=document.createElement("link");e.id=t,e.rel="stylesheet",e.href=`https://fonts.googleapis.com/css2?family=${encodeURIComponent(n)}:wght@400;700&display=swap`,document.head.appendChild(e)}var x=document.createElement("div");x.id="bd-tuner";var M=document.createElement("button");M.id="bd-tuner-toggle";M.textContent="Tuner";M.onclick=()=>x.classList.remove("collapsed");document.body.appendChild(x);document.body.appendChild(M);x.innerHTML=`
1
+ (()=>{var m=structuredClone(window.__BD_TOKENS__||JSON.parse(document.querySelector('script[type="application/json"][data-bluedither-tokens]')?.textContent||"{}")),A=structuredClone(window.__BD_DEFAULTS__||JSON.parse(document.querySelector('script[type="application/json"][data-bluedither-defaults]')?.textContent||"{}")),O=!!(window.__BD_TOKENS__&&window.__BD_DEFAULTS__)||!!window.__BD_TUNER_SERVER_MODE__,z=["Arial","Arial Black","Bebas Neue Pro","Consolas","Courier New","Georgia","Helvetica","Impact","Lucida Console","Segoe UI","Tahoma","Times New Roman","Trebuchet MS","Verdana","SF Pro Display","SF Mono","Cascadia Code","Menlo","Monaco"],j=["Bebas Neue","Space Mono","Inter","Roboto","Roboto Mono","Roboto Condensed","Open Sans","Montserrat","Lato","Oswald","Raleway","Poppins","Nunito","Playfair Display","Merriweather","Source Sans 3","Source Code Pro","PT Sans","PT Serif","PT Mono","Ubuntu","Ubuntu Mono","Fira Sans","Fira Code","Fira Mono","Work Sans","Noto Sans","Noto Serif","DM Sans","DM Serif Display","DM Mono","IBM Plex Sans","IBM Plex Mono","IBM Plex Serif","JetBrains Mono","Inconsolata","Space Grotesk","Archivo","Archivo Black","Barlow","Barlow Condensed","Lexend","Outfit","Sora","Manrope","Bitter","Crimson Text","Libre Baskerville","Abril Fatface","Anton","Permanent Marker","Righteous","Orbitron","Teko","Rubik","Quicksand","Cabin","Karla","Josefin Sans","Comfortaa","Fredoka","Geologica","Instrument Sans","Instrument Serif"],q=[...new Set([...z,...j])].sort();function v(n,r,e){let i=r.split("."),t=n;for(let d=0;d<i.length-1;d++)t=t[i[d]];t[i[i.length-1]]=e}function L(n,r){return r.split(".").reduce((e,i)=>e?.[i],n)}function k(n,r){document.documentElement.style.setProperty(n,r)}function S(n,r){let e=window.__BD_SHADER__;e&&e.updateParams({[n]:r})}function I(n){let r=m.layout.designWidth,e=n/16,i=n/r*100;return`clamp(${(e*.55).toFixed(4)}rem, ${i.toFixed(4)}vw, ${e.toFixed(4)}rem)`}function $(n){let r="bd-gf-"+n.replace(/\s+/g,"-").toLowerCase();if(document.getElementById(r))return;let e=document.createElement("link");e.id=r,e.rel="stylesheet",e.href=`https://fonts.googleapis.com/css2?family=${encodeURIComponent(n)}:wght@400;700&display=swap`,document.head.appendChild(e)}var x=document.createElement("div");x.id="bd-tuner";var P=document.createElement("button");P.id="bd-tuner-toggle";P.textContent="Tuner";P.onclick=()=>x.classList.remove("collapsed");document.body.appendChild(x);document.body.appendChild(P);x.innerHTML=`
2
2
  <div class="bd-tuner-title">
3
3
  <span>BlueDither Tuner</span>
4
4
  <div style="display:flex;gap:8px;align-items:center;">
@@ -6,37 +6,37 @@
6
6
  <button id="bd-tuner-close" title="Close">&times;</button>
7
7
  </div>
8
8
  </div>
9
- `;x.querySelector("#bd-tuner-close").onclick=()=>x.classList.add("collapsed");x.querySelector("#bd-tuner-reset").onclick=async()=>{confirm("Reset all tokens to defaults?")&&(Object.assign(p,structuredClone(N)),await I(),location.reload())};function T(n){let t=document.createElement("div");return t.className="bd-tuner-section",t.innerHTML=`<div class="bd-tuner-section-label">${n}</div>`,x.appendChild(t),t}function C(n,t,e,i,o){let d=E(p,e)||"#000000",a=document.createElement("div");a.className="bd-tuner-row",a.innerHTML=`
10
- <span class="bd-tuner-label">${t}</span>
9
+ `;x.querySelector("#bd-tuner-close").onclick=()=>x.classList.add("collapsed");x.querySelector("#bd-tuner-reset").onclick=async()=>{confirm("Reset all tokens to defaults?")&&(Object.assign(m,structuredClone(A)),await H(),location.reload())};function E(n){let r=document.createElement("div");return r.className="bd-tuner-section",r.innerHTML=`<div class="bd-tuner-section-label">${n}</div>`,x.appendChild(r),r}function C(n,r,e,i,t){let d=L(m,e)||"#000000",a=document.createElement("div");a.className="bd-tuner-row",a.innerHTML=`
10
+ <span class="bd-tuner-label">${r}</span>
11
11
  <span class="bd-tuner-input"><input type="color" value="${d.substring(0,7)}"></span>
12
12
  <span class="bd-tuner-value">${d.substring(0,7)}</span>
13
- `;let s=a.querySelector("input"),c=a.querySelector(".bd-tuner-value");return s.addEventListener("input",l=>{let m=l.target.value;v(p,e,m),c.textContent=m,i&&k(i,m),o&&o(m)}),n.appendChild(a),{setValue(l){v(p,e,l),s.value=l.substring(0,7),c.textContent=l.substring(0,7),i&&k(i,l)}}}function f(n,t,e,i,o,d,a){let s=E(p,e),c=document.createElement("div");c.className="bd-tuner-row bd-tuner-row-stacked",c.innerHTML=`
13
+ `;let s=a.querySelector("input"),c=a.querySelector(".bd-tuner-value");return s.addEventListener("input",l=>{let b=l.target.value;v(m,e,b),c.textContent=b,i&&k(i,b),t&&t(b)}),n.appendChild(a),{setValue(l){v(m,e,l),s.value=l.substring(0,7),c.textContent=l.substring(0,7),i&&k(i,l)}}}function g(n,r,e,i,t,d,a){let s=L(m,e),c=document.createElement("div");c.className="bd-tuner-row bd-tuner-row-stacked",c.innerHTML=`
14
14
  <div class="bd-tuner-row-top">
15
- <span class="bd-tuner-label">${t}</span>
15
+ <span class="bd-tuner-label">${r}</span>
16
16
  <div class="bd-tuner-px-input-wrap">
17
- <input type="number" class="bd-tuner-px-num" value="${s}" min="${i}" max="${o}" step="${d}">
17
+ <input type="number" class="bd-tuner-px-num" value="${s}" min="${i}" max="${t}" step="${d}">
18
18
  <span class="bd-tuner-unit">px</span>
19
19
  </div>
20
20
  </div>
21
- <input type="range" class="bd-tuner-slider" min="${i}" max="${o}" step="${d}" value="${s}">
22
- `;let l=c.querySelector(".bd-tuner-px-num"),m=c.querySelector(".bd-tuner-slider");function u(r){if(r=Math.max(i,Math.min(o,r)),v(p,e,r),l.value=r,m.value=r,a){let w=U(r);(Array.isArray(a)?a:[a]).forEach(g=>k(g,w))}}l.addEventListener("input",r=>u(parseFloat(r.target.value)||0)),m.addEventListener("input",r=>u(parseFloat(r.target.value))),l.addEventListener("keydown",r=>{if(r.key==="ArrowUp"||r.key==="ArrowDown"){r.preventDefault();let w=r.shiftKey?10:1,g=(r.key==="ArrowUp"?d:-d)*w;u(parseFloat(l.value)+g)}}),n.appendChild(c)}function P(n,t,e,i,o,d,a){let s=E(p,e),c=document.createElement("div");c.className="bd-tuner-row bd-tuner-row-stacked",c.innerHTML=`
21
+ <input type="range" class="bd-tuner-slider" min="${i}" max="${t}" step="${d}" value="${s}">
22
+ `;let l=c.querySelector(".bd-tuner-px-num"),b=c.querySelector(".bd-tuner-slider");function p(o){if(o=Math.max(i,Math.min(t,o)),v(m,e,o),l.value=o,b.value=o,a){let w=I(o);(Array.isArray(a)?a:[a]).forEach(f=>k(f,w))}}l.addEventListener("input",o=>p(parseFloat(o.target.value)||0)),b.addEventListener("input",o=>p(parseFloat(o.target.value))),l.addEventListener("keydown",o=>{if(o.key==="ArrowUp"||o.key==="ArrowDown"){o.preventDefault();let w=o.shiftKey?10:1,f=(o.key==="ArrowUp"?d:-d)*w;p(parseFloat(l.value)+f)}}),n.appendChild(c)}function M(n,r,e,i,t,d,a){let s=L(m,e),c=document.createElement("div");c.className="bd-tuner-row bd-tuner-row-stacked",c.innerHTML=`
23
23
  <div class="bd-tuner-row-top">
24
- <span class="bd-tuner-label">${t}</span>
24
+ <span class="bd-tuner-label">${r}</span>
25
25
  <span class="bd-tuner-value">${s}</span>
26
26
  </div>
27
- <input type="range" class="bd-tuner-slider" min="${i}" max="${o}" step="${d}" value="${s}">
28
- `;let l=c.querySelector(".bd-tuner-slider"),m=c.querySelector(".bd-tuner-value");l.addEventListener("input",u=>{let r=parseFloat(u.target.value);v(p,e,r),m.textContent=r,a&&a(r)}),n.appendChild(c)}function B(n,t,e,i){let o=E(p,e),d=document.createElement("div");d.className="bd-tuner-row",d.innerHTML=`
29
- <span class="bd-tuner-label">${t}</span>
27
+ <input type="range" class="bd-tuner-slider" min="${i}" max="${t}" step="${d}" value="${s}">
28
+ `;let l=c.querySelector(".bd-tuner-slider"),b=c.querySelector(".bd-tuner-value");l.addEventListener("input",p=>{let o=parseFloat(p.target.value);v(m,e,o),b.textContent=o,a&&a(o)}),n.appendChild(c)}function B(n,r,e,i){let t=L(m,e),d=document.createElement("div");d.className="bd-tuner-row",d.innerHTML=`
29
+ <span class="bd-tuner-label">${r}</span>
30
30
  <span class="bd-tuner-input bd-tuner-font-input">
31
- <input type="text" class="bd-tuner-font-search" value="${o}" placeholder="Search fonts...">
31
+ <input type="text" class="bd-tuner-font-search" value="${t}" placeholder="Search fonts...">
32
32
  <div class="bd-tuner-font-dropdown"></div>
33
33
  </span>
34
- `;let a=d.querySelector(".bd-tuner-font-search"),s=d.querySelector(".bd-tuner-font-dropdown"),c=m=>$.some(u=>u.toLowerCase()===m.toLowerCase());function l(m=""){let u=A.filter(r=>r.toLowerCase().includes(m.toLowerCase())).slice(0,20);s.innerHTML=u.map(r=>{let w=c(r)?' <span style="opacity:0.4;font-size:9px">SYSTEM</span>':"";return`<div class="bd-tuner-font-option" data-font="${r}" style="font-family:'${r}',system-ui">${r}${w}</div>`}).join(""),u.filter(r=>!c(r)).forEach(R),s.querySelectorAll(".bd-tuner-font-option").forEach(r=>{r.addEventListener("mousedown",w=>{w.preventDefault();let g=r.dataset.font;a.value=g,v(p,e,g),c(g)||R(g),k(i,`"${g}", system-ui, sans-serif`),s.classList.remove("open")})})}a.addEventListener("focus",()=>{l(a.value),s.classList.add("open")}),a.addEventListener("input",()=>{l(a.value),s.classList.add("open")}),a.addEventListener("blur",()=>{setTimeout(()=>s.classList.remove("open"),150)}),a.addEventListener("keydown",m=>{if(m.key==="Enter"){let u=a.value.trim();u&&(v(p,e,u),c(u)||R(u),k(i,`"${u}", system-ui, sans-serif`),s.classList.remove("open"),a.blur())}}),n.appendChild(d)}function D(n,t,e,i,o){let d=E(p,e),a=document.createElement("div");a.className="bd-tuner-row bd-tuner-row-stacked",a.innerHTML=`
35
- <span class="bd-tuner-label">${t}</span>
34
+ `;let a=d.querySelector(".bd-tuner-font-search"),s=d.querySelector(".bd-tuner-font-dropdown"),c=b=>z.some(p=>p.toLowerCase()===b.toLowerCase());function l(b=""){let p=q.filter(o=>o.toLowerCase().includes(b.toLowerCase())).slice(0,20);s.innerHTML=p.map(o=>{let w=c(o)?' <span style="opacity:0.4;font-size:9px">SYSTEM</span>':"";return`<div class="bd-tuner-font-option" data-font="${o}" style="font-family:'${o}',system-ui">${o}${w}</div>`}).join(""),p.filter(o=>!c(o)).forEach($),s.querySelectorAll(".bd-tuner-font-option").forEach(o=>{o.addEventListener("mousedown",w=>{w.preventDefault();let f=o.dataset.font;a.value=f,v(m,e,f),c(f)||$(f),k(i,`"${f}", system-ui, sans-serif`),s.classList.remove("open")})})}a.addEventListener("focus",()=>{l(a.value),s.classList.add("open")}),a.addEventListener("input",()=>{l(a.value),s.classList.add("open")}),a.addEventListener("blur",()=>{setTimeout(()=>s.classList.remove("open"),150)}),a.addEventListener("keydown",b=>{if(b.key==="Enter"){let p=a.value.trim();p&&(v(m,e,p),c(p)||$(p),k(i,`"${p}", system-ui, sans-serif`),s.classList.remove("open"),a.blur())}}),n.appendChild(d)}function N(n,r,e,i,t){let d=L(m,e),a=document.createElement("div");a.className="bd-tuner-row bd-tuner-row-stacked",a.innerHTML=`
35
+ <span class="bd-tuner-label">${r}</span>
36
36
  <div class="bd-tuner-segmented">
37
37
  ${i.map(s=>`<button class="bd-tuner-seg-btn${s===d?" active":""}" data-val="${s}">${s}</button>`).join("")}
38
38
  </div>
39
- `,a.querySelectorAll(".bd-tuner-seg-btn").forEach(s=>{s.addEventListener("click",()=>{a.querySelectorAll(".bd-tuner-seg-btn").forEach(l=>l.classList.remove("active")),s.classList.add("active");let c=s.dataset.val;v(p,e,c),o&&o(c)})}),n.appendChild(a)}var _=T("Colors");C(_,"Background","colors.background","--bd-color-background");var O;C(_,"Primary","colors.primary","--bd-color-primary",n=>{O.setValue(n),S("colorFront",n)});C(_,"Text","colors.text","--bd-color-text");C(_,"CTA Background","colors.ctaBackground","--bd-color-cta-bg");C(_,"CTA Text","colors.ctaText","--bd-color-cta-text");O=C(_,"Shader Front","colors.shaderFront",null,n=>S("colorFront",n));var y=T("Typography");B(y,"Primary Font","typography.primaryFont","--bd-font-primary");B(y,"Secondary Font","typography.secondaryFont","--bd-font-secondary");f(y,"Headline Size","typography.headline.referencePx",32,300,1,"--bd-headline-size");f(y,"Headline LH","typography.headline.lineHeightPx",24,280,1,"--bd-headline-lh");f(y,"Sub Size","typography.subHeadline.referencePx",10,48,1,"--bd-subheadline-size");f(y,"Sub LH","typography.subHeadline.lineHeightPx",12,80,1,"--bd-subheadline-lh");f(y,"Logo Size","typography.logo.referencePx",12,80,1,"--bd-logo-size");f(y,"Nav Size","typography.navItem.referencePx",10,48,1,"--bd-nav-size");var h=T("Spacing");f(h,"Header Pad X","spacing.headerPaddingX",0,80,1,"--bd-header-px");f(h,"Header Pad Y","spacing.headerPaddingY",0,60,1,"--bd-header-py");f(h,"Hero Pad Top","spacing.heroPaddingTop",0,120,1,"--bd-hero-pt");f(h,"Hero Pad Bottom","spacing.heroPaddingBottom",0,120,1,"--bd-hero-pb");f(h,"Hero Pad X","spacing.heroPaddingX",0,120,1,"--bd-hero-px");f(h,"Nav Gap","spacing.navGap",0,100,1,"--bd-nav-gap");f(h,"CTA Pad X","spacing.ctaPaddingX",0,60,1,"--bd-cta-px");f(h,"CTA Pad Y","spacing.ctaPaddingY",0,30,1,"--bd-cta-py");f(h,"CTA Radius","spacing.ctaBorderRadius",0,32,1,"--bd-cta-radius");var L=T("Shader");D(L,"Shape","shader.shape",["warp","simplex","dots","wave","ripple","swirl","sphere"],n=>S("shape",n));D(L,"Dither Type","shader.type",["random","2x2","4x4","8x8"],n=>S("type",n));P(L,"Speed","shader.speed",0,2,.01,n=>S("speed",n));P(L,"Scale","shader.scale",.1,5,.01,n=>S("scale",n));P(L,"Dither Size","shader.size",.5,10,.1,n=>S("size",n));var q=T("Opacity");P(q,"Nav Links","opacity.navLinks",0,1,.01,n=>k("--bd-nav-opacity",n));var b=document.createElement("button");b.id="bd-tuner-commit";b.textContent="Commit Changes";var F=null;async function I(){let n=JSON.stringify(p,null,2);try{let o=await fetch("/__bluedither/commit",{method:"POST",headers:{"Content-Type":"application/json"},body:n});if(o.ok&&(await o.json()).ok)return"saved"}catch{}try{let o=await fetch("http://localhost:3344/commit",{method:"POST",headers:{"Content-Type":"application/json"},body:n});if(o.ok&&(await o.json()).ok)return"saved"}catch{}if(j)try{let o=await fetch("/commit",{method:"POST",headers:{"Content-Type":"application/json"},body:n});if(o.ok&&(await o.json()).ok)return"saved"}catch{}if(window.showSaveFilePicker)try{F||(F=await window.showSaveFilePicker({suggestedName:"tokens.json",types:[{description:"JSON",accept:{"application/json":[".json"]}}]}));let o=await F.createWritable();return await o.write(n),await o.close(),"saved"}catch(o){if(o.name==="AbortError")return"cancelled";F=null}let t=new Blob([n],{type:"application/json"}),e=URL.createObjectURL(t),i=document.createElement("a");return i.href=e,i.download="tokens.json",i.click(),URL.revokeObjectURL(e),"downloaded"}b.onclick=async()=>{b.textContent="Exporting...",b.disabled=!0;try{if(window.__BD_SERVER_MODE__){let t=await(await fetch("/commit",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(p,null,2)})).json();if(!t.ok)throw new Error(t.error);b.textContent="Committed!"}else{let n=new Blob([JSON.stringify(p,null,2)],{type:"application/json"}),t=URL.createObjectURL(n),e=document.createElement("a");e.href=t,e.download="tokens.json",e.click(),URL.revokeObjectURL(t),b.textContent="Downloaded!"}b.classList.add("success"),setTimeout(()=>{b.textContent=window.__BD_SERVER_MODE__?"Commit Changes":"Export Tokens",b.classList.remove("success"),b.disabled=!1},2e3)}catch(n){b.textContent="Error: "+n.message,b.disabled=!1,setTimeout(()=>{b.textContent=window.__BD_SERVER_MODE__?"Commit Changes":"Export Tokens"},3e3)}};window.__BD_SERVER_MODE__||(b.textContent="Export Tokens");x.appendChild(b);var z=document.createElement("style");z.textContent=`/* =============================================
39
+ `,a.querySelectorAll(".bd-tuner-seg-btn").forEach(s=>{s.addEventListener("click",()=>{a.querySelectorAll(".bd-tuner-seg-btn").forEach(l=>l.classList.remove("active")),s.classList.add("active");let c=s.dataset.val;v(m,e,c),t&&t(c)})}),n.appendChild(a)}var T=E("Colors");C(T,"Background","colors.background","--bd-color-background");var D;C(T,"Primary","colors.primary","--bd-color-primary",n=>{D.setValue(n),S("colorFront",n)});C(T,"Text","colors.text","--bd-color-text");C(T,"CTA Background","colors.ctaBackground","--bd-color-cta-bg");C(T,"CTA Text","colors.ctaText","--bd-color-cta-text");D=C(T,"Shader Front","colors.shaderFront",null,n=>S("colorFront",n));var y=E("Typography");B(y,"Primary Font","typography.primaryFont","--bd-font-primary");B(y,"Secondary Font","typography.secondaryFont","--bd-font-secondary");g(y,"Headline Size","typography.headline.referencePx",32,300,1,"--bd-headline-size");g(y,"Headline LH","typography.headline.lineHeightPx",24,280,1,"--bd-headline-lh");g(y,"Sub Size","typography.subHeadline.referencePx",10,48,1,"--bd-subheadline-size");g(y,"Sub LH","typography.subHeadline.lineHeightPx",12,80,1,"--bd-subheadline-lh");g(y,"Logo Size","typography.logo.referencePx",12,80,1,"--bd-logo-size");g(y,"Nav Size","typography.navItem.referencePx",10,48,1,"--bd-nav-size");var h=E("Spacing");g(h,"Header Pad X","spacing.headerPaddingX",0,80,1,"--bd-header-px");g(h,"Header Pad Y","spacing.headerPaddingY",0,60,1,"--bd-header-py");g(h,"Hero Pad Top","spacing.heroPaddingTop",0,120,1,"--bd-hero-pt");g(h,"Hero Pad Bottom","spacing.heroPaddingBottom",0,120,1,"--bd-hero-pb");g(h,"Hero Pad X","spacing.heroPaddingX",0,120,1,"--bd-hero-px");g(h,"Nav Gap","spacing.navGap",0,100,1,"--bd-nav-gap");g(h,"CTA Pad X","spacing.ctaPaddingX",0,60,1,"--bd-cta-px");g(h,"CTA Pad Y","spacing.ctaPaddingY",0,30,1,"--bd-cta-py");g(h,"CTA Radius","spacing.ctaBorderRadius",0,32,1,"--bd-cta-radius");var F=E("Shader");N(F,"Shape","shader.shape",["warp","simplex","dots","wave","ripple","swirl","sphere"],n=>S("shape",n));N(F,"Dither Type","shader.type",["random","2x2","4x4","8x8"],n=>S("type",n));M(F,"Speed","shader.speed",0,2,.01,n=>S("speed",n));M(F,"Scale","shader.scale",.1,5,.01,n=>S("scale",n));M(F,"Dither Size","shader.size",.5,10,.1,n=>S("size",n));var U=E("Opacity");M(U,"Nav Links","opacity.navLinks",0,1,.01,n=>k("--bd-nav-opacity",n));var u=document.createElement("button");u.id="bd-tuner-commit";u.textContent="Commit Changes";var _=null;async function H(){let n=JSON.stringify(m,null,2);try{let t=await fetch("/__bluedither/commit",{method:"POST",headers:{"Content-Type":"application/json"},body:n});if(t.ok&&(await t.json()).ok)return"saved"}catch{}try{let t=await fetch("http://localhost:3344/commit",{method:"POST",headers:{"Content-Type":"application/json"},body:n});if(t.ok&&(await t.json()).ok)return"saved"}catch{}if(O)try{let t=await fetch("/commit",{method:"POST",headers:{"Content-Type":"application/json"},body:n});if(t.ok&&(await t.json()).ok)return"saved"}catch{}if(window.showSaveFilePicker)try{_||(_=await window.showSaveFilePicker({suggestedName:"tokens.json",types:[{description:"JSON",accept:{"application/json":[".json"]}}]}));let t=await _.createWritable();return await t.write(n),await t.close(),"saved"}catch(t){if(t.name==="AbortError")return"cancelled";_=null}let r=new Blob([n],{type:"application/json"}),e=URL.createObjectURL(r),i=document.createElement("a");return i.href=e,i.download="tokens.json",i.click(),URL.revokeObjectURL(e),"downloaded"}u.onclick=async()=>{u.textContent="Saving...",u.disabled=!0;try{let n=await H();if(n==="cancelled"){u.textContent="Commit Changes",u.disabled=!1;return}u.textContent=n==="saved"?"Saved!":"Downloaded!",u.classList.add("success"),setTimeout(()=>{u.textContent="Commit Changes",u.classList.remove("success"),u.disabled=!1},2e3)}catch(n){u.textContent="Error: "+n.message,u.disabled=!1,setTimeout(()=>{u.textContent="Commit Changes"},3e3)}};x.appendChild(u);var R=document.createElement("style");R.textContent=`/* =============================================
40
40
  BlueDither Fine-Tuner \u2014 Overlay Panel v2
41
41
  ============================================= */
42
42
 
@@ -398,4 +398,4 @@
398
398
  background: rgba(255, 255, 255, 0.1);
399
399
  border-radius: 2px;
400
400
  }
401
- `;document.head.appendChild(z);})();
401
+ `;document.head.appendChild(R);})();
@@ -81,8 +81,40 @@ export function bluedither() {
81
81
  return {
82
82
  name: 'bluedither-tuner',
83
83
  configureServer(server) {
84
- server.middlewares.use((req, res, next) => {
85
- if (!handleRequest(req, res)) next();
84
+ server.middlewares.use(ENDPOINT, (req, res, next) => {
85
+ if (req.method === 'OPTIONS') {
86
+ res.writeHead(204, {
87
+ 'Access-Control-Allow-Origin': '*',
88
+ 'Access-Control-Allow-Methods': 'POST, OPTIONS',
89
+ 'Access-Control-Allow-Headers': 'Content-Type',
90
+ });
91
+ res.end();
92
+ return;
93
+ }
94
+ if (req.method === 'POST') {
95
+ let body = '';
96
+ req.on('data', chunk => { body += chunk; });
97
+ req.on('end', () => {
98
+ try {
99
+ JSON.parse(body);
100
+ const tokensPath = findTokensPath();
101
+ writeFileSync(tokensPath, body);
102
+ res.writeHead(200, {
103
+ 'Content-Type': 'application/json',
104
+ 'Access-Control-Allow-Origin': '*',
105
+ });
106
+ res.end('{"ok":true}');
107
+ } catch (e) {
108
+ res.writeHead(500, {
109
+ 'Content-Type': 'application/json',
110
+ 'Access-Control-Allow-Origin': '*',
111
+ });
112
+ res.end(JSON.stringify({ error: e.message }));
113
+ }
114
+ });
115
+ return;
116
+ }
117
+ next();
86
118
  });
87
119
  },
88
120
  };
@@ -99,12 +131,9 @@ export function blueditherMiddleware() {
99
131
  };
100
132
  }
101
133
 
102
- /**
103
- * Raw handler for custom Node http servers:
104
- * import { blueditherHandler } from './bluedither/dev-middleware.js'
105
- * // In your request handler:
106
- * if (!blueditherHandler(req, res)) { /* your normal handling */ }
107
- */
134
+ // Raw handler for custom Node http servers:
135
+ // import { blueditherHandler } from './bluedither/dev-middleware.js'
136
+ // if (!blueditherHandler(req, res)) { yourNormalHandling() }
108
137
  export { handleRequest as blueditherHandler };
109
138
 
110
139
  export default bluedither;
@@ -2,7 +2,7 @@
2
2
  * BlueDither Tuner — Injectable Loader
3
3
  *
4
4
  * Drop this script into ANY page to get the tuner overlay.
5
- * Reads tokens from bluedither/tokens.json, updates CSS vars live.
5
+ * On load: reads tokens.json, applies CSS vars + shader params (so saved changes persist).
6
6
  * "Commit Changes" saves via dev middleware → sidecar → File System API → download.
7
7
  *
8
8
  * Usage:
@@ -31,11 +31,102 @@
31
31
  defaults = structuredClone(tokens);
32
32
  }
33
33
 
34
+ // ── Apply saved tokens to the page ──
35
+ // This makes "Commit Changes" persist across page reloads.
36
+ const root = document.documentElement;
37
+ const t = tokens;
38
+
39
+ function pxToClamp(px) {
40
+ const dw = t.layout?.designWidth || 1364;
41
+ const maxRem = px / 16;
42
+ const vw = (px / dw) * 100;
43
+ const minRem = maxRem * 0.55;
44
+ return `clamp(${minRem.toFixed(4)}rem, ${vw.toFixed(4)}vw, ${maxRem.toFixed(4)}rem)`;
45
+ }
46
+
47
+ // Colors
48
+ if (t.colors) {
49
+ if (t.colors.background) root.style.setProperty('--bd-bg', t.colors.background);
50
+ if (t.colors.primary) root.style.setProperty('--bd-primary', t.colors.primary);
51
+ if (t.colors.text) root.style.setProperty('--bd-text', t.colors.text);
52
+ if (t.colors.ctaBackground) root.style.setProperty('--bd-cta-bg', t.colors.ctaBackground);
53
+ if (t.colors.ctaText) root.style.setProperty('--bd-cta-text', t.colors.ctaText);
54
+ }
55
+
56
+ // Typography
57
+ if (t.typography) {
58
+ if (t.typography.primaryFont) root.style.setProperty('--bd-font-primary', t.typography.primaryFont);
59
+ if (t.typography.secondaryFont) root.style.setProperty('--bd-font-secondary', t.typography.secondaryFont);
60
+ if (t.typography.headline) {
61
+ root.style.setProperty('--bd-headline-size', pxToClamp(t.typography.headline.referencePx));
62
+ root.style.setProperty('--bd-headline-lh', pxToClamp(t.typography.headline.lineHeightPx));
63
+ }
64
+ if (t.typography.subHeadline) {
65
+ root.style.setProperty('--bd-sub-size', pxToClamp(t.typography.subHeadline.referencePx));
66
+ root.style.setProperty('--bd-sub-lh', pxToClamp(t.typography.subHeadline.lineHeightPx));
67
+ }
68
+ if (t.typography.logo) {
69
+ root.style.setProperty('--bd-logo-size', pxToClamp(t.typography.logo.referencePx));
70
+ root.style.setProperty('--bd-logo-lh', pxToClamp(t.typography.logo.lineHeightPx));
71
+ }
72
+ if (t.typography.navItem) {
73
+ root.style.setProperty('--bd-nav-size', pxToClamp(t.typography.navItem.referencePx));
74
+ root.style.setProperty('--bd-nav-lh', pxToClamp(t.typography.navItem.lineHeightPx));
75
+ }
76
+ if (t.typography.ctaButton) {
77
+ root.style.setProperty('--bd-cta-size', pxToClamp(t.typography.ctaButton.referencePx));
78
+ root.style.setProperty('--bd-cta-lh', pxToClamp(t.typography.ctaButton.lineHeightPx));
79
+ }
80
+ }
81
+
82
+ // Spacing
83
+ if (t.spacing) {
84
+ if (t.spacing.headerPaddingX) root.style.setProperty('--bd-header-px', pxToClamp(t.spacing.headerPaddingX));
85
+ if (t.spacing.headerPaddingY) root.style.setProperty('--bd-header-py', pxToClamp(t.spacing.headerPaddingY));
86
+ if (t.spacing.heroPaddingTop) root.style.setProperty('--bd-hero-pt', pxToClamp(t.spacing.heroPaddingTop));
87
+ if (t.spacing.heroPaddingBottom) root.style.setProperty('--bd-hero-pb', pxToClamp(t.spacing.heroPaddingBottom));
88
+ if (t.spacing.heroPaddingX) root.style.setProperty('--bd-hero-px', pxToClamp(t.spacing.heroPaddingX));
89
+ if (t.spacing.navGap) root.style.setProperty('--bd-nav-gap', pxToClamp(t.spacing.navGap));
90
+ if (t.spacing.ctaPaddingX) root.style.setProperty('--bd-cta-px', pxToClamp(t.spacing.ctaPaddingX));
91
+ if (t.spacing.ctaPaddingY) root.style.setProperty('--bd-cta-py', pxToClamp(t.spacing.ctaPaddingY));
92
+ if (t.spacing.ctaBorderRadius != null) root.style.setProperty('--bd-cta-radius', `${(t.spacing.ctaBorderRadius / 16).toFixed(4)}rem`);
93
+ }
94
+
95
+ // Opacity
96
+ if (t.opacity) {
97
+ if (t.opacity.navLinks != null) root.style.setProperty('--bd-nav-opacity', t.opacity.navLinks);
98
+ }
99
+
100
+ // Shader — wait for __BD_SHADER__ to be available, then update
101
+ if (t.shader || t.colors) {
102
+ const applyShader = () => {
103
+ const shader = window.__BD_SHADER__;
104
+ if (!shader) return false;
105
+ const params = {};
106
+ if (t.colors?.shaderFront) params.colorFront = t.colors.shaderFront;
107
+ if (t.colors?.shaderBack) params.colorBack = t.colors.shaderBack;
108
+ if (t.shader?.shape) params.shape = t.shader.shape;
109
+ if (t.shader?.type) params.type = t.shader.type;
110
+ if (t.shader?.speed != null) params.speed = t.shader.speed;
111
+ if (t.shader?.scale != null) params.scale = t.shader.scale;
112
+ if (t.shader?.size != null) params.size = t.shader.size;
113
+ if (t.shader?.rotation != null) params.rotation = t.shader.rotation;
114
+ shader.updateParams(params);
115
+ return true;
116
+ };
117
+
118
+ // Try immediately, then poll briefly (shader may init after this script)
119
+ if (!applyShader()) {
120
+ let attempts = 0;
121
+ const interval = setInterval(() => {
122
+ if (applyShader() || ++attempts > 20) clearInterval(interval);
123
+ }, 100);
124
+ }
125
+ }
126
+
34
127
  // Inject into globals for the tuner
35
128
  window.__BD_TOKENS__ = tokens;
36
129
  window.__BD_DEFAULTS__ = defaults;
37
-
38
- // Signal that we're in injectable mode (tuner will use commitTokens cascade)
39
130
  window.__BD_TUNER_SERVER_MODE__ = true;
40
131
 
41
132
  // Load tuner CSS
@@ -47,9 +138,7 @@
47
138
  style.textContent = css;
48
139
  document.head.appendChild(style);
49
140
  }
50
- } catch {
51
- // CSS will be inlined in the bundled version
52
- }
141
+ } catch {}
53
142
 
54
143
  // Load and execute tuner script
55
144
  const tunerScript = document.createElement('script');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bluedither",
3
- "version": "1.0.14",
3
+ "version": "1.0.16",
4
4
  "description": "A bold, dithered-shader hero theme for Claude Code — skill + fine-tuner",
5
5
  "type": "module",
6
6
  "bin": {