1515 .config-panel .active { display : block; animation : slideUp 0.4s ease; }
1616 @keyframes slideUp { from { opacity : 0 ; transform : translateY (20px ); } to { opacity : 1 ; transform : translateY (0 ); } }
1717 .modal-overlay { background : rgba (15 , 23 , 42 , 0.9 ); backdrop-filter : blur (12px ); }
18+ .pfp-ring { padding : 2px ; background : linear-gradient (to bottom right, # 4f46e5, # ec4899 ); }
1819 </ style >
1920</ head >
2021< body class ="bg-slate-50 text-slate-900 antialiased min-h-screen ">
2324 < div class ="flex items-center gap-2 font-black text-2xl text-indigo-600 cursor-pointer " onclick ="location.href='/dashboard' ">
2425 < i data-feather ="layers "> </ i > < span > LinkStack< span class ="text-slate-400 "> .vault</ span > </ span >
2526 </ div >
26- < a href ="/dashboard " class ="text-sm font-bold text-slate-500 hover:text-slate-800 transition "> Dashboard</ a >
27+
28+ < div id ="user-display " class ="hidden flex items-center gap-4 ">
29+ < div class ="text-right hidden sm:block ">
30+ < p class ="text-[10px] font-black text-slate-400 uppercase leading-none "> Logged in as</ p >
31+ < p id ="user-handle " class ="font-bold text-slate-900 "> @username</ p >
32+ </ div >
33+ < div class ="relative group ">
34+ < div class ="w-10 h-10 rounded-full pfp-ring cursor-pointer ">
35+ < img id ="user-pfp " src ="" class ="w-full h-full object-cover rounded-full bg-slate-200 border-2 border-white " alt ="Profile ">
36+ </ div >
37+ < div class ="absolute right-0 mt-2 w-48 bg-white rounded-2xl shadow-xl border border-slate-100 py-2 opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all duration-200 translate-y-2 group-hover:translate-y-0 ">
38+ < a href ="/dashboard " class ="flex items-center gap-3 px-4 py-2 text-sm font-bold text-slate-600 hover:bg-slate-50 ">
39+ < i data-feather ="home " class ="w-4 h-4 "> </ i > Dashboard
40+ </ a >
41+ < hr class ="my-2 border-slate-50 ">
42+ < button onclick ="handleLogout() " class ="w-full flex items-center gap-3 px-4 py-2 text-sm font-bold text-red-500 hover:bg-red-50 ">
43+ < i data-feather ="log-out " class ="w-4 h-4 "> </ i > Logout
44+ </ button >
45+ </ div >
46+ </ div >
47+ </ div >
2748 </ nav >
2849
2950 < main id ="main-content " class ="max-w-4xl mx-auto pt-40 px-6 pb-24 opacity-0 transition-opacity duration-500 ">
3051 < header class ="mb-12 text-center ">
3152 < h1 class ="text-4xl font-black tracking-tight mb-2 "> Encrypted Vault</ h1 >
32- < p class ="text-slate-500 font-medium text-lg "> Securely export your digital identity with AES-GCM encryption.</ p >
53+ < p class ="text-slate-500 font-medium text-lg "> Secure your digital identity with industrial-grade encryption.</ p >
3354 </ header >
3455
3556 < div class ="grid grid-cols-1 md:grid-cols-2 gap-8 mb-12 ">
3657 < div onclick ="selectMode('export') " id ="card-export " class ="action-card p-10 bg-white rounded-[3rem] shadow-xl shadow-slate-200/40 ">
3758 < div class ="w-14 h-14 bg-indigo-50 text-indigo-600 rounded-3xl flex items-center justify-center mb-8 "> < i data-feather ="lock "> </ i > </ div >
3859 < h3 class ="font-black text-2xl mb-2 "> Protected Export</ h3 >
39- < p class ="text-slate-400 font-medium "> Encrypt and download your stack.</ p >
60+ < p class ="text-slate-400 font-medium "> Bundle and encrypt your stack.</ p >
4061 </ div >
4162 < div onclick ="selectMode('restore') " id ="card-restore " class ="action-card p-10 bg-white rounded-[3rem] shadow-xl shadow-slate-200/40 ">
4263 < div class ="w-14 h-14 bg-red-50 text-red-600 rounded-3xl flex items-center justify-center mb-8 "> < i data-feather ="unlock "> </ i > </ div >
4364 < h3 class ="font-black text-2xl mb-2 "> Decrypted Restore</ h3 >
44- < p class ="text-slate-400 font-medium "> Decrypt and select components .</ p >
65+ < p class ="text-slate-400 font-medium "> Unlock and recover data .</ p >
4566 </ div >
4667 </ div >
4768
4869 < div id ="panel-export " class ="config-panel p-10 bg-white rounded-[3rem] shadow-2xl border-2 border-slate-50 ">
49- < h2 class ="text-2xl font-black mb-6 "> Backup Security</ h2 >
70+ < h2 class ="text-2xl font-black mb-6 "> Security Settings </ h2 >
5071 < div class ="space-y-4 max-w-md ">
51- < input type ="password " id ="export-pass " placeholder ="Set backup password... " class ="w-full px-6 py-4 bg-slate-50 border border-slate-100 rounded-2xl font-bold outline-none focus:ring-4 ring-indigo-500/10 transition ">
72+ < input type ="password " id ="export-pass " placeholder ="Create backup password... " class ="w-full px-6 py-4 bg-slate-50 border border-slate-100 rounded-2xl font-bold outline-none focus:ring-4 ring-indigo-500/10 transition ">
5273 < button onclick ="performProtectedExport() " id ="export-btn " class ="w-full py-5 bg-indigo-600 text-white font-black rounded-2xl shadow-xl hover:bg-indigo-700 transition flex items-center justify-center gap-3 ">
5374 < i data-feather ="download-cloud "> </ i > Secure Download
5475 </ button >
5576 </ div >
5677 </ div >
5778
5879 < div id ="panel-restore " class ="config-panel p-10 bg-white rounded-[3rem] shadow-2xl border-2 border-red-50 ">
59- < h2 class ="text-2xl font-black mb-6 "> Import Backup</ h2 >
80+ < h2 class ="text-2xl font-black mb-6 "> Import Local Backup</ h2 >
6081 < div class ="p-16 border-4 border-dashed border-slate-100 rounded-[3rem] text-center ">
6182 < input type ="file " id ="restore-input " class ="hidden " accept =".linkstackbackup " onchange ="initiateRestore(event) ">
62- < button onclick ="document.getElementById('restore-input').click() " class ="px-12 py-5 bg-slate-900 text-white font-black rounded-2xl hover:bg-black transition "> Upload .linkstackbackup</ button >
83+ < button onclick ="document.getElementById('restore-input').click() " class ="px-12 py-5 bg-slate-900 text-white font-black rounded-2xl hover:bg-black transition "> Select .linkstackbackup</ button >
6384 </ div >
6485 </ div >
6586
@@ -72,21 +93,16 @@ <h2 class="text-2xl font-black mb-6">Import Backup</h2>
7293 < div class ="w-16 h-16 bg-indigo-600 text-white rounded-2xl flex items-center justify-center mx-auto mb-4 ">
7394 < i data-feather ="layers " class ="w-8 h-8 "> </ i >
7495 </ div >
75- < h2 class ="text-3xl font-black tracking-tight "> Welcome Back </ h2 >
76- < p class ="text-slate-400 font-medium "> Login to access your vault </ p >
96+ < h2 class ="text-3xl font-black tracking-tight "> Access Restricted </ h2 >
97+ < p class ="text-slate-400 font-medium "> Please sign in to continue </ p >
7798 </ div >
7899
79100 < button onclick ="loginWithGithub() " class ="w-full py-4 bg-slate-900 text-white font-black rounded-2xl flex items-center justify-center gap-3 hover:bg-black transition mb-6 ">
80101 < i data-feather ="github "> </ i > Continue with GitHub
81102 </ button >
82103
83- < div class ="relative mb-6 ">
84- < div class ="absolute inset-0 flex items-center "> < div class ="w-full border-t border-slate-100 "> </ div > </ div >
85- < div class ="relative flex justify-center text-xs uppercase font-black text-slate-300 "> < span class ="bg-white px-4 "> Or use email</ span > </ div >
86- </ div >
87-
88104 < div class ="space-y-3 ">
89- < input type ="email " id ="auth-email " placeholder ="Email address " class ="w-full px-6 py-4 bg-slate-50 border border-slate-100 rounded-2xl outline-none focus:ring-4 ring-indigo-500/10 transition ">
105+ < input type ="email " id ="auth-email " placeholder ="Email " class ="w-full px-6 py-4 bg-slate-50 border border-slate-100 rounded-2xl outline-none focus:ring-4 ring-indigo-500/10 transition ">
90106 < input type ="password " id ="auth-password " placeholder ="Password " class ="w-full px-6 py-4 bg-slate-50 border border-slate-100 rounded-2xl outline-none focus:ring-4 ring-indigo-500/10 transition ">
91107 < button onclick ="loginWithEmail() " class ="w-full py-4 bg-indigo-600 text-white font-black rounded-2xl shadow-lg hover:bg-indigo-700 transition "> Sign In</ button >
92108 </ div >
@@ -101,7 +117,7 @@ <h2 class="text-3xl font-black mb-6" id="modal-title">Unlock Vault</h2>
101117 < button onclick ="decryptAndProceed() " class ="w-full py-4 bg-indigo-600 text-white font-black rounded-2xl "> Decrypt File</ button >
102118 </ div >
103119 < div id ="options-step " class ="hidden space-y-4 ">
104- < div class ="space-y-2 mb-8 " id =" restore-toggles " >
120+ < div class ="space-y-2 mb-8 ">
105121 < label class ="flex items-center p-4 bg-slate-50 rounded-2xl cursor-pointer "> < input type ="checkbox " id ="opt-profile " checked class ="mr-3 w-5 h-5 rounded "> < span class ="font-bold "> Profile Info</ span > </ label >
106122 < label class ="flex items-center p-4 bg-slate-50 rounded-2xl cursor-pointer "> < input type ="checkbox " id ="opt-links " checked class ="mr-3 w-5 h-5 rounded "> < span class ="font-bold "> Links & Socials</ span > </ label >
107123 </ div >
@@ -126,11 +142,26 @@ <h2 class="text-3xl font-black mb-6" id="modal-title">Unlock Vault</h2>
126142 document . getElementById ( 'auth-modal' ) . classList . remove ( 'hidden' ) ;
127143 } else {
128144 currentUser = session . user ;
145+ await loadUserProfile ( ) ;
129146 document . getElementById ( 'main-content' ) . classList . remove ( 'opacity-0' ) ;
130147 }
131148 feather . replace ( ) ;
132149 }
133150
151+ async function loadUserProfile ( ) {
152+ const { data : profile } = await supabase . from ( 'profiles' ) . select ( '*' ) . eq ( 'id' , currentUser . id ) . single ( ) ;
153+ if ( profile ) {
154+ document . getElementById ( 'user-handle' ) . innerText = `@${ profile . handle || 'user' } ` ;
155+ document . getElementById ( 'user-pfp' ) . src = profile . avatar_url || `https://api.dicebear.com/7.x/initials/svg?seed=${ profile . handle } ` ;
156+ document . getElementById ( 'user-display' ) . classList . remove ( 'hidden' ) ;
157+ }
158+ }
159+
160+ window . handleLogout = async ( ) => {
161+ await supabase . auth . signOut ( ) ;
162+ location . reload ( ) ;
163+ } ;
164+
134165 window . loginWithGithub = async ( ) => {
135166 await supabase . auth . signInWithOAuth ( { provider : 'github' , options : { redirectTo : window . location . href } } ) ;
136167 } ;
@@ -158,10 +189,11 @@ <h2 class="text-3xl font-black mb-6" id="modal-title">Unlock Vault</h2>
158189
159190 window . performProtectedExport = async ( ) => {
160191 const pass = document . getElementById ( 'export-pass' ) . value ;
161- if ( ! pass ) return alert ( "Please set a password." ) ;
192+ if ( ! pass ) return alert ( "Security required. Set a password." ) ;
162193
163194 const btn = document . getElementById ( 'export-btn' ) ;
164- btn . innerHTML = 'Encrypting...' ;
195+ btn . innerHTML = '<i data-feather="loader" class="w-4 h-4 animate-spin"></i> Encrypting...' ;
196+ feather . replace ( ) ;
165197
166198 const { data : profile } = await supabase . from ( 'profiles' ) . select ( '*' ) . eq ( 'id' , currentUser . id ) . single ( ) ;
167199 const { data : links } = await supabase . from ( 'links' ) . select ( '*' ) . eq ( 'user_id' , currentUser . id ) ;
@@ -175,10 +207,11 @@ <h2 class="text-3xl font-black mb-6" id="modal-title">Unlock Vault</h2>
175207 const finalBlob = new Blob ( [ salt , iv , new Uint8Array ( encrypted ) ] , { type : 'application/octet-stream' } ) ;
176208 const a = document . createElement ( 'a' ) ;
177209 a . href = URL . createObjectURL ( finalBlob ) ;
178- a . download = `${ profile . handle } .linkstackbackup` ;
210+ a . download = `${ profile . handle || 'backup' } .linkstackbackup` ;
179211 a . click ( ) ;
180- log ( "Vault exported and encrypted." ) ;
181- btn . innerHTML = 'Secure Download' ;
212+ log ( "Vault exported successfully." ) ;
213+ btn . innerHTML = '<i data-feather="check"></i> Downloaded' ;
214+ feather . replace ( ) ;
182215 } ;
183216
184217 window . initiateRestore = async ( e ) => {
@@ -199,8 +232,8 @@ <h2 class="text-3xl font-black mb-6" id="modal-title">Unlock Vault</h2>
199232
200233 document . getElementById ( 'password-step' ) . classList . add ( 'hidden' ) ;
201234 document . getElementById ( 'options-step' ) . classList . remove ( 'hidden' ) ;
202- document . getElementById ( 'modal-title' ) . innerText = "Configure Restore" ;
203- } catch ( e ) { alert ( "Incorrect password or corrupt file ." ) ; }
235+ document . getElementById ( 'modal-title' ) . innerText = "Finalize Restore" ;
236+ } catch ( e ) { alert ( "Invalid password." ) ; }
204237 } ;
205238
206239 window . executeFinalRestore = async ( ) => {
0 commit comments