@@ -18,7 +18,7 @@ function filterEntries(entries, filterText) {
1818}
1919
2020async function renderEntries ( filterText = '' , tagFilter = '' ) {
21- const { entries = [ ] } = await chrome . storage . sync . get ( 'entries' ) ;
21+ const { entries = [ ] } = await chrome . storage . local . get ( 'entries' ) ;
2222
2323 if ( ! window . location . search . includes ( filterText ) ) {
2424 window . history . pushState ( { } , '' , `?search=${ filterText } ` ) ;
@@ -111,7 +111,7 @@ function downloadJson(entries) {
111111
112112// Add this function near the other event handlers
113113async function handleDeleteFiltered ( filterText = '' ) {
114- const { entries = [ ] } = await chrome . storage . sync . get ( 'entries' ) ;
114+ const { entries = [ ] } = await chrome . storage . local . get ( 'entries' ) ;
115115 const filteredEntries = filterEntries ( entries , filterText ) ;
116116
117117 if ( ! filteredEntries . length ) {
@@ -132,15 +132,15 @@ async function handleDeleteFiltered(filterText = '') {
132132 const remainingEntries = entries . filter ( e => ! idsToDelete . has ( e . id ) ) ;
133133
134134 // Save remaining entries
135- await chrome . storage . sync . set ( { entries : remainingEntries } ) ;
135+ await chrome . storage . local . set ( { entries : remainingEntries } ) ;
136136
137137 // Refresh the view
138138 await renderEntries ( filterText ) ;
139139}
140140
141141// Add these functions near the top
142142async function loadConfig ( ) {
143- const config = await chrome . storage . sync . get ( [
143+ const config = await chrome . storage . local . get ( [
144144 'archivebox_server_url' ,
145145 'archivebox_api_key' , // Added this
146146 'match_urls' ,
@@ -181,7 +181,7 @@ function createAutosaveHandler() {
181181 exclude_urls : document . getElementById ( 'exclude_urls' ) . value || ''
182182 } ;
183183
184- await chrome . storage . sync . set ( config ) ;
184+ await chrome . storage . local . set ( config ) ;
185185
186186 status_div . textContent = 'Saved.' ;
187187 setTimeout ( ( ) => {
@@ -319,7 +319,7 @@ async function testApiKey() {
319319
320320// Add this function near the other utility functions
321321async function syncToArchiveBox ( entry ) {
322- const { archivebox_server_url, archivebox_api_key } = await chrome . storage . sync . get ( [
322+ const { archivebox_server_url, archivebox_api_key } = await chrome . storage . local . get ( [
323323 'archivebox_server_url' ,
324324 'archivebox_api_key'
325325 ] ) ;
@@ -358,7 +358,7 @@ async function syncToArchiveBox(entry) {
358358
359359// Add this function to handle the sync operation
360360async function handleSync ( filterText = '' ) {
361- const { entries = [ ] } = await chrome . storage . sync . get ( 'entries' ) ;
361+ const { entries = [ ] } = await chrome . storage . local . get ( 'entries' ) ;
362362 const filteredEntries = filterEntries ( entries , filterText ) ;
363363
364364 if ( ! filteredEntries . length ) {
@@ -450,6 +450,123 @@ async function testAdding() {
450450 }
451451}
452452
453+ // Import functionality
454+ async function loadHistoryItems ( days ) {
455+ const startTime = new Date ( ) ;
456+ startTime . setDate ( startTime . getDate ( ) - days ) ;
457+
458+ return new Promise ( ( resolve ) => {
459+ chrome . history . search ( {
460+ text : '' ,
461+ startTime : startTime . getTime ( ) ,
462+ maxResults : 10000
463+ } , resolve ) ;
464+ } ) ;
465+ }
466+
467+ async function loadBookmarkItems ( bookmarks = [ ] ) {
468+ const results = [ ] ;
469+
470+ function processNode ( node ) {
471+ if ( node . url ) {
472+ results . push ( {
473+ url : node . url ,
474+ title : node . title ,
475+ dateAdded : node . dateAdded
476+ } ) ;
477+ }
478+ if ( node . children ) {
479+ node . children . forEach ( processNode ) ;
480+ }
481+ }
482+
483+ return new Promise ( ( resolve ) => {
484+ chrome . bookmarks . getTree ( tree => {
485+ tree . forEach ( processNode ) ;
486+ resolve ( results ) ;
487+ } ) ;
488+ } ) ;
489+ }
490+
491+ async function updateImportList ( items , filter = '' , onlyNew = false ) {
492+ const { entries = [ ] } = await chrome . storage . local . get ( 'entries' ) ;
493+ const existingUrls = new Set ( entries . map ( e => e . url ) ) ;
494+
495+ // Filter and sort items
496+ const filteredItems = items
497+ . filter ( item => {
498+ const matchesFilter = ! filter ||
499+ item . url . toLowerCase ( ) . includes ( filter . toLowerCase ( ) ) ||
500+ ( item . title && item . title . toLowerCase ( ) . includes ( filter . toLowerCase ( ) ) ) ;
501+ const isNew = ! existingUrls . has ( item . url ) ;
502+ return matchesFilter && ( ! onlyNew || isNew ) ;
503+ } )
504+ . sort ( ( a , b ) => ( b . lastVisited || b . dateAdded || 0 ) - ( a . lastVisited || a . dateAdded || 0 ) ) ;
505+
506+ // Update the list
507+ const importList = document . getElementById ( 'importList' ) ;
508+ importList . innerHTML = filteredItems . map ( item => `
509+ <div class="list-group-item">
510+ <div class="form-check">
511+ <input class="form-check-input import-check" type="checkbox" value="${ item . url } "
512+ id="check-${ item . url } " ${ existingUrls . has ( item . url ) ? 'disabled' : '' } >
513+ <label class="form-check-label" for="check-${ item . url } ">
514+ <div>
515+ <strong>${ item . title || 'Untitled' } </strong>
516+ <small class="text-muted">
517+ (${ new Date ( item . lastVisited || item . dateAdded ) . toLocaleString ( ) } )
518+ </small>
519+ </div>
520+ <small class="text-muted">${ item . url } </small>
521+ </label>
522+ </div>
523+ </div>
524+ ` ) . join ( '' ) ;
525+
526+ updateSelectedCount ( ) ;
527+ }
528+
529+ function updateSelectedCount ( ) {
530+ const count = document . querySelectorAll ( '.import-check:checked' ) . length ;
531+ document . getElementById ( 'selectedCount' ) . textContent = count ;
532+ document . getElementById ( 'importSelected' ) . disabled = count === 0 ;
533+ }
534+
535+ async function importSelected ( ) {
536+ const selectedUrls = Array . from ( document . querySelectorAll ( '.import-check:checked' ) )
537+ . map ( checkbox => ( {
538+ url : checkbox . value ,
539+ title : checkbox . closest ( '.list-group-item' ) . querySelector ( 'strong' ) . textContent
540+ } ) ) ;
541+
542+ const { entries = [ ] } = await chrome . storage . local . get ( 'entries' ) ;
543+
544+ // Create new entries
545+ const newEntries = selectedUrls . map ( ( { url, title } ) => ( {
546+ id : crypto . randomUUID ( ) ,
547+ url,
548+ title,
549+ timestamp : new Date ( ) . toISOString ( ) ,
550+ tags : [ ] ,
551+ notes : ''
552+ } ) ) ;
553+
554+ // Add to storage
555+ await chrome . storage . local . set ( {
556+ entries : [ ...entries , ...newEntries ]
557+ } ) ;
558+
559+ // Refresh the list
560+ const filter = document . getElementById ( 'importFilter' ) . value ;
561+ const onlyNew = document . getElementById ( 'showOnlyNew' ) . checked ;
562+ await updateImportList ( window . importItems || [ ] , filter , onlyNew ) ;
563+
564+ // Update the main entries list if we're on that tab
565+ if ( document . getElementById ( 'urls-tab' ) . classList . contains ( 'active' ) ) {
566+ await renderEntries ( ) ;
567+ }
568+ }
569+
453570// Modify the loadOptions function to include config initialization
454571async function loadOptions ( ) {
455572 // Initial render
@@ -471,7 +588,7 @@ async function loadOptions() {
471588 } ) ;
472589
473590 // Collect and display all unique tags
474- const { entries = [ ] } = await chrome . storage . sync . get ( 'entries' ) ;
591+ const { entries = [ ] } = await chrome . storage . local . get ( 'entries' ) ;
475592 const allTags = [ ...new Set ( entries . flatMap ( entry => entry . tags ) ) ] ;
476593 const tagsList = document . getElementById ( 'tagsList' ) ;
477594 tagsList . innerHTML = allTags . map ( tag => `
@@ -499,14 +616,14 @@ async function loadOptions() {
499616
500617 downloadBtn . addEventListener ( 'click' , async ( ) => {
501618 const filterInput = document . getElementById ( 'filterInput' ) ;
502- const { entries = [ ] } = await chrome . storage . sync . get ( 'entries' ) ;
619+ const { entries = [ ] } = await chrome . storage . local . get ( 'entries' ) ;
503620 const filteredEntries = filterEntries ( entries , filterInput . value ) ;
504621 downloadCsv ( filteredEntries ) ;
505622 } ) ;
506623
507624 downloadJsonBtn . addEventListener ( 'click' , async ( ) => {
508625 const filterInput = document . getElementById ( 'filterInput' ) ;
509- const { entries = [ ] } = await chrome . storage . sync . get ( 'entries' ) ;
626+ const { entries = [ ] } = await chrome . storage . local . get ( 'entries' ) ;
510627 const filteredEntries = filterEntries ( entries , filterInput . value ) ;
511628 downloadJson ( filteredEntries ) ;
512629 } ) ;
@@ -611,6 +728,54 @@ async function loadOptions() {
611728
612729 // Add test adding button handler
613730 document . getElementById ( 'testAdding' ) . addEventListener ( 'click' , testAdding ) ;
731+
732+ // Import tab handlers
733+ let importItems = [ ] ;
734+
735+ document . getElementById ( 'loadHistory' ) . addEventListener ( 'click' , async ( ) => {
736+ const days = parseInt ( document . getElementById ( 'historyDays' ) . value , 10 ) ;
737+ window . importItems = await loadHistoryItems ( days ) ;
738+ const filter = document . getElementById ( 'importFilter' ) . value ;
739+ const onlyNew = document . getElementById ( 'showOnlyNew' ) . checked ;
740+ await updateImportList ( window . importItems , filter , onlyNew ) ;
741+ } ) ;
742+
743+ document . getElementById ( 'loadBookmarks' ) . addEventListener ( 'click' , async ( ) => {
744+ window . importItems = await loadBookmarkItems ( ) ;
745+ const filter = document . getElementById ( 'importFilter' ) . value ;
746+ const onlyNew = document . getElementById ( 'showOnlyNew' ) . checked ;
747+ await updateImportList ( window . importItems , filter , onlyNew ) ;
748+ } ) ;
749+
750+ document . getElementById ( 'importFilter' ) . addEventListener ( 'input' , async ( e ) => {
751+ const filter = e . target . value ;
752+ const onlyNew = document . getElementById ( 'showOnlyNew' ) . checked ;
753+ await updateImportList ( window . importItems || [ ] , filter , onlyNew ) ;
754+ } ) ;
755+
756+ document . getElementById ( 'showOnlyNew' ) . addEventListener ( 'change' , async ( e ) => {
757+ const filter = document . getElementById ( 'importFilter' ) . value ;
758+ const onlyNew = e . target . checked ;
759+ await updateImportList ( window . importItems || [ ] , filter , onlyNew ) ;
760+ } ) ;
761+
762+ document . getElementById ( 'importList' ) . addEventListener ( 'change' , updateSelectedCount ) ;
763+
764+ document . getElementById ( 'selectAll' ) . addEventListener ( 'click' , ( ) => {
765+ document . querySelectorAll ( '.import-check:not(:disabled)' ) . forEach ( checkbox => {
766+ checkbox . checked = true ;
767+ } ) ;
768+ updateSelectedCount ( ) ;
769+ } ) ;
770+
771+ document . getElementById ( 'deselectAll' ) . addEventListener ( 'click' , ( ) => {
772+ document . querySelectorAll ( '.import-check' ) . forEach ( checkbox => {
773+ checkbox . checked = false ;
774+ } ) ;
775+ updateSelectedCount ( ) ;
776+ } ) ;
777+
778+ document . getElementById ( 'importSelected' ) . addEventListener ( 'click' , importSelected ) ;
614779}
615780
616781document . addEventListener ( 'DOMContentLoaded' , loadOptions ) ;
0 commit comments