@@ -15,7 +15,6 @@ if (window.matchMedia('(max-width: 1024px)').matches) {
1515 history . scrollRestoration = 'manual' ;
1616 }
1717}
18- /* ── HTML Entity Escaping Utility ── */
1918function esc ( s ) {
2019 if ( s == null ) return '' ;
2120 return String ( s ) . replace ( / & / g, '&' ) . replace ( / < / g, '<' ) . replace ( / > / g, '>' ) . replace ( / " / g, '"' ) . replace ( / ' / g, ''' ) ;
@@ -37,80 +36,28 @@ if (lastTab) {
3736 restoreTab ( ) ;
3837}
3938
40- /* ── Global Application State ── */
39+ // ─── State ────────────────────────────────────────────────
4140const state = {
4241 enc : { files : [ ] , blob : null , filename : null , isEncrypting : false , abort : false } ,
4342 dec : { file : null , blob : null , filename : null , unzippedFiles : null } ,
4443 recents : { enc : [ ] , dec : [ ] }
4544} ;
4645
47- // Session-based history encryption helpers
48- async function getHistoryKey ( ) {
49- let keyB64 = sessionStorage . getItem ( 'fk_hist_key' ) ;
50- if ( ! keyB64 ) {
51- const key = crypto . getRandomValues ( new Uint8Array ( 32 ) ) ;
52- keyB64 = btoa ( String . fromCharCode ( ...key ) ) ;
53- sessionStorage . setItem ( 'fk_hist_key' , keyB64 ) ;
54- }
55- const keyRaw = new Uint8Array ( [ ...atob ( keyB64 ) ] . map ( c => c . charCodeAt ( 0 ) ) ) ;
56- return crypto . subtle . importKey ( 'raw' , keyRaw , 'AES-GCM' , false , [ 'encrypt' , 'decrypt' ] ) ;
57- }
58-
59- async function encryptHistory ( data ) {
60- const key = await getHistoryKey ( ) ;
61- const iv = crypto . getRandomValues ( new Uint8Array ( 12 ) ) ;
62- const encoded = new TextEncoder ( ) . encode ( JSON . stringify ( data ) ) ;
63- const ciphertext = await crypto . subtle . encrypt ( { name : 'AES-GCM' , iv } , key , encoded ) ;
64- const combined = new Uint8Array ( iv . length + ciphertext . byteLength ) ;
65- combined . set ( iv ) ;
66- combined . set ( new Uint8Array ( ciphertext ) , iv . length ) ;
67- return btoa ( String . fromCharCode ( ...combined ) ) ;
68- }
69-
70- async function decryptHistory ( encB64 ) {
71- try {
72- const key = await getHistoryKey ( ) ;
73- const raw = new Uint8Array ( [ ...atob ( encB64 ) ] . map ( c => c . charCodeAt ( 0 ) ) ) ;
74- const iv = raw . slice ( 0 , 12 ) ;
75- const ciphertext = raw . slice ( 12 ) ;
76- const decrypted = await crypto . subtle . decrypt ( { name : 'AES-GCM' , iv } , key , ciphertext ) ;
77- return JSON . parse ( new TextDecoder ( ) . decode ( decrypted ) ) ;
78- } catch ( e ) { return null ; }
79- }
80-
81- async function saveRecents ( ) {
82- const encrypted = await encryptHistory ( state . recents ) ;
83- localStorage . setItem ( 'fk_recents' , encrypted ) ;
84- }
85-
86- // Async load recents
87- async function initRecents ( ) {
46+ // Load recents from storage
47+ try {
8848 const stored = localStorage . getItem ( 'fk_recents' ) ;
89- if ( ! stored ) return ;
90- const decrypted = await decryptHistory ( stored ) ;
91- if ( decrypted ) {
92- state . recents = decrypted ;
93- } else {
94- try {
95- const parsed = JSON . parse ( stored ) ;
96- if ( parsed . enc || parsed . dec ) {
97- state . recents = parsed ;
98- await saveRecents ( ) ;
99- }
100- } catch ( e ) { }
49+ if ( stored ) {
50+ const parsed = JSON . parse ( stored ) ;
51+ if ( parsed . enc ) state . recents . enc = parsed . enc ;
52+ if ( parsed . dec ) state . recents . dec = parsed . dec ;
10153 }
102- renderRecents ( 'enc' ) ;
103- renderRecents ( 'dec' ) ;
104- }
105- initRecents ( ) ;
54+ } catch ( e ) { }
10655
107- /* ── Encryption Constants & Magic Numbers ── */
108- const MAGIC = new Uint8Array ( [ 0x46 , 0x4B , 0x52 , 0x59 , 0x50 , 0x54 , 0x31 ] ) ; // "FKRYPT1"
56+ // Magic bytes to identify .enc format (ASCII "FKRYPT1")
57+ const MAGIC = new Uint8Array ( [ 0x46 , 0x4B , 0x52 , 0x59 , 0x50 , 0x54 , 0x31 ] ) ;
10958const PBKDF2_ITERS = 310_000 ;
11059const SALT_LEN = 32 ;
11160const IV_LEN = 12 ;
112-
113- /* ── UI Icons ── */
11461const ICON_EYE = `<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" style="display:block;"><path d="M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0z"/><circle cx="12" cy="12" r="3"/></svg>` ;
11562const ICON_EYE_OFF = `<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" style="display:block;"><path d="M10.73 5.08A10.43 10.43 0 0 1 12 5c7 0 10 7 10 7a13.16 13.16 0 0 1-1.67 2.68"/><path d="M6.61 6.61A13.52 13.52 0 0 0 2 12s3 7 10 7a9.74 9.74 0 0 0 1.55-.12"/><path d="M22 22 2 2"/><path d="M9.88 9.88a3 3 0 1 0 4.24 4.24"/></svg>` ;
11663
@@ -154,20 +101,17 @@ function switchTab(tab) {
154101 localStorage . setItem ( 'fk_active_tab' , tab ) ;
155102}
156103
157- /* ── Drag & Drop Event Handlers ── */
104+ // ─── Drag & drop ──────────────────────────────────────────
158105function onDragOver ( e ) {
159106 e . preventDefault ( ) ;
160107 e . currentTarget . classList . add ( 'drag-over' ) ;
161108}
162109function onDragLeave ( e ) {
163110 e . currentTarget . classList . remove ( 'drag-over' ) ;
164111}
165-
166- /* ── File Size & Limit Constants ── */
167112const MAX_FILES = 1 ;
168113const MAX_SIZE_BYTES = 2 * 1024 * 1024 * 1024 ; // 2GB limit
169114
170- /* ── File Selection Handlers ── */
171115function onDrop ( e , mode ) {
172116 e . preventDefault ( ) ;
173117 e . currentTarget . classList . remove ( 'drag-over' ) ;
@@ -179,7 +123,6 @@ function onFileSelect(e, mode) {
179123 if ( fileList . length ) { applyFiles ( fileList , mode ) ; }
180124}
181125
182- /* ── Process Selected Files ── */
183126function applyFiles ( fileList , mode , append = false ) {
184127 if ( mode === 'enc' ) {
185128 let newFiles = Array . from ( fileList ) . filter ( f => {
@@ -199,8 +142,6 @@ function applyFiles(fileList, mode, append = false) {
199142 }
200143 state . enc . files = combined ;
201144 showEncPreview ( state . enc . files ) ;
202- // Reset encryption flow UI when new files are selected
203- if ( typeof resetEncryptFlow === 'function' ) resetEncryptFlow ( ) ;
204145 } else {
205146 const file = fileList [ 0 ] ;
206147 if ( file && ! file . name . toLowerCase ( ) . endsWith ( '.enc' ) ) {
@@ -314,8 +255,6 @@ function fmtSize(n) {
314255
315256function truncateName ( name , limit = 14 , show = 11 ) {
316257 if ( ! name ) return '' ;
317- // Show full name if on desktop (width > 1024px)
318- if ( ! window . matchMedia ( '(max-width: 1024px)' ) . matches ) return name ;
319258 return name . length > limit ? name . substring ( 0 , show ) + '...' : name ;
320259}
321260
@@ -412,8 +351,6 @@ async function deriveKey(password, salt) {
412351 const enc = new TextEncoder ( ) ;
413352 const raw = enc . encode ( password ) ;
414353 const keyMat = await crypto . subtle . importKey ( 'raw' , raw , 'PBKDF2' , false , [ 'deriveKey' ] ) ;
415- // Zero out the raw password buffer immediately after use
416- raw . fill ( 0 ) ;
417354 return crypto . subtle . deriveKey (
418355 { name : 'PBKDF2' , salt, iterations : PBKDF2_ITERS , hash : 'SHA-256' } ,
419356 keyMat ,
@@ -670,9 +607,7 @@ function handleTouchHover(e) {
670607 clientY : touch . clientY ,
671608 target : btn ,
672609 getBoundingClientRect : ( ) => btn . getBoundingClientRect ( ) ,
673- querySelector : ( sel ) => btn . querySelector ( sel ) ,
674- preventDefault : ( ) => { } ,
675- stopPropagation : ( ) => { }
610+ querySelector : ( sel ) => btn . querySelector ( sel )
676611 } ;
677612 // For simple inline handlers that use 'event'
678613 window . event = fakeEvent ;
@@ -740,12 +675,12 @@ document.addEventListener('touchend', (e) => {
740675 renderRecents ( 'dec' ) ;
741676} ) ( ) ;
742677
743- /* ─── Recents ────────────────────────────────────────────── */
744- async function addRecent ( mode , data ) {
678+ // ─── Recents ──────────────────────────────────────────────
679+ function addRecent ( mode , data ) {
745680 const item = { ...data , id : Date . now ( ) } ;
746681 state . recents [ mode ] . unshift ( item ) ;
747682 state . recents [ mode ] = state . recents [ mode ] . slice ( 0 , 30 ) ; // Keep last 30
748- await saveRecents ( ) ;
683+ localStorage . setItem ( 'fk_recents' , JSON . stringify ( state . recents ) ) ;
749684 renderRecents ( mode ) ;
750685}
751686
@@ -788,15 +723,15 @@ function renderRecents(mode) {
788723 } ) . join ( '' ) ;
789724}
790725
791- async function clearRecents ( mode ) {
726+ function clearRecents ( mode ) {
792727 state . recents [ mode ] = [ ] ;
793- await saveRecents ( ) ;
728+ localStorage . setItem ( 'fk_recents' , JSON . stringify ( state . recents ) ) ;
794729 renderRecents ( mode ) ;
795730}
796731
797- async function removeRecent ( mode , id ) {
732+ function removeRecent ( mode , id ) {
798733 state . recents [ mode ] = state . recents [ mode ] . filter ( item => item . id !== id ) ;
799- await saveRecents ( ) ;
734+ localStorage . setItem ( 'fk_recents' , JSON . stringify ( state . recents ) ) ;
800735 renderRecents ( mode ) ;
801736 toast ( 'info' , 'Item removed from history.' ) ;
802737}
@@ -1209,7 +1144,36 @@ document.querySelectorAll('.scroll-reveal').forEach(el => {
12091144
12101145// ─── Mobile Back Button Handling ───
12111146window . pushModalState = function ( ) {
1212- // Disabled as per user request for direct back navigation
1147+ // Push a dummy state so back button can be intercepted
1148+ history . pushState ( { modalOpen : true } , "" ) ;
12131149} ;
12141150
1215- // popstate listener removed as per user request for direct back navigation
1151+ window . addEventListener ( 'popstate' , function ( e ) {
1152+ // Sidebars
1153+ [ 'recents-wrapper' , 'tutorial-wrapper' , 'cp-wrapper' ] . forEach ( id => {
1154+ if ( typeof window . hideSidebarBox === 'function' ) {
1155+ const el = document . getElementById ( id ) ;
1156+ if ( el && ! el . classList . contains ( 'hidden' ) ) {
1157+ window . hideSidebarBox ( id ) ;
1158+ }
1159+ }
1160+ } ) ;
1161+
1162+ // Previews
1163+ if ( typeof updateEncSidebarPreview === 'function' ) {
1164+ const encCard = document . getElementById ( 'enc-preview-card' ) ;
1165+ if ( encCard && ! encCard . classList . contains ( 'hidden' ) ) updateEncSidebarPreview ( null ) ;
1166+ }
1167+ if ( typeof updateDecSidebarPreview === 'function' ) {
1168+ const decCard = document . getElementById ( 'dec-preview-card' ) ;
1169+ if ( decCard && ! decCard . classList . contains ( 'hidden' ) ) updateDecSidebarPreview ( null ) ;
1170+ }
1171+
1172+ // Tutorial Modal
1173+ if ( typeof closeTutorialModal === 'function' ) {
1174+ const tutModal = document . getElementById ( 'tutorial-modal' ) ;
1175+ if ( tutModal && tutModal . classList . contains ( 'active' ) ) {
1176+ closeTutorialModal ( ) ;
1177+ }
1178+ }
1179+ } ) ;
0 commit comments