@@ -39,6 +39,7 @@ const AVAILABLE_SCRIPTS = [
3939let statusDot , statusText , scriptsList , accountsContainer , accountPlaceholder ;
4040let addAccountModal , accountNameInput , accountTokenInput ;
4141let refreshAccountBtn , addAccountBtn , saveAccountBtn , cancelAccountBtn , closeModalBtn ;
42+ let exportAccountsBtn , importAccountsBtn ;
4243
4344// Global state
4445let accounts = [ ] ;
@@ -75,6 +76,8 @@ function initializeElements() {
7576 saveAccountBtn = document . getElementById ( 'saveAccountBtn' ) ;
7677 cancelAccountBtn = document . getElementById ( 'cancelAccountBtn' ) ;
7778 closeModalBtn = document . getElementById ( 'closeModalBtn' ) ;
79+ exportAccountsBtn = document . getElementById ( 'exportAccountsBtn' ) ;
80+ importAccountsBtn = document . getElementById ( 'importAccountsBtn' ) ;
7881}
7982
8083async function loadScripts ( ) {
@@ -126,6 +129,8 @@ function setupEventListeners() {
126129 saveAccountBtn . addEventListener ( 'click' , handleSaveAccount ) ;
127130 cancelAccountBtn . addEventListener ( 'click' , closeAddAccountModal ) ;
128131 closeModalBtn . addEventListener ( 'click' , closeAddAccountModal ) ;
132+ exportAccountsBtn . addEventListener ( 'click' , handleExportAccounts ) ;
133+ importAccountsBtn . addEventListener ( 'click' , handleImportAccounts ) ;
129134
130135 // Modal close on overlay click
131136 addAccountModal . addEventListener ( 'click' , ( e ) => {
@@ -663,6 +668,194 @@ async function loadStartupScript() {
663668 }
664669}
665670
671+ async function handleExportAccounts ( ) {
672+ try {
673+ if ( isLoading ) return ;
674+
675+ setLoading ( true ) ;
676+ showNotification ( 'Exporting accounts...' , 'info' ) ;
677+
678+ console . log ( '📤 Starting account export process' ) ;
679+
680+ // Get accounts from storage
681+ const result = await chrome . storage . local . get ( 'infoAccounts' ) ;
682+ const accounts = result . infoAccounts || [ ] ;
683+
684+ if ( accounts . length === 0 ) {
685+ showNotification ( 'No accounts to export' , 'warning' ) ;
686+ return ;
687+ }
688+
689+ // Create export data structure
690+ const exportData = {
691+ version : '1.0' ,
692+ exportDate : new Date ( ) . toISOString ( ) ,
693+ accountCount : accounts . length ,
694+ accounts : accounts . map ( account => ( {
695+ name : account . name ,
696+ token : account . token ,
697+ ID : account . ID ,
698+ // Include static metadata only (exclude dynamic status like Charges, Max, Droplets)
699+ ...( account . lastActive && { lastActive : account . lastActive } ) ,
700+ ...( account . level && { level : account . level } ) ,
701+ ...( account . totalPixelsPainted && { totalPixelsPainted : account . totalPixelsPainted } ) ,
702+ ...( account . allianceName && { allianceName : account . allianceName } ) ,
703+ ...( account . allianceRole && { allianceRole : account . allianceRole } )
704+ } ) )
705+ } ;
706+
707+ // Create filename with timestamp
708+ const timestamp = new Date ( ) . toISOString ( ) . slice ( 0 , 19 ) . replace ( / : / g, '-' ) ;
709+ const filename = `wplace-accounts-${ timestamp } .json` ;
710+
711+ // Create and download file
712+ const blob = new Blob ( [ JSON . stringify ( exportData , null , 2 ) ] , { type : 'application/json' } ) ;
713+ const url = URL . createObjectURL ( blob ) ;
714+ const a = document . createElement ( 'a' ) ;
715+ a . href = url ;
716+ a . download = filename ;
717+ document . body . appendChild ( a ) ;
718+ a . click ( ) ;
719+ document . body . removeChild ( a ) ;
720+ URL . revokeObjectURL ( url ) ;
721+
722+ console . log ( `✅ Exported ${ accounts . length } accounts to ${ filename } ` ) ;
723+ showNotification ( `Exported ${ accounts . length } accounts successfully` , 'success' ) ;
724+
725+ } catch ( error ) {
726+ console . error ( '❌ Error exporting accounts:' , error ) ;
727+ showNotification ( 'Failed to export accounts' , 'error' ) ;
728+ } finally {
729+ setLoading ( false ) ;
730+ }
731+ }
732+
733+ async function handleImportAccounts ( ) {
734+ try {
735+ if ( isLoading ) return ;
736+
737+ setLoading ( true ) ;
738+ showNotification ( 'Importing accounts...' , 'info' ) ;
739+
740+ console . log ( '📥 Starting account import process' ) ;
741+
742+ // Create file input
743+ const input = document . createElement ( 'input' ) ;
744+ input . type = 'file' ;
745+ input . accept = '.json' ;
746+
747+ input . onchange = async ( e ) => {
748+ const file = e . target . files [ 0 ] ;
749+ if ( ! file ) {
750+ setLoading ( false ) ;
751+ return ;
752+ }
753+
754+ try {
755+ const text = await file . text ( ) ;
756+ const importData = JSON . parse ( text ) ;
757+
758+ // Validate import data structure
759+ if ( ! importData . accounts || ! Array . isArray ( importData . accounts ) ) {
760+ throw new Error ( 'Invalid file format: missing accounts array' ) ;
761+ }
762+
763+ // Validate each account has required fields
764+ for ( const account of importData . accounts ) {
765+ if ( ! account . name || ! account . token ) {
766+ throw new Error ( 'Invalid account data: missing name or token' ) ;
767+ }
768+ }
769+
770+ // Get existing accounts
771+ const result = await chrome . storage . local . get ( 'infoAccounts' ) ;
772+ let existingAccounts = result . infoAccounts || [ ] ;
773+
774+ let addedCount = 0 ;
775+ let updatedCount = 0 ;
776+ let skippedCount = 0 ;
777+
778+ // Process each imported account
779+ for ( const importAccount of importData . accounts ) {
780+ // Check if account already exists (by token or ID)
781+ const existingIndex = existingAccounts . findIndex ( existing =>
782+ existing . token === importAccount . token ||
783+ ( existing . ID && importAccount . ID && existing . ID === importAccount . ID )
784+ ) ;
785+
786+ if ( existingIndex > - 1 ) {
787+ // Account exists - ask user what to do
788+ const shouldUpdate = confirm (
789+ `Account "${ importAccount . name } " already exists. Update it?\n\n` +
790+ `Click OK to update, Cancel to skip.`
791+ ) ;
792+
793+ if ( shouldUpdate ) {
794+ // Update existing account
795+ existingAccounts [ existingIndex ] = {
796+ ...existingAccounts [ existingIndex ] ,
797+ ...importAccount ,
798+ lastImported : new Date ( ) . toISOString ( )
799+ } ;
800+ updatedCount ++ ;
801+ console . log ( `🔄 Updated account: ${ importAccount . name } ` ) ;
802+ } else {
803+ skippedCount ++ ;
804+ console . log ( `⏭️ Skipped account: ${ importAccount . name } ` ) ;
805+ }
806+ } else {
807+ // New account - add it
808+ existingAccounts . push ( {
809+ ...importAccount ,
810+ lastImported : new Date ( ) . toISOString ( ) ,
811+ // Set default values for missing fields
812+ Charges : importAccount . Charges || 0 ,
813+ Max : importAccount . Max || 100 ,
814+ Droplets : importAccount . Droplets || 0
815+ } ) ;
816+ addedCount ++ ;
817+ console . log ( `➕ Added new account: ${ importAccount . name } ` ) ;
818+ }
819+ }
820+
821+ // Save updated accounts
822+ await chrome . storage . local . set ( { infoAccounts : existingAccounts } ) ;
823+
824+ // Update the accounts array for compatibility
825+ const accountTokens = existingAccounts . map ( account => account . token ) ;
826+ await chrome . storage . local . set ( { accounts : accountTokens } ) ;
827+
828+ // Reload the accounts display
829+ await loadAccounts ( ) ;
830+
831+ // Show summary
832+ const summary = [
833+ `Import completed:` ,
834+ addedCount > 0 ? `${ addedCount } added` : '' ,
835+ updatedCount > 0 ? `${ updatedCount } updated` : '' ,
836+ skippedCount > 0 ? `${ skippedCount } skipped` : ''
837+ ] . filter ( Boolean ) . join ( ', ' ) ;
838+
839+ console . log ( `✅ Import summary: ${ summary } ` ) ;
840+ showNotification ( summary , 'success' ) ;
841+
842+ } catch ( error ) {
843+ console . error ( '❌ Error importing accounts:' , error ) ;
844+ showNotification ( `Import failed: ${ error . message } ` , 'error' ) ;
845+ } finally {
846+ setLoading ( false ) ;
847+ }
848+ } ;
849+
850+ input . click ( ) ;
851+
852+ } catch ( error ) {
853+ console . error ( '❌ Error starting import:' , error ) ;
854+ showNotification ( 'Failed to start import' , 'error' ) ;
855+ setLoading ( false ) ;
856+ }
857+ }
858+
666859// Enhanced account validation
667860async function validateAccountToken ( token ) {
668861 try {
0 commit comments