@@ -5,6 +5,61 @@ import { writeFile, updateRowField, createPluRow } from '../parser/writer';
55import type { NewPluParams } from '../parser/writer' ;
66import { PLU_FIELDS , SCP_FIELDS } from '../parser/fieldMap' ;
77
8+ const STORAGE_KEY = 'scaleconfig:last-session' ;
9+
10+ function canUseStorage ( ) : boolean {
11+ return typeof window !== 'undefined' && typeof window . localStorage !== 'undefined' ;
12+ }
13+
14+ function bytesToBase64 ( bytes : Uint8Array ) : string {
15+ let binary = '' ;
16+ const chunkSize = 0x8000 ;
17+ for ( let i = 0 ; i < bytes . length ; i += chunkSize ) {
18+ binary += String . fromCharCode ( ...bytes . subarray ( i , i + chunkSize ) ) ;
19+ }
20+ return btoa ( binary ) ;
21+ }
22+
23+ function base64ToBytes ( base64 : string ) : Uint8Array {
24+ const binary = atob ( base64 ) ;
25+ const bytes = new Uint8Array ( binary . length ) ;
26+ for ( let i = 0 ; i < binary . length ; i ++ ) {
27+ bytes [ i ] = binary . charCodeAt ( i ) ;
28+ }
29+ return bytes ;
30+ }
31+
32+ function loadPersistedFile ( ) : { fileName : string ; bytes : Uint8Array } | null {
33+ if ( ! canUseStorage ( ) ) return null ;
34+ try {
35+ const raw = window . localStorage . getItem ( STORAGE_KEY ) ;
36+ if ( ! raw ) return null ;
37+ const parsed = JSON . parse ( raw ) as { fileName : string ; data : string } ;
38+ if ( ! parsed ?. data ) return null ;
39+ const bytes = base64ToBytes ( parsed . data ) ;
40+ return { fileName : parsed . fileName || 'restored.TMS' , bytes } ;
41+ } catch {
42+ window . localStorage . removeItem ( STORAGE_KEY ) ;
43+ return null ;
44+ }
45+ }
46+
47+ function persistFile ( fileName : string , bytes : Uint8Array ) : void {
48+ if ( ! canUseStorage ( ) ) return ;
49+ try {
50+ const data = bytesToBase64 ( bytes ) ;
51+ window . localStorage . setItem ( STORAGE_KEY , JSON . stringify ( { fileName, data } ) ) ;
52+ } catch {
53+ // Ignore storage errors (quota, private mode, etc.)
54+ }
55+ }
56+
57+ function persistDoc ( fileDoc : FileDoc | null , fileName : string ) : void {
58+ if ( ! fileDoc ) return ;
59+ const bytes = writeFile ( fileDoc ) ;
60+ persistFile ( fileName || 'output.TMS' , bytes ) ;
61+ }
62+
863interface AppState {
964 // File state
1065 fileDoc : FileDoc | null ;
@@ -26,6 +81,7 @@ interface AppState {
2681 // Actions - File
2782 loadFile : ( buffer : ArrayBuffer , fileName : string ) => void ;
2883 exportFile : ( ) => Uint8Array | null ;
84+ clearLocalData : ( ) => void ;
2985
3086 // Actions - PLU editing
3187 addPlu : ( params : NewPluParams ) => { success : boolean ; error ?: string } ;
@@ -49,17 +105,40 @@ interface AppState {
49105 isDirty : ( ) => boolean ;
50106}
51107
108+ const persisted = loadPersistedFile ( ) ;
109+ const persistedState = ( ( ) => {
110+ if ( ! persisted ) return null ;
111+ try {
112+ const buffer = persisted . bytes . buffer . slice (
113+ persisted . bytes . byteOffset ,
114+ persisted . bytes . byteOffset + persisted . bytes . byteLength ,
115+ ) ;
116+ const doc = parseFile ( buffer ) ;
117+ return {
118+ fileDoc : doc ,
119+ fileName : persisted . fileName ,
120+ originalBytes : new Uint8Array ( buffer ) ,
121+ pluRecords : extractPluRecords ( doc ) ,
122+ scpEntries : extractScpEntries ( doc ) ,
123+ dptRecords : extractDptRecords ( doc ) ,
124+ currentScreen : 'keyboard' as ScreenId ,
125+ } ;
126+ } catch {
127+ return null ;
128+ }
129+ } ) ( ) ;
130+
52131export const useStore = create < AppState > ( ( set , get ) => ( {
53132 // Initial state
54- fileDoc : null ,
55- fileName : '' ,
56- originalBytes : null ,
57- pluRecords : [ ] ,
58- scpEntries : [ ] ,
59- dptRecords : [ ] ,
133+ fileDoc : persistedState ?. fileDoc ?? null ,
134+ fileName : persistedState ?. fileName ?? '' ,
135+ originalBytes : persistedState ?. originalBytes ?? null ,
136+ pluRecords : persistedState ?. pluRecords ?? [ ] ,
137+ scpEntries : persistedState ?. scpEntries ?? [ ] ,
138+ dptRecords : persistedState ?. dptRecords ?? [ ] ,
60139 activeLayer : 0 ,
61140 selectedKeyIndex : null ,
62- currentScreen : 'dashboard' ,
141+ currentScreen : persistedState ?. currentScreen ?? 'dashboard' ,
63142 searchQuery : '' ,
64143 pluSearchQuery : '' ,
65144
@@ -82,6 +161,7 @@ export const useStore = create<AppState>((set, get) => ({
82161 currentScreen : 'keyboard' ,
83162 selectedKeyIndex : null ,
84163 } ) ;
164+ persistFile ( fileName , bytes ) ;
85165 } ,
86166
87167 exportFile : ( ) => {
@@ -90,6 +170,25 @@ export const useStore = create<AppState>((set, get) => ({
90170 return writeFile ( fileDoc ) ;
91171 } ,
92172
173+ clearLocalData : ( ) => {
174+ if ( canUseStorage ( ) ) {
175+ window . localStorage . removeItem ( STORAGE_KEY ) ;
176+ }
177+ set ( {
178+ fileDoc : null ,
179+ fileName : '' ,
180+ originalBytes : null ,
181+ pluRecords : [ ] ,
182+ scpEntries : [ ] ,
183+ dptRecords : [ ] ,
184+ activeLayer : 0 ,
185+ selectedKeyIndex : null ,
186+ currentScreen : 'dashboard' ,
187+ searchQuery : '' ,
188+ pluSearchQuery : '' ,
189+ } ) ;
190+ } ,
191+
93192 // ─── PLU editing ──────────────────────────────────
94193
95194 addPlu : ( params ) => {
@@ -111,6 +210,7 @@ export const useStore = create<AppState>((set, get) => ({
111210 // Refresh parsed records
112211 const updatedRecords = extractPluRecords ( fileDoc ) ;
113212 set ( { pluRecords : updatedRecords } ) ;
213+ persistDoc ( fileDoc , get ( ) . fileName ) ;
114214
115215 return { success : true } ;
116216 } ,
@@ -129,6 +229,7 @@ export const useStore = create<AppState>((set, get) => ({
129229
130230 const pluRecords = extractPluRecords ( fileDoc ) ;
131231 set ( { pluRecords } ) ;
232+ persistDoc ( fileDoc , get ( ) . fileName ) ;
132233 } ,
133234
134235 updatePluField : ( pluId , field , value ) => {
@@ -156,6 +257,7 @@ export const useStore = create<AppState>((set, get) => ({
156257 // Refresh parsed records
157258 const pluRecords = extractPluRecords ( fileDoc ) ;
158259 set ( { pluRecords } ) ;
260+ persistDoc ( fileDoc , get ( ) . fileName ) ;
159261 } ,
160262
161263 // ─── Keyboard mapping ────────────────────────────
@@ -184,6 +286,7 @@ export const useStore = create<AppState>((set, get) => ({
184286 // Refresh SCP entries
185287 const scpEntries = extractScpEntries ( fileDoc ) ;
186288 set ( { scpEntries } ) ;
289+ persistDoc ( fileDoc , get ( ) . fileName ) ;
187290 } ,
188291
189292 clearKey : ( layer , keyIndex ) => {
0 commit comments