@@ -10,17 +10,74 @@ const els = {
1010 status : document . getElementById ( "save-status" ) ,
1111} ;
1212
13- // Storage Wrapper
13+ // Detect browser API (browser vs chrome)
14+ const browserAPI = typeof browser !== "undefined" ? browser : chrome ;
15+
16+ // Storage Wrapper - now uses browser.storage.sync with proper error handling
1417const store = {
18+ get : ( key , callback ) => {
19+ const handleError = ( error ) => {
20+ if ( error ) {
21+ console . error ( "Storage get error:" , error ) ;
22+ els . status . textContent = "Error loading" ;
23+ }
24+ } ;
25+
26+ if ( typeof callback === "function" ) {
27+ // Chrome-style callback with error handling
28+ browserAPI . storage . sync . get ( [ key ] , ( result ) => {
29+ if ( browserAPI . runtime . lastError ) {
30+ handleError ( browserAPI . runtime . lastError ) ;
31+ return ;
32+ }
33+ callback ( result ) ;
34+ } ) ;
35+ } else {
36+ // Promise-style (Firefox) with error handling
37+ return browserAPI . storage . sync . get ( [ key ] ) . catch ( handleError ) ;
38+ }
39+ } ,
40+ set : ( key , value , callback ) => {
41+ const data = { } ;
42+ data [ key ] = value ;
43+
44+ const handleError = ( error ) => {
45+ if ( error ) {
46+ console . error ( "Storage set error:" , error ) ;
47+ els . status . textContent = "Error saving" ;
48+ els . status . classList . remove ( "saved" ) ;
49+ }
50+ } ;
51+
52+ if ( typeof callback === "function" ) {
53+ browserAPI . storage . sync . set ( data , ( ) => {
54+ if ( browserAPI . runtime . lastError ) {
55+ handleError ( browserAPI . runtime . lastError ) ;
56+ return ;
57+ }
58+ callback ( ) ;
59+ } ) ;
60+ } else {
61+ browserAPI . storage . sync . set ( data ) . catch ( handleError ) ;
62+ }
63+ } ,
64+ } ;
65+
66+ // LocalStorage fallback (for theme/font settings that don't need sync)
67+ const localStore = {
1568 get : ( k ) => {
1669 try {
1770 return localStorage . getItem ( k ) ;
18- } catch ( e ) { }
71+ } catch ( e ) {
72+ console . error ( "LocalStorage get error:" , e ) ;
73+ }
1974 } ,
2075 set : ( k , v ) => {
2176 try {
2277 localStorage . setItem ( k , v ) ;
23- } catch ( e ) { }
78+ } catch ( e ) {
79+ console . error ( "LocalStorage set error:" , e ) ;
80+ }
2481 } ,
2582} ;
2683
@@ -29,7 +86,7 @@ const toggleTheme = () => {
2986 const next =
3087 els . html . getAttribute ( "data-theme" ) === "dark" ? "light" : "dark" ;
3188 els . html . setAttribute ( "data-theme" , next ) ;
32- store . set ( "theme" , next ) ;
89+ localStore . set ( "theme" , next ) ;
3390 els . toggleTheme . setAttribute (
3491 "aria-label" ,
3592 `Switch to ${ next === "dark" ? "light" : "dark" } `
@@ -50,7 +107,7 @@ const updateFontSize = (val) => {
50107 const size = `${ val } px` ;
51108 els . html . style . setProperty ( "--note-size" , size ) ;
52109 els . fontDisplay . textContent = size ;
53- store . set ( "fontsize" , size ) ;
110+ localStore . set ( "fontsize" , size ) ;
54111} ;
55112
56113// Menu Listeners
@@ -85,54 +142,93 @@ const save = (text) => {
85142 els . status . classList . remove ( "saved" ) ;
86143 clearTimeout ( timeout ) ;
87144 timeout = setTimeout ( ( ) => {
88- store . set ( "notekeeper_content" , text ) ;
89- els . status . textContent = "Saved" ;
90- els . status . classList . add ( "saved" ) ;
145+ store . set ( "notekeeper_content" , text , ( ) => {
146+ // Only update UI on success (error handling is in store.set)
147+ els . status . textContent = "Saved" ;
148+ els . status . classList . add ( "saved" ) ;
149+ } ) ;
91150 } , 500 ) ;
92151} ;
93152
94- // Migration Logic (Legacy Chrome Storage -> LocalStorage)
95- const migrateFromExtension = ( ) => {
96- // If we already have data in the new system, do not overwrite
97- if ( store . get ( "notekeeper_content" ) ) return ;
98-
99- // Detect extension environment
100- const ext =
101- typeof browser !== "undefined"
102- ? browser
103- : typeof chrome !== "undefined"
104- ? chrome
105- : null ;
106-
107- if ( ext && ext . storage && ext . storage . sync ) {
108- ext . storage . sync . get ( [ "tab_note" ] , ( result ) => {
109- // Check if legacy data exists
110- if ( result && result . tab_note ) {
111- // Save to new storage
112- store . set ( "notekeeper_content" , result . tab_note ) ;
113-
114- // Update UI immediately
115- els . note . value = result . tab_note ;
116- updateStats ( result . tab_note ) ;
153+ // Listen for storage changes from other tabs/extensions
154+ // This keeps notes in sync across multiple tabs
155+ browserAPI . storage . onChanged . addListener ( ( changes , area ) => {
156+ if ( area === "sync" && changes . notekeeper_content ) {
157+ const newValue = changes . notekeeper_content . newValue ;
158+ if ( newValue !== undefined && newValue !== els . note . value ) {
159+ els . note . value = newValue ;
160+ updateStats ( newValue ) ;
161+ els . status . textContent = "Synced" ;
162+ els . status . classList . add ( "saved" ) ;
163+ }
164+ }
165+ } ) ;
166+
167+ // Refresh when the page gains visibility (e.g., switching back to tab)
168+ // This handles tab switching without requiring tabs permission
169+ document . addEventListener ( "visibilitychange" , ( ) => {
170+ if ( ! document . hidden ) {
171+ store . get ( "notekeeper_content" , ( result ) => {
172+ if ( result && result . notekeeper_content ) {
173+ els . note . value = result . notekeeper_content ;
174+ updateStats ( result . notekeeper_content ) ;
117175 els . status . textContent = "Restored" ;
118176 els . status . classList . add ( "saved" ) ;
119177 }
120178 } ) ;
121179 }
180+ } ) ;
181+
182+ // Initialize - load from sync storage and run migration if needed
183+ // Combined into single operation to avoid race condition
184+ const initializeNotes = ( ) => {
185+ store . get ( "notekeeper_content" , ( syncResult ) => {
186+ if ( syncResult && syncResult . notekeeper_content ) {
187+ // We have data in sync storage, load it
188+ els . note . value = syncResult . notekeeper_content ;
189+ updateStats ( syncResult . notekeeper_content ) ;
190+ els . status . textContent = "Restored" ;
191+ els . status . classList . add ( "saved" ) ;
192+ return ;
193+ }
194+
195+ // No sync data, check for localStorage migration
196+ const localContent = localStore . get ( "notekeeper_content" ) ;
197+ if ( localContent ) {
198+ console . log ( "Migrating notes from localStorage to sync storage..." ) ;
199+ store . set ( "notekeeper_content" , localContent , ( ) => {
200+ els . note . value = localContent ;
201+ updateStats ( localContent ) ;
202+ els . status . textContent = "Migrated" ;
203+ els . status . classList . add ( "saved" ) ;
204+ } ) ;
205+ return ;
206+ }
207+
208+ // Check for legacy tab_note (from original extension)
209+ browserAPI . storage . sync . get ( [ "tab_note" ] , ( legacyResult ) => {
210+ if ( browserAPI . runtime . lastError ) {
211+ console . error ( "Legacy storage check error:" , browserAPI . runtime . lastError ) ;
212+ return ;
213+ }
214+
215+ if ( legacyResult && legacyResult . tab_note ) {
216+ console . log ( "Migrating notes from legacy tab_note to notekeeper_content..." ) ;
217+ store . set ( "notekeeper_content" , legacyResult . tab_note , ( ) => {
218+ els . note . value = legacyResult . tab_note ;
219+ updateStats ( legacyResult . tab_note ) ;
220+ els . status . textContent = "Restored" ;
221+ els . status . classList . add ( "saved" ) ;
222+ } ) ;
223+ }
224+ } ) ;
225+ } ) ;
122226} ;
123227
124- // Init Content
125- const content = store . get ( "notekeeper_content" ) ;
126- if ( content ) {
127- els . note . value = content ;
128- updateStats ( content ) ;
129- } else {
130- // Only attempt migration if new storage is empty
131- migrateFromExtension ( ) ;
132- }
228+ initializeNotes ( ) ;
133229
134230// Init Font Size
135- const storedFontSize = store . get ( "fontsize" ) ;
231+ const storedFontSize = localStore . get ( "fontsize" ) ;
136232if ( storedFontSize ) {
137233 const val = parseInt ( storedFontSize ) ;
138234 els . slider . value = val ;
0 commit comments