feat(electorn): added fill
This commit is contained in:
@@ -33,7 +33,6 @@
|
||||
<div class="action-buttons">
|
||||
<button id="sign-bundle-btn">Sign</button>
|
||||
<button id="validate-bundle-btn">Validate</button>
|
||||
<button id="fill-bundle-btn">Fill</button>
|
||||
<button id="clear-bundle-btn">Clear</button>
|
||||
<button id="save-metadata-btn" class="hidden">Save Changes</button>
|
||||
</div>
|
||||
@@ -42,7 +41,8 @@
|
||||
<div class="tab-nav">
|
||||
<button class="tab-link active" data-tab="overview-tab">Overview</button>
|
||||
<button class="tab-link" data-tab="plugins-tab">Plugins</button>
|
||||
<button class="tab-link" data-tab="validation-tab" class="hidden">Validation</button>
|
||||
<button class="tab-link" data-tab="fill-tab" id="fill-tab-link">Fill</button>
|
||||
<button class="tab-link hidden" data-tab="validation-tab">Validation</button>
|
||||
</div>
|
||||
|
||||
<div id="tab-content">
|
||||
@@ -55,6 +55,35 @@
|
||||
<div id="validation-tab" class="tab-pane">
|
||||
<pre id="validation-results"></pre>
|
||||
</div>
|
||||
<div id="fill-tab" class="tab-pane">
|
||||
<div class="fill-header">
|
||||
<h3>Fill Bundle with Compiled Binaries</h3>
|
||||
<p>Select the targets you want to build and add to the bundle:</p>
|
||||
<div class="fill-header-actions">
|
||||
<button id="load-fillable-targets-btn" class="action-button primary">Load Available Targets</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="fill-targets-container">
|
||||
<div id="fill-loading" class="hidden">
|
||||
<p>Loading available targets...</p>
|
||||
</div>
|
||||
<div id="fill-no-targets" class="hidden">
|
||||
<p>No fillable targets available. The bundle may already be complete.</p>
|
||||
</div>
|
||||
<div id="fill-targets-content" class="hidden">
|
||||
<div id="fill-plugins-tables"></div>
|
||||
<div class="fill-actions">
|
||||
<button id="select-all-targets" class="action-button secondary">Select All</button>
|
||||
<button id="deselect-all-targets" class="action-button secondary">Deselect All</button>
|
||||
<button id="start-fill-process" class="action-button primary">Start Building</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="fill-progress-container" class="hidden">
|
||||
<h4>Build Progress</h4>
|
||||
<div id="fill-progress-content"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -103,7 +132,7 @@
|
||||
</div>
|
||||
|
||||
<!-- Fill Modal -->
|
||||
<div id="fill-modal" class="modal">
|
||||
<div id="fill-modal" class="modal-container hidden">
|
||||
<div class="modal-content">
|
||||
<span class="close-fill-modal-button">×</span>
|
||||
<h2 id="fill-modal-title">Fill Bundle</h2>
|
||||
|
||||
@@ -17,7 +17,7 @@ const createBundleBtn = document.getElementById('create-bundle-btn');
|
||||
// Bundle action buttons
|
||||
const signBundleBtn = document.getElementById('sign-bundle-btn');
|
||||
const validateBundleBtn = document.getElementById('validate-bundle-btn');
|
||||
const fillBundleBtn = document.getElementById('fill-bundle-btn');
|
||||
// Fill button removed - Fill tab is now always visible
|
||||
const clearBundleBtn = document.getElementById('clear-bundle-btn');
|
||||
const saveMetadataBtn = document.getElementById('save-metadata-btn');
|
||||
|
||||
@@ -26,6 +26,19 @@ const saveOptionsModal = document.getElementById('save-options-modal');
|
||||
const overwriteBundleBtn = document.getElementById('overwrite-bundle-btn');
|
||||
const saveAsNewBtn = document.getElementById('save-as-new-btn');
|
||||
|
||||
// Fill tab elements
|
||||
const fillTabLink = document.getElementById('fill-tab-link');
|
||||
const loadFillableTargetsBtn = document.getElementById('load-fillable-targets-btn');
|
||||
const fillLoading = document.getElementById('fill-loading');
|
||||
const fillPluginsTables = document.getElementById('fill-plugins-tables');
|
||||
const fillNoTargets = document.getElementById('fill-no-targets');
|
||||
const fillTargetsContent = document.getElementById('fill-targets-content');
|
||||
const selectAllTargetsBtn = document.getElementById('select-all-targets');
|
||||
const deselectAllTargetsBtn = document.getElementById('deselect-all-targets');
|
||||
const startFillProcessBtn = document.getElementById('start-fill-process');
|
||||
const fillProgressContainer = document.getElementById('fill-progress-container');
|
||||
const fillProgressContent = document.getElementById('fill-progress-content');
|
||||
|
||||
// Bundle display
|
||||
const bundleTitle = document.getElementById('bundle-title');
|
||||
const manifestDetails = document.getElementById('manifest-details');
|
||||
@@ -103,160 +116,230 @@ function setupEventListeners() {
|
||||
saveMetadataBtn.addEventListener('click', showSaveOptionsModal);
|
||||
overwriteBundleBtn.addEventListener('click', () => handleSaveMetadata(false));
|
||||
saveAsNewBtn.addEventListener('click', () => handleSaveMetadata(true));
|
||||
fillBundleBtn.addEventListener('click', async () => {
|
||||
|
||||
// Load fillable targets button
|
||||
loadFillableTargetsBtn.addEventListener('click', async () => {
|
||||
await loadFillableTargets();
|
||||
});
|
||||
// Load fillable targets for the Fill tab
|
||||
async function loadFillableTargets() {
|
||||
console.log('loadFillableTargets called, currentBundlePath:', currentBundlePath);
|
||||
|
||||
// Check if required DOM elements exist
|
||||
if (!fillNoTargets || !fillTargetsContent || !fillLoading) {
|
||||
console.error('Fill tab DOM elements not found');
|
||||
showModal('Error', 'Fill tab interface not properly initialized.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!currentBundlePath) {
|
||||
showModal('Error', 'No bundle is currently open.');
|
||||
console.log('No bundle path, showing no targets message');
|
||||
hideAllFillStates();
|
||||
fillNoTargets.classList.remove('hidden');
|
||||
return;
|
||||
}
|
||||
showSpinner();
|
||||
const result = await ipcRenderer.invoke('get-fillable-targets', currentBundlePath);
|
||||
hideSpinner();
|
||||
|
||||
try {
|
||||
// Show loading state
|
||||
hideAllFillStates();
|
||||
fillLoading.classList.remove('hidden');
|
||||
loadFillableTargetsBtn.disabled = true;
|
||||
|
||||
console.log('Calling get-fillable-targets...');
|
||||
const result = await ipcRenderer.invoke('get-fillable-targets', currentBundlePath);
|
||||
console.log('get-fillable-targets result:', result);
|
||||
|
||||
if (!result.success) {
|
||||
showModal('Error', `Failed to get fillable targets: ${result.error}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const targets = result.data;
|
||||
if (Object.keys(targets).length === 0) {
|
||||
showModal('Info', 'The bundle is already full. No new targets to build.');
|
||||
return;
|
||||
}
|
||||
|
||||
populateFillTargetsList(targets);
|
||||
fillModal.style.display = 'block';
|
||||
});
|
||||
|
||||
closeFillModalButton.addEventListener('click', () => {
|
||||
fillModal.style.display = 'none';
|
||||
});
|
||||
|
||||
function populateFillTargetsList(plugins) {
|
||||
fillTargetsList.innerHTML = '';
|
||||
for (const [pluginName, targets] of Object.entries(plugins)) {
|
||||
if (targets.length > 0) {
|
||||
const pluginHeader = document.createElement('h4');
|
||||
pluginHeader.textContent = `Plugin: ${pluginName}`;
|
||||
fillTargetsList.appendChild(pluginHeader);
|
||||
|
||||
targets.forEach(target => {
|
||||
const item = document.createElement('div');
|
||||
item.className = 'fill-target-item';
|
||||
const checkbox = document.createElement('input');
|
||||
checkbox.type = 'checkbox';
|
||||
checkbox.checked = true;
|
||||
checkbox.id = `target-${pluginName}-${target.triplet}`;
|
||||
checkbox.dataset.pluginName = pluginName;
|
||||
checkbox.dataset.targetTriplet = target.triplet;
|
||||
checkbox.dataset.targetInfo = JSON.stringify(target);
|
||||
|
||||
const label = document.createElement('label');
|
||||
label.htmlFor = checkbox.id;
|
||||
label.textContent = `${target.triplet} (${target.type})`;
|
||||
|
||||
item.appendChild(checkbox);
|
||||
item.appendChild(label);
|
||||
fillTargetsList.appendChild(item);
|
||||
});
|
||||
if (!result.success) {
|
||||
console.log('get-fillable-targets failed:', result.error);
|
||||
hideAllFillStates();
|
||||
showModal('Error', `Failed to load fillable targets: ${result.error}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const targets = result.data;
|
||||
console.log('Fillable targets:', targets);
|
||||
|
||||
hideAllFillStates();
|
||||
|
||||
if (!targets || Object.keys(targets).length === 0) {
|
||||
console.log('No fillable targets found');
|
||||
fillNoTargets.classList.remove('hidden');
|
||||
} else {
|
||||
console.log('Populating fillable targets table');
|
||||
fillTargetsContent.classList.remove('hidden');
|
||||
populateFillTargetsTable(targets);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error in loadFillableTargets:', error);
|
||||
hideAllFillStates();
|
||||
showModal('Error', `Error loading fillable targets: ${error.message}`);
|
||||
} finally {
|
||||
loadFillableTargetsBtn.disabled = false;
|
||||
}
|
||||
// Reset view
|
||||
fillModalBody.style.display = 'block';
|
||||
fillProgressView.style.display = 'none';
|
||||
}
|
||||
|
||||
// Helper function to hide all fill tab states
|
||||
function hideAllFillStates() {
|
||||
fillLoading.classList.add('hidden');
|
||||
fillNoTargets.classList.add('hidden');
|
||||
fillTargetsContent.classList.add('hidden');
|
||||
}
|
||||
|
||||
startFillButton.addEventListener('click', async () => {
|
||||
const selectedTargets = {};
|
||||
const checkboxes = fillTargetsList.querySelectorAll('input[type="checkbox"]:checked');
|
||||
// Old modal code removed - now using tab-based interface
|
||||
|
||||
if (checkboxes.length === 0) {
|
||||
showModal('Info', 'No targets selected to fill.');
|
||||
return;
|
||||
}
|
||||
|
||||
checkboxes.forEach(cb => {
|
||||
const pluginName = cb.dataset.pluginName;
|
||||
if (!selectedTargets[pluginName]) {
|
||||
selectedTargets[pluginName] = [];
|
||||
}
|
||||
selectedTargets[pluginName].push(JSON.parse(cb.dataset.targetInfo));
|
||||
});
|
||||
|
||||
fillModalBody.style.display = 'none';
|
||||
fillProgressView.style.display = 'block';
|
||||
fillModalTitle.textContent = 'Filling Bundle...';
|
||||
populateFillProgressList(selectedTargets);
|
||||
|
||||
const result = await ipcRenderer.invoke('fill-bundle', {
|
||||
bundlePath: currentBundlePath,
|
||||
targetsToBuild: selectedTargets
|
||||
});
|
||||
|
||||
fillModalTitle.textContent = 'Fill Complete';
|
||||
if (!result.success) {
|
||||
// A final error message if the whole process fails.
|
||||
const p = document.createElement('p');
|
||||
p.style.color = 'var(--error-color)';
|
||||
p.textContent = `Error: ${result.error}`;
|
||||
fillProgressList.appendChild(p);
|
||||
}
|
||||
});
|
||||
|
||||
function populateFillProgressList(plugins) {
|
||||
fillProgressList.innerHTML = '';
|
||||
// Create modern table-based interface for fillable targets
|
||||
function populateFillTargetsTable(plugins) {
|
||||
fillPluginsTables.innerHTML = '';
|
||||
|
||||
for (const [pluginName, targets] of Object.entries(plugins)) {
|
||||
targets.forEach(target => {
|
||||
const item = document.createElement('div');
|
||||
item.className = 'fill-target-item';
|
||||
item.id = `progress-${pluginName}-${target.triplet}`;
|
||||
|
||||
const indicator = document.createElement('div');
|
||||
indicator.className = 'progress-indicator';
|
||||
if (targets.length > 0) {
|
||||
// Create plugin table container
|
||||
const pluginTable = document.createElement('div');
|
||||
pluginTable.className = 'fill-plugin-table';
|
||||
|
||||
const label = document.createElement('span');
|
||||
label.textContent = `${pluginName} - ${target.triplet}`;
|
||||
// Plugin header
|
||||
const pluginHeader = document.createElement('div');
|
||||
pluginHeader.className = 'fill-plugin-header';
|
||||
pluginHeader.textContent = `${pluginName} (${targets.length} target${targets.length > 1 ? 's' : ''})`;
|
||||
pluginTable.appendChild(pluginHeader);
|
||||
|
||||
// Create table
|
||||
const table = document.createElement('table');
|
||||
table.className = 'fill-targets-table';
|
||||
|
||||
// Table header
|
||||
const thead = document.createElement('thead');
|
||||
thead.innerHTML = `
|
||||
<tr>
|
||||
<th style="width: 50px;">
|
||||
<input type="checkbox" class="plugin-select-all" data-plugin="${pluginName}" checked>
|
||||
</th>
|
||||
<th>Target Platform</th>
|
||||
<th>Architecture</th>
|
||||
<th>Type</th>
|
||||
<th>Compiler</th>
|
||||
</tr>
|
||||
`;
|
||||
table.appendChild(thead);
|
||||
|
||||
// Table body
|
||||
const tbody = document.createElement('tbody');
|
||||
targets.forEach(target => {
|
||||
const row = document.createElement('tr');
|
||||
row.innerHTML = `
|
||||
<td>
|
||||
<input type="checkbox" class="fill-target-checkbox"
|
||||
data-plugin="${pluginName}"
|
||||
data-target='${JSON.stringify(target)}'
|
||||
checked>
|
||||
</td>
|
||||
<td><strong>${target.triplet}</strong></td>
|
||||
<td>${target.arch}</td>
|
||||
<td><span class="target-type ${target.type}">${target.type}</span></td>
|
||||
<td>${target.details?.compiler || 'N/A'} ${target.details?.compiler_version || ''}</td>
|
||||
`;
|
||||
tbody.appendChild(row);
|
||||
});
|
||||
table.appendChild(tbody);
|
||||
|
||||
pluginTable.appendChild(table);
|
||||
fillPluginsTables.appendChild(pluginTable);
|
||||
}
|
||||
}
|
||||
|
||||
// Add event listeners for select all functionality
|
||||
setupFillTargetEventListeners();
|
||||
}
|
||||
|
||||
item.appendChild(indicator);
|
||||
item.appendChild(label);
|
||||
fillProgressList.appendChild(item);
|
||||
// Setup event listeners for Fill tab functionality
|
||||
function setupFillTargetEventListeners() {
|
||||
// Plugin-level select all checkboxes
|
||||
document.querySelectorAll('.plugin-select-all').forEach(checkbox => {
|
||||
checkbox.addEventListener('change', (e) => {
|
||||
const pluginName = e.target.dataset.plugin;
|
||||
const pluginCheckboxes = document.querySelectorAll(`.fill-target-checkbox[data-plugin="${pluginName}"]`);
|
||||
pluginCheckboxes.forEach(cb => cb.checked = e.target.checked);
|
||||
});
|
||||
});
|
||||
|
||||
// Global select/deselect all buttons
|
||||
selectAllTargetsBtn.addEventListener('click', () => {
|
||||
document.querySelectorAll('.fill-target-checkbox, .plugin-select-all').forEach(cb => cb.checked = true);
|
||||
});
|
||||
|
||||
deselectAllTargetsBtn.addEventListener('click', () => {
|
||||
document.querySelectorAll('.fill-target-checkbox, .plugin-select-all').forEach(cb => cb.checked = false);
|
||||
});
|
||||
|
||||
// Start fill process button
|
||||
startFillProcessBtn.addEventListener('click', async () => {
|
||||
const selectedTargets = {};
|
||||
const checkboxes = document.querySelectorAll('.fill-target-checkbox:checked');
|
||||
|
||||
if (checkboxes.length === 0) {
|
||||
showModal('Info', 'No targets selected to fill.');
|
||||
return;
|
||||
}
|
||||
|
||||
checkboxes.forEach(cb => {
|
||||
const pluginName = cb.dataset.plugin;
|
||||
const target = JSON.parse(cb.dataset.target);
|
||||
if (!selectedTargets[pluginName]) {
|
||||
selectedTargets[pluginName] = [];
|
||||
}
|
||||
selectedTargets[pluginName].push(target);
|
||||
});
|
||||
|
||||
// Hide target selection and show progress
|
||||
fillTargetsContent.classList.add('hidden');
|
||||
fillProgressContainer.classList.remove('hidden');
|
||||
populateFillProgress(selectedTargets);
|
||||
|
||||
const result = await ipcRenderer.invoke('fill-bundle', {
|
||||
bundlePath: currentBundlePath,
|
||||
targetsToBuild: selectedTargets
|
||||
});
|
||||
|
||||
if (!result.success) {
|
||||
const errorItem = document.createElement('div');
|
||||
errorItem.className = 'progress-item';
|
||||
errorItem.innerHTML = `
|
||||
<span class="progress-status failure">Error</span>
|
||||
<span>Fill process failed: ${result.error}</span>
|
||||
`;
|
||||
fillProgressContent.appendChild(errorItem);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Create progress display for fill process
|
||||
function populateFillProgress(selectedTargets) {
|
||||
fillProgressContent.innerHTML = '';
|
||||
|
||||
for (const [pluginName, targets] of Object.entries(selectedTargets)) {
|
||||
targets.forEach(target => {
|
||||
const progressItem = document.createElement('div');
|
||||
progressItem.className = 'progress-item';
|
||||
progressItem.id = `progress-${pluginName}-${target.triplet}`;
|
||||
progressItem.innerHTML = `
|
||||
<span class="progress-status building">Building</span>
|
||||
<span>${pluginName} - ${target.triplet}</span>
|
||||
`;
|
||||
fillProgressContent.appendChild(progressItem);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Handle progress updates from backend
|
||||
ipcRenderer.on('fill-bundle-progress', (event, progress) => {
|
||||
console.log('Progress update:', progress);
|
||||
if (typeof progress === 'object' && progress.status) {
|
||||
const { status, plugin, target, message } = progress;
|
||||
const { status, plugin, target } = progress;
|
||||
const progressItem = document.getElementById(`progress-${plugin}-${target}`);
|
||||
if (progressItem) {
|
||||
const indicator = progressItem.querySelector('.progress-indicator');
|
||||
indicator.className = 'progress-indicator'; // Reset classes
|
||||
switch (status) {
|
||||
case 'building':
|
||||
indicator.classList.add('spinner-icon');
|
||||
break;
|
||||
case 'success':
|
||||
indicator.classList.add('success-icon');
|
||||
break;
|
||||
case 'failure':
|
||||
indicator.classList.add('failure-icon');
|
||||
break;
|
||||
}
|
||||
const label = progressItem.querySelector('span');
|
||||
if (message) {
|
||||
label.textContent = `${plugin} - ${target}: ${message}`;
|
||||
}
|
||||
const statusSpan = progressItem.querySelector('.progress-status');
|
||||
statusSpan.className = `progress-status ${status}`;
|
||||
statusSpan.textContent = status.charAt(0).toUpperCase() + status.slice(1);
|
||||
}
|
||||
} else if (typeof progress === 'object' && progress.message) {
|
||||
// Handle final completion message
|
||||
if (progress.message.includes('✅')) {
|
||||
fillModalTitle.textContent = 'Fill Complete!';
|
||||
}
|
||||
} else {
|
||||
// Handle simple string progress messages
|
||||
const p = document.createElement('p');
|
||||
p.textContent = progress;
|
||||
fillProgressList.appendChild(p);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -512,6 +595,13 @@ function displayBundleInfo(report) {
|
||||
validationTabLink.classList.add('hidden');
|
||||
}
|
||||
|
||||
// Temporarily disabled to fix bundle opening hang
|
||||
// TODO: Re-enable after debugging fillable targets functionality
|
||||
// loadFillableTargets().catch(error => {
|
||||
// console.error('Failed to load fillable targets:', error);
|
||||
// // Don't block bundle opening if fill targets fail to load
|
||||
// });
|
||||
|
||||
// Reset to overview tab by default
|
||||
switchTab('overview-tab');
|
||||
}
|
||||
|
||||
@@ -418,6 +418,166 @@ body {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* Fill Tab Styles */
|
||||
#fill-tab {
|
||||
max-height: calc(100vh - 200px);
|
||||
overflow-y: auto;
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
.fill-header {
|
||||
margin-bottom: 20px;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
background-color: var(--main-bg);
|
||||
z-index: 10;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.fill-header h3 {
|
||||
margin-bottom: 8px;
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
.fill-header p {
|
||||
color: var(--text-light);
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.fill-header-actions {
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
#fill-targets-container {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
#fill-plugins-tables {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.fill-plugin-table {
|
||||
margin-bottom: 24px;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.fill-plugin-header {
|
||||
background-color: var(--bg-color);
|
||||
padding: 12px 16px;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
font-weight: 600;
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
.fill-targets-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.fill-targets-table th,
|
||||
.fill-targets-table td {
|
||||
padding: 12px 16px;
|
||||
text-align: left;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.fill-targets-table th {
|
||||
background-color: var(--sidebar-bg);
|
||||
font-weight: 600;
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
.fill-targets-table tr:last-child td {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.fill-targets-table tr:hover {
|
||||
background-color: rgba(52, 152, 219, 0.05);
|
||||
}
|
||||
|
||||
.fill-target-checkbox {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.fill-actions {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
padding: 16px 0;
|
||||
border-top: 1px solid var(--border-color);
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.action-button.secondary {
|
||||
background-color: var(--sidebar-bg);
|
||||
color: var(--text-color);
|
||||
border: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.action-button.secondary:hover {
|
||||
background-color: var(--border-color);
|
||||
}
|
||||
|
||||
.action-button.primary {
|
||||
background-color: var(--primary-color);
|
||||
color: white;
|
||||
border: 1px solid var(--primary-color);
|
||||
}
|
||||
|
||||
.action-button.primary:hover {
|
||||
background-color: var(--primary-hover);
|
||||
border-color: var(--primary-hover);
|
||||
}
|
||||
|
||||
#fill-progress-container {
|
||||
margin-top: 20px;
|
||||
padding: 16px;
|
||||
background-color: var(--sidebar-bg);
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
#fill-progress-content {
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.progress-item {
|
||||
padding: 8px 0;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.progress-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.progress-status {
|
||||
padding: 4px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.progress-status.building {
|
||||
background-color: #fef3c7;
|
||||
color: #92400e;
|
||||
}
|
||||
|
||||
.progress-status.success {
|
||||
background-color: #d1fae5;
|
||||
color: #065f46;
|
||||
}
|
||||
|
||||
.progress-status.failure {
|
||||
background-color: #fee2e2;
|
||||
color: #991b1b;
|
||||
}
|
||||
|
||||
/* Save Options Modal */
|
||||
.save-options {
|
||||
display: flex;
|
||||
|
||||
@@ -973,6 +973,10 @@ def fill_bundle(bundle_path: Path, targets_to_build: dict, progress_callback: Op
|
||||
# No fallback to print() - all output goes through callback only
|
||||
|
||||
staging_dir = Path(tempfile.mkdtemp(prefix="fourdst_fill_"))
|
||||
successful_builds = 0
|
||||
failed_builds = 0
|
||||
build_details = []
|
||||
|
||||
try:
|
||||
report_progress("Unpacking bundle to temporary directory...")
|
||||
with zipfile.ZipFile(bundle_path, 'r') as bundle_zip:
|
||||
@@ -1026,6 +1030,14 @@ def fill_bundle(bundle_path: Path, targets_to_build: dict, progress_callback: Op
|
||||
}
|
||||
plugin_info.setdefault('binaries', []).append(new_binary_entry)
|
||||
|
||||
successful_builds += 1
|
||||
build_details.append({
|
||||
'plugin': plugin_name,
|
||||
'target': target_triplet,
|
||||
'status': 'success',
|
||||
'filename': tagged_filename
|
||||
})
|
||||
|
||||
report_progress({
|
||||
'status': 'success',
|
||||
'plugin': plugin_name,
|
||||
@@ -1034,6 +1046,14 @@ def fill_bundle(bundle_path: Path, targets_to_build: dict, progress_callback: Op
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
failed_builds += 1
|
||||
build_details.append({
|
||||
'plugin': plugin_name,
|
||||
'target': target_triplet,
|
||||
'status': 'failure',
|
||||
'error': str(e)
|
||||
})
|
||||
|
||||
report_progress({
|
||||
'status': 'failure',
|
||||
'plugin': plugin_name,
|
||||
|
||||
Reference in New Issue
Block a user