// ===== Editor Core =====
const editor = ace.edit("editor");
// Disable workers to avoid security errors in sandboxed environments
editor.session.setUseWorker(false);
editor.session.setMode("ace/mode/html");
editor.setTheme("ace/theme/monokai");
editor.setOptions({
tabSize: 2,
useSoftTabs: true,
showPrintMargin: false,
wrap: false,
enableBasicAutocompletion: true,
enableLiveAutocompletion: true,
enableSnippets: true,
fontSize: "14px",
});
// Demo content
// ===== STRESS DEMO for Ace tokens (HTML mode) =====
// ===== STRESS DEMO with @editIdentifiers markers =====
// ===== STRESS DEMO with @editIdentifiers markers in HTML + JS comments =====
// ===== STRESS DEMO with inline // @editIdentifiers markers =====
const demo = `<!doctype html>
<html lang="en" data-app="stress">
<head>
<meta charset="utf-8">
<title>Hello</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- @editIdentifiers@ -->
<style>
:root { --brand:#4B9; --pad: 8px }
body[data-theme="dark"] .hi { font: italic 600 1.5rem/1.3 system-ui; margin: var(--pad); }
</style>
</head>
<body>
<h1 title="Greeting" data-role="title">Hello, world! & 👋</h1>
<button id="btn">Click me</button>
<!-- Scoped: only identifiers named sayHello and user -->
<script type="module">
const user = { name: "Ada" }; // @editIdentifiers.sayHello.user@
function sayHello(name) { console.log("Hello,", name); } // @editIdentifiers.sayHello.user@
sayHello(user.name); // @editIdentifiers.sayHello.user@
const notMe = 123; // ignored
function helper() { return "nope"; } // ignored
</script>
<!-- Different scope: only 'x' and 'y' -->
<script>
let x = 10, y = 20; // @editIdentifiers.x.y@
function add(x, y) { return x + y; } // @editIdentifiers.x.y@
const sum = add(x, y); // @editIdentifiers.x.y@
let z = 3; // ignored
function mul(a, b) { return a * b; } // ignored
const t = mul(z, sum); // ignored
</script>
<!-- Catch all -->
<script>
const n = 10, s = "10", b = true; // @editIdentifiers@
function helper(rate = 0.25) { return Number(rate) * 2; } // @editIdentifiers@
function main(name = \`User-\${n}\`) { // @editIdentifiers@
for (let i = 0; i < 3; i++) { // @editIdentifiers@
if (i === 1 && b) console.log(name, i, new Date().toISOString()); // @editIdentifiers@
}
document.getElementById('btn')?.addEventListener('click', (ev) => { // @editIdentifiers@
ev.preventDefault(); // @editIdentifiers@
alert('Hello from JavaScript! ' + helper(0.5)); // @editIdentifiers@
});
}
main(); // @editIdentifiers@
</script>
<!-- Control: no marker here -->
<script>
const locked = 1;
function closed() { return locked + 1; }
const result = closed();
</script>
<div id="PLAY" data-add-here>
<!-- You can paste extra code with inline markers like:
let foo = 1; // @editIdentifiers.foo@
-->
</div>
</body>
</html>`;
editor.setValue(demo, -1);
// ===== Helpers =====
const $ = (id) => document.getElementById(id);
// ===== Settings Panel & Tabs =====
const panel = $("panel");
const settingsBtn = $("settingsBtn");
settingsBtn.addEventListener("click", (e) => {
panel.classList.toggle("open");
e.stopPropagation();
});
document.addEventListener("click", (e) => {
if (!panel.contains(e.target) && !settingsBtn.contains(e.target)) {
panel.classList.remove("open");
}
});
// Tab management
const tabEditor = $("tab-editor");
const tabAI = $("tab-ai");
const panelEditor = $("panel-editor");
const panelAI = $("panel-ai");
function selectTab(which) {
const isEditor = which === "editor";
tabEditor.setAttribute("aria-selected", String(isEditor));
tabAI.setAttribute("aria-selected", String(!isEditor));
panelEditor.classList.toggle("active", isEditor);
panelAI.classList.toggle("active", !isEditor);
}
tabEditor.addEventListener("click", () => selectTab("editor"));
tabAI.addEventListener("click", () => selectTab("ai"));
// ===== Settings & Preferences =====
const themeSel = $("theme");
const wrapChk = $("wrap");
const fontRange = $("fontsize");
const modeSel = $("mode");
const aiProvider = $("ai-provider");
const aiModel = $("ai-model");
const aiTemp = $("ai-temp");
const aiKey = $("ai-key");
// Safe localStorage access with fallback
function getPrefs() {
try {
return JSON.parse(localStorage?.getItem?.("ace_min_prefs") || "{}");
} catch (e) {
console.warn("localStorage not available, using defaults");
return {};
}
}
function savePrefs() {
try {
localStorage?.setItem?.("ace_min_prefs", JSON.stringify({
theme: editor.getTheme(),
wrap: editor.session.getUseWrapMode(),
fontSize: editor.getFontSize(),
mode: editor.session.getMode().$id,
ai: {
provider: aiProvider.value,
model: aiModel.value,
temperature: parseFloat(aiTemp.value),
key: aiKey.value
}
}));
} catch (e) {
console.warn("Could not save preferences:", e.message);
}
}
// Load saved preferences
const prefs = getPrefs();
if (prefs.theme) {
editor.setTheme(prefs.theme);
themeSel.value = prefs.theme;
}
if (prefs.wrap != null) {
editor.session.setUseWrapMode(!!prefs.wrap);
wrapChk.checked = !!prefs.wrap;
}
if (prefs.fontSize) {
editor.setFontSize(prefs.fontSize);
fontRange.value = parseInt(prefs.fontSize);
}
if (prefs.mode) {
editor.session.setMode(prefs.mode);
modeSel.value = prefs.mode;
}
if (prefs.ai) {
aiProvider.value = prefs.ai.provider ?? "none";
aiModel.value = prefs.ai.model ?? "";
aiTemp.value = prefs.ai.temperature ?? 0.4;
aiKey.value = prefs.ai.key ?? "";
}
// Settings event listeners
themeSel.addEventListener("change", (e) => {
editor.setTheme(e.target.value);
savePrefs();
});
wrapChk.addEventListener("change", (e) => {
editor.session.setUseWrapMode(e.target.checked);
savePrefs();
});
fontRange.addEventListener("input", (e) => {
editor.setFontSize(e.target.value + "px");
});
fontRange.addEventListener("change", savePrefs);
modeSel.addEventListener("change", (e) => {
editor.session.setMode(e.target.value);
savePrefs();
});
// AI settings
aiProvider.addEventListener("change", savePrefs);
aiModel.addEventListener("change", savePrefs);
aiTemp.addEventListener("input", savePrefs);
aiKey.addEventListener("change", savePrefs);
// ===== Export for other modules =====
window.editorAPI = {
editor,
getSelectedText: () => editor.getSelectedText(),
getValue: () => editor.getValue(),
focus: () => editor.focus(),
savePrefs,
getPrefs
};
console.log("Editor module loaded successfully");