|
@@ -48,6 +48,12 @@
|
|
|
.stat .k{color:var(--mut);font-size:11px;text-transform:uppercase;letter-spacing:.05em}
|
|
.stat .k{color:var(--mut);font-size:11px;text-transform:uppercase;letter-spacing:.05em}
|
|
|
.stat .v{font-size:18px;margin-top:2px}
|
|
.stat .v{font-size:18px;margin-top:2px}
|
|
|
.modes{display:flex;gap:10px;flex-wrap:wrap}
|
|
.modes{display:flex;gap:10px;flex-wrap:wrap}
|
|
|
|
|
+ /* back4 chords: face buttons in the left column, D-pad in the right; auto-fit collapses to one column on a
|
|
|
|
|
+ narrow viewport, and also when the D-pad column is hidden on pre-v18 firmware (display:none drops the item) */
|
|
|
|
|
+ .chord-cols{display:grid;grid-template-columns:repeat(auto-fit,minmax(290px,1fr));gap:0 24px}
|
|
|
|
|
+ .chord-cols .row{margin:10px 0}
|
|
|
|
|
+ .chord-cols .row label{flex:0 0 108px}
|
|
|
|
|
+ .colhead{color:var(--mut);font-size:11px;text-transform:uppercase;letter-spacing:.06em;margin:2px 0 0}
|
|
|
.note{color:var(--mut);font-size:12px;margin-top:10px}
|
|
.note{color:var(--mut);font-size:12px;margin-top:10px}
|
|
|
#log{font:12px ui-monospace,Menlo,monospace;color:var(--mut);white-space:pre-wrap;margin-top:8px;max-height:140px;overflow:auto}
|
|
#log{font:12px ui-monospace,Menlo,monospace;color:var(--mut);white-space:pre-wrap;margin-top:8px;max-height:140px;overflow:auto}
|
|
|
.hide{display:none}
|
|
.hide{display:none}
|
|
@@ -168,17 +174,23 @@
|
|
|
<div class="card">
|
|
<div class="card">
|
|
|
<h2>Back4 chords (face + D-pad)</h2>
|
|
<h2>Back4 chords (face + D-pad)</h2>
|
|
|
<p class="note" style="margin-top:0">Hold all four back paddles (L4+R4+L5+R5) plus a face button or D-pad direction to switch mode without the WebUI. <strong>A is always Steam</strong> — that is the way back out of any mode. B / X / Y and all four D-pad directions are configurable below (defaults: B=Lizard, X=Xbox, Y=Switch Pro).</p>
|
|
<p class="note" style="margin-top:0">Hold all four back paddles (L4+R4+L5+R5) plus a face button or D-pad direction to switch mode without the WebUI. <strong>A is always Steam</strong> — that is the way back out of any mode. B / X / Y and all four D-pad directions are configurable below (defaults: B=Lizard, X=Xbox, Y=Switch Pro).</p>
|
|
|
- <div class="row"><label>back4 + B</label><select class="chord" data-i="0"></select></div>
|
|
|
|
|
- <div class="row"><label>back4 + X</label><select class="chord" data-i="1"></select></div>
|
|
|
|
|
- <div class="row"><label>back4 + Y</label><select class="chord" data-i="2"></select></div>
|
|
|
|
|
- <!-- D-pad chords (firmware >= v18). Unlike the face selects these offer the game/clean modes too: those
|
|
|
|
|
- modes have no WebUSB interface, so a chord is the only way IN, and back4+A is always the way out.
|
|
|
|
|
- Older firmware has no D-pad chords at all -> the whole block hides (see #dpadChords in applyBlob). -->
|
|
|
|
|
- <div id="dpadChords" class="hide">
|
|
|
|
|
- <div class="row"><label>back4 + Left</label><select class="chordD" data-i="0"></select></div>
|
|
|
|
|
- <div class="row"><label>back4 + Up</label><select class="chordD" data-i="1"></select></div>
|
|
|
|
|
- <div class="row"><label>back4 + Right</label><select class="chordD" data-i="2"></select></div>
|
|
|
|
|
- <div class="row"><label>back4 + Down</label><select class="chordD" data-i="3"></select></div>
|
|
|
|
|
|
|
+ <div class="chord-cols">
|
|
|
|
|
+ <div>
|
|
|
|
|
+ <div class="colhead">Face buttons</div>
|
|
|
|
|
+ <div class="row"><label>back4 + B</label><select class="chord" data-i="0"></select></div>
|
|
|
|
|
+ <div class="row"><label>back4 + X</label><select class="chord" data-i="1"></select></div>
|
|
|
|
|
+ <div class="row"><label>back4 + Y</label><select class="chord" data-i="2"></select></div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <!-- D-pad chords (firmware >= v18). Unlike the face selects these offer the game/clean modes too: those
|
|
|
|
|
+ modes have no WebUSB interface, so a chord is the only way IN, and back4+A is always the way out.
|
|
|
|
|
+ Older firmware has no D-pad chords at all -> the whole column hides (see #dpadChords in applyBlob). -->
|
|
|
|
|
+ <div id="dpadChords" class="hide">
|
|
|
|
|
+ <div class="colhead">D-pad</div>
|
|
|
|
|
+ <div class="row"><label>back4 + Left</label><select class="chordD" data-i="0"></select></div>
|
|
|
|
|
+ <div class="row"><label>back4 + Up</label><select class="chordD" data-i="1"></select></div>
|
|
|
|
|
+ <div class="row"><label>back4 + Right</label><select class="chordD" data-i="2"></select></div>
|
|
|
|
|
+ <div class="row"><label>back4 + Down</label><select class="chordD" data-i="3"></select></div>
|
|
|
|
|
+ </div>
|
|
|
</div>
|
|
</div>
|
|
|
<p id="dpadOld" class="note hide">This puck's firmware predates the D-pad chords — update it to configure them.</p>
|
|
<p id="dpadOld" class="note hide">This puck's firmware predates the D-pad chords — update it to configure them.</p>
|
|
|
</div>
|
|
</div>
|
|
@@ -243,18 +255,15 @@
|
|
|
<p class="note">Right-pad cursor in Steam-mode lizard (Steam closed) and in Xbox mode. Sensitivity is a divisor — lower = faster pointer. Glide = how long the cursor coasts after a flick.</p>
|
|
<p class="note">Right-pad cursor in Steam-mode lizard (Steam closed) and in Xbox mode. Sensitivity is a divisor — lower = faster pointer. Glide = how long the cursor coasts after a flick.</p>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
- <div class="card">
|
|
|
|
|
- <h2>Rumble</h2>
|
|
|
|
|
- <div class="row"><label>Strength</label><input type="range" id="rumble" min="0" max="255" step="5"><span class="val" id="rumbleV"></span></div>
|
|
|
|
|
- <p class="note">Scales rumble amplitude across all modes (100% = native, 200% = double, 0% = off).</p>
|
|
|
|
|
- </div>
|
|
|
|
|
-
|
|
|
|
|
<div class="card">
|
|
<div class="card">
|
|
|
<h2>Switch Pro motion</h2>
|
|
<h2>Switch Pro motion</h2>
|
|
|
- <div class="row"><label>Report rate</label><select id="swRate"></select></div>
|
|
|
|
|
- <p class="note" style="margin:0 0 6px">Full rate (~250 Hz, default) is lowest-latency. 120 Hz is low-latency and drift-free. 66 Hz is a genuine-cadence compatibility fallback — drop to it only if your console rejects the faster rates.</p>
|
|
|
|
|
- <div class="row"><label>Gyro scale</label><select id="swGyro"></select></div>
|
|
|
|
|
- <p class="note">Gyro sensitivity multiplier for Switch Pro mode (1.0× = unchanged).</p>
|
|
|
|
|
|
|
+ <!-- Gyro mapping (firmware >= v19). Older firmware always uses the corrected mapping and has no field
|
|
|
|
|
+ to set -> the whole block hides (see #swGyroMapBlock in applyBlob). -->
|
|
|
|
|
+ <div id="swGyroMapBlock">
|
|
|
|
|
+ <div class="row"><label>Gyro mapping</label><select id="swGyroMap"></select></div>
|
|
|
|
|
+ <p class="note">Corrected (default) trims the gyro so Switch Pro mode matches a genuine Pro Controller and Steam mode (roll ×0.8, pitch/yaw ×0.9). Legacy sends the raw axes untrimmed — the pre-fix behavior, if you preferred the faster feel or tuned your in-game sensitivity around it.</p>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <p id="swGyroOld" class="note hide">This puck's firmware predates the gyro-mapping setting — update it to configure it.</p>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
<div class="card wide">
|
|
<div class="card wide">
|
|
@@ -662,12 +671,8 @@ for(const sel of document.querySelectorAll("select.chordD")){
|
|
|
MODE_NAMES.forEach((n,i)=>{
|
|
MODE_NAMES.forEach((n,i)=>{
|
|
|
const o=document.createElement("option"); o.value=i; o.textContent=n; sel.appendChild(o); });
|
|
const o=document.createElement("option"); o.value=i; o.textContent=n; sel.appendChild(o); });
|
|
|
}
|
|
}
|
|
|
-{ const sel=document.getElementById("swRate");
|
|
|
|
|
- for(const [lbl,v] of [["Full rate (~250 Hz, default)",2],["120 Hz",1],["66 Hz (compatibility)",0]]){
|
|
|
|
|
- const o=document.createElement("option"); o.value=v; o.textContent=lbl; sel.appendChild(o); }
|
|
|
|
|
-}
|
|
|
|
|
-{ const sel=document.getElementById("swGyro");
|
|
|
|
|
- for(const [lbl,v] of [["0.5×",5],["1.0×",10],["1.5×",15],["2.0×",20],["2.5×",25],["3.0×",30]]){
|
|
|
|
|
|
|
+{ const sel=document.getElementById("swGyroMap");
|
|
|
|
|
+ for(const [lbl,v] of [["Corrected (default)",0],["Legacy (untrimmed)",1]]){
|
|
|
const o=document.createElement("option"); o.value=v; o.textContent=lbl; sel.appendChild(o); }
|
|
const o=document.createElement("option"); o.value=v; o.textContent=lbl; sel.appendChild(o); }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -861,9 +866,11 @@ function applyBlob(p){
|
|
|
// the built-in default behavior, so the editor is shown only when the device is in Lizard mode.
|
|
// the built-in default behavior, so the editor is shown only when the device is in Lizard mode.
|
|
|
$("#lizardCard").style.display = (mode===3 && p[0]>=16)?"":"none";
|
|
$("#lizardCard").style.display = (mode===3 && p[0]>=16)?"":"none";
|
|
|
const battery=(p.length>36?p[36]:0);
|
|
const battery=(p.length>36?p[36]:0);
|
|
|
- const rumbleScale=(p.length>51?p[51]:200);
|
|
|
|
|
- const swProRate=(p.length>52?p[52]:2);
|
|
|
|
|
- const swGyro10=(p.length>53?p[53]:10);
|
|
|
|
|
|
|
+ // Switch Pro gyro mapping (protocol v19, firmware p[186] = payload p[184]). Older firmware has no such
|
|
|
|
|
+ // setting at all -> the select hides (see #swGyroMap below). p[51..53] are the reserved bytes that used to
|
|
|
|
|
+ // carry rumble strength / Switch report rate / Switch gyro scale, all removed settings.
|
|
|
|
|
+ const gyroMapCap=(p[0]>=19 && p.length>184);
|
|
|
|
|
+ const swGyroLegacy=gyroMapCap?p[184]:0;
|
|
|
const rssi=(p.length>37?p[37]:0);
|
|
const rssi=(p.length>37?p[37]:0);
|
|
|
|
|
|
|
|
// per-slot link status (protocol v8, p.length >= 73)
|
|
// per-slot link status (protocol v8, p.length >= 73)
|
|
@@ -1043,7 +1050,7 @@ function applyBlob(p){
|
|
|
updateVersionUI();
|
|
updateVersionUI();
|
|
|
updateFwGate(); // (re)evaluate the firmware-tab gate on every blob — version is at p[0]
|
|
updateFwGate(); // (re)evaluate the firmware-tab gate on every blob — version is at p[0]
|
|
|
checkUpdateNotice(); // needs BOTH the installed version (this blob) and relCache (fetched on connect)
|
|
checkUpdateNotice(); // needs BOTH the installed version (this blob) and relCache (fetched on connect)
|
|
|
- setSlider("mDiv",mDiv); setSlider("mFric",mFric); setSlider("rumble",rumbleScale);
|
|
|
|
|
|
|
+ setSlider("mDiv",mDiv); setSlider("mFric",mFric);
|
|
|
$("#persistMode").textContent = persistMode? "on":"off"; $("#persistMode").classList.toggle("active",!!persistMode);
|
|
$("#persistMode").textContent = persistMode? "on":"off"; $("#persistMode").classList.toggle("active",!!persistMode);
|
|
|
// land-all-0x87 experiment toggle (blob v14/v17, firmware p[181] -> JS p[179])
|
|
// land-all-0x87 experiment toggle (blob v14/v17, firmware p[181] -> JS p[179])
|
|
|
{ const la=(p.length>179?p[179]:0); $("#landAll87").textContent = la?"on":"off"; $("#landAll87").classList.toggle("active",!!la); }
|
|
{ const la=(p.length>179?p[179]:0); $("#landAll87").textContent = la?"on":"off"; $("#landAll87").classList.toggle("active",!!la); }
|
|
@@ -1071,14 +1078,16 @@ function applyBlob(p){
|
|
|
const amag=Math.round(Math.sqrt(ax*ax+ay*ay+az*az));
|
|
const amag=Math.round(Math.sqrt(ax*ax+ay*ay+az*az));
|
|
|
$("#stImu").textContent = `a=(${ax}, ${ay}, ${az}) |a|=${amag}`;
|
|
$("#stImu").textContent = `a=(${ax}, ${ay}, ${az}) |a|=${amag}`;
|
|
|
}
|
|
}
|
|
|
- { const sel=$("#swRate"); if(document.activeElement!==sel) sel.value=swProRate; }
|
|
|
|
|
- { const sel=$("#swGyro"); if(document.activeElement!==sel) sel.value=swGyro10; }
|
|
|
|
|
|
|
+ { const sel=$("#swGyroMap");
|
|
|
|
|
+ $("#swGyroMapBlock").classList.toggle("hide", !gyroMapCap);
|
|
|
|
|
+ $("#swGyroOld").classList.toggle("hide", gyroMapCap);
|
|
|
|
|
+ if(gyroMapCap && document.activeElement!==sel) sel.value=swGyroLegacy; }
|
|
|
for(const sel of document.querySelectorAll("select.chord")){ if(document.activeElement!==sel) sel.value=chord[+sel.dataset.i]; }
|
|
for(const sel of document.querySelectorAll("select.chord")){ if(document.activeElement!==sel) sel.value=chord[+sel.dataset.i]; }
|
|
|
$("#dpadChords").classList.toggle("hide", !dpadCap);
|
|
$("#dpadChords").classList.toggle("hide", !dpadCap);
|
|
|
$("#dpadOld").classList.toggle("hide", dpadCap);
|
|
$("#dpadOld").classList.toggle("hide", dpadCap);
|
|
|
if(dpadCap) for(const sel of document.querySelectorAll("select.chordD")){ if(document.activeElement!==sel) sel.value=chordD[+sel.dataset.i]; }
|
|
if(dpadCap) for(const sel of document.querySelectorAll("select.chordD")){ if(document.activeElement!==sel) sel.value=chordD[+sel.dataset.i]; }
|
|
|
}
|
|
}
|
|
|
-function fmtSlider(id,v){ return id==="rumble" ? v+"%" : id==="hapBlockS" ? v+" s" : ""+v; }
|
|
|
|
|
|
|
+function fmtSlider(id,v){ return id==="hapBlockS" ? v+" s" : ""+v; }
|
|
|
function setSlider(id,v){ const el=$("#"+id); if(document.activeElement!==el){ el.value=v; } $("#"+id+"V").textContent=fmtSlider(id,el.value); }
|
|
function setSlider(id,v){ const el=$("#"+id); if(document.activeElement!==el){ el.value=v; } $("#"+id+"V").textContent=fmtSlider(id,el.value); }
|
|
|
|
|
|
|
|
let inflight=false;
|
|
let inflight=false;
|
|
@@ -1170,11 +1179,12 @@ function buildBackup(p, bp){
|
|
|
const types=[];
|
|
const types=[];
|
|
|
for(let et=0; et<TYPE_DEFS.length; et++){ const q=73+et*9;
|
|
for(let et=0; et<TYPE_DEFS.length; et++){ const q=73+et*9;
|
|
|
types.push({back:[p[q],p[q+1],p[q+2],p[q+3]], qam:p[q+4], abSwap:p[q+5], pad:p[q+6], led:p[q+7], rumble:p[q+8]}); }
|
|
types.push({back:[p[q],p[q+1],p[q+2],p[q+3]], qam:p[q+4], abSwap:p[q+5], pad:p[q+6], led:p[q+7], rumble:p[q+8]}); }
|
|
|
- const cfg={ mode:p[1], mDiv:p[2], mFric:p[3], persistMode:p[22], chord:[p[23],p[24],p[25]],
|
|
|
|
|
- rumble:p[51], swProRate:p[52], swGyro10:p[53], types };
|
|
|
|
|
- // D-pad chords only exist from protocol v18; omit the key entirely on older pucks so a restore doesn't
|
|
|
|
|
- // replay defaults over a newer puck's real settings.
|
|
|
|
|
|
|
+ const cfg={ mode:p[1], mDiv:p[2], mFric:p[3], persistMode:p[22], chord:[p[23],p[24],p[25]], types };
|
|
|
|
|
+ // D-pad chords only exist from protocol v18, the Switch gyro mapping from v19; omit the keys entirely on
|
|
|
|
|
+ // older pucks so a restore doesn't replay defaults over a newer puck's real settings. (Backups from before
|
|
|
|
|
+ // those settings existed carry rumble/swProRate/swGyro10 keys instead — now-removed settings, ignored.)
|
|
|
if(p[0]>=18 && p.length>183) cfg.chordD=[p[180],p[181],p[182],p[183]];
|
|
if(p[0]>=18 && p.length>183) cfg.chordD=[p[180],p[181],p[182],p[183]];
|
|
|
|
|
+ if(p[0]>=19 && p.length>184) cfg.swGyroLegacy=p[184];
|
|
|
// version 2: include the lizard map so it survives a backup/restore cycle.
|
|
// version 2: include the lizard map so it survives a backup/restore cycle.
|
|
|
// Guard on !lizardBusy to avoid a race with the one-shot lazy load (lizardExchange sets
|
|
// Guard on !lizardBusy to avoid a race with the one-shot lazy load (lizardExchange sets
|
|
|
// lizardBusy while the initial 0x11 fetch is in flight); if the load is still pending the
|
|
// lizardBusy while the initial 0x11 fetch is in flight); if the load is still pending the
|
|
@@ -1221,11 +1231,13 @@ async function importBackup(file){
|
|
|
// driven while the import replays every setting onto the puck, then closes on Done / on error.
|
|
// driven while the import replays every setting onto the puck, then closes on Done / on error.
|
|
|
modalOpen("Restore backup");
|
|
modalOpen("Restore backup");
|
|
|
const c=obj.config;
|
|
const c=obj.config;
|
|
|
- // Pre-count every write so the bar tracks real progress: settings (9 base fields + 4 D-pad chords when the
|
|
|
|
|
- // backup carries them), per-type config (9 each), the lizard bindings, 4 bond slots, and the final commit.
|
|
|
|
|
|
|
+ // Pre-count every write so the bar tracks real progress: settings (6 base fields, + 4 D-pad chords and the
|
|
|
|
|
+ // Switch gyro mapping when the backup carries them), per-type config (9 each), the lizard bindings, 4 bond
|
|
|
|
|
+ // slots, and the final commit.
|
|
|
const typeN=Array.isArray(c.types)?Math.min(c.types.length,4):0;
|
|
const typeN=Array.isArray(c.types)?Math.min(c.types.length,4):0;
|
|
|
const lizardN=(Array.isArray(c.lizardMap) && lizardCapable())?Math.min(c.lizardMap.length,LZ_MAX):0;
|
|
const lizardN=(Array.isArray(c.lizardMap) && lizardCapable())?Math.min(c.lizardMap.length,LZ_MAX):0;
|
|
|
- const total=9 + (Array.isArray(c.chordD)?4:0) + typeN*9 + lizardN + 4 + 1;
|
|
|
|
|
|
|
+ const baseN=6 + (Array.isArray(c.chordD)?4:0) + (c.swGyroLegacy!==undefined?1:0);
|
|
|
|
|
+ const total=baseN + typeN*9 + lizardN + 4 + 1;
|
|
|
let step=0, stage="";
|
|
let step=0, stage="";
|
|
|
const tick=()=>modalStage(stage, Math.min(99,Math.floor(step*100/total)));
|
|
const tick=()=>modalStage(stage, Math.min(99,Math.floor(step*100/total)));
|
|
|
try{
|
|
try{
|
|
@@ -1247,11 +1259,13 @@ async function importBackup(file){
|
|
|
const sf=async(f,v)=>{ await w([0x02, f, (v|0)&0xff]); };
|
|
const sf=async(f,v)=>{ await w([0x02, f, (v|0)&0xff]); };
|
|
|
// replay every setting (each persists; none reboot). mode is applied by the 0x0E commit below.
|
|
// replay every setting (each persists; none reboot). mode is applied by the 0x0E commit below.
|
|
|
stage="Restoring settings"; tick();
|
|
stage="Restoring settings"; tick();
|
|
|
- await sf(1,c.mDiv); await sf(2,c.mFric); await sf(22,c.rumble); await sf(16,c.persistMode?1:0);
|
|
|
|
|
|
|
+ await sf(1,c.mDiv); await sf(2,c.mFric); await sf(16,c.persistMode?1:0);
|
|
|
await sf(17,c.chord[0]); await sf(18,c.chord[1]); await sf(19,c.chord[2]);
|
|
await sf(17,c.chord[0]); await sf(18,c.chord[1]); await sf(19,c.chord[2]);
|
|
|
// absent in backups taken before the D-pad chords existed (or from a pre-v18 puck) -- skip, don't default
|
|
// absent in backups taken before the D-pad chords existed (or from a pre-v18 puck) -- skip, don't default
|
|
|
if(Array.isArray(c.chordD)) for(let i=0;i<4;i++) await sf(CHORD_DPAD_FIELD[i], c.chordD[i]);
|
|
if(Array.isArray(c.chordD)) for(let i=0;i<4;i++) await sf(CHORD_DPAD_FIELD[i], c.chordD[i]);
|
|
|
- await sf(23,c.swProRate); await sf(24,c.swGyro10);
|
|
|
|
|
|
|
+ // Switch Pro gyro mapping (v19+ backups only). Backups also carry rumble/swProRate/swGyro10 from the
|
|
|
|
|
+ // removed settings; those fields no longer exist on the puck, so they are not replayed.
|
|
|
|
|
+ if(c.swGyroLegacy!==undefined) await sf(38, c.swGyroLegacy?1:0);
|
|
|
// c.types is absent in some very old backups created before per-type paddle/haptic config
|
|
// c.types is absent in some very old backups created before per-type paddle/haptic config
|
|
|
// was added to the panel export; guard so a missing field never crashes the import mid-flight.
|
|
// was added to the panel export; guard so a missing field never crashes the import mid-flight.
|
|
|
if(typeN){
|
|
if(typeN){
|
|
@@ -1260,8 +1274,8 @@ async function importBackup(file){
|
|
|
await sf(40+et*9+4, t.qam); await sf(40+et*9+5, t.abSwap?1:0);
|
|
await sf(40+et*9+4, t.qam); await sf(40+et*9+5, t.abSwap?1:0);
|
|
|
await sf(40+et*9+6, t.pad?1:0); await sf(40+et*9+7, t.led);
|
|
await sf(40+et*9+6, t.pad?1:0); await sf(40+et*9+7, t.led);
|
|
|
await sf(40+et*9+8, t.rumble!==undefined?t.rumble:1); }
|
|
await sf(40+et*9+8, t.rumble!==undefined?t.rumble:1); }
|
|
|
- log("settings replayed ("+(9+typeN*9)+" fields)");
|
|
|
|
|
- } else { log("settings replayed (9 fields — no per-type config in this backup)"); }
|
|
|
|
|
|
|
+ log("settings replayed ("+(baseN+typeN*9)+" fields)");
|
|
|
|
|
+ } else { log("settings replayed ("+baseN+" fields — no per-type config in this backup)"); }
|
|
|
// restore the lizard map if the backup includes it (version 2+) and the puck speaks v16+.
|
|
// restore the lizard map if the backup includes it (version 2+) and the puck speaks v16+.
|
|
|
// pre-lizard-map backups simply lack c.lizardMap; skip silently so they still import cleanly.
|
|
// pre-lizard-map backups simply lack c.lizardMap; skip silently so they still import cleanly.
|
|
|
if(lizardN){
|
|
if(lizardN){
|
|
@@ -2167,14 +2181,13 @@ $("#wipeBoard").onclick=async()=>{
|
|
|
$("#persistMode").onclick=()=>{ const on=$("#persistMode").classList.contains("active"); setField(16, on?0:1); };
|
|
$("#persistMode").onclick=()=>{ const on=$("#persistMode").classList.contains("active"); setField(16, on?0:1); };
|
|
|
$("#landAll87").onclick=async()=>{ const on=$("#landAll87").classList.contains("active"); await setField(29, on?0:1); log("land-all-0x87 "+(on?"disabled":"enabled")); };
|
|
$("#landAll87").onclick=async()=>{ const on=$("#landAll87").classList.contains("active"); await setField(29, on?0:1); log("land-all-0x87 "+(on?"disabled":"enabled")); };
|
|
|
// Connection tuning: post-connect haptic block on (field 27) + seconds (field 28). (poll RX window is fixed.)
|
|
// Connection tuning: post-connect haptic block on (field 27) + seconds (field 28). (poll RX window is fixed.)
|
|
|
-for(const id of ["mDiv","mFric","rumble"]){
|
|
|
|
|
- const field={mDiv:1,mFric:2,rumble:22}[id];
|
|
|
|
|
|
|
+for(const id of ["mDiv","mFric"]){
|
|
|
|
|
+ const field={mDiv:1,mFric:2}[id];
|
|
|
$("#"+id).addEventListener("input", ()=>{ $("#"+id+"V").textContent=fmtSlider(id,$("#"+id).value); });
|
|
$("#"+id).addEventListener("input", ()=>{ $("#"+id+"V").textContent=fmtSlider(id,$("#"+id).value); });
|
|
|
$("#"+id).addEventListener("change", ()=>setField(field, +$("#"+id).value));
|
|
$("#"+id).addEventListener("change", ()=>setField(field, +$("#"+id).value));
|
|
|
}
|
|
}
|
|
|
// per-type back/QAM/A-B-swap/haptics handlers are wired in buildTypeCfgs()
|
|
// per-type back/QAM/A-B-swap/haptics handlers are wired in buildTypeCfgs()
|
|
|
-$("#swRate").addEventListener("change", ()=>setField(23, +$("#swRate").value));
|
|
|
|
|
-$("#swGyro").addEventListener("change", ()=>setField(24, +$("#swGyro").value));
|
|
|
|
|
|
|
+$("#swGyroMap").addEventListener("change", ()=>setField(38, +$("#swGyroMap").value));
|
|
|
for(const sel of document.querySelectorAll("select.chord")){
|
|
for(const sel of document.querySelectorAll("select.chord")){
|
|
|
sel.addEventListener("change", ()=>setField(CHORD_FIELD[+sel.dataset.i], +sel.value));
|
|
sel.addEventListener("change", ()=>setField(CHORD_FIELD[+sel.dataset.i], +sel.value));
|
|
|
}
|
|
}
|