@@ -217,7 +217,7 @@ image:
217217 <option>Loading...</option>
218218 </select>
219219 <div id="releases-container" style="display: none;">
220- <p></p><p></p><label >Select one to load:</label >
220+ <p></p><p></p><p >Select one to load:</p >
221221 <div id="release-list"></div>
222222 <p></p><p></p><sup>✶</sup>Older Releases may not be fully compatible with the Selector
223223 </div>
@@ -233,7 +233,7 @@ image:
233233 <label for="broadenSearch"><i class="icon fas fa-magnifying-glass-plus"></i> Broaden Search Results?</label>
234234 </div>
235235 <div class="select-container">
236- <select id="broadenSearch" onchange="updateCandidates()" >
236+ <select id="broadenSearch">
237237 <option value="Yes" title="Use broader filtering">Yes</option>
238238 <option value="No" title="Use strict filtering">No</option>
239239 </select>
@@ -244,7 +244,7 @@ image:
244244 <label for="model"><i class="icon fas fa-cubes"></i> Model:</label>
245245 </div>
246246 <div class="select-container">
247- <select id="model" onchange="updateModelSelections()" >
247+ <select id="model">
248248 <option value="none" title="May help to choose a model first">--Select Model--</option>
249249 <option value="Aquila" title="Aquila OG/X2">Aquila</option>
250250 <option value="Aquila X3" title="Aquila X3/S3 Induction Probe">Aquila X3/S3</option>
@@ -259,7 +259,7 @@ image:
259259 <label for="proUIExtraFeatures"><i class="icon fas fa-shield-halved"></i> ProUI Extra Features:</label>
260260 </div>
261261 <div class="select-container">
262- <select id="proUIExtraFeatures" onchange="updateCandidates()" >
262+ <select id="proUIExtraFeatures">
263263 <option value="-ProUI" title="ProUI-EX">Yes</option>
264264 <option value="" title="No ProUI Extra Features">No</option>
265265 </select>
@@ -270,7 +270,7 @@ image:
270270 <label for="screen"><i class="icon fas fa-mobile-screen-button"></i> LCD Display:</label>
271271 </div>
272272 <div class="select-container">
273- <select id="screen" onchange="updateCandidates()" >
273+ <select id="screen">
274274 <option value="" title="No specific display selected">--Select--</option>
275275 <option value="DWIN">DWIN</option>
276276 <option value="TJC-" title="TJC-">TJC</option>
@@ -283,7 +283,7 @@ image:
283283 <label for="type"><i class="icon fas fa-microchip"></i> Board Type:</label>
284284 </div>
285285 <div class="select-container">
286- <select id="type" onchange="updateCandidates()" >
286+ <select id="type">
287287 <option value="" title="No specific board type">--Select--</option>
288288 <option value="_GD32" title="_GD32">GD32</option>
289289 <option value="_N32" title="_N32">N32</option>
@@ -302,7 +302,7 @@ image:
302302 <label for="features"><i class="icon fas fa-bars"></i> Features:</label>
303303 </div>
304304 <div class="select-container">
305- <select id="features" onchange="updateCandidates()" >
305+ <select id="features">
306306 <option value="none" title="No specific features">--Select--</option>
307307 <option value="" title="Bed Probe Only">CR/3D/BL-Touch Only</option>
308308 <option value="_BMP" title="_BMP">BIQU MicroProbe V2</option>
@@ -318,7 +318,7 @@ image:
318318 <label for="secondaryFeatures"><i class="icon fas fa-bars-staggered"></i> Secondary Features:</label>
319319 </div>
320320 <div class="select-container">
321- <select id="secondaryFeatures" onchange="updateCandidates()" >
321+ <select id="secondaryFeatures">
322322 <option value="" title="No specific secondary features">--Select--</option>
323323 <option value="_BMP" title="_BMP">BIQU MicroProbe V2</option>
324324 <option value="_IND" title="_IND">Induction Probe</option>
@@ -333,7 +333,7 @@ image:
333333 <label for="leveling"><i class="icon fas fa-layer-group"></i> Leveling:</label>
334334 </div>
335335 <div class="select-container">
336- <select id="leveling" onchange="updateCandidates()" >
336+ <select id="leveling">
337337 <option value="" title="Choose bed leveling">--Select--</option>
338338 <option value="_UBL" title="_UBL">Unified Bed Leveling</option>
339339 <option value="_BLT" title="_BLT">Bilinear Bed Leveling</option>
@@ -347,7 +347,7 @@ image:
347347 <label for="options"><i class="icon fas fa-gears"></i> Options:</label>
348348 </div>
349349 <div class="select-container">
350- <select id="options" onchange="updateCandidates(); handleOptionsChange();" >
350+ <select id="options">
351351 <option value="" title="No specific options">--Select--</option>
352352 <option value="-MPC" title="-MPC">MPC</option>
353353 <option value="-IS" title="-IS">Input Shaping</option>
@@ -361,7 +361,7 @@ image:
361361 <label for="secondaryOptions"><i class="icon fas fa-gear"></i> Secondary Options:</label>
362362 </div>
363363 <div class="select-container">
364- <select id="secondaryOptions" onchange="updateCandidates(); handleOptionsChange();" >
364+ <select id="secondaryOptions">
365365 <option value="" title="No specific secondary options">--Select--</option>
366366 <option value="-MPC" title="-MPC">MPC</option>
367367 <option value="-IS" title="-IS">Input Shaping</option>
@@ -387,6 +387,10 @@ image:
387387 <span class="icon2">🔄</span>
388388 </button>
389389 </div>
390+ <br>
391+ <hr>
392+ <p style="text-align: center; font-size: 1.1em;">If you find there is an error loading after many attempts, <br>
393+ you may have reached the <a href="#api-rate-limit-header">API limit</a>.</p>
390394 <hr>
391395 <div class="candidates-header-text">
392396 <a href="#" style="font-size: 26px;" class="icon fas fa-rectangle-list"></a><strong> Candidates:</strong><br>
@@ -551,7 +555,40 @@ image:
551555 <sup><sup>✶</sup>Reminder: Do not use `Input Shaping` (file suffix <i>-IS</i>) <b>unless</b> you do the necessary
552556 calibration.<p>
553557 Prints will be effected regardless. May require a special breakout board or test prints to calibrate.
558+ </p>
554559 </sup>
560+ <hr>
561+ <h2 id="api-rate-limit-header">GitHub API Rate Limit</h2>
562+ <h3>Enter your GitHub <i>Personal Access Token</i> :</h3>
563+ <input type="password" id="tokenInput" placeholder="Paste your token here" size="40" />
564+ <button id="fetchBtn">Enter</button>
565+ <p>
566+ <h4>Setting a personal access token <strong>(PAT)</strong></h4>
567+ <p>Select one to create a new token:</p>
568+ <ol style="list-style-type: upper-roman;">
569+ <li><a href="https://github.com/settings/personal-access-tokens/new"><strong>Fine-Grained PAT</strong></a></li>
570+ <ul>
571+ <li><strong>Repository access:</strong></li>
572+ <ul>
573+ <li>
574+ <em>Public repositories</em>
575+ <span style="padding-left: 40px; font-size: 0.8em;">Read-only access to public repositories.</span>
576+ </li>
577+ </ul>
578+ </ul>
579+ <li><a href="https://github.com/settings/tokens/new"><strong>Classic PAT</strong></a></li>
580+ <ul>
581+ <li><strong>Repository Permissions:</strong></li>
582+ <ul>
583+ <li>
584+ <em>read:public_key</em>
585+ <span style="padding-left: 40px; font-size: 0.8em;">Read user public keys</span>
586+ </li>
587+ </ul>
588+ </ul>
589+ </ol>
590+ </p>
591+ <pre id="output"></pre>
555592 <script>
556593 document.addEventListener("DOMContentLoaded", () => {
557594 const selectMonth = document.getElementById("month-select");
@@ -567,7 +604,9 @@ image:
567604 let releaseTag = "latest";
568605 const repoUrl = '/assets/data/releases.json';
569606 let allReleasesData = [];
607+ let cachedLatestTagName = null;
570608 let proUIExtraFeaturesWasForcedToNo = false;
609+ const getToken = () => document.getElementById('tokenInput').value.trim();
571610 async function fetchAllReleases(url) {
572611 try {
573612 const response = await fetch(url);
@@ -583,7 +622,16 @@ image:
583622 }
584623 async function fetchLatestReleaseDetails() {
585624 try {
586- const response = await fetch('https://api.github.com/repos/classicrocker883/MRiscoCProUI/releases/latest');
625+ const tokenValue = getToken();
626+ if (!tokenValue) {
627+ console.warn("No token provided for fetchLatestReleaseDetails.");
628+ const response = await fetch('https://api.github.com/repos/classicrocker883/MRiscoCProUI/releases/latest');
629+ }
630+ const response = await fetch('https://api.github.com/repos/classicrocker883/MRiscoCProUI/releases/latest', {
631+ headers: {
632+ 'Authorization': `token ${tokenValue}`
633+ }
634+ });
587635 if (!response.ok) {
588636 throw new Error(`HTTP error! status: ${response.status}`);
589637 }
@@ -648,11 +696,9 @@ image:
648696 const selectedScreen = document.getElementById("screen").value;
649697 let actualBaseTagName = currentBaseTag;
650698 if (currentBaseTag === "latest") {
651- const latestRelease = await fetchLatestReleaseDetails();
652- if (latestRelease) {
653- actualBaseTagName = latestRelease.tag_name;
654- } else {
655- console.error("Could not determine actual latest release tag.");
699+ actualBaseTagName = cachedLatestTagName;
700+ if (!actualBaseTagName) {
701+ console.error("Could not determine actual latest release tag from cache.");
656702 selectedReleaseTagDiv.textContent = "Error loading tag";
657703 totalDownloads.innerHTML = `<label><img alt='GitHub Downloads (all assets)' src='https://img.shields.io/github/downloads/classicrocker883/MRiscoCProUI/latest/total'> - Total</label>`;
658704 return;
@@ -711,7 +757,15 @@ image:
711757 }
712758 async function initializeDropdowns() {
713759 try {
760+ const latestReleaseFromApi = await fetchLatestReleaseDetails();
761+ if (latestReleaseFromApi) {
762+ cachedLatestTagName = latestReleaseFromApi.tag_name;
763+ }
714764 allReleasesData = await fetchAllReleases(repoUrl);
765+ if (!cachedLatestTagName && allReleasesData.length > 0) {
766+ allReleasesData.sort((a, b) => new Date(b.published_at) - new Date(a.published_at));
767+ cachedLatestTagName = allReleasesData[0].tag_name;
768+ }
715769 if (allReleasesData.length > 0) {
716770 const releaseMonths = getReleaseMonths(allReleasesData);
717771 populateMonthOptions(releaseMonths);
@@ -737,13 +791,11 @@ image:
737791 return { assets: selectedRelease.assets || [], actualTagNameUsed: currentReleaseTag };
738792 } else {
739793 console.error("Selected specific release not found in local data:", currentReleaseTag);
740- const latestRelease = await fetchLatestReleaseDetails();
741- actualBaseTagName = latestRelease ? latestRelease.tag_name : null;
742- console.warn("Falling back to latest release as explicit tag was not found.");
794+ actualBaseTagName = cachedLatestTagName;
795+ console.warn("Falling back to cached latest release as explicit tag was not found.");
743796 }
744797 } else {
745- const latestRelease = await fetchLatestReleaseDetails();
746- actualBaseTagName = latestRelease ? latestRelease.tag_name : null;
798+ actualBaseTagName = cachedLatestTagName;
747799 }
748800 if (!actualBaseTagName) {
749801 console.error("Could not determine actual base release tag for fetching assets.");
@@ -774,21 +826,33 @@ image:
774826 potentialApiTags = [...new Set(potentialApiTags)];
775827 console.log("Attempting to fetch assets with tags (in order of preference):", potentialApiTags);
776828 for (const tagToTry of potentialApiTags) {
829+ const tokenValue = getToken();
830+ const headers = tokenValue ? { 'Authorization': `token ${tokenValue}` } : {};
777831 const apiUrl = `https://api.github.com/repos/classicrocker883/MRiscoCProUI/releases/tags/${tagToTry}`;
778832 try {
779- const response = await fetch(apiUrl);
833+ const response = tokenValue
834+ ? await fetch(apiUrl, { headers: headers })
835+ : await fetch(apiUrl);
780836 if (response.ok) {
781837 const data = await response.json();
782838 if (data.assets && data.assets.length > 0) {
783- console.log(`Successfully fetched assets for tag: ${tagToTry}`);
839+ console.log(`Successfully fetched assets from Live API for tag: ${tagToTry}`);
784840 return { assets: data.assets, actualTagNameUsed: tagToTry };
785841 }
786842 }
787843 } catch (error) {
788- console.warn(`Attempting tag ${tagToTry} failed or returned no assets. Trying next...`, error );
844+ console.warn(`Live API attempt for tag ${tagToTry} failed. Trying local cache as fallback.` );
789845 }
790846 }
791- console.error("No release assets found for the selected model and revisions after all attempts.");
847+ console.warn("Live API failed for all potential tags. Falling back to local releases.json for assets.");
848+ for (const tagToTry of potentialApiTags) {
849+ const localRelease = allReleasesData.find(release => release.tag_name === tagToTry);
850+ if (localRelease && localRelease.assets && localRelease.assets.length > 0) {
851+ console.log(`Successfully fetched assets from local cache for tag: ${tagToTry}`);
852+ return { assets: localRelease.assets, actualTagNameUsed: tagToTry };
853+ }
854+ }
855+ console.error("No release assets found in Live API or local data for the determined tag and selected model.");
792856 return { assets: [], actualTagNameUsed: null };
793857 }
794858 async function updateCandidates() {
@@ -1116,20 +1180,35 @@ image:
11161180 deprecatedPopup.style.display = "none";
11171181 }
11181182 }
1183+ document.getElementById('fetchBtn').addEventListener('click', async () => {
1184+ const token = document.getElementById('tokenInput').value.trim();
1185+ document.getElementById('output').textContent = '';
1186+ if (token) {
1187+ alert('GitHub Token has been applied for subsequent firmware selector searches and release fetching.');
1188+ } else {
1189+ alert('Token input cleared. Searching will proceed without GitHub API authorization.');
1190+ }
1191+ await updateCandidates();
1192+ });
11191193 document.getElementById("features").addEventListener("change", () => {
11201194 document.getElementById("secondaryFeatures").value = "";
1195+ updateCandidates();
11211196 });
11221197 document.getElementById("options").addEventListener("change", () => {
11231198 document.getElementById("secondaryOptions").value = "";
11241199 if (releaseTag === "latest") {
11251200 handleOptionsChange();
11261201 }
1202+ updateCandidates();
11271203 });
11281204 document.getElementById("secondaryOptions").addEventListener("change", () => {
11291205 if (releaseTag === "latest") {
11301206 handleOptionsChange();
11311207 }
1208+ updateCandidates();
11321209 });
1210+ document.getElementById("model").addEventListener("change", updateModelSelections);
1211+ document.querySelectorAll('#broadenSearch, #proUIExtraFeatures, #screen, #type, #secondaryFeatures, #leveling').forEach(input => input.addEventListener("change", updateCandidates));
11331212 selectMonth.addEventListener("change", async (event) => {
11341213 const selectedMonth = event.target.value;
11351214 if (selectedMonth === "latest") {
@@ -1149,13 +1228,10 @@ image:
11491228 resetButton.addEventListener("mousedown", () => resetButton.style.animationPlayState = "running");
11501229 resetButton.addEventListener("mouseup", () => resetButton.style.animationPlayState = "paused");
11511230 resetButton.addEventListener("click", resetSelections);
1152- document.getElementById("model").addEventListener("change", updateModelSelections);
1153- document.querySelectorAll('#broadenSearch, #proUIExtraFeatures, #screen, #type, #features, #secondaryFeatures, #leveling, #options, #secondaryOptions').forEach(input => input.addEventListener("change", updateCandidates));
11541231 okButton.addEventListener("click", () => {
11551232 deprecatedPopup.style.display = "none";
11561233 });
11571234 initializeDropdowns();
11581235 });
11591236 </script>
1160- </body >
11611237</html >
0 commit comments