mirror of
https://github.com/dualshock-tools/dualshock-tools.github.io.git
synced 2026-03-01 11:19:54 +03:00
Review and improve error handling and error messages
This commit is contained in:
committed by
dualshock-tools
parent
48fc4b5dce
commit
c295cfa508
@@ -184,7 +184,7 @@ class ControllerManager {
|
||||
async nvsLock() {
|
||||
const res = await this.currentController.nvsLock();
|
||||
if (!res.ok) {
|
||||
throw new Error(this.l("NVS Lock failed: ") + String(res.error));
|
||||
throw new Error(this.l("NVS Lock failed"), { cause: res.error });
|
||||
}
|
||||
|
||||
await this.queryNvStatus(); // Refresh NVS status
|
||||
@@ -197,8 +197,7 @@ class ControllerManager {
|
||||
async calibrateSticksBegin() {
|
||||
const res = await this.currentController.calibrateSticksBegin();
|
||||
if (!res.ok) {
|
||||
const detail = res.code ? (this.l("Error ") + String(res.code)) : String(res.error || "");
|
||||
throw new Error(this.l("Stick calibration failed: ") + detail);
|
||||
throw new Error(this.l("Stick calibration failed"), { cause: res.error });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -209,8 +208,7 @@ class ControllerManager {
|
||||
const res = await this.currentController.calibrateSticksSample();
|
||||
if (!res.ok) {
|
||||
await sleep(500);
|
||||
const detail = res.code ? (this.l("Error ") + String(res.code)) : String(res.error || "");
|
||||
throw new Error(this.l("Stick calibration failed: ") + detail);
|
||||
throw new Error(this.l("Stick calibration failed"), { cause: res.error });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -221,8 +219,7 @@ class ControllerManager {
|
||||
const res = await this.currentController.calibrateSticksEnd();
|
||||
if (!res.ok) {
|
||||
await sleep(500);
|
||||
const detail = res.code ? (this.l("Error ") + String(res.code)) : String(res.error || "");
|
||||
throw new Error(this.l("Stick calibration failed: ") + detail);
|
||||
throw new Error(this.l("Stick calibration failed"), { cause: res.error });
|
||||
}
|
||||
|
||||
this.setHasChangesToWrite(true);
|
||||
@@ -234,8 +231,7 @@ class ControllerManager {
|
||||
async calibrateRangeBegin() {
|
||||
const ret = await this.currentController.calibrateRangeBegin();
|
||||
if (!ret.ok) {
|
||||
const detail = ret.code ? (this.l("Error ") + String(ret.code)) : String(ret.error || "");
|
||||
throw new Error(this.l("Range calibration failed: ") + detail);
|
||||
throw new Error(this.l("Range calibration failed"), { cause: ret.error } );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -259,8 +255,8 @@ class ControllerManager {
|
||||
|
||||
console.log("Range calibration end failed with unexpected error:", res);
|
||||
await sleep(500);
|
||||
const msg = res?.code ? (this.l("Range calibration failed: ") + this.l("Error ") + String(res.code)) : (this.l("Range calibration failed: ") + String(res?.error || ""));
|
||||
return { success: false, message: msg };
|
||||
const msg = res?.code ? (this.l("Range calibration failed") + this.l("Error ") + String(res.code)) : (this.l("Range calibration failed") + String(res?.error || ""));
|
||||
return { success: false, message: msg, error: res?.error };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -94,7 +94,7 @@ class BaseController {
|
||||
* Close the HID device connection
|
||||
*/
|
||||
async close() {
|
||||
if (this.device && this.device.opened) {
|
||||
if (this.device?.opened) {
|
||||
await this.device.close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,9 +113,9 @@ class DS4Controller extends BaseController {
|
||||
const disable_bits = is_clone ? 1 : 0; // 1: clone
|
||||
|
||||
return { ok: true, infoItems, nv, disable_bits, rare };
|
||||
} catch(e) {
|
||||
} catch(error) {
|
||||
// Return error but do not touch DOM
|
||||
return { ok: false, error: e, disable_bits: 1 };
|
||||
return { ok: false, error, disable_bits: 1 };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,7 +128,7 @@ class DS4Controller extends BaseController {
|
||||
|
||||
return { success: true, message: this.l("Changes saved successfully") };
|
||||
} catch(error) {
|
||||
throw new Error(this.l("Error while saving changes: ") + String(error));
|
||||
throw new Error(this.l("Error while saving changes"), { cause: error });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,8 +145,8 @@ class DS4Controller extends BaseController {
|
||||
try {
|
||||
await this.sendFeatureReport(0xa0, [10,1,0]);
|
||||
return { ok: true };
|
||||
} catch(e) {
|
||||
return { ok: false, error: e };
|
||||
} catch(error) {
|
||||
return { ok: false, error };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,8 +155,8 @@ class DS4Controller extends BaseController {
|
||||
try {
|
||||
await this.sendFeatureReport(0xa0, [10,2,0x3e,0x71,0x7f,0x89]);
|
||||
return { ok: true };
|
||||
} catch(e) {
|
||||
return { ok: false, error: e };
|
||||
} catch(error) {
|
||||
return { ok: false, error };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,9 +181,9 @@ class DS4Controller extends BaseController {
|
||||
return { ok: false, code: 1, d1, d2 };
|
||||
}
|
||||
return { ok: true };
|
||||
} catch(e) {
|
||||
la("ds4_calibrate_range_begin_failed", {"r": e});
|
||||
return { ok: false, error: String(e) };
|
||||
} catch(error) {
|
||||
la("ds4_calibrate_range_begin_failed", {"r": error});
|
||||
return { ok: false, error };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -203,9 +203,9 @@ class DS4Controller extends BaseController {
|
||||
}
|
||||
|
||||
return { ok: true };
|
||||
} catch(e) {
|
||||
la("ds4_calibrate_range_end_failed", {"r": e});
|
||||
return { ok: false, error: String(e) };
|
||||
} catch(error) {
|
||||
la("ds4_calibrate_range_end_failed", {"r": error});
|
||||
return { ok: false, error };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -226,9 +226,9 @@ class DS4Controller extends BaseController {
|
||||
}
|
||||
|
||||
return { ok: true };
|
||||
} catch(e) {
|
||||
la("ds4_calibrate_sticks_begin_failed", {"r": e});
|
||||
return { ok: false, error: String(e) };
|
||||
} catch(error) {
|
||||
la("ds4_calibrate_sticks_begin_failed", {"r": error});
|
||||
return { ok: false, error };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -248,8 +248,8 @@ class DS4Controller extends BaseController {
|
||||
return { ok: false, code: 2, d1, d2 };
|
||||
}
|
||||
return { ok: true };
|
||||
} catch(e) {
|
||||
return { ok: false, error: String(e) };
|
||||
} catch(error) {
|
||||
return { ok: false, error };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -269,9 +269,9 @@ class DS4Controller extends BaseController {
|
||||
}
|
||||
|
||||
return { ok: true };
|
||||
} catch(e) {
|
||||
la("ds4_calibrate_sticks_end_failed", {"r": e});
|
||||
return { ok: false, error: String(e) };
|
||||
} catch(error) {
|
||||
la("ds4_calibrate_sticks_end_failed", {"r": error});
|
||||
return { ok: false, error };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -289,8 +289,8 @@ class DS4Controller extends BaseController {
|
||||
default:
|
||||
return { ...res, status: 'unknown', locked: null };
|
||||
}
|
||||
} catch (e) {
|
||||
return { device: 'ds4', status: 'error', locked: null, code: 2, error: e };
|
||||
} catch (error) {
|
||||
return { device: 'ds4', status: 'error', locked: null, code: 2, error };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -160,10 +160,9 @@ class DS5Controller extends BaseController {
|
||||
const pending_reboot = (nv?.status === 'pending_reboot');
|
||||
|
||||
return { ok: true, infoItems, nv, disable_bits, pending_reboot };
|
||||
} catch(e) {
|
||||
la("ds5_info_error", {"r": e})
|
||||
console.error(e.stack);
|
||||
return { ok: false, error: e, disable_bits: 1 };
|
||||
} catch(error) {
|
||||
la("ds5_info_error", {"r": error})
|
||||
return { ok: false, error, disable_bits: 1 };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,7 +175,7 @@ class DS5Controller extends BaseController {
|
||||
|
||||
return { success: true, message: this.l("Changes saved successfully") };
|
||||
} catch(error) {
|
||||
throw new Error(this.l("Error while saving changes: ") + String(error));
|
||||
throw new Error(this.l("Error while saving changes"), { cause: error });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -194,8 +193,8 @@ class DS5Controller extends BaseController {
|
||||
await this.sendFeatureReport(0x80, [3,1]);
|
||||
await this.receiveFeatureReport(0x81);
|
||||
return { ok: true };
|
||||
} catch(e) {
|
||||
return { ok: false, error: e };
|
||||
} catch(error) {
|
||||
return { ok: false, error };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,9 +203,9 @@ class DS5Controller extends BaseController {
|
||||
try {
|
||||
await this.sendFeatureReport(0x80, [3,2, 101, 50, 64, 12]);
|
||||
const data = await this.receiveFeatureReport(0x81);
|
||||
} catch(e) {
|
||||
} catch(error) {
|
||||
await sleep(500);
|
||||
throw new Error(this.l("NVS Unlock failed: ") + e);
|
||||
throw new Error(this.l("NVS Unlock failed"), { cause: error });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,12 +238,12 @@ class DS5Controller extends BaseController {
|
||||
if(data.getUint32(0, false) != 0x83010101) {
|
||||
const d1 = dec2hex32(data.getUint32(0, false));
|
||||
la("ds5_calibrate_sticks_begin_failed", {"d1": d1});
|
||||
return { ok: false, code: 1, d1 };
|
||||
throw new Error(`Stick center calibration begin failed: ${d1}`);
|
||||
}
|
||||
return { ok: true };
|
||||
} catch(e) {
|
||||
} catch(error) {
|
||||
la("ds5_calibrate_sticks_begin_failed", {"r": e});
|
||||
return { ok: false, error: String(e) };
|
||||
return { ok: false, error };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -259,12 +258,12 @@ class DS5Controller extends BaseController {
|
||||
if(data.getUint32(0, false) != 0x83010101) {
|
||||
const d1 = dec2hex32(data.getUint32(0, false));
|
||||
la("ds5_calibrate_sticks_sample_failed", {"d1": d1});
|
||||
return { ok: false, code: 2, d1 };
|
||||
throw new Error(`Stick center calibration sample failed: ${d1}`);
|
||||
}
|
||||
return { ok: true };
|
||||
} catch(e) {
|
||||
} catch(error) {
|
||||
la("ds5_calibrate_sticks_sample_failed", {"r": e});
|
||||
return { ok: false, error: String(e) };
|
||||
return { ok: false, error };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -279,13 +278,13 @@ class DS5Controller extends BaseController {
|
||||
if(data.getUint32(0, false) != 0x83010102) {
|
||||
const d1 = dec2hex32(data.getUint32(0, false));
|
||||
la("ds5_calibrate_sticks_failed", {"s": 3, "d1": d1});
|
||||
return { ok: false, code: 3, d1 };
|
||||
throw new Error(`Stick center calibration end failed: ${d1}`);
|
||||
}
|
||||
|
||||
return { ok: true };
|
||||
} catch(e) {
|
||||
} catch(error) {
|
||||
la("ds5_calibrate_sticks_end_failed", {"r": e});
|
||||
return { ok: false, error: String(e) };
|
||||
return { ok: false, error };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -300,12 +299,12 @@ class DS5Controller extends BaseController {
|
||||
if(data.getUint32(0, false) != 0x83010201) {
|
||||
const d1 = dec2hex32(data.getUint32(0, false));
|
||||
la("ds5_calibrate_range_begin_failed", {"d1": d1});
|
||||
return { ok: false, code: 1, d1 };
|
||||
throw new Error(`Stick range calibration begin failed: ${d1}`);
|
||||
}
|
||||
return { ok: true };
|
||||
} catch(e) {
|
||||
} catch(error) {
|
||||
la("ds5_calibrate_range_begin_failed", {"r": e});
|
||||
return { ok: false, error: String(e) };
|
||||
return { ok: false, error };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -321,13 +320,13 @@ class DS5Controller extends BaseController {
|
||||
if(data.getUint32(0, false) != 0x83010202) {
|
||||
const d1 = dec2hex32(data.getUint32(0, false));
|
||||
la("ds5_calibrate_range_end_failed", {"d1": d1});
|
||||
return { ok: false, code: 3, d1 };
|
||||
throw new Error(`Stick range calibration end failed: ${d1}`);
|
||||
}
|
||||
|
||||
return { ok: true };
|
||||
} catch(e) {
|
||||
} catch(error) {
|
||||
la("ds5_calibrate_range_end_failed", {"r": e});
|
||||
return { ok: false, error: String(e) };
|
||||
return { ok: false, error };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ class DS5EdgeController extends DS5Controller {
|
||||
};
|
||||
}
|
||||
} catch(error) {
|
||||
throw new Error(this.l("Error while saving changes: ") + String(error));
|
||||
throw new Error(this.l("Error while saving changes"), { cause: error });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
209
js/core.js
209
js/core.js
@@ -35,9 +35,66 @@ let controller = null;
|
||||
|
||||
function gboot() {
|
||||
app.gu = crypto.randomUUID();
|
||||
$("#infoshowall").hide();
|
||||
|
||||
async function initializeApp() {
|
||||
window.addEventListener("error", (event) => {
|
||||
console.error(event.error?.stack || event.message);
|
||||
show_popup(event.error?.message || event.message);
|
||||
});
|
||||
|
||||
window.addEventListener("unhandledrejection", async (event) => {
|
||||
console.error("Unhandled rejection:", event.reason?.stack || event.reason);
|
||||
close_all_modals();
|
||||
// show_popup(event.reason?.message || event.reason);
|
||||
|
||||
// Format the error message for better readability
|
||||
let errorMessage = "An unexpected error occurred";
|
||||
if (event.reason) {
|
||||
if (event.reason.message) {
|
||||
errorMessage = `<strong>Error:</strong> ${event.reason.message}`;
|
||||
} else if (typeof event.reason === 'string') {
|
||||
errorMessage = `<strong>Error:</strong> ${event.reason}`;
|
||||
}
|
||||
|
||||
// Collect all stack traces (main error and causes) for a single expandable section
|
||||
let allStackTraces = '';
|
||||
if (event.reason.stack) {
|
||||
const stackTrace = event.reason.stack.replace(/\n/g, '<br>').replace(/ /g, ' ');
|
||||
allStackTraces += `<strong>Main Error Stack:</strong><br>${stackTrace}`;
|
||||
}
|
||||
|
||||
// Add error chain information if available (ES2022 error chaining)
|
||||
let currentError = event.reason;
|
||||
let chainLevel = 0;
|
||||
while (currentError?.cause && chainLevel < 5) {
|
||||
chainLevel++;
|
||||
currentError = currentError.cause;
|
||||
if (currentError.stack) {
|
||||
const causeStackTrace = currentError.stack.replace(/\n/g, '<br>').replace(/ /g, ' ');
|
||||
if (allStackTraces) allStackTraces += '<br><br>';
|
||||
allStackTraces += `<strong>Cause ${chainLevel} Stack:</strong><br>${causeStackTrace}`;
|
||||
}
|
||||
}
|
||||
|
||||
// Add single expandable section if we have any stack traces
|
||||
if (allStackTraces) {
|
||||
errorMessage += `
|
||||
<br>
|
||||
<details style="margin-top: 0px;">
|
||||
<summary style="cursor: pointer; color: #666;">Details</summary>
|
||||
<div style="font-family: monospace; font-size: 0.85em; margin-top: 8px; padding: 8px; background-color: #f8f9fa; border-radius: 4px; overflow-x: auto;">
|
||||
${allStackTraces}
|
||||
</div>
|
||||
</details>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
errorAlert(errorMessage);
|
||||
// Prevent the default browser behavior (logging to console, again)
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
await loadAllTemplates();
|
||||
await init_svg_controller();
|
||||
|
||||
@@ -45,19 +102,6 @@ function gboot() {
|
||||
show_welcome_modal();
|
||||
|
||||
$("input[name='displayMode']").on('change', on_stick_mode_change);
|
||||
|
||||
window.addEventListener("error", (event) => {
|
||||
console.error(event.error?.stack || event.message);
|
||||
show_popup(event.error?.message || event.message);
|
||||
});
|
||||
|
||||
window.addEventListener("unhandledrejection", (event) => {
|
||||
console.error("Unhandled rejection:", event.reason?.stack || event.reason);
|
||||
close_all_modals();
|
||||
show_popup(event.reason?.message || event.reason);
|
||||
// Prevent the default browser behavior (logging to console, again)
|
||||
event.preventDefault();
|
||||
});
|
||||
}
|
||||
|
||||
// Since modules are deferred, DOM might already be loaded
|
||||
@@ -87,6 +131,9 @@ async function connect() {
|
||||
|
||||
la("begin");
|
||||
reset_circularity_mode();
|
||||
clearAllAlerts();
|
||||
await sleep(200);
|
||||
|
||||
try {
|
||||
$("#btnconnect").prop("disabled", true);
|
||||
$("#connectspinner").show();
|
||||
@@ -101,11 +148,16 @@ async function connect() {
|
||||
if (devices.length == 0) {
|
||||
$("#btnconnect").prop("disabled", false);
|
||||
$("#connectspinner").hide();
|
||||
await disconnect();
|
||||
return;
|
||||
}
|
||||
|
||||
if (devices.length > 1) {
|
||||
throw new Error(l("Please connect only one controller at time."));
|
||||
if (devices.length > 1) { //mm: this should never happen
|
||||
infoAlert(l("Please connect only one controller at time."));
|
||||
$("#btnconnect").prop("disabled", false);
|
||||
$("#connectspinner").hide();
|
||||
await disconnect();
|
||||
return;
|
||||
}
|
||||
|
||||
const [device] = devices;
|
||||
@@ -117,28 +169,29 @@ async function connect() {
|
||||
await device.open();
|
||||
|
||||
la("connect", {"p": device.productId, "v": device.vendorId});
|
||||
device.oninputreport = continue_connection
|
||||
device.oninputreport = continue_connection; // continue below
|
||||
} catch(error) {
|
||||
$("#btnconnect").prop("disabled", false);
|
||||
$("#connectspinner").hide();
|
||||
throw new Error(l("Error: ") + error);
|
||||
await disconnect();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async function continue_connection({data, device}) {
|
||||
try {
|
||||
if (!controller || controller.isConnected()) {
|
||||
console.log("Already connected. Reset input report handler.");
|
||||
controller?.setInputReportHandler(null);
|
||||
device.oninputreport = null; // this function is called repeatedly if not cleared
|
||||
return;
|
||||
}
|
||||
|
||||
let connected = false;
|
||||
|
||||
// Detect if the controller is connected via USB
|
||||
const reportLen = data.byteLength;
|
||||
if(reportLen != 63) {
|
||||
throw new Error(l("Please connect the device using a USB cable."));
|
||||
// throw new Error(l("Please connect the device using a USB cable."));
|
||||
infoAlert(l("The device is connected via Bluetooth. Disconnect and reconnect using a USB cable instead."));
|
||||
await disconnect();
|
||||
return;
|
||||
}
|
||||
|
||||
// Helper to apply basic UI visibility based on device type
|
||||
@@ -159,20 +212,18 @@ async function continue_connection({data, device}) {
|
||||
|
||||
info = await controllerInstance.getInfo();
|
||||
} catch (error) {
|
||||
if (device) {
|
||||
throw new Error(l("Connected invalid device: ") + dec2hex(device.vendorId) + ":" + dec2hex(device.productId));
|
||||
} else {
|
||||
throw new Error(l("Failed to connect to device"));
|
||||
}
|
||||
const contextMessage = device
|
||||
? l("Connected invalid device: ") + dec2hex(device.vendorId) + ":" + dec2hex(device.productId)
|
||||
: l("Failed to connect to device");
|
||||
throw new Error(contextMessage, { cause: error });
|
||||
}
|
||||
|
||||
if(!info?.ok) {
|
||||
// Not connected/failed to fetch info
|
||||
if(info) console.error(JSON.stringify(info, null, 2));
|
||||
throw new Error(l("Connected invalid device: ") + l("Error 1") + (info?.error ? ` (${info.error}).` : '. '));
|
||||
throw new Error(l("Connected invalid device: ") + l("Error 1"), { cause: info?.error });
|
||||
}
|
||||
|
||||
connected = true;
|
||||
// Get UI configuration and device name
|
||||
const ui = ControllerFactory.getUIConfig(device.productId);
|
||||
applyDeviceUI(ui);
|
||||
@@ -198,7 +249,9 @@ async function continue_connection({data, device}) {
|
||||
|
||||
// Edge-specific: pending reboot check (from nv)
|
||||
if (model == "DS5_Edge" && info?.pending_reboot) {
|
||||
throw new Error(l("A reboot is needed to continue using this DualSense Edge. Please disconnect and reconnect your controller."));
|
||||
infoAlert(l("A reboot is needed to continue using this DualSense Edge. Please disconnect and reconnect your controller."));
|
||||
await disconnect();
|
||||
return;
|
||||
}
|
||||
|
||||
// Render info collected from device
|
||||
@@ -256,8 +309,7 @@ async function disconnect() {
|
||||
// Wrapper function for HTML onclick handlers
|
||||
function disconnectSync() {
|
||||
disconnect().catch(error => {
|
||||
console.error("Error during disconnect:", error);
|
||||
show_popup("Error during disconnect: " + error.message);
|
||||
throw new Error("Failed to disconnect", { cause: error });
|
||||
});
|
||||
}
|
||||
|
||||
@@ -269,7 +321,7 @@ async function handleDisconnectedDevice(e) {
|
||||
|
||||
function render_nvstatus_to_dom(nv) {
|
||||
if(!nv?.status) {
|
||||
throw new Error("Invalid NVS status data");
|
||||
throw new Error("Invalid NVS status data", { cause: nv?.error });
|
||||
}
|
||||
|
||||
switch (nv.status) {
|
||||
@@ -676,7 +728,11 @@ async function flash_all_changes() {
|
||||
const progressCallback = controller.getModel() == "DS5_Edge" ? set_edge_progress : null;
|
||||
const result = await controller.flash(progressCallback);
|
||||
if (result?.success) {
|
||||
show_popup(result.message, result.isHtml);
|
||||
if(result.isHtml) {
|
||||
show_popup(result.message, result.isHtml);
|
||||
} else {
|
||||
successAlert(result.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -833,8 +889,8 @@ const trigger_haptic_motors = (() => {
|
||||
// Stop rumble after duration
|
||||
clearTimeout(haptic_timeout);
|
||||
haptic_timeout = setTimeout(stop_haptic_motors, 250);
|
||||
} catch(e) {
|
||||
throw new Error(l("Error triggering rumble: ") + e);
|
||||
} catch(error) {
|
||||
throw new Error(l("Error triggering rumble"), { cause: error });
|
||||
}
|
||||
};
|
||||
})();
|
||||
@@ -854,15 +910,90 @@ async function stop_haptic_motors() {
|
||||
}
|
||||
|
||||
|
||||
// Alert Management Functions
|
||||
let alertCounter = 0;
|
||||
|
||||
/**
|
||||
* Push a new alert message to the bottom of the screen
|
||||
* @param {string} message - The message to display
|
||||
* @param {string} type - Bootstrap alert type: 'primary', 'secondary', 'success', 'danger', 'warning', 'info', 'light', 'dark'
|
||||
* @param {number} duration - Auto-dismiss duration in milliseconds (0 = no auto-dismiss)
|
||||
* @param {boolean} dismissible - Whether the alert can be manually dismissed
|
||||
* @returns {string} - The ID of the created alert element
|
||||
*/
|
||||
function pushAlert(message, type = 'info', duration = 0, dismissible = true) {
|
||||
const alertContainer = document.getElementById('alert-container');
|
||||
if (!alertContainer) {
|
||||
console.error('Alert container not found');
|
||||
return null;
|
||||
}
|
||||
|
||||
const alertId = `alert-${++alertCounter}`;
|
||||
const alertDiv = document.createElement('div');
|
||||
alertDiv.id = alertId;
|
||||
alertDiv.className = `alert alert-${type} alert-dismissible fade show`;
|
||||
alertDiv.setAttribute('role', 'alert');
|
||||
alertDiv.innerHTML = `
|
||||
${message}
|
||||
${dismissible ? '<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>' : ''}
|
||||
`;
|
||||
|
||||
alertContainer.appendChild(alertDiv);
|
||||
|
||||
if (duration > 0) {
|
||||
setTimeout(() => {
|
||||
dismissAlert(alertId);
|
||||
}, duration);
|
||||
}
|
||||
|
||||
return alertId;
|
||||
}
|
||||
|
||||
function dismissAlert(alertId) {
|
||||
const alertElement = document.getElementById(alertId);
|
||||
if (alertElement) {
|
||||
const bsAlert = new bootstrap.Alert(alertElement);
|
||||
bsAlert.close();
|
||||
}
|
||||
}
|
||||
|
||||
function clearAllAlerts() {
|
||||
const alertContainer = document.getElementById('alert-container');
|
||||
if (alertContainer) {
|
||||
const alerts = alertContainer.querySelectorAll('.alert');
|
||||
alerts.forEach(alert => {
|
||||
const bsAlert = new bootstrap.Alert(alert);
|
||||
bsAlert.close();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function successAlert(message, duration = 1_500) {
|
||||
return pushAlert(message, 'success', duration, false);
|
||||
}
|
||||
|
||||
function errorAlert(message, duration = 15_000) {
|
||||
return pushAlert(message, 'danger', /* duration */);
|
||||
}
|
||||
|
||||
function warningAlert(message, duration = 8_000) {
|
||||
return pushAlert(message, 'warning', duration);
|
||||
}
|
||||
|
||||
function infoAlert(message, duration = 5_000) {
|
||||
return pushAlert(message, 'info', duration, false);
|
||||
}
|
||||
|
||||
|
||||
// Export functions to global scope for HTML onclick handlers
|
||||
window.gboot = gboot;
|
||||
window.connect = connect;
|
||||
window.disconnect = disconnectSync;
|
||||
window.show_faq_modal = show_faq_modal;
|
||||
window.show_info_tab = show_info_tab;
|
||||
window.calibrate_range = () => calibrate_range(controller, { resetStickDiagrams, show_popup });
|
||||
window.calibrate_range = () => calibrate_range(controller, { resetStickDiagrams, successAlert });
|
||||
window.calibrate_stick_centers = () => calibrate_stick_centers(controller, { resetStickDiagrams, show_popup, set_progress });
|
||||
window.auto_calibrate_stick_centers = () => auto_calibrate_stick_centers(controller, { resetStickDiagrams, show_popup, set_progress });
|
||||
window.auto_calibrate_stick_centers = () => auto_calibrate_stick_centers(controller, { resetStickDiagrams, successAlert, set_progress });
|
||||
window.ds5_finetune = () => ds5_finetune(controller, { ll_data, rr_data, clear_circularity });
|
||||
window.flash_all_changes = flash_all_changes;
|
||||
window.reboot_controller = reboot_controller;
|
||||
|
||||
@@ -8,10 +8,10 @@ import { l } from '../translations.js';
|
||||
* Handles step-by-step manual stick center calibration
|
||||
*/
|
||||
export class CalibCenterModal {
|
||||
constructor(controllerInstance, { resetStickDiagrams, show_popup, set_progress }) {
|
||||
constructor(controllerInstance, { resetStickDiagrams, successAlert, set_progress }) {
|
||||
this.controller = controllerInstance;
|
||||
this.resetStickDiagrams = resetStickDiagrams;
|
||||
this.show_popup = show_popup;
|
||||
this.successAlert = successAlert;
|
||||
this.set_progress = set_progress;
|
||||
|
||||
this._initEventListeners();
|
||||
@@ -130,7 +130,7 @@ export class CalibCenterModal {
|
||||
this.resetStickDiagrams();
|
||||
|
||||
if (result?.message) {
|
||||
this.show_popup(result.message);
|
||||
this.successAlert(result.message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,11 +7,11 @@ import { sleep } from '../utils.js';
|
||||
* Handles stick range calibration
|
||||
*/
|
||||
export class CalibRangeModal {
|
||||
constructor(controllerInstance, { resetStickDiagrams, show_popup }) {
|
||||
constructor(controllerInstance, { resetStickDiagrams, successAlert }) {
|
||||
// Dependencies
|
||||
this.controller = controllerInstance;
|
||||
this.resetStickDiagrams = resetStickDiagrams;
|
||||
this.show_popup = show_popup;
|
||||
this.successAlert = successAlert;
|
||||
}
|
||||
|
||||
async open() {
|
||||
@@ -30,7 +30,7 @@ export class CalibRangeModal {
|
||||
|
||||
const result = await this.controller.calibrateRangeOnClose();
|
||||
if (result?.message) {
|
||||
this.show_popup(result.message);
|
||||
this.successAlert(result.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,33 +14,28 @@ async function loadTemplate(templateName) {
|
||||
return templateCache.get(templateName);
|
||||
}
|
||||
|
||||
try {
|
||||
// Check if we have bundled assets (production mode)
|
||||
if (window.BUNDLED_ASSETS && window.BUNDLED_ASSETS.templates) {
|
||||
const templateHtml = window.BUNDLED_ASSETS.templates[templateName];
|
||||
if (templateHtml) {
|
||||
templateCache.set(templateName, templateHtml);
|
||||
return templateHtml;
|
||||
}
|
||||
// Check if we have bundled assets (production mode)
|
||||
if (window.BUNDLED_ASSETS && window.BUNDLED_ASSETS.templates) {
|
||||
const templateHtml = window.BUNDLED_ASSETS.templates[templateName];
|
||||
if (templateHtml) {
|
||||
templateCache.set(templateName, templateHtml);
|
||||
return templateHtml;
|
||||
}
|
||||
|
||||
// Fallback to fetching from server (development mode)
|
||||
// Only append .html if the templateName doesn't already have an extension
|
||||
const hasExtension = templateName.includes('.');
|
||||
const templatePath = hasExtension ? `templates/${templateName}` : `templates/${templateName}.html`;
|
||||
|
||||
const response = await fetch(templatePath);
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to load template: ${templateName}`);
|
||||
}
|
||||
|
||||
const templateHtml = await response.text();
|
||||
templateCache.set(templateName, templateHtml);
|
||||
return templateHtml;
|
||||
} catch (error) {
|
||||
console.error(`Error loading template ${templateName}:`, error);
|
||||
return '';
|
||||
}
|
||||
|
||||
// Fallback to fetching from server (development mode)
|
||||
// Only append .html if the templateName doesn't already have an extension
|
||||
const hasExtension = templateName.includes('.');
|
||||
const templatePath = hasExtension ? `templates/${templateName}` : `templates/${templateName}.html`;
|
||||
|
||||
const response = await fetch(templatePath);
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to load template: ${templateName}`);
|
||||
}
|
||||
|
||||
const templateHtml = await response.text();
|
||||
templateCache.set(templateName, templateHtml);
|
||||
return templateHtml;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -49,59 +44,48 @@ async function loadTemplate(templateName) {
|
||||
* @returns {Promise<string>} - Promise that resolves with the SVG content
|
||||
*/
|
||||
async function loadSvgAsset(assetPath) {
|
||||
try {
|
||||
// Check if we have bundled assets (production mode)
|
||||
if (window.BUNDLED_ASSETS && window.BUNDLED_ASSETS.svg) {
|
||||
const svgContent = window.BUNDLED_ASSETS.svg[assetPath];
|
||||
if (svgContent) {
|
||||
return svgContent;
|
||||
}
|
||||
// Check if we have bundled assets (production mode)
|
||||
if (window.BUNDLED_ASSETS && window.BUNDLED_ASSETS.svg) {
|
||||
const svgContent = window.BUNDLED_ASSETS.svg[assetPath];
|
||||
if (svgContent) {
|
||||
return svgContent;
|
||||
}
|
||||
|
||||
// Fallback to fetching from server (development mode)
|
||||
const response = await fetch(`assets/${assetPath}`);
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to load SVG asset: ${assetPath}`);
|
||||
}
|
||||
|
||||
return await response.text();
|
||||
} catch (error) {
|
||||
console.error(`Error loading SVG asset ${assetPath}:`, error);
|
||||
return '';
|
||||
}
|
||||
|
||||
// Fallback to fetching from server (development mode)
|
||||
const response = await fetch(`assets/${assetPath}`);
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to load SVG asset: ${assetPath}`);
|
||||
}
|
||||
|
||||
return await response.text();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load all templates and insert them into the DOM
|
||||
*/
|
||||
export async function loadAllTemplates() {
|
||||
try {
|
||||
// Load SVG icons
|
||||
const iconsHtml = await loadSvgAsset('icons.svg');
|
||||
const iconsContainer = document.createElement('div');
|
||||
iconsContainer.innerHTML = iconsHtml;
|
||||
document.body.prepend(iconsContainer);
|
||||
// Load SVG icons
|
||||
const iconsHtml = await loadSvgAsset('icons.svg');
|
||||
const iconsContainer = document.createElement('div');
|
||||
iconsContainer.innerHTML = iconsHtml;
|
||||
document.body.prepend(iconsContainer);
|
||||
|
||||
// Load modals
|
||||
const faqModalHtml = await loadTemplate('faq-modal');
|
||||
const popupModalHtml = await loadTemplate('popup-modal');
|
||||
const finetuneModalHtml = await loadTemplate('finetune-modal');
|
||||
const calibCenterModalHtml = await loadTemplate('calib-center-modal');
|
||||
const welcomeModalHtml = await loadTemplate('welcome-modal');
|
||||
const calibrateModalHtml = await loadTemplate('calibrate-modal');
|
||||
const rangeModalHtml = await loadTemplate('range-modal');
|
||||
const edgeProgressModalHtml = await loadTemplate('edge-progress-modal');
|
||||
const edgeModalHtml = await loadTemplate('edge-modal');
|
||||
const donateModalHtml = await loadTemplate('donate-modal');
|
||||
// Load modals
|
||||
const faqModalHtml = await loadTemplate('faq-modal');
|
||||
const popupModalHtml = await loadTemplate('popup-modal');
|
||||
const finetuneModalHtml = await loadTemplate('finetune-modal');
|
||||
const calibCenterModalHtml = await loadTemplate('calib-center-modal');
|
||||
const welcomeModalHtml = await loadTemplate('welcome-modal');
|
||||
const calibrateModalHtml = await loadTemplate('calibrate-modal');
|
||||
const rangeModalHtml = await loadTemplate('range-modal');
|
||||
const edgeProgressModalHtml = await loadTemplate('edge-progress-modal');
|
||||
const edgeModalHtml = await loadTemplate('edge-modal');
|
||||
const donateModalHtml = await loadTemplate('donate-modal');
|
||||
|
||||
// Create modals container
|
||||
const modalsContainer = document.createElement('div');
|
||||
modalsContainer.id = 'modals-container';
|
||||
modalsContainer.innerHTML = faqModalHtml + popupModalHtml + finetuneModalHtml + calibCenterModalHtml + welcomeModalHtml + calibrateModalHtml + rangeModalHtml + edgeProgressModalHtml + edgeModalHtml + donateModalHtml;
|
||||
document.body.appendChild(modalsContainer);
|
||||
|
||||
console.log('All templates loaded successfully');
|
||||
} catch (error) {
|
||||
console.error('Error loading templates:', error);
|
||||
}
|
||||
// Create modals container
|
||||
const modalsContainer = document.createElement('div');
|
||||
modalsContainer.id = 'modals-container';
|
||||
modalsContainer.innerHTML = faqModalHtml + popupModalHtml + finetuneModalHtml + calibCenterModalHtml + welcomeModalHtml + calibrateModalHtml + rangeModalHtml + edgeProgressModalHtml + edgeModalHtml + donateModalHtml;
|
||||
document.body.appendChild(modalsContainer);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user