Skip to content

Commit 8934286

Browse files
committed
feat: add eruda devtools for debugging
1 parent 15955ad commit 8934286

File tree

7 files changed

+220
-0
lines changed

7 files changed

+220
-0
lines changed

src/ace/commands.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,22 @@ const commands = [
365365
},
366366
readOnly: true,
367367
},
368+
{
369+
name: "dev:toggleInspector",
370+
description: "Toggle Inspector",
371+
exec() {
372+
acode.exec("toggle-inspector");
373+
},
374+
readOnly: true,
375+
},
376+
{
377+
name: "dev:openInspector",
378+
description: "Open Inspector",
379+
exec() {
380+
acode.exec("open-inspector");
381+
},
382+
readOnly: true,
383+
},
368384
];
369385

370386
export function setCommands(editor) {

src/lib/commands.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -487,4 +487,12 @@ Additional Info:
487487
welcome() {
488488
openWelcomeTab();
489489
},
490+
async "toggle-inspector"() {
491+
const devTools = (await import("lib/devTools")).default;
492+
devTools.toggle();
493+
},
494+
async "open-inspector"() {
495+
const devTools = (await import("lib/devTools")).default;
496+
devTools.show();
497+
},
490498
};

src/lib/devTools.js

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
import fsOperation from "fileSystem";
2+
import ajax from "@deadlyjack/ajax";
3+
import loader from "dialogs/loader";
4+
import helpers from "utils/helpers";
5+
import Url from "utils/Url";
6+
import constants from "./constants";
7+
8+
let erudaInstance = null;
9+
let isInitialized = false;
10+
11+
/**
12+
* Developer tools module for debugging Acode
13+
*/
14+
const devTools = {
15+
/**
16+
* Check if Eruda is initialized
17+
* @returns {boolean}
18+
*/
19+
get isInitialized() {
20+
return isInitialized;
21+
},
22+
23+
/**
24+
* Get the Eruda instance
25+
* @returns {object|null}
26+
*/
27+
get eruda() {
28+
return erudaInstance;
29+
},
30+
31+
/**
32+
* Initialize Eruda for developer mode
33+
* @param {boolean} showLoader - Whether to show a loading dialog
34+
* @returns {Promise<void>}
35+
*/
36+
async init(showLoader = false) {
37+
if (isInitialized) return;
38+
39+
try {
40+
const erudaPath = Url.join(DATA_STORAGE, "eruda.js");
41+
const fs = fsOperation(erudaPath);
42+
43+
if (!(await fs.exists())) {
44+
if (showLoader) {
45+
loader.create(
46+
strings["downloading file"]?.replace("{file}", "eruda.js") ||
47+
"Downloading eruda.js...",
48+
strings["downloading..."] || "Downloading...",
49+
);
50+
}
51+
52+
try {
53+
const erudaScript = await ajax({
54+
url: constants.ERUDA_CDN,
55+
responseType: "text",
56+
contentType: "application/x-www-form-urlencoded",
57+
});
58+
await fsOperation(DATA_STORAGE).createFile("eruda.js", erudaScript);
59+
} finally {
60+
if (showLoader) loader.destroy();
61+
}
62+
}
63+
64+
const internalUri = await helpers.toInternalUri(erudaPath);
65+
66+
await new Promise((resolve, reject) => {
67+
const script = document.createElement("script");
68+
script.src = internalUri;
69+
script.id = "eruda-script";
70+
script.onload = resolve;
71+
script.onerror = reject;
72+
document.head.appendChild(script);
73+
});
74+
75+
if (window.eruda) {
76+
window.eruda.init({
77+
useShadowDom: true,
78+
autoScale: true,
79+
defaults: {
80+
displaySize: 50,
81+
},
82+
});
83+
84+
window.eruda._shadowRoot.querySelector(
85+
".eruda-entry-btn",
86+
).style.display = "none";
87+
88+
erudaInstance = window.eruda;
89+
isInitialized = true;
90+
}
91+
} catch (error) {
92+
console.error("Failed to initialize developer tools", error);
93+
throw error;
94+
}
95+
},
96+
97+
/**
98+
* Show the inspector panel
99+
*/
100+
show() {
101+
if (!isInitialized) {
102+
window.toast?.("Developer mode is not enabled");
103+
return;
104+
}
105+
const entryBtn =
106+
erudaInstance?._shadowRoot?.querySelector(".eruda-entry-btn");
107+
if (entryBtn) entryBtn.style.display = "";
108+
erudaInstance?.show();
109+
},
110+
111+
/**
112+
* Hide the inspector panel
113+
*/
114+
hide() {
115+
if (!isInitialized) return;
116+
erudaInstance?.hide();
117+
const entryBtn =
118+
erudaInstance?._shadowRoot?.querySelector(".eruda-entry-btn");
119+
if (entryBtn) entryBtn.style.display = "none";
120+
},
121+
122+
/**
123+
* Toggle the inspector panel visibility
124+
*/
125+
toggle() {
126+
if (!isInitialized) {
127+
window.toast?.("Developer mode is not enabled");
128+
return;
129+
}
130+
if (erudaInstance?._isShow) {
131+
this.hide();
132+
} else {
133+
this.show();
134+
}
135+
},
136+
137+
/**
138+
* Destroy Eruda instance
139+
*/
140+
destroy() {
141+
if (!isInitialized) return;
142+
erudaInstance?.destroy();
143+
erudaInstance = null;
144+
isInitialized = false;
145+
const script = document.getElementById("eruda-script");
146+
if (script) script.remove();
147+
},
148+
};
149+
150+
export default devTools;

src/lib/keyBindings.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -703,4 +703,10 @@ export default {
703703
readOnly: true,
704704
action: "run-tests",
705705
},
706+
toggleInspector: {
707+
description: "Toggle Inspector",
708+
key: "Ctrl-Shift-I",
709+
readOnly: true,
710+
action: "toggle-inspector",
711+
},
706712
};

src/lib/settings.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ class Settings {
177177
showSideButtons: true,
178178
showAnnotations: false,
179179
pluginsDisabled: {}, // pluginId: true/false
180+
developerMode: false,
180181
};
181182
this.value = structuredClone(this.#defaultSettings);
182183
}

src/main.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,15 @@ async function onDeviceReady() {
219219
acode.setLoadingMessage("Loading language...");
220220
await lang.set(settings.value.lang);
221221

222+
if (settings.value.developerMode) {
223+
try {
224+
const devTools = (await import("lib/devTools")).default;
225+
await devTools.init(false);
226+
} catch (error) {
227+
console.error("Failed to initialize developer tools", error);
228+
}
229+
}
230+
222231
try {
223232
await loadApp();
224233
} catch (error) {

src/settings/appSettings.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,12 @@ export default function otherSettings() {
7777
value: values.console,
7878
select: [appSettings.CONSOLE_LEGACY, appSettings.CONSOLE_ERUDA],
7979
},
80+
{
81+
key: "developerMode",
82+
text: strings["developer mode"],
83+
checkbox: values.developerMode,
84+
info: strings["info-developerMode"],
85+
},
8086
{
8187
key: "cleanInstallState",
8288
text: strings["clean install state"],
@@ -247,6 +253,30 @@ export default function otherSettings() {
247253
} catch (error) {
248254
helpers.error(error);
249255
}
256+
break;
257+
}
258+
259+
case "developerMode": {
260+
if (value) {
261+
const devTools = (await import("lib/devTools")).default;
262+
try {
263+
await devTools.init(true);
264+
toast(
265+
strings["developer mode enabled"] ||
266+
"Developer mode enabled. Use command palette to toggle inspector.",
267+
);
268+
} catch (error) {
269+
helpers.error(error);
270+
value = false;
271+
}
272+
} else {
273+
const devTools = (await import("lib/devTools")).default;
274+
devTools.destroy();
275+
toast(
276+
strings["developer mode disabled"] || "Developer mode disabled",
277+
);
278+
}
279+
break;
250280
}
251281

252282
case "cleanInstallState": {

0 commit comments

Comments
 (0)