diff --git a/package-lock.json b/package-lock.json
index fc85203ce..3e8ea3c81 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -153,7 +153,6 @@
"integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@babel/code-frame": "^7.29.0",
"@babel/generator": "^7.29.0",
@@ -1979,7 +1978,6 @@
"resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.20.0.tgz",
"integrity": "sha512-bOwvTOIJcG5FVo5gUUupiwYh8MioPLQ4UcqbcRf7UQ98X90tCa9E1kZ3Z7tqwpZxYyOvh1YTYbmZE9RTfTp5hg==",
"license": "MIT",
- "peer": true,
"dependencies": {
"@codemirror/language": "^6.0.0",
"@codemirror/state": "^6.0.0",
@@ -1992,7 +1990,6 @@
"resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.10.2.tgz",
"integrity": "sha512-vvX1fsih9HledO1c9zdotZYUZnE4xV0m6i3m25s5DIfXofuprk6cRcLUZvSk3CASUbwjQX21tOGbkY2BH8TpnQ==",
"license": "MIT",
- "peer": true,
"dependencies": {
"@codemirror/language": "^6.0.0",
"@codemirror/state": "^6.4.0",
@@ -2029,7 +2026,6 @@
"resolved": "https://registry.npmjs.org/@codemirror/lang-css/-/lang-css-6.3.1.tgz",
"integrity": "sha512-kr5fwBGiGtmz6l0LSJIbno9QrifNMUusivHbnA1H6Dmqy4HZFte3UAICix1VuKo0lMPKQr2rqB+0BkKi/S3Ejg==",
"license": "MIT",
- "peer": true,
"dependencies": {
"@codemirror/autocomplete": "^6.0.0",
"@codemirror/language": "^6.0.0",
@@ -2056,7 +2052,6 @@
"resolved": "https://registry.npmjs.org/@codemirror/lang-html/-/lang-html-6.4.11.tgz",
"integrity": "sha512-9NsXp7Nwp891pQchI7gPdTwBuSuT3K65NGTHWHNJ55HjYcHLllr0rbIZNdOzas9ztc1EUVBlHou85FFZS4BNnw==",
"license": "MIT",
- "peer": true,
"dependencies": {
"@codemirror/autocomplete": "^6.0.0",
"@codemirror/lang-css": "^6.0.0",
@@ -2284,7 +2279,6 @@
"resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.12.1.tgz",
"integrity": "sha512-Fa6xkSiuGKc8XC8Cn96T+TQHYj4ZZ7RdFmXA3i9xe/3hLHfwPZdM+dqfX0Cp0zQklBKhVD8Yzc8LS45rkqcwpQ==",
"license": "MIT",
- "peer": true,
"dependencies": {
"@codemirror/state": "^6.0.0",
"@codemirror/view": "^6.23.0",
@@ -2377,7 +2371,6 @@
"resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.5.4.tgz",
"integrity": "sha512-8y7xqG/hpB53l25CIoit9/ngxdfoG+fx+V3SHBrinnhOtLvKHRyAJJuHzkWrR4YXXLX8eXBsejgAAxHUOdW1yw==",
"license": "MIT",
- "peer": true,
"dependencies": {
"@marijn/find-cluster-break": "^1.0.0"
}
@@ -2399,7 +2392,6 @@
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.39.15.tgz",
"integrity": "sha512-aCWjgweIIXLBHh7bY6cACvXuyrZ0xGafjQ2VInjp4RM4gMfscK5uESiNdrH0pE+e1lZr2B4ONGsjchl2KsKZzg==",
"license": "MIT",
- "peer": true,
"dependencies": {
"@codemirror/state": "^6.5.0",
"crelt": "^1.0.6",
@@ -4212,7 +4204,6 @@
"integrity": "sha512-Iax6UhrfZqJajA778c1d5DBFbSIqPOSrI34kpNIiNpWd8Jq7mFIa+Z60SQb5ZQDZuUxcCZikjz5BxinFjTkg7Q==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@module-federation/runtime-tools": "0.22.0",
"@rspack/binding": "1.7.6",
@@ -4502,7 +4493,8 @@
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz",
"integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==",
- "license": "MIT"
+ "license": "MIT",
+ "peer": true
},
"node_modules/@types/markdown-it": {
"version": "14.1.2",
@@ -4519,7 +4511,8 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz",
"integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==",
- "license": "MIT"
+ "license": "MIT",
+ "peer": true
},
"node_modules/@types/mime": {
"version": "1.3.5",
@@ -4936,8 +4929,7 @@
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-5.5.0.tgz",
"integrity": "sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A==",
- "license": "MIT",
- "peer": true
+ "license": "MIT"
},
"node_modules/@xtuc/ieee754": {
"version": "1.2.0",
@@ -5014,7 +5006,6 @@
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz",
"integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
"license": "MIT",
- "peer": true,
"bin": {
"acorn": "bin/acorn"
},
@@ -5063,7 +5054,6 @@
"integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"fast-deep-equal": "^3.1.3",
"fast-uri": "^3.0.1",
@@ -5580,7 +5570,6 @@
}
],
"license": "MIT",
- "peer": true,
"dependencies": {
"baseline-browser-mapping": "^2.9.0",
"caniuse-lite": "^1.0.30001759",
@@ -5743,7 +5732,6 @@
"integrity": "sha512-ci2iJH6LeIkvP9eJW6gpueU8cnZhv85ELY8w8WiFtNjMHA5ad6pQLaJo9mEly/9qUyCpvqX8/POVUTf18/HFdw==",
"dev": true,
"license": "Apache-2.0",
- "peer": true,
"dependencies": {
"@chevrotain/cst-dts-gen": "11.0.3",
"@chevrotain/gast": "11.0.3",
@@ -8977,7 +8965,6 @@
"resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.1.tgz",
"integrity": "sha512-BuU2qnTti9YKgK5N+IeMubp14ZUKUUw7yeJbkjtosvHiP0AZ5c8IAgEMk79D0eC8F23r4Ac/q8cAIFdm2FtyoA==",
"license": "MIT",
- "peer": true,
"dependencies": {
"argparse": "^2.0.1",
"entities": "^4.4.0",
@@ -10076,7 +10063,6 @@
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"license": "MIT",
- "peer": true,
"engines": {
"node": ">=12"
},
@@ -10197,7 +10183,6 @@
}
],
"license": "MIT",
- "peer": true,
"dependencies": {
"nanoid": "^3.3.11",
"picocolors": "^1.1.1",
@@ -10341,7 +10326,6 @@
"integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==",
"dev": true,
"license": "MIT",
- "peer": true,
"bin": {
"prettier": "bin/prettier.cjs"
},
@@ -10612,7 +10596,6 @@
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
@@ -12039,8 +12022,7 @@
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
"dev": true,
- "license": "0BSD",
- "peer": true
+ "license": "0BSD"
},
"node_modules/tuf-js": {
"version": "4.1.0",
@@ -12108,7 +12090,6 @@
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"dev": true,
"license": "Apache-2.0",
- "peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@@ -12411,7 +12392,6 @@
"integrity": "sha512-dRXm0a2qcHPUBEzVk8uph0xWSjV/xZxenQQbLwnwP7caQCYpqG1qddwlyEkIDkYn0K8tvmcrZ+bOrzoQ3HxCDw==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@types/eslint-scope": "^3.7.7",
"@types/estree": "^1.0.8",
diff --git a/src/cm/lsp/workspace.ts b/src/cm/lsp/workspace.ts
index d6218843b..01a071dee 100644
--- a/src/cm/lsp/workspace.ts
+++ b/src/cm/lsp/workspace.ts
@@ -109,7 +109,12 @@ export default class AcodeWorkspace extends Workspace {
if (mode?.name) {
return String(mode.name).toLowerCase();
}
- } catch (_) {}
+ } catch (error) {
+ console.warn(
+ `[LSP:Workspace] Failed to resolve language id for ${uri}`,
+ error,
+ );
+ }
return "plaintext";
}
diff --git a/src/lib/editorFile.js b/src/lib/editorFile.js
index e5d8113bd..2863027e5 100644
--- a/src/lib/editorFile.js
+++ b/src/lib/editorFile.js
@@ -1000,7 +1000,12 @@ export default class EditorFile {
EditorState.readOnly.of(!!value),
),
});
- } catch (_) {}
+ } catch (error) {
+ console.warn(
+ `Failed to update read-only state for ${this.filename || this.uri}`,
+ error,
+ );
+ }
// Sync internal flags and header
this.readOnly = !!value;
@@ -1072,7 +1077,9 @@ export default class EditorFile {
// Ensure any native DOM selection is cleared on blur to avoid sticky selection handles
try {
document.getSelection()?.removeAllRanges();
- } catch (_) {}
+ } catch (error) {
+ console.warn("Failed to clear native text selection.", error);
+ }
}
} else {
editorManager.container.style.display = "none";
@@ -1322,7 +1329,9 @@ export default class EditorFile {
if (activeFile?.id === this.id) {
emit("file-loaded", this);
}
- } catch (_) {}
+ } catch (error) {
+ console.warn("Failed to emit interim file-loaded event.", error);
+ }
try {
const cacheFs = fsOperation(this.cacheFile);
diff --git a/src/lib/editorManager.js b/src/lib/editorManager.js
index 2e7b91e03..fb57388d4 100644
--- a/src/lib/editorManager.js
+++ b/src/lib/editorManager.js
@@ -106,6 +106,15 @@ async function EditorManager($header, $body) {
let touchSelectionController = null;
let touchSelectionSyncRaf = 0;
let nativeContextMenuDisabled = null;
+ const recoverableWarningKeys = new Set();
+
+ function warnRecoverable(message, error, key) {
+ if (key) {
+ if (recoverableWarningKeys.has(key)) return;
+ recoverableWarningKeys.add(key);
+ }
+ console.warn(message, error);
+ }
const setNativeContextMenuDisabled = (disabled) => {
const value = !!disabled;
@@ -626,7 +635,13 @@ async function EditorManager($header, $body) {
try {
const guess = getModeForPath(file.filename || file.name || "");
if (guess?.name) return String(guess.name).toLowerCase();
- } catch (_) {}
+ } catch (error) {
+ warnRecoverable(
+ `Failed to resolve language id for ${file.filename || file.name || "untitled file"}`,
+ error,
+ "language-id-resolution",
+ );
+ }
return "plaintext";
}
@@ -634,11 +649,9 @@ async function EditorManager($header, $body) {
const uri = context.uri || context.file?.uri;
if (!uri) return null;
for (const folder of addedFolder) {
- try {
- const base = folder?.url;
- if (!base) continue;
- if (uri.startsWith(base)) return base;
- } catch (_) {}
+ const base = typeof folder?.url === "string" ? folder.url : "";
+ if (!base) continue;
+ if (uri.startsWith(base)) return base;
}
return uri;
}
@@ -1153,7 +1166,13 @@ async function EditorManager($header, $body) {
editor.dispatch({
effects: languageCompartment.reconfigure(ext || []),
});
- } catch (_) {}
+ } catch (error) {
+ warnRecoverable(
+ "Failed to apply async language extensions.",
+ error,
+ "async-language-reconfigure",
+ );
+ }
})
.catch(() => {
// ignore load errors; remain in plain text
@@ -1208,7 +1227,13 @@ async function EditorManager($header, $body) {
const mainIndex = sel.mainIndex ?? 0;
restoreSelection(editor, { ranges, mainIndex });
}
- } catch (_) {}
+ } catch (error) {
+ warnRecoverable(
+ "Failed to restore selection from previous session state.",
+ error,
+ "restore-selection",
+ );
+ }
// Restore folds from previous state if available
try {
@@ -1216,7 +1241,13 @@ async function EditorManager($header, $body) {
if (folds && folds.length) {
restoreFolds(editor, folds);
}
- } catch (_) {}
+ } catch (error) {
+ warnRecoverable(
+ "Failed to restore folded regions from previous session state.",
+ error,
+ "restore-folds",
+ );
+ }
// Restore last known scroll position if present
if (
@@ -1360,9 +1391,7 @@ async function EditorManager($header, $body) {
const listener = () => {
const active = manager.activeFile;
if (active?.type === "editor") {
- try {
- active.session = editor.state;
- } catch (_) {}
+ active.session = editor.state;
}
toggleProblemButton();
};
@@ -1424,7 +1453,13 @@ async function EditorManager($header, $body) {
try {
const mode = getModeForPath(uri);
if (mode?.name) return String(mode.name).toLowerCase();
- } catch (_) {}
+ } catch (error) {
+ warnRecoverable(
+ `Failed to resolve language id for URI: ${uri}`,
+ error,
+ "lsp-language-id-resolution",
+ );
+ }
return "plaintext";
},
clientExtensions: [diagnosticsClientExt],
@@ -1440,7 +1475,14 @@ async function EditorManager($header, $body) {
try {
const desired = appSettings?.value?.editorTheme || "one_dark";
editor.setTheme(desired);
- } catch (_) {}
+ } catch (error) {
+ warnRecoverable(
+ "Failed to apply configured editor theme. Falling back to one_dark.",
+ error,
+ "initial-editor-theme",
+ );
+ editor.setTheme("one_dark");
+ }
// Ensure initial options reflect settings
applyOptions();
@@ -1608,9 +1650,7 @@ async function EditorManager($header, $body) {
if (!update.docChanged) return;
// Mirror latest state only on doc changes to avoid clobbering async loads
- try {
- file.session = update.state;
- } catch (_) {}
+ file.session = update.state;
// Debounced change handling (unsaved flag, cache, autosave)
if (checkTimeout) clearTimeout(checkTimeout);
@@ -1621,7 +1661,13 @@ async function EditorManager($header, $body) {
file.isUnsaved = changed;
try {
await file.writeToCache();
- } catch (_) {}
+ } catch (error) {
+ warnRecoverable(
+ `Failed to write cache for ${file.filename || file.uri}`,
+ error,
+ `cache-write-${file.id}`,
+ );
+ }
events.emit("file-content-changed", file);
manager.onupdate("file-changed");
@@ -1657,7 +1703,12 @@ async function EditorManager($header, $body) {
effects: readOnlyCompartment.reconfigure(EditorState.readOnly.of(ro)),
});
touchSelectionController?.onStateChanged();
- } catch (_) {
+ } catch (error) {
+ warnRecoverable(
+ "Failed to apply read-only compartment update. Recreating editor state.",
+ error,
+ "readonly-reconfigure",
+ );
// Fallback: full re-apply
applyFileToEditor(file);
}
@@ -1682,7 +1733,13 @@ async function EditorManager($header, $body) {
editor.dispatch({
effects: StateEffect.appendConfig.of(getDocSyncListener()),
});
- } catch (_) {}
+ } catch (error) {
+ warnRecoverable(
+ "Failed to attach document sync listener to editor.",
+ error,
+ "doc-sync-listener",
+ );
+ }
return manager;
@@ -2043,14 +2100,26 @@ async function EditorManager($header, $body) {
try {
const annotations = session.getAnnotations() || [];
if (annotations.length) return true;
- } catch (_) {}
+ } catch (error) {
+ warnRecoverable(
+ "Failed to read editor annotations while checking problems.",
+ error,
+ "read-annotations",
+ );
+ }
}
if (typeof state.field !== "function") return false;
try {
const diagnostics = getLspDiagnostics(state);
return diagnostics.length > 0;
- } catch (_) {}
+ } catch (error) {
+ warnRecoverable(
+ "Failed to read LSP diagnostics while checking problems.",
+ error,
+ "read-lsp-diagnostics",
+ );
+ }
return false;
}
@@ -2135,13 +2204,9 @@ async function EditorManager($header, $body) {
// Persist the previous editor's state before switching away
const prev = manager.activeFile;
if (prev?.type === "editor") {
- try {
- prev.session = editor.state;
- } catch (_) {}
- try {
- prev.lastScrollTop = editor.scrollDOM?.scrollTop || 0;
- prev.lastScrollLeft = editor.scrollDOM?.scrollLeft || 0;
- } catch (_) {}
+ prev.session = editor.state;
+ prev.lastScrollTop = editor.scrollDOM?.scrollTop || 0;
+ prev.lastScrollLeft = editor.scrollDOM?.scrollLeft || 0;
}
manager.activeFile = file;
diff --git a/src/lib/notificationManager.js b/src/lib/notificationManager.js
index 52518402d..2ff308981 100644
--- a/src/lib/notificationManager.js
+++ b/src/lib/notificationManager.js
@@ -1,4 +1,5 @@
import sidebarApps from "sidebarApps";
+import DOMPurify from "dompurify";
// Singleton instance
let instance = null;
@@ -107,21 +108,24 @@ export default class NotificationManager {
data-id={notification.id}
>
);
+ const safeIcon = this.sanitizeIcon(this.parseIcon(notification.icon));
+ const safeTitle = this.sanitizeText(notification.title);
+ const safeMessage = this.sanitizeText(notification.message);
element.innerHTML = `
-
- ${this.parseIcon(notification.icon)}
-
-
-
- ${notification.title}
-
${this.formatTime(notification.time)}
+
+ ${safeIcon}
-
${notification.message}
-
-
Dismiss
+
+
+ ${safeTitle}
+ ${this.formatTime(notification.time)}
+
+
${safeMessage}
+
-
- `;
+ `;
if (notification.action) {
element.addEventListener("click", (e) => {
if (e.target.closest(".action-button")) {
@@ -140,16 +144,27 @@ export default class NotificationManager {
data-id={notification.id}
>
);
+ const safeIcon = this.sanitizeIcon(this.parseIcon(notification.icon));
+ const safeTitle = this.sanitizeText(notification.title);
+ const safeMessage = this.sanitizeText(notification.message);
element.innerHTML = `
-
${this.parseIcon(notification.icon)}
-
-
- ${notification.title}
+
${safeIcon}
+
+
+ ${safeTitle}
+
+
${safeMessage}
-
${notification.message}
-
- ${notification.autoClose ? "" : `
`}
- `;
+ ${notification.autoClose ? "" : `
`}
+ `;
+
+ const closeIcon = element.querySelector(".close-icon");
+ if (closeIcon) {
+ closeIcon.addEventListener("click", (event) => {
+ event.stopPropagation();
+ element.remove();
+ });
+ }
if (notification.action) {
element.addEventListener("click", () =>
notification.action(notification),
@@ -202,13 +217,27 @@ export default class NotificationManager {
}
parseIcon(icon) {
- if (!icon) return this.DEFAULT_ICON;
+ if (typeof icon !== "string" || !icon) return this.DEFAULT_ICON;
if (icon.startsWith("