Production's Content-Security-Policy has no script-src, so it falls back to default-src 'self' and blocks inline scripts. The inline head script that applied the saved theme never ran on prod, leaving the page on the system theme while the selector showed the saved choice. Move it to resources/js/theme-init.js, built by esbuild alongside app.js into a minified, source-mapped file loaded as a blocking script in the head. ThemeSelector now also applies the saved colour scheme, so the page and buttons can't disagree. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 lines
4.6 KiB
JavaScript
2 lines
4.6 KiB
JavaScript
(()=>{var l=class{constructor(){}async register(){let e=await this.getCreateOptions(),t={challenge:this.base64URLStringToBuffer(e.challenge),rp:{id:e.rp.id,name:e.rp.name},user:{id:new TextEncoder().encode(window.atob(e.user.id)),name:e.user.name,displayName:e.user.displayName},pubKeyCredParams:e.pubKeyCredParams,excludeCredentials:[],authenticatorSelection:e.authenticatorSelection,timeout:6e4},r=await navigator.credentials.create({publicKey:t});if(!r)throw new Error("Error generating a passkey");let n={id:r.id?r.id:null,type:r.type?r.type:null,rawId:r.rawId?this.bufferToBase64URLString(r.rawId):null,response:{attestationObject:r.response.attestationObject?this.bufferToBase64URLString(r.response.attestationObject):null,clientDataJSON:r.response.clientDataJSON?this.bufferToBase64URLString(r.response.clientDataJSON):null}};if(!(await window.fetch("/admin/passkeys/register",{method:"POST",body:JSON.stringify(n),cache:"no-cache",headers:{"Content-Type":"application/json","X-CSRF-TOKEN":document.querySelector('meta[name="csrf-token"]').getAttribute("content")}})).ok)throw new Error("Error saving the passkey");window.location.reload()}async getCreateOptions(){return await(await fetch("/admin/passkeys/register",{method:"GET"})).json()}async login(){let e=await this.getLoginData(),t=await navigator.credentials.get({publicKey:{challenge:this.base64URLStringToBuffer(e.challenge),userVerification:e.userVerification,timeout:6e4}});if(!t)throw new Error("Authentication failed");let r={id:t.id?t.id:"",type:t.type?t.type:"",rawId:t.rawId?this.bufferToBase64URLString(t.rawId):"",response:{authenticatorData:t.response.authenticatorData?this.bufferToBase64URLString(t.response.authenticatorData):"",clientDataJSON:t.response.clientDataJSON?this.bufferToBase64URLString(t.response.clientDataJSON):"",signature:t.response.signature?this.bufferToBase64URLString(t.response.signature):"",userHandle:t.response.userHandle?this.bufferToBase64URLString(t.response.userHandle):""}};if(!(await window.fetch("/login/passkey",{method:"POST",body:JSON.stringify(r),headers:{"Content-Type":"application/json","X-CSRF-TOKEN":document.querySelector('meta[name="csrf-token"]').getAttribute("content")}})).ok)throw new Error("Login failed");window.location.assign("/admin")}async getLoginData(){return await(await fetch("/login/passkey",{method:"GET"})).json()}base64URLStringToBuffer(e){let t=e.replace(/-/g,"+").replace(/_/g,"/"),r=(4-t.length%4)%4,n=t.padEnd(t.length+r,"="),s=window.atob(n),i=new ArrayBuffer(s.length),o=new Uint8Array(i);for(let c=0;c<s.length;c++)o[c]=s.charCodeAt(c);return i}bufferToBase64URLString(e){let t=new Uint8Array(e),r="";for(let s of t)r+=String.fromCharCode(s);return btoa(r).replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")}};var h=class a{constructor(){this.widget=document.querySelector("#theme-selector"),this.btnLight=this.widget.querySelector("#btn-light"),this.btnDark=this.widget.querySelector("#btn-dark"),this.status=this.widget.querySelector("#theme-status"),this.currentTheme=a.storedTheme(),this.currentTheme!=="system"&&document.documentElement.style.setProperty("color-scheme",this.currentTheme),this.updateButtons()}static storedTheme(){try{let e=localStorage.getItem("theme");return e==="light"||e==="dark"?e:"system"}catch{return"system"}}static storeTheme(e){try{e==="system"?localStorage.removeItem("theme"):localStorage.setItem("theme",e)}catch{}}updateButtons(){this.btnLight.setAttribute("aria-pressed",String(this.currentTheme==="light")),this.btnDark.setAttribute("aria-pressed",String(this.currentTheme==="dark"))}setupEventListeners(){this.btnLight.addEventListener("click",()=>{this.applyTheme(this.currentTheme==="light"?"system":"light")}),this.btnDark.addEventListener("click",()=>{this.applyTheme(this.currentTheme==="dark"?"system":"dark")})}applyTheme(e){let t=e==="light"||e==="dark"?e:"light dark",r=window.matchMedia&&window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light",n=document.documentElement,s=n.style.getPropertyValue("color-scheme");s===""&&(s="light dark");let i=!1;t!==s&&(t==="light dark"?i=s!==r:s==="light dark"?i=t!==r:i=!0);let o=()=>{this.currentTheme=e,n.style.setProperty("color-scheme",t),a.storeTheme(e),this.updateButtons(),this.status.textContent=e==="system"?"Theme set to system default":""};n.style.viewTransitionName="changing-theme",i&&document.startViewTransition?document.startViewTransition(o):o()}};var d=new l;document.querySelectorAll(".add-passkey").forEach(a=>{a.addEventListener("click",()=>{d.register()})});document.querySelectorAll(".login-passkey").forEach(a=>{a.addEventListener("click",()=>{d.login()})});var u=new h;u.setupEventListeners();})();
|
|
//# sourceMappingURL=app.js.map
|