diff --git a/integrations/bricks/admin-app/src/components/SaveBar.svelte b/integrations/bricks/admin-app/src/components/SaveBar.svelte index af9b633a..17693d23 100644 --- a/integrations/bricks/admin-app/src/components/SaveBar.svelte +++ b/integrations/bricks/admin-app/src/components/SaveBar.svelte @@ -9,6 +9,7 @@ */ import { tokens, ui, meta, clearSection } from '../lib/stores.svelte.js'; import * as api from '../lib/api.js'; + import { generateExportCSS, hasOverrides } from '../lib/export.js'; /** * Persist the active tab's tokens via the REST controller. Replaces @@ -72,6 +73,27 @@ return () => clearTimeout(t); } }); + + /** Whether any overrides exist (enables the Export button). */ + const canExport = $derived(hasOverrides(tokens)); + + /** + * Generate CSS from current token overrides and trigger a file download. + * Creates a Blob URL, clicks a temporary anchor element, then revokes. + */ + function downloadCSS() { + const css = generateExportCSS(tokens); + if (!css) return; + const blob = new Blob([css], { type: 'text/css' }); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = 'slashed-custom.css'; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + URL.revokeObjectURL(url); + }
@@ -92,6 +114,9 @@ +
`);function vl(e,t){se(t,!0);const s=["primary","secondary","tertiary","action","neutral","base"],r=["success","warning","error","info","danger"],n=E(()=>{const _=[],b=Y.colors??{},v=Y.typography??{};for(const m of s){const w=b[`brand_${m}`];w&&_.push(`--sf-color-${m}-light:${w}`);const x=b[`brand_dark_${m}`];x&&_.push(`--sf-color-${m}-dark:${x}`)}for(const m of r){const w=b[`status_${m}`];w&&_.push(`--sf-color-${m}-light:${w}`);const x=b[`status_dark_${m}`];x&&_.push(`--sf-color-${m}-dark:${x}`)}return v.font_body&&_.push(`--sf-font-body:${v.font_body}`),v.font_heading&&_.push(`--sf-font-heading:${v.font_heading}`),_.join(";")}),a=E(()=>{const _=[],b=Y.colors??{},v=Y.typography??{};for(const m of s){const w=b[`brand_${m}`];w&&_.push(`--sf-color-${m}-light: ${w}`);const x=b[`brand_dark_${m}`];x&&_.push(`--sf-color-${m}-dark: ${x}`)}for(const m of r){const w=b[`status_${m}`];w&&_.push(`--sf-color-${m}-light: ${w}`);const x=b[`status_dark_${m}`];x&&_.push(`--sf-color-${m}-dark: ${x}`)}return v.font_body&&_.push(`--sf-font-body: ${v.font_body}`),v.font_heading&&_.push(`--sf-font-heading: ${v.font_heading}`),_.length===0?"":`.slashed-preview { ${_.join(`; `)}; -}`});var i=ul(),l=k(y(i),6);Q(l,20,()=>s,_=>_,(_,b)=>{var v=dl();let m;var w=y(v);I(()=>{U(v,"title",b),m=bt(v,"",m,{background:`var(--sf-color-${b}-light, #ddd)`}),C(w,b)}),j(_,v)});var o=k(l,2),c=y(o);bt(c,"",{},{background:"var(--sf-color-primary-light, #4338ca)"});var h=k(c,2);bt(h,"",{},{background:"var(--sf-color-action-light, #0891b2)"});var d=k(o,2),f=k(y(d)),g=y(f);I(()=>{bt(i,u(n)),C(g,u(a)||"/* (defaults — no overrides set) */")}),j(e,i),re()}var hl=A(' '),pl=A('Saving…'),_l=A('Saved'),gl=A('Unsaved changes'),ml=A('All changes saved'),bl=A('
');function yl(e,t){se(t,!0);async function s(){if(!z.saving){z.saving=!0,z.error="";try{const w=z.activeTab,x=Y[w]??{},T=await Bo(w,x);T&&T.values&&(Y[w]=T.values),z.dirty=!1,z.lastSavedAt=Date.now()}catch(w){z.error=w&&w.message||String(w)}finally{z.saving=!1}}}async function r(){if(!z.saving&&confirm(`Reset the ${ee.tabs[z.activeTab]??z.activeTab} tab to defaults?`)){z.saving=!0,z.error="";try{await Ko(z.activeTab),Gi(z.activeTab),z.dirty=!1,z.lastSavedAt=Date.now()}catch(w){z.error=w&&w.message||String(w)}finally{z.saving=!1}}}let n=ne(!1);In(()=>{if(z.lastSavedAt){K(n,!0);const w=setTimeout(()=>K(n,!1),2e3);return()=>clearTimeout(w)}});var a=bl();let i;var l=y(a),o=y(l);{var c=w=>{var x=hl(),T=y(x);I(()=>C(T,z.error)),j(w,x)},h=w=>{var x=pl();j(w,x)},d=w=>{var x=_l();j(w,x)},f=w=>{var x=gl();j(w,x)},g=w=>{var x=ml();j(w,x)};le(o,w=>{z.error?w(c):z.saving?w(h,1):u(n)?w(d,2):z.dirty?w(f,3):w(g,-1)})}var _=k(l,2),b=y(_),v=k(b,2),m=y(v);I(()=>{i=Zt(a,1,"bar svelte-1oj49qa",null,i,{dirty:z.dirty}),b.disabled=z.saving,v.disabled=z.saving||!z.dirty,C(m,z.saving?"Saving…":"Save changes")}),oe("click",b,r),oe("click",v,s),j(e,a),re()}Be(["click"]);var wl=A('

SLASHED Design Tokens

Adjust framework tokens, inspect generated variables, and manage the bundled CSS in one place.

');function kl(e,t){se(t,!0);const s=["cheatsheet","hooks","variables","classes","bundle"];let r=E(()=>s.includes(z.activeTab));In(()=>{function S(H){z.dirty&&(H.preventDefault(),H.returnValue="")}return window.addEventListener("beforeunload",S),()=>window.removeEventListener("beforeunload",S)});var n=wl(),a=k(y(n),2);Wi(a,{});var i=k(a,2),l=y(i);{var o=S=>{eo(S,{})},c=S=>{vo(S,{})},h=S=>{bo(S,{})},d=S=>{wo(S,{})},f=S=>{xo(S,{})},g=S=>{Eo(S,{})},_=S=>{jo(S,{})},b=S=>{Co(S,{})},v=S=>{Lo(S,{})},m=S=>{Fo(S,{})},w=S=>{Yo(S,{})},x=S=>{Xo(S)},T=S=>{fl(S,{})};le(l,S=>{z.activeTab==="colors"?S(o):z.activeTab==="contrast"?S(c,1):z.activeTab==="typography"?S(h,2):z.activeTab==="spacing"?S(d,3):z.activeTab==="radius"?S(f,4):z.activeTab==="shadows"?S(g,5):z.activeTab==="motion"?S(_,6):z.activeTab==="zindex"?S(b,7):z.activeTab==="variables"?S(v,8):z.activeTab==="classes"?S(m,9):z.activeTab==="bundle"?S(w,10):z.activeTab==="hooks"?S(x,11):z.activeTab==="cheatsheet"&&S(T,12)})}var F=k(i,2);vl(F,{});var G=k(F,2);{var W=S=>{yl(S,{})};le(G,S=>{u(r)||S(W)})}j(e,n),re()}const rn=document.getElementById("slashed-admin-app");rn&&Ci(kl,{target:rn}); +}`});var i=ul(),l=k(y(i),6);Q(l,20,()=>s,_=>_,(_,b)=>{var v=dl();let m;var w=y(v);I(()=>{U(v,"title",b),m=bt(v,"",m,{background:`var(--sf-color-${b}-light, #ddd)`}),C(w,b)}),j(_,v)});var o=k(l,2),c=y(o);bt(c,"",{},{background:"var(--sf-color-primary-light, #4338ca)"});var h=k(c,2);bt(h,"",{},{background:"var(--sf-color-action-light, #0891b2)"});var d=k(o,2),f=k(y(d)),g=y(f);I(()=>{bt(i,u(n)),C(g,u(a)||"/* (defaults — no overrides set) */")}),j(e,i),re()}var hl=A(' '),pl=A('Saving…'),_l=A('Saved'),gl=A('Unsaved changes'),ml=A('All changes saved'),bl=A('
');function yl(e,t){se(t,!0);async function s(){if(!z.saving){z.saving=!0,z.error="";try{const w=z.activeTab,x=Y[w]??{},T=await Bo(w,x);T&&T.values&&(Y[w]=T.values),z.dirty=!1,z.lastSavedAt=Date.now()}catch(w){z.error=w&&w.message||String(w)}finally{z.saving=!1}}}async function r(){if(!z.saving&&confirm(`Reset the ${ee.tabs[z.activeTab]??z.activeTab} tab to defaults?`)){z.saving=!0,z.error="";try{await Ko(z.activeTab),Gi(z.activeTab),z.dirty=!1,z.lastSavedAt=Date.now()}catch(w){z.error=w&&w.message||String(w)}finally{z.saving=!1}}}let n=ne(!1);In(()=>{if(z.lastSavedAt){K(n,!0);const w=setTimeout(()=>K(n,!1),2e3);return()=>clearTimeout(w)}});function sl_hasOverrides(tk){if(!tk||typeof tk!=="object")return!1;for(const sec of Object.values(tk)){if(!sec||typeof sec!=="object")continue;for(const val of Object.values(sec)){if(typeof val==="object"&&val!==null){for(const vv of Object.values(val)){if(vv!==""&&vv!==null&&vv!==void 0)return!0}}else if(val!==""&&val!==null&&val!==void 0)return!0}}return!1}function sl_generateExportCSS(tk){const decls=[];const colors=tk.colors??{};const typo=tk.typography??{};const spacing=tk.spacing??{};const radius=tk.radius??{};const shadows=tk.shadows??{};const motion=tk.motion??{};const zindex=tk.zindex??{};const contrast=tk.contrast??{};const brands=["primary","secondary","tertiary","action","neutral","base"];const statuses=["success","warning","error","info","danger"];for(const c of brands){if(colors[`brand_${c}`])decls.push(`--sf-color-${c}-light: ${colors[`brand_${c}`]};`);if(colors[`brand_dark_${c}`])decls.push(`--sf-color-${c}-dark: ${colors[`brand_dark_${c}`]};`)}for(const c of statuses){if(colors[`status_${c}`])decls.push(`--sf-color-${c}-light: ${colors[`status_${c}`]};`);if(colors[`status_dark_${c}`])decls.push(`--sf-color-${c}-dark: ${colors[`status_dark_${c}`]};`)}const fonts=["body","heading","mono","display","humanist","geometric","slab"];for(const f of fonts){if(typo[`font_${f}`])decls.push(`--sf-font-${f}: ${typo[`font_${f}`]};`)}if(typo.text_scale)decls.push(`--sf-text-scale: ${typo.text_scale};`);if(typo.text_display_scale)decls.push(`--sf-text-display-scale: ${typo.text_display_scale};`);const sizes=["2xs","xs","s","m","l","xl","2xl","3xl","4xl","display-s","display-m","display-l"];for(const sz of sizes){const mn=typo[`size_${sz}_min`],mx=typo[`size_${sz}_max`];if(mn&&mx){const minV=parseFloat(mn),maxV=parseFloat(mx);if(minV>0&&maxV>0){const slope=parseFloat(((maxV-minV)/72.5).toFixed(6)),minR=parseFloat(minV.toFixed(4)),maxR=parseFloat(maxV.toFixed(4));decls.push(`--sf-text-${sz}: clamp(${minR}rem, calc(${slope} * (100vw - 22.5rem) + ${minR}rem), ${maxR}rem);`)}}}if(spacing.space_scale)decls.push(`--sf-space-scale: ${spacing.space_scale};`);const spAliases={gutter:"--sf-space-gutter",gap:"--sf-gap",content_gap:"--sf-content-gap",component_pad:"--sf-component-pad",section_pad:"--sf-section-pad"};for(const[sk,sp] of Object.entries(spAliases)){if(spacing[sk])decls.push(`${sp}: ${spacing[sk]};`)}if(radius.radius_scale)decls.push(`--sf-radius-scale: ${radius.radius_scale};`);if(shadows.shadow_strength)decls.push(`--sf-shadow-strength: calc(${shadows.shadow_strength} + var(--sf-is-dark) * 0.17);`);if(motion.motion_scale)decls.push(`--sf-motion-scale: ${motion.motion_scale};`);const durs=["instant","fast","normal","slow","slower"];for(const d of durs){if(motion[`duration_${d}`])decls.push(`--sf-duration-${d}: calc(${motion[`duration_${d}`]}ms * var(--sf-motion-scale));`)}const zlevels=["below","base","raised","low","mid","high","top","max"];for(const zl of zlevels){if(zindex[zl]!==void 0&&zindex[zl]!=="")decls.push(`--sf-z-${zl}: ${parseInt(zindex[zl],10)};`)}const nums={contrast_bias:"--sf-contrast-bias",contrast_threshold:"--sf-contrast-threshold",opacity_disabled:"--sf-opacity-disabled"};for(const[ck,cp] of Object.entries(nums)){if(contrast[ck]!==void 0&&contrast[ck]!=="")decls.push(`${cp}: ${parseFloat(contrast[ck])};`)}const pxm={focus_ring_width:"--sf-focus-ring-width",focus_ring_offset:"--sf-focus-ring-offset"};for(const[pk,pp] of Object.entries(pxm)){if(contrast[pk]!==void 0&&contrast[pk]!=="")decls.push(`${pp}: ${parseFloat(contrast[pk])}px;`)}if(contrast.focus_ring_style&&["solid","dashed","dotted","double","none"].includes(contrast.focus_ring_style))decls.push(`--sf-focus-ring-style: ${contrast.focus_ring_style};`);if(decls.length===0)return"";return`@layer slashed.overrides {\n\t:root {\n${decls.map(x=>"\t\t"+x).join("\n")}\n\t}\n}`}function sl_downloadCSS(){const css=sl_generateExportCSS(Y);if(!css)return;const blob=new Blob([css],{type:"text/css"});const url=URL.createObjectURL(blob);const a2=document.createElement("a");a2.href=url;a2.download="slashed-custom.css";document.body.appendChild(a2);a2.click();document.body.removeChild(a2);URL.revokeObjectURL(url)}const sl_canExport=E(()=>sl_hasOverrides(Y));var a=bl();let i;var l=y(a),o=y(l);{var c=w=>{var x=hl(),T=y(x);I(()=>C(T,z.error)),j(w,x)},h=w=>{var x=pl();j(w,x)},d=w=>{var x=_l();j(w,x)},f=w=>{var x=gl();j(w,x)},g=w=>{var x=ml();j(w,x)};le(o,w=>{z.error?w(c):z.saving?w(h,1):u(n)?w(d,2):z.dirty?w(f,3):w(g,-1)})}var _=k(l,2),b=y(_),sl_eb=k(b,2),v=k(sl_eb,2),m=y(v);I(()=>{i=Zt(a,1,"bar svelte-1oj49qa",null,i,{dirty:z.dirty}),b.disabled=z.saving,sl_eb.disabled=!u(sl_canExport),v.disabled=z.saving||!z.dirty,C(m,z.saving?"Saving\u2026":"Save changes")}),oe("click",b,r),oe("click",sl_eb,sl_downloadCSS),oe("click",v,s),j(e,a),re()}Be(["click"]);var wl=A('

SLASHED Design Tokens

Adjust framework tokens, inspect generated variables, and manage the bundled CSS in one place.

');function kl(e,t){se(t,!0);const s=["cheatsheet","hooks","variables","classes","bundle"];let r=E(()=>s.includes(z.activeTab));In(()=>{function S(H){z.dirty&&(H.preventDefault(),H.returnValue="")}return window.addEventListener("beforeunload",S),()=>window.removeEventListener("beforeunload",S)});var n=wl(),a=k(y(n),2);Wi(a,{});var i=k(a,2),l=y(i);{var o=S=>{eo(S,{})},c=S=>{vo(S,{})},h=S=>{bo(S,{})},d=S=>{wo(S,{})},f=S=>{xo(S,{})},g=S=>{Eo(S,{})},_=S=>{jo(S,{})},b=S=>{Co(S,{})},v=S=>{Lo(S,{})},m=S=>{Fo(S,{})},w=S=>{Yo(S,{})},x=S=>{Xo(S)},T=S=>{fl(S,{})};le(l,S=>{z.activeTab==="colors"?S(o):z.activeTab==="contrast"?S(c,1):z.activeTab==="typography"?S(h,2):z.activeTab==="spacing"?S(d,3):z.activeTab==="radius"?S(f,4):z.activeTab==="shadows"?S(g,5):z.activeTab==="motion"?S(_,6):z.activeTab==="zindex"?S(b,7):z.activeTab==="variables"?S(v,8):z.activeTab==="classes"?S(m,9):z.activeTab==="bundle"?S(w,10):z.activeTab==="hooks"?S(x,11):z.activeTab==="cheatsheet"&&S(T,12)})}var F=k(i,2);vl(F,{});var G=k(F,2);{var W=S=>{yl(S,{})};le(G,S=>{u(r)||S(W)})}j(e,n),re()}const rn=document.getElementById("slashed-admin-app");rn&&Ci(kl,{target:rn}); //# sourceMappingURL=app.js.map