UNPKG

snes-disassembler

Version:

A Super Nintendo (SNES) ROM disassembler for 65816 assembly

551 lines 26.9 kB
"use strict"; /** * Interactive Preferences Manager * * Provides an interactive interface for managing user preferences * for advanced options, ensuring a personalized experience. */ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.preferencesManager = exports.PreferencesManager = void 0; const prompts_1 = require("@clack/prompts"); const chalk_1 = __importDefault(require("chalk")); const session_manager_1 = require("./session-manager"); class PreferencesManager { /** * Run the interactive preferences configuration interface */ async runPreferencesInterface() { await session_manager_1.sessionManager.load(); (0, prompts_1.intro)(chalk_1.default.bgBlue.black(' ⚙️ User Preferences Configuration ⚙️ ')); try { let shouldContinue = true; while (shouldContinue) { const action = await (0, prompts_1.select)({ message: 'What would you like to configure?', options: [ { value: 'view', label: '👀 View Current Preferences', hint: 'Display all current settings' }, { value: 'basic', label: '⚡ Basic Settings', hint: 'Output format, directories, etc.' }, { value: 'advanced', label: '🔧 Advanced Analysis Options', hint: 'Analysis, disassembly features' }, { value: 'assets', label: '🎨 Asset Extraction', hint: 'Graphics, audio, text preferences' }, { value: 'brr', label: '🎵 BRR Audio Settings', hint: 'Audio decoding preferences' }, { value: 'analysis', label: '📊 Analysis Settings', hint: 'Analysis types and output' }, { value: 'format', label: '📝 Output Format', hint: 'Comments, symbols, formatting' }, { value: 'ui', label: '🖥️ UI/UX Settings', hint: 'Interactive mode, colors, progress' }, { value: 'ai', label: '🤖 AI Features', hint: 'AI-powered analysis settings' }, { value: 'import-export', label: '💾 Import/Export', hint: 'Backup and restore preferences' }, { value: 'reset', label: '🔄 Reset to Defaults', hint: 'Clear all preferences' }, { value: 'exit', label: '❌ Exit', hint: 'Save and exit preferences' } ] }); if ((0, prompts_1.isCancel)(action) || action === 'exit') { shouldContinue = false; break; } switch (action) { case 'view': this.viewPreferences(); break; case 'basic': await this.configureBasicSettings(); break; case 'advanced': await this.configureAdvancedOptions(); break; case 'assets': await this.configureAssetPreferences(); break; case 'brr': await this.configureBRRPreferences(); break; case 'analysis': await this.configureAnalysisPreferences(); break; case 'format': await this.configureFormatPreferences(); break; case 'ui': await this.configureUIPreferences(); break; case 'ai': await this.configureAIPreferences(); break; case 'import-export': await this.handleImportExport(); break; case 'reset': await this.resetPreferences(); break; } } (0, prompts_1.outro)(chalk_1.default.green('✅ Preferences saved! Your settings will be applied to future sessions.')); } catch (error) { if ((0, prompts_1.isCancel)(error)) { (0, prompts_1.cancel)('Preferences configuration cancelled.'); } else { (0, prompts_1.outro)(chalk_1.default.red(`❌ Error: ${error instanceof Error ? error.message : error}`)); } } } /** * Display current preferences */ viewPreferences() { const summary = session_manager_1.sessionManager.getPreferencesSummary(); (0, prompts_1.note)(summary, 'Current Preferences'); } /** * Configure basic settings */ async configureBasicSettings() { const currentPrefs = session_manager_1.sessionManager.getPreferences(); const defaultFormat = await (0, prompts_1.select)({ message: 'Default output format:', options: [ { value: 'ca65', label: 'CA65 Assembly', hint: 'Compatible with cc65 assembler' }, { value: 'wla-dx', label: 'WLA-DX Assembly', hint: 'Compatible with WLA-DX assembler' }, { value: 'bass', label: 'BASS Assembly', hint: 'Compatible with BASS assembler' }, { value: 'html', label: 'HTML Report', hint: 'Interactive HTML documentation' }, { value: 'json', label: 'JSON Data', hint: 'Machine-readable JSON format' }, { value: 'markdown', label: 'Markdown Documentation', hint: 'Human-readable documentation' } ], initialValue: currentPrefs.defaultFormat ?? 'ca65' }); if ((0, prompts_1.isCancel)(defaultFormat)) return; const defaultOutputDir = await (0, prompts_1.text)({ message: 'Default output directory:', placeholder: './output', defaultValue: currentPrefs.defaultOutputDir ?? './output' }); if ((0, prompts_1.isCancel)(defaultOutputDir)) return; const maxRecentFiles = await (0, prompts_1.text)({ message: 'Maximum recent files to track:', placeholder: '10', defaultValue: (currentPrefs.maxRecentFiles ?? 10).toString(), validate: (value) => { const num = parseInt(value); if (isNaN(num) || num < 1 || num > 50) { return 'Please enter a number between 1 and 50'; } return undefined; } }); if ((0, prompts_1.isCancel)(maxRecentFiles)) return; const showHelp = await (0, prompts_1.confirm)({ message: 'Show contextual help by default?', initialValue: currentPrefs.showHelp !== false }); if ((0, prompts_1.isCancel)(showHelp)) return; const confirmActions = await (0, prompts_1.confirm)({ message: 'Confirm destructive actions?', initialValue: currentPrefs.confirmActions !== false }); if ((0, prompts_1.isCancel)(confirmActions)) return; // Update preferences session_manager_1.sessionManager.updatePreferences({ defaultFormat: defaultFormat, defaultOutputDir: defaultOutputDir, maxRecentFiles: parseInt(maxRecentFiles), showHelp: showHelp, confirmActions: confirmActions }); (0, prompts_1.note)('✅ Basic settings updated successfully!', 'Success'); } /** * Configure advanced analysis options */ async configureAdvancedOptions() { const currentOptions = session_manager_1.sessionManager.getAdvancedOptions(); const selectedOptions = await (0, prompts_1.multiselect)({ message: 'Select advanced options to enable by default:', options: [ { value: 'analysis', label: 'Full Analysis', hint: 'Detect functions and data structures' }, { value: 'enhancedDisasm', label: 'Enhanced Disassembly', hint: 'Use MCP server insights' }, { value: 'bankAware', label: 'Bank-Aware Addressing', hint: '24-bit addressing mode' }, { value: 'detectFunctions', label: 'Function Detection', hint: 'Automatically detect functions' }, { value: 'generateDocs', label: 'Generate Documentation', hint: 'Create comprehensive docs' }, { value: 'extractAssets', label: 'Extract Assets', hint: 'Also extract graphics/audio' }, { value: 'quality', label: 'Quality Reports', hint: 'Generate code quality metrics' } ], required: false, initialValues: currentOptions ? [ ...(currentOptions.analysis ? ['analysis'] : []), ...(currentOptions.enhancedDisasm ? ['enhancedDisasm'] : []), ...(currentOptions.bankAware ? ['bankAware'] : []), ...(currentOptions.detectFunctions ? ['detectFunctions'] : []), ...(currentOptions.generateDocs ? ['generateDocs'] : []), ...(currentOptions.extractAssets ? ['extractAssets'] : []), ...(currentOptions.quality ? ['quality'] : []) ] : [] }); if ((0, prompts_1.isCancel)(selectedOptions)) return; const disableAI = await (0, prompts_1.confirm)({ message: 'Disable AI features by default?', initialValue: currentOptions?.disableAI ?? false }); if ((0, prompts_1.isCancel)(disableAI)) return; const options = selectedOptions; await session_manager_1.sessionManager.updateAdvancedOptions({ analysis: options.includes('analysis'), enhancedDisasm: options.includes('enhancedDisasm'), bankAware: options.includes('bankAware'), detectFunctions: options.includes('detectFunctions'), generateDocs: options.includes('generateDocs'), extractAssets: options.includes('extractAssets'), quality: options.includes('quality'), disableAI: disableAI }); (0, prompts_1.note)('✅ Advanced options updated successfully!', 'Success'); } /** * Configure asset extraction preferences */ async configureAssetPreferences() { const currentPrefs = session_manager_1.sessionManager.getAssetPreferences(); const defaultAssetTypes = await (0, prompts_1.multiselect)({ message: 'Default asset types to extract:', options: [ { value: 'graphics', label: '🎨 Graphics', hint: 'Sprites, backgrounds, tiles' }, { value: 'audio', label: '🎵 Audio', hint: 'Music and sound effects' }, { value: 'text', label: '📝 Text', hint: 'Dialogue and strings' } ], required: false, initialValues: currentPrefs?.defaultAssetTypes ?? [] }); if ((0, prompts_1.isCancel)(defaultAssetTypes)) return; const defaultAssetFormats = await (0, prompts_1.multiselect)({ message: 'Default graphics formats to extract:', options: [ { value: '2bpp', label: '2BPP', hint: '2 bits per pixel' }, { value: '4bpp', label: '4BPP', hint: '4 bits per pixel (most common)' }, { value: '8bpp', label: '8BPP', hint: '8 bits per pixel' } ], required: false, initialValues: currentPrefs?.defaultAssetFormats ?? ['4bpp'] }); if ((0, prompts_1.isCancel)(defaultAssetFormats)) return; const defaultAssetOutputDir = await (0, prompts_1.text)({ message: 'Default asset output directory:', placeholder: './assets', defaultValue: currentPrefs?.defaultAssetOutputDir ?? './assets' }); if ((0, prompts_1.isCancel)(defaultAssetOutputDir)) return; await session_manager_1.sessionManager.updateAssetPreferences({ defaultAssetTypes: defaultAssetTypes, defaultAssetFormats: defaultAssetFormats, defaultAssetOutputDir: defaultAssetOutputDir }); (0, prompts_1.note)('✅ Asset preferences updated successfully!', 'Success'); } /** * Configure BRR audio preferences */ async configureBRRPreferences() { const currentPrefs = session_manager_1.sessionManager.getBRRPreferences(); const defaultSampleRate = await (0, prompts_1.text)({ message: 'Default sample rate for BRR decoding (Hz):', placeholder: '32000', defaultValue: (currentPrefs?.defaultSampleRate ?? 32000).toString(), validate: (value) => { const num = parseInt(value); if (isNaN(num) || num < 1000 || num > 96000) { return 'Please enter a sample rate between 1000 and 96000 Hz'; } return undefined; } }); if ((0, prompts_1.isCancel)(defaultSampleRate)) return; const enableLooping = await (0, prompts_1.confirm)({ message: 'Enable BRR loop processing by default?', initialValue: currentPrefs?.enableLooping !== false }); if ((0, prompts_1.isCancel)(enableLooping)) return; const maxSamples = await (0, prompts_1.text)({ message: 'Maximum samples to decode:', placeholder: '1000000', defaultValue: (currentPrefs?.maxSamples ?? 1000000).toString(), validate: (value) => { const num = parseInt(value); if (isNaN(num) || num < 1000) { return 'Please enter a number of at least 1000 samples'; } return undefined; } }); if ((0, prompts_1.isCancel)(maxSamples)) return; const defaultOutputFormat = await (0, prompts_1.select)({ message: 'Default output format for BRR decoding:', options: [ { value: 'wav', label: 'WAV', hint: 'Standard WAV format' }, { value: 'flac', label: 'FLAC', hint: 'Lossless compression' } ], initialValue: currentPrefs?.defaultOutputFormat ?? 'wav' }); if ((0, prompts_1.isCancel)(defaultOutputFormat)) return; await session_manager_1.sessionManager.updateBRRPreferences({ defaultSampleRate: parseInt(defaultSampleRate), enableLooping: enableLooping, maxSamples: parseInt(maxSamples), defaultOutputFormat: defaultOutputFormat }); (0, prompts_1.note)('✅ BRR preferences updated successfully!', 'Success'); } /** * Configure analysis preferences */ async configureAnalysisPreferences() { const currentPrefs = session_manager_1.sessionManager.getAnalysisPreferences(); const defaultAnalysisTypes = await (0, prompts_1.multiselect)({ message: 'Default analysis types to run:', options: [ { value: 'functions', label: '📊 Function Analysis', hint: 'Detect and analyze functions' }, { value: 'data-structures', label: '📋 Data Structure Analysis', hint: 'Identify data patterns' }, { value: 'cross-references', label: '🔗 Cross References', hint: 'Track code relationships' }, { value: 'quality-report', label: '📈 Quality Report', hint: 'Generate code quality metrics' }, { value: 'ai-patterns', label: '🤖 AI Pattern Recognition', hint: 'Use AI for pattern detection' } ], required: false, initialValues: currentPrefs?.defaultAnalysisTypes ?? [] }); if ((0, prompts_1.isCancel)(defaultAnalysisTypes)) return; const defaultAnalysisOutputDir = await (0, prompts_1.text)({ message: 'Default analysis output directory:', placeholder: './analysis', defaultValue: currentPrefs?.defaultAnalysisOutputDir ?? './analysis' }); if ((0, prompts_1.isCancel)(defaultAnalysisOutputDir)) return; const generateHtmlReports = await (0, prompts_1.confirm)({ message: 'Generate HTML reports by default?', initialValue: currentPrefs?.generateHtmlReports !== false }); if ((0, prompts_1.isCancel)(generateHtmlReports)) return; const includeQualityMetrics = await (0, prompts_1.confirm)({ message: 'Include quality metrics in analysis?', initialValue: currentPrefs?.includeQualityMetrics !== false }); if ((0, prompts_1.isCancel)(includeQualityMetrics)) return; await session_manager_1.sessionManager.updateAnalysisPreferences({ defaultAnalysisTypes: defaultAnalysisTypes, defaultAnalysisOutputDir: defaultAnalysisOutputDir, generateHtmlReports: generateHtmlReports, includeQualityMetrics: includeQualityMetrics }); (0, prompts_1.note)('✅ Analysis preferences updated successfully!', 'Success'); } /** * Configure output format preferences */ async configureFormatPreferences() { const currentPrefs = session_manager_1.sessionManager.getFormatPreferences(); const formatOptions = await (0, prompts_1.multiselect)({ message: 'Default output format options:', options: [ { value: 'includeComments', label: 'Include Comments', hint: 'Add explanatory comments' }, { value: 'prettyPrint', label: 'Pretty Print', hint: 'Format output for readability' }, { value: 'generateSymbols', label: 'Generate Symbols', hint: 'Create symbol files' }, { value: 'useCustomLabels', label: 'Use Custom Labels', hint: 'Apply custom label files' } ], required: false, initialValues: [ ...(currentPrefs?.includeComments !== false ? ['includeComments'] : []), ...(currentPrefs?.prettyPrint !== false ? ['prettyPrint'] : []), ...(currentPrefs?.generateSymbols !== false ? ['generateSymbols'] : []), ...(currentPrefs?.useCustomLabels !== false ? ['useCustomLabels'] : []) ] }); if ((0, prompts_1.isCancel)(formatOptions)) return; const options = formatOptions; await session_manager_1.sessionManager.updateFormatPreferences({ includeComments: options.includes('includeComments'), prettyPrint: options.includes('prettyPrint'), generateSymbols: options.includes('generateSymbols'), useCustomLabels: options.includes('useCustomLabels') }); (0, prompts_1.note)('✅ Format preferences updated successfully!', 'Success'); } /** * Configure UI preferences */ async configureUIPreferences() { const currentPrefs = session_manager_1.sessionManager.getUIPreferences(); const uiOptions = await (0, prompts_1.multiselect)({ message: 'UI/UX preferences:', options: [ { value: 'preferInteractiveMode', label: 'Prefer Interactive Mode', hint: 'Launch interactive mode by default' }, { value: 'showProgressBars', label: 'Show Progress Bars', hint: 'Display operation progress' }, { value: 'colorOutput', label: 'Colored Output', hint: 'Use colors in terminal output' }, { value: 'compactOutput', label: 'Compact Output', hint: 'Use condensed output format' }, { value: 'autoSaveResults', label: 'Auto-save Results', hint: 'Automatically save analysis results' } ], required: false, initialValues: [ ...(currentPrefs?.preferInteractiveMode ? ['preferInteractiveMode'] : []), ...(currentPrefs?.showProgressBars !== false ? ['showProgressBars'] : []), ...(currentPrefs?.colorOutput !== false ? ['colorOutput'] : []), ...(currentPrefs?.compactOutput ? ['compactOutput'] : []), ...(currentPrefs?.autoSaveResults ? ['autoSaveResults'] : []) ] }); if ((0, prompts_1.isCancel)(uiOptions)) return; const options = uiOptions; await session_manager_1.sessionManager.updateUIPreferences({ preferInteractiveMode: options.includes('preferInteractiveMode'), showProgressBars: options.includes('showProgressBars'), colorOutput: options.includes('colorOutput'), compactOutput: options.includes('compactOutput'), autoSaveResults: options.includes('autoSaveResults') }); (0, prompts_1.note)('✅ UI preferences updated successfully!', 'Success'); } /** * Configure AI preferences */ async configureAIPreferences() { const currentPrefs = session_manager_1.sessionManager.getAIPreferences(); const enableAIFeatures = await (0, prompts_1.confirm)({ message: 'Enable AI features globally?', initialValue: currentPrefs?.enableAIFeatures !== false }); if ((0, prompts_1.isCancel)(enableAIFeatures)) return; if (!enableAIFeatures) { await session_manager_1.sessionManager.updateAIPreferences({ enableAIFeatures: false }); (0, prompts_1.note)('✅ AI features disabled globally.', 'Success'); return; } const aiConfidenceThreshold = await (0, prompts_1.text)({ message: 'AI confidence threshold (0.0-1.0):', placeholder: '0.7', defaultValue: (currentPrefs?.aiConfidenceThreshold ?? 0.7).toString(), validate: (value) => { const num = parseFloat(value); if (isNaN(num) || num < 0 || num > 1) { return 'Please enter a number between 0.0 and 1.0'; } return undefined; } }); if ((0, prompts_1.isCancel)(aiConfidenceThreshold)) return; const aiOptions = await (0, prompts_1.multiselect)({ message: 'AI feature options:', options: [ { value: 'useContextualNaming', label: 'Contextual Naming', hint: 'Use AI for smart naming' }, { value: 'generateAIDocumentation', label: 'AI Documentation', hint: 'Generate AI-powered docs' } ], required: false, initialValues: [ ...(currentPrefs?.useContextualNaming !== false ? ['useContextualNaming'] : []), ...(currentPrefs?.generateAIDocumentation !== false ? ['generateAIDocumentation'] : []) ] }); if ((0, prompts_1.isCancel)(aiOptions)) return; const options = aiOptions; await session_manager_1.sessionManager.updateAIPreferences({ enableAIFeatures: true, aiConfidenceThreshold: parseFloat(aiConfidenceThreshold), useContextualNaming: options.includes('useContextualNaming'), generateAIDocumentation: options.includes('generateAIDocumentation') }); (0, prompts_1.note)('✅ AI preferences updated successfully!', 'Success'); } /** * Handle import/export operations */ async handleImportExport() { const action = await (0, prompts_1.select)({ message: 'Import/Export preferences:', options: [ { value: 'export', label: '📤 Export Preferences', hint: 'Save preferences to file' }, { value: 'import', label: '📥 Import Preferences', hint: 'Load preferences from file' } ] }); if ((0, prompts_1.isCancel)(action)) return; if (action === 'export') { const exportPath = await (0, prompts_1.text)({ message: 'Export file path:', placeholder: './snes-disasm-preferences.json', defaultValue: './snes-disasm-preferences.json' }); if ((0, prompts_1.isCancel)(exportPath)) return; try { await session_manager_1.sessionManager.exportPreferences(exportPath); (0, prompts_1.note)(`✅ Preferences exported to: ${exportPath}`, 'Success'); } catch (error) { (0, prompts_1.note)(`❌ Export failed: ${error instanceof Error ? error.message : error}`, 'Error'); } } else { const importPath = await (0, prompts_1.text)({ message: 'Import file path:', placeholder: './snes-disasm-preferences.json', validate: (value) => { if (!value) return 'File path is required'; // Basic validation - could be enhanced with file existence check return undefined; } }); if ((0, prompts_1.isCancel)(importPath)) return; const confirmImport = await (0, prompts_1.confirm)({ message: 'This will overwrite your current preferences. Continue?' }); if ((0, prompts_1.isCancel)(confirmImport) || !confirmImport) return; try { await session_manager_1.sessionManager.importPreferences(importPath); (0, prompts_1.note)(`✅ Preferences imported from: ${importPath}`, 'Success'); } catch (error) { (0, prompts_1.note)(`❌ Import failed: ${error instanceof Error ? error.message : error}`, 'Error'); } } } /** * Reset preferences to defaults */ async resetPreferences() { const confirmReset = await (0, prompts_1.confirm)({ message: 'Are you sure you want to reset all preferences to defaults? This cannot be undone.' }); if ((0, prompts_1.isCancel)(confirmReset) || !confirmReset) return; await session_manager_1.sessionManager.resetPreferences(); (0, prompts_1.note)('✅ All preferences have been reset to defaults.', 'Success'); } } exports.PreferencesManager = PreferencesManager; // Export singleton instance exports.preferencesManager = new PreferencesManager(); //# sourceMappingURL=preferences-manager.js.map