mirror of
https://github.com/torlando-tech/pyxis.git
synced 2026-10-23 22:32:49 +00:00
Add firmware version picker to web flasher
Lets users choose between the latest dev build and tagged GitHub releases. The dropdown queries the GitHub Releases API on page load and swaps firmware fetch paths between Pages-relative and release-asset URLs. CI now attaches all 4 firmware files to releases (bootloader, partitions, boot_app0, firmware) so full installs work from any release version. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
1a6c9aee44
commit
fd6fb4bcda
2 files changed
+138
-20
No files matched your search
@@ -102,6 +102,9 @@ jobs:
|
||||
tag_name: ${{ steps.version.outputs.VERSION }}
|
||||
name: ${{ steps.version.outputs.VERSION }}
|
||||
files: |
|
||||
docs/flasher/firmware/bootloader.bin
|
||||
docs/flasher/firmware/partitions.bin
|
||||
docs/flasher/firmware/boot_app0.bin
|
||||
docs/flasher/firmware/firmware.bin
|
||||
generate_release_notes: true
|
||||
draft: true
|
||||
+135
-20
@@ -175,6 +175,44 @@
|
||||
}
|
||||
.warning strong { color: var(--warning); }
|
||||
|
||||
.version-select-group {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.version-select-group label {
|
||||
display: block;
|
||||
color: var(--muted);
|
||||
font-size: 0.875rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.version-select-group select {
|
||||
width: 100%;
|
||||
padding: 0.625rem 1rem;
|
||||
background: rgba(0,0,0,0.3);
|
||||
color: var(--text);
|
||||
border: 1px solid rgba(255,255,255,0.15);
|
||||
border-radius: 8px;
|
||||
font-family: inherit;
|
||||
font-size: 1rem;
|
||||
cursor: pointer;
|
||||
appearance: none;
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='%2394a3b8' viewBox='0 0 16 16'%3E%3Cpath d='M8 11L3 6h10z'/%3E%3C/svg%3E");
|
||||
background-repeat: no-repeat;
|
||||
background-position: right 1rem center;
|
||||
}
|
||||
|
||||
.version-select-group select:focus {
|
||||
outline: none;
|
||||
border-color: var(--primary);
|
||||
}
|
||||
|
||||
.version-note {
|
||||
font-size: 0.8rem;
|
||||
color: var(--warning);
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
footer {
|
||||
text-align: center;
|
||||
margin-top: 2rem;
|
||||
@@ -202,6 +240,14 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="version-select-group">
|
||||
<label for="version-select">Firmware Version</label>
|
||||
<select id="version-select">
|
||||
<option value="latest">Latest (loading...)</option>
|
||||
</select>
|
||||
<div id="version-note" class="version-note hidden"></div>
|
||||
</div>
|
||||
|
||||
<div class="checkbox-group">
|
||||
<input type="checkbox" id="erase-checkbox">
|
||||
<label for="erase-checkbox">Full install (required for first install, erases settings & messages)</label>
|
||||
@@ -266,18 +312,29 @@
|
||||
const progressContainer = document.getElementById('progress-container');
|
||||
const progressFill = document.getElementById('progress-fill');
|
||||
const progressText = document.getElementById('progress-text');
|
||||
const versionSelect = document.getElementById('version-select');
|
||||
const versionNote = document.getElementById('version-note');
|
||||
|
||||
const FIRMWARE_FILES = {
|
||||
full: [
|
||||
{ offset: 0x0, name: 'bootloader.bin', path: 'firmware/bootloader.bin' },
|
||||
{ offset: 0x8000, name: 'partitions.bin', path: 'firmware/partitions.bin' },
|
||||
{ offset: 0xe000, name: 'boot_app0.bin', path: 'firmware/boot_app0.bin' },
|
||||
{ offset: 0x10000, name: 'firmware.bin', path: 'firmware/firmware.bin' }
|
||||
],
|
||||
update: [
|
||||
{ offset: 0x10000, name: 'firmware.bin', path: 'firmware/firmware.bin' }
|
||||
]
|
||||
};
|
||||
const REQUIRED_FULL_ASSETS = ['bootloader.bin', 'partitions.bin', 'boot_app0.bin', 'firmware.bin'];
|
||||
|
||||
// Current firmware path prefix — empty string for relative (latest), or a release download URL
|
||||
let firmwarePathPrefix = '';
|
||||
// Track whether the selected release has all assets for full install
|
||||
let releaseHasFullAssets = true;
|
||||
|
||||
function makeFirmwareFiles(prefix) {
|
||||
return {
|
||||
full: [
|
||||
{ offset: 0x0, name: 'bootloader.bin', path: prefix + 'bootloader.bin' },
|
||||
{ offset: 0x8000, name: 'partitions.bin', path: prefix + 'partitions.bin' },
|
||||
{ offset: 0xe000, name: 'boot_app0.bin', path: prefix + 'boot_app0.bin' },
|
||||
{ offset: 0x10000, name: 'firmware.bin', path: prefix + 'firmware.bin' }
|
||||
],
|
||||
update: [
|
||||
{ offset: 0x10000, name: 'firmware.bin', path: prefix + 'firmware.bin' }
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
function log(message, type = 'info') {
|
||||
logDiv.classList.remove('hidden');
|
||||
@@ -300,10 +357,72 @@
|
||||
progressText.textContent = text;
|
||||
}
|
||||
|
||||
// Identical to rnode-flasher's approach
|
||||
// Version selector logic
|
||||
let latestVersion = 'dev';
|
||||
|
||||
async function loadVersions() {
|
||||
// Load deployed manifest version first
|
||||
try {
|
||||
const manifest = await fetch('manifest-install.json').then(r => r.json());
|
||||
latestVersion = manifest.version;
|
||||
document.getElementById('firmware-version').textContent = latestVersion;
|
||||
} catch (e) {}
|
||||
|
||||
// Update the "Latest" option text
|
||||
versionSelect.options[0].textContent = `Latest (${latestVersion})`;
|
||||
|
||||
// Fetch GitHub releases
|
||||
try {
|
||||
const resp = await fetch('https://api.github.com/repos/torlando-tech/pyxis/releases');
|
||||
if (!resp.ok) return;
|
||||
const releases = await resp.json();
|
||||
|
||||
for (const release of releases) {
|
||||
if (release.draft || release.prerelease) continue;
|
||||
const assetNames = release.assets.map(a => a.name);
|
||||
if (!assetNames.includes('firmware.bin')) continue;
|
||||
|
||||
const option = document.createElement('option');
|
||||
const hasAll = REQUIRED_FULL_ASSETS.every(f => assetNames.includes(f));
|
||||
option.value = release.tag_name;
|
||||
option.textContent = release.tag_name + (release.name && release.name !== release.tag_name ? ` — ${release.name}` : '');
|
||||
option.dataset.downloadUrl = `https://github.com/torlando-tech/pyxis/releases/download/${release.tag_name}/`;
|
||||
option.dataset.hasFullAssets = hasAll ? 'true' : 'false';
|
||||
versionSelect.appendChild(option);
|
||||
}
|
||||
} catch (e) {
|
||||
// API failures are non-critical — user can still flash latest
|
||||
}
|
||||
}
|
||||
|
||||
versionSelect.addEventListener('change', function() {
|
||||
const selected = this.options[this.selectedIndex];
|
||||
if (this.value === 'latest') {
|
||||
firmwarePathPrefix = 'firmware/';
|
||||
releaseHasFullAssets = true;
|
||||
document.getElementById('firmware-version').textContent = latestVersion;
|
||||
} else {
|
||||
firmwarePathPrefix = selected.dataset.downloadUrl;
|
||||
releaseHasFullAssets = selected.dataset.hasFullAssets === 'true';
|
||||
document.getElementById('firmware-version').textContent = this.value;
|
||||
}
|
||||
|
||||
// Handle full-install availability
|
||||
if (!releaseHasFullAssets) {
|
||||
eraseCheckbox.checked = false;
|
||||
eraseCheckbox.disabled = true;
|
||||
versionNote.textContent = 'Full install unavailable for this release (missing bootloader/partition assets)';
|
||||
versionNote.classList.remove('hidden');
|
||||
} else {
|
||||
eraseCheckbox.disabled = false;
|
||||
versionNote.classList.add('hidden');
|
||||
}
|
||||
});
|
||||
|
||||
async function flash() {
|
||||
const useFullInstall = eraseCheckbox.checked;
|
||||
const files = useFullInstall ? FIRMWARE_FILES.full : FIRMWARE_FILES.update;
|
||||
const fw = makeFirmwareFiles(firmwarePathPrefix);
|
||||
const files = useFullInstall ? fw.full : fw.update;
|
||||
let transport = null;
|
||||
|
||||
flashBtn.disabled = true;
|
||||
@@ -393,13 +512,9 @@
|
||||
|
||||
flashBtn.addEventListener('click', flash);
|
||||
|
||||
// Load manifest version
|
||||
fetch('manifest-install.json')
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
document.getElementById('firmware-version').textContent = data.version;
|
||||
})
|
||||
.catch(() => {});
|
||||
// Initialize: set default prefix and load versions
|
||||
firmwarePathPrefix = 'firmware/';
|
||||
loadVersions();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in new issue
Block a user