feat(electorn): added fill
This commit is contained in:
@@ -33,7 +33,6 @@
|
|||||||
<div class="action-buttons">
|
<div class="action-buttons">
|
||||||
<button id="sign-bundle-btn">Sign</button>
|
<button id="sign-bundle-btn">Sign</button>
|
||||||
<button id="validate-bundle-btn">Validate</button>
|
<button id="validate-bundle-btn">Validate</button>
|
||||||
<button id="fill-bundle-btn">Fill</button>
|
|
||||||
<button id="clear-bundle-btn">Clear</button>
|
<button id="clear-bundle-btn">Clear</button>
|
||||||
<button id="save-metadata-btn" class="hidden">Save Changes</button>
|
<button id="save-metadata-btn" class="hidden">Save Changes</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -42,7 +41,8 @@
|
|||||||
<div class="tab-nav">
|
<div class="tab-nav">
|
||||||
<button class="tab-link active" data-tab="overview-tab">Overview</button>
|
<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="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>
|
||||||
|
|
||||||
<div id="tab-content">
|
<div id="tab-content">
|
||||||
@@ -55,6 +55,35 @@
|
|||||||
<div id="validation-tab" class="tab-pane">
|
<div id="validation-tab" class="tab-pane">
|
||||||
<pre id="validation-results"></pre>
|
<pre id="validation-results"></pre>
|
||||||
</div>
|
</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>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -103,7 +132,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Fill Modal -->
|
<!-- Fill Modal -->
|
||||||
<div id="fill-modal" class="modal">
|
<div id="fill-modal" class="modal-container hidden">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<span class="close-fill-modal-button">×</span>
|
<span class="close-fill-modal-button">×</span>
|
||||||
<h2 id="fill-modal-title">Fill Bundle</h2>
|
<h2 id="fill-modal-title">Fill Bundle</h2>
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ const createBundleBtn = document.getElementById('create-bundle-btn');
|
|||||||
// Bundle action buttons
|
// Bundle action buttons
|
||||||
const signBundleBtn = document.getElementById('sign-bundle-btn');
|
const signBundleBtn = document.getElementById('sign-bundle-btn');
|
||||||
const validateBundleBtn = document.getElementById('validate-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 clearBundleBtn = document.getElementById('clear-bundle-btn');
|
||||||
const saveMetadataBtn = document.getElementById('save-metadata-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 overwriteBundleBtn = document.getElementById('overwrite-bundle-btn');
|
||||||
const saveAsNewBtn = document.getElementById('save-as-new-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
|
// Bundle display
|
||||||
const bundleTitle = document.getElementById('bundle-title');
|
const bundleTitle = document.getElementById('bundle-title');
|
||||||
const manifestDetails = document.getElementById('manifest-details');
|
const manifestDetails = document.getElementById('manifest-details');
|
||||||
@@ -103,71 +116,165 @@ function setupEventListeners() {
|
|||||||
saveMetadataBtn.addEventListener('click', showSaveOptionsModal);
|
saveMetadataBtn.addEventListener('click', showSaveOptionsModal);
|
||||||
overwriteBundleBtn.addEventListener('click', () => handleSaveMetadata(false));
|
overwriteBundleBtn.addEventListener('click', () => handleSaveMetadata(false));
|
||||||
saveAsNewBtn.addEventListener('click', () => handleSaveMetadata(true));
|
saveAsNewBtn.addEventListener('click', () => handleSaveMetadata(true));
|
||||||
fillBundleBtn.addEventListener('click', async () => {
|
|
||||||
if (!currentBundlePath) {
|
// Load fillable targets button
|
||||||
showModal('Error', 'No bundle is currently open.');
|
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;
|
return;
|
||||||
}
|
}
|
||||||
showSpinner();
|
|
||||||
|
if (!currentBundlePath) {
|
||||||
|
console.log('No bundle path, showing no targets message');
|
||||||
|
hideAllFillStates();
|
||||||
|
fillNoTargets.classList.remove('hidden');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
const result = await ipcRenderer.invoke('get-fillable-targets', currentBundlePath);
|
||||||
hideSpinner();
|
console.log('get-fillable-targets result:', result);
|
||||||
|
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
showModal('Error', `Failed to get fillable targets: ${result.error}`);
|
console.log('get-fillable-targets failed:', result.error);
|
||||||
|
hideAllFillStates();
|
||||||
|
showModal('Error', `Failed to load fillable targets: ${result.error}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const targets = result.data;
|
const targets = result.data;
|
||||||
if (Object.keys(targets).length === 0) {
|
console.log('Fillable targets:', targets);
|
||||||
showModal('Info', 'The bundle is already full. No new targets to build.');
|
|
||||||
return;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
populateFillTargetsList(targets);
|
// Helper function to hide all fill tab states
|
||||||
fillModal.style.display = 'block';
|
function hideAllFillStates() {
|
||||||
});
|
fillLoading.classList.add('hidden');
|
||||||
|
fillNoTargets.classList.add('hidden');
|
||||||
|
fillTargetsContent.classList.add('hidden');
|
||||||
|
}
|
||||||
|
|
||||||
closeFillModalButton.addEventListener('click', () => {
|
// Old modal code removed - now using tab-based interface
|
||||||
fillModal.style.display = 'none';
|
|
||||||
});
|
// Create modern table-based interface for fillable targets
|
||||||
|
function populateFillTargetsTable(plugins) {
|
||||||
|
fillPluginsTables.innerHTML = '';
|
||||||
|
|
||||||
function populateFillTargetsList(plugins) {
|
|
||||||
fillTargetsList.innerHTML = '';
|
|
||||||
for (const [pluginName, targets] of Object.entries(plugins)) {
|
for (const [pluginName, targets] of Object.entries(plugins)) {
|
||||||
if (targets.length > 0) {
|
if (targets.length > 0) {
|
||||||
const pluginHeader = document.createElement('h4');
|
// Create plugin table container
|
||||||
pluginHeader.textContent = `Plugin: ${pluginName}`;
|
const pluginTable = document.createElement('div');
|
||||||
fillTargetsList.appendChild(pluginHeader);
|
pluginTable.className = 'fill-plugin-table';
|
||||||
|
|
||||||
|
// 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 => {
|
targets.forEach(target => {
|
||||||
const item = document.createElement('div');
|
const row = document.createElement('tr');
|
||||||
item.className = 'fill-target-item';
|
row.innerHTML = `
|
||||||
const checkbox = document.createElement('input');
|
<td>
|
||||||
checkbox.type = 'checkbox';
|
<input type="checkbox" class="fill-target-checkbox"
|
||||||
checkbox.checked = true;
|
data-plugin="${pluginName}"
|
||||||
checkbox.id = `target-${pluginName}-${target.triplet}`;
|
data-target='${JSON.stringify(target)}'
|
||||||
checkbox.dataset.pluginName = pluginName;
|
checked>
|
||||||
checkbox.dataset.targetTriplet = target.triplet;
|
</td>
|
||||||
checkbox.dataset.targetInfo = JSON.stringify(target);
|
<td><strong>${target.triplet}</strong></td>
|
||||||
|
<td>${target.arch}</td>
|
||||||
const label = document.createElement('label');
|
<td><span class="target-type ${target.type}">${target.type}</span></td>
|
||||||
label.htmlFor = checkbox.id;
|
<td>${target.details?.compiler || 'N/A'} ${target.details?.compiler_version || ''}</td>
|
||||||
label.textContent = `${target.triplet} (${target.type})`;
|
`;
|
||||||
|
tbody.appendChild(row);
|
||||||
item.appendChild(checkbox);
|
|
||||||
item.appendChild(label);
|
|
||||||
fillTargetsList.appendChild(item);
|
|
||||||
});
|
});
|
||||||
|
table.appendChild(tbody);
|
||||||
|
|
||||||
|
pluginTable.appendChild(table);
|
||||||
|
fillPluginsTables.appendChild(pluginTable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Reset view
|
|
||||||
fillModalBody.style.display = 'block';
|
|
||||||
fillProgressView.style.display = 'none';
|
|
||||||
}
|
|
||||||
|
|
||||||
startFillButton.addEventListener('click', async () => {
|
// Add event listeners for select all functionality
|
||||||
|
setupFillTargetEventListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 selectedTargets = {};
|
||||||
const checkboxes = fillTargetsList.querySelectorAll('input[type="checkbox"]:checked');
|
const checkboxes = document.querySelectorAll('.fill-target-checkbox:checked');
|
||||||
|
|
||||||
if (checkboxes.length === 0) {
|
if (checkboxes.length === 0) {
|
||||||
showModal('Info', 'No targets selected to fill.');
|
showModal('Info', 'No targets selected to fill.');
|
||||||
@@ -175,88 +282,64 @@ function setupEventListeners() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
checkboxes.forEach(cb => {
|
checkboxes.forEach(cb => {
|
||||||
const pluginName = cb.dataset.pluginName;
|
const pluginName = cb.dataset.plugin;
|
||||||
|
const target = JSON.parse(cb.dataset.target);
|
||||||
if (!selectedTargets[pluginName]) {
|
if (!selectedTargets[pluginName]) {
|
||||||
selectedTargets[pluginName] = [];
|
selectedTargets[pluginName] = [];
|
||||||
}
|
}
|
||||||
selectedTargets[pluginName].push(JSON.parse(cb.dataset.targetInfo));
|
selectedTargets[pluginName].push(target);
|
||||||
});
|
});
|
||||||
|
|
||||||
fillModalBody.style.display = 'none';
|
// Hide target selection and show progress
|
||||||
fillProgressView.style.display = 'block';
|
fillTargetsContent.classList.add('hidden');
|
||||||
fillModalTitle.textContent = 'Filling Bundle...';
|
fillProgressContainer.classList.remove('hidden');
|
||||||
populateFillProgressList(selectedTargets);
|
populateFillProgress(selectedTargets);
|
||||||
|
|
||||||
const result = await ipcRenderer.invoke('fill-bundle', {
|
const result = await ipcRenderer.invoke('fill-bundle', {
|
||||||
bundlePath: currentBundlePath,
|
bundlePath: currentBundlePath,
|
||||||
targetsToBuild: selectedTargets
|
targetsToBuild: selectedTargets
|
||||||
});
|
});
|
||||||
|
|
||||||
fillModalTitle.textContent = 'Fill Complete';
|
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
// A final error message if the whole process fails.
|
const errorItem = document.createElement('div');
|
||||||
const p = document.createElement('p');
|
errorItem.className = 'progress-item';
|
||||||
p.style.color = 'var(--error-color)';
|
errorItem.innerHTML = `
|
||||||
p.textContent = `Error: ${result.error}`;
|
<span class="progress-status failure">Error</span>
|
||||||
fillProgressList.appendChild(p);
|
<span>Fill process failed: ${result.error}</span>
|
||||||
|
`;
|
||||||
|
fillProgressContent.appendChild(errorItem);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function populateFillProgressList(plugins) {
|
// Create progress display for fill process
|
||||||
fillProgressList.innerHTML = '';
|
function populateFillProgress(selectedTargets) {
|
||||||
for (const [pluginName, targets] of Object.entries(plugins)) {
|
fillProgressContent.innerHTML = '';
|
||||||
|
|
||||||
|
for (const [pluginName, targets] of Object.entries(selectedTargets)) {
|
||||||
targets.forEach(target => {
|
targets.forEach(target => {
|
||||||
const item = document.createElement('div');
|
const progressItem = document.createElement('div');
|
||||||
item.className = 'fill-target-item';
|
progressItem.className = 'progress-item';
|
||||||
item.id = `progress-${pluginName}-${target.triplet}`;
|
progressItem.id = `progress-${pluginName}-${target.triplet}`;
|
||||||
|
progressItem.innerHTML = `
|
||||||
const indicator = document.createElement('div');
|
<span class="progress-status building">Building</span>
|
||||||
indicator.className = 'progress-indicator';
|
<span>${pluginName} - ${target.triplet}</span>
|
||||||
|
`;
|
||||||
const label = document.createElement('span');
|
fillProgressContent.appendChild(progressItem);
|
||||||
label.textContent = `${pluginName} - ${target.triplet}`;
|
|
||||||
|
|
||||||
item.appendChild(indicator);
|
|
||||||
item.appendChild(label);
|
|
||||||
fillProgressList.appendChild(item);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle progress updates from backend
|
||||||
ipcRenderer.on('fill-bundle-progress', (event, progress) => {
|
ipcRenderer.on('fill-bundle-progress', (event, progress) => {
|
||||||
console.log('Progress update:', progress);
|
|
||||||
if (typeof progress === 'object' && progress.status) {
|
if (typeof progress === 'object' && progress.status) {
|
||||||
const { status, plugin, target, message } = progress;
|
const { status, plugin, target } = progress;
|
||||||
const progressItem = document.getElementById(`progress-${plugin}-${target}`);
|
const progressItem = document.getElementById(`progress-${plugin}-${target}`);
|
||||||
if (progressItem) {
|
if (progressItem) {
|
||||||
const indicator = progressItem.querySelector('.progress-indicator');
|
const statusSpan = progressItem.querySelector('.progress-status');
|
||||||
indicator.className = 'progress-indicator'; // Reset classes
|
statusSpan.className = `progress-status ${status}`;
|
||||||
switch (status) {
|
statusSpan.textContent = status.charAt(0).toUpperCase() + status.slice(1);
|
||||||
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}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} 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');
|
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
|
// Reset to overview tab by default
|
||||||
switchTab('overview-tab');
|
switchTab('overview-tab');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -418,6 +418,166 @@ body {
|
|||||||
display: none !important;
|
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 Modal */
|
||||||
.save-options {
|
.save-options {
|
||||||
display: flex;
|
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
|
# No fallback to print() - all output goes through callback only
|
||||||
|
|
||||||
staging_dir = Path(tempfile.mkdtemp(prefix="fourdst_fill_"))
|
staging_dir = Path(tempfile.mkdtemp(prefix="fourdst_fill_"))
|
||||||
|
successful_builds = 0
|
||||||
|
failed_builds = 0
|
||||||
|
build_details = []
|
||||||
|
|
||||||
try:
|
try:
|
||||||
report_progress("Unpacking bundle to temporary directory...")
|
report_progress("Unpacking bundle to temporary directory...")
|
||||||
with zipfile.ZipFile(bundle_path, 'r') as bundle_zip:
|
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)
|
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({
|
report_progress({
|
||||||
'status': 'success',
|
'status': 'success',
|
||||||
'plugin': plugin_name,
|
'plugin': plugin_name,
|
||||||
@@ -1034,6 +1046,14 @@ def fill_bundle(bundle_path: Path, targets_to_build: dict, progress_callback: Op
|
|||||||
})
|
})
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
failed_builds += 1
|
||||||
|
build_details.append({
|
||||||
|
'plugin': plugin_name,
|
||||||
|
'target': target_triplet,
|
||||||
|
'status': 'failure',
|
||||||
|
'error': str(e)
|
||||||
|
})
|
||||||
|
|
||||||
report_progress({
|
report_progress({
|
||||||
'status': 'failure',
|
'status': 'failure',
|
||||||
'plugin': plugin_name,
|
'plugin': plugin_name,
|
||||||
|
|||||||
Reference in New Issue
Block a user