// Bundle operations module for the 4DSTAR Bundle Manager // Extracted from renderer.js to centralize bundle-specific business logic const { ipcRenderer } = require('electron'); const path = require('path'); // Import dependencies (these will be injected or imported when integrated) let stateManager, domManager, uiComponents; // --- BUNDLE ACTIONS HANDLERS --- async function handleOpenBundle() { const bundlePath = await ipcRenderer.invoke('select-file'); if (!bundlePath) return; await openBundleFromPath(bundlePath); } async function openBundleFromPath(bundlePath) { if (!bundlePath) return; // Small delay to ensure file dialog closes properly await new Promise(resolve => setTimeout(resolve, 100)); domManager.showSpinner(); domManager.showModal('Opening...', `Opening bundle: ${path.basename(bundlePath)}`); const result = await ipcRenderer.invoke('open-bundle', bundlePath); domManager.hideSpinner(); if (result.success) { stateManager.setBundleState(result, bundlePath); displayBundleInfo(result.report); domManager.showView('bundle-view'); domManager.hideModal(); } else { domManager.showModal('Error Opening Bundle', `Failed to open bundle: ${result ? result.error : 'Unknown error'}`); } } async function handleSignBundle() { const currentBundlePath = stateManager.getCurrentBundlePath(); if (!currentBundlePath) return; domManager.showSpinner(); const signResult = await ipcRenderer.invoke('sign-bundle', currentBundlePath); domManager.hideSpinner(); if (signResult.success) { domManager.showModal('Success', 'Bundle signed successfully.'); await reloadCurrentBundle(); domManager.hideModal(); } else { domManager.showModal('Sign Error', `Failed to sign bundle: ${signResult.error}`); } } async function handleValidateBundle() { const currentBundlePath = stateManager.getCurrentBundlePath(); if (!currentBundlePath) return; domManager.showSpinner(); const result = await ipcRenderer.invoke('validate-bundle', currentBundlePath); domManager.hideSpinner(); if (result.success) { // With the new JSON architecture, validation data is directly in result const errors = result.errors || []; const warnings = result.warnings || []; const validationIssues = errors.concat(warnings); const elements = domManager.getElements(); if (validationIssues.length > 0) { elements.validationResults.textContent = validationIssues.join('\n'); elements.validationTabLink.classList.remove('hidden'); } else { elements.validationResults.textContent = 'Bundle is valid.'; elements.validationTabLink.classList.add('hidden'); } // Switch to the validation tab to show the results domManager.switchTab('validation-tab'); // Show summary in modal const summary = result.summary || { errors: errors.length, warnings: warnings.length }; const message = `Validation finished with ${summary.errors} errors and ${summary.warnings} warnings.`; domManager.showModal('Validation Complete', message); } else { domManager.showModal('Validation Error', `Failed to validate bundle: ${result.error}`); } } async function handleClearBundle() { const currentBundlePath = stateManager.getCurrentBundlePath(); if (!currentBundlePath) return; domManager.showSpinner(); const result = await ipcRenderer.invoke('clear-bundle', currentBundlePath); domManager.hideSpinner(); if (result.success) { domManager.showModal('Success', 'All binaries have been cleared. Reloading...'); await reloadCurrentBundle(); domManager.hideModal(); } else { domManager.showModal('Clear Error', `Failed to clear binaries: ${result.error}`); } } async function handleFillBundle() { const currentBundle = stateManager.getCurrentBundle(); if (!currentBundle) return domManager.showModal('Action Canceled', 'Please open a bundle first.'); domManager.showSpinner(); domManager.showModal('Filling Bundle...', 'Adding local binaries to bundle.'); const result = await ipcRenderer.invoke('fill-bundle', currentBundle.bundlePath); domManager.hideSpinner(); if (result.success) { domManager.showModal('Success', 'Binaries filled successfully. Reloading...'); await reloadCurrentBundle(); domManager.hideModal(); } else { domManager.showModal('Fill Error', `Failed to fill bundle: ${result.error}`); } } // --- DATA DISPLAY --- async function reloadCurrentBundle() { const currentBundle = stateManager.getCurrentBundle(); if (!currentBundle) return; const reloadResult = await ipcRenderer.invoke('open-bundle', currentBundle.bundlePath); if (reloadResult.success) { stateManager.setBundleState(reloadResult, currentBundle.bundlePath); displayBundleInfo(reloadResult.report); } else { domManager.showModal('Reload Error', `Failed to reload bundle details: ${reloadResult.error}`); } } function displayBundleInfo(report) { if (!report) { domManager.showModal('Display Error', 'Could not load bundle information.'); return; } const { manifest, signature, validation, plugins } = report; const elements = domManager.getElements(); // Store original metadata for comparison stateManager.updateOriginalMetadata({ bundleVersion: manifest.bundleVersion || '', bundleAuthor: manifest.bundleAuthor || '', bundleComment: manifest.bundleComment || '' }); stateManager.markUnsavedChanges(false); updateSaveButtonVisibility(); // Set bundle title elements.bundleTitle.textContent = manifest.bundleName || 'Untitled Bundle'; // --- Overview Tab --- const trustStatus = signature.status || 'UNSIGNED'; const trustColorClass = { 'TRUSTED': 'trusted', 'UNTRUSTED': 'untrusted', 'INVALID': 'untrusted', 'TAMPERED': 'untrusted', 'UNSIGNED': 'unsigned', 'ERROR': 'untrusted', 'UNSUPPORTED': 'warning' }[trustStatus] || 'unsigned'; elements.manifestDetails.innerHTML = `
Bundled On: ${manifest.bundledOn || 'N/A'}
${uiComponents.createEditableField('Comment', 'bundleComment', manifest.bundleComment || 'N/A')} ${manifest.bundleAuthorKeyFingerprint ? `Author Key: ${manifest.bundleAuthorKeyFingerprint}
` : ''} ${manifest.bundleSignature ? `Signature: ${manifest.bundleSignature}
` : ''}Source: ${pluginData.sdist_path}
Binaries:
No plugins found in this bundle.