From 97dabde57400bbc95dc56da26d09831fbe5c6baa Mon Sep 17 00:00:00 2001 From: Raunak Raj <71929976+bajrangCoder@users.noreply.github.com> Date: Tue, 9 Jun 2026 13:55:41 +0530 Subject: [PATCH 1/2] fix(editor): stabilize restored selection and loading state which was causing two edge cases --- bun.lock | 7 +++++++ src/lib/editorFile.js | 31 +++++++++++++++++-------------- src/lib/saveState.js | 18 ++++++++++++++++++ 3 files changed, 42 insertions(+), 14 deletions(-) diff --git a/bun.lock b/bun.lock index 977d48ad1..1e52fe600 100644 --- a/bun.lock +++ b/bun.lock @@ -91,9 +91,11 @@ "@rspack/cli": "^2.0.0", "@rspack/core": "^2.0.0", "@types/ace": "^0.0.52", + "@types/cordova": "^11.0.3", "@types/url-parse": "^1.4.11", "autoprefixer": "^10.5.0", "babel-loader": "^10.1.1", + "chokidar": "^4.0.3", "com.foxdebug.acode.rk.auth": "file:src/plugins/auth", "com.foxdebug.acode.rk.customtabs": "file:src/plugins/custom-tabs", "com.foxdebug.acode.rk.exec.proot": "file:src/plugins/proot", @@ -127,6 +129,7 @@ "ts-loader": "^9.5.7", "typescript": "^5.9.3", "vscode-languageserver-types": "^3.17.5", + "ws": "^8.21.0", }, }, }, @@ -622,6 +625,8 @@ "@types/ace": ["@types/ace@0.0.52", "", {}, "sha512-YPF9S7fzpuyrxru+sG/rrTpZkC6gpHBPF14W3x70kqVOD+ks6jkYLapk4yceh36xej7K4HYxcyz9ZDQ2lTvwgQ=="], + "@types/cordova": ["@types/cordova@11.0.3", "", {}, "sha512-kyuRQ40/NWQVhqGIHq78Ehu2Bf9Mlg0LhmSmis6ZFJK7z933FRfYi8tHe/k/0fB+PGfCf95rJC6TO7dopaFvAg=="], + "@types/d3": ["@types/d3@7.4.3", "", { "dependencies": { "@types/d3-array": "*", "@types/d3-axis": "*", "@types/d3-brush": "*", "@types/d3-chord": "*", "@types/d3-color": "*", "@types/d3-contour": "*", "@types/d3-delaunay": "*", "@types/d3-dispatch": "*", "@types/d3-drag": "*", "@types/d3-dsv": "*", "@types/d3-ease": "*", "@types/d3-fetch": "*", "@types/d3-force": "*", "@types/d3-format": "*", "@types/d3-geo": "*", "@types/d3-hierarchy": "*", "@types/d3-interpolate": "*", "@types/d3-path": "*", "@types/d3-polygon": "*", "@types/d3-quadtree": "*", "@types/d3-random": "*", "@types/d3-scale": "*", "@types/d3-scale-chromatic": "*", "@types/d3-selection": "*", "@types/d3-shape": "*", "@types/d3-time": "*", "@types/d3-time-format": "*", "@types/d3-timer": "*", "@types/d3-transition": "*", "@types/d3-zoom": "*" } }, "sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww=="], "@types/d3-array": ["@types/d3-array@3.2.2", "", {}, "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw=="], @@ -1656,6 +1661,8 @@ "write-file-atomic": ["write-file-atomic@3.0.3", "", { "dependencies": { "imurmurhash": "^0.1.4", "is-typedarray": "^1.0.0", "signal-exit": "^3.0.2", "typedarray-to-buffer": "^3.1.5" } }, "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q=="], + "ws": ["ws@8.21.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g=="], + "xdg-basedir": ["xdg-basedir@4.0.0", "", {}, "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q=="], "xmlbuilder": ["xmlbuilder@15.1.1", "", {}, "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg=="], diff --git a/src/lib/editorFile.js b/src/lib/editorFile.js index 1a6c43a97..736e36ce2 100644 --- a/src/lib/editorFile.js +++ b/src/lib/editorFile.js @@ -927,7 +927,7 @@ export default class EditorFile { ); } - markLoaded({ mtime, isUnsaved = false } = {}) { + markLoaded({ mtime, isUnsaved = false, savedDoc = null } = {}) { const normalizedMtime = helpers.normalizeMtime(mtime); this.docVersion = isUnsaved ? 1 : 0; this.savedVersion = isUnsaved ? 0 : this.docVersion; @@ -936,7 +936,8 @@ export default class EditorFile { this.diskMtime = normalizedMtime; this.hasDiskConflict = false; this.#hasVersionMetadata = true; - this.#savedDoc = isUnsaved ? null : this.#rawSession?.doc || null; + this.#savedDoc = + savedDoc || (isUnsaved ? null : this.#rawSession?.doc || null); this.isUnsaved = isUnsaved || this.hasUnsavedChanges(); } @@ -1573,9 +1574,9 @@ export default class EditorFile { this.loading = true; this.markChanged = false; this.#emit("loadstart", createFileEvent(this)); - this.session.setValue(strings["loading..."]); - // Immediately reflect "loading..." in the visible editor if this tab is active + // Immediately apply the loading read-only state without inserting placeholder + // text into the real document or undo history. try { const { activeFile, emit } = editorManager; if (activeFile?.id === this.id) { @@ -1589,6 +1590,7 @@ export default class EditorFile { const cacheFs = fsOperation(this.cacheFile); const cacheExists = await cacheFs.exists(); let loadedMtime = this.savedMtime; + let savedDoc = null; if (cacheExists) { value = await cacheFs.readFile(this.encoding); @@ -1600,13 +1602,14 @@ export default class EditorFile { if (!fileExists && cacheExists) { this.deletedFile = true; this.isUnsaved = true; - } else if (!cacheExists && fileExists) { - const stat = await file.stat().catch(() => null); - loadedMtime = helpers.getStatMtime(stat); - value = await file.readFile(this.encoding); } else if (fileExists) { const stat = await file.stat().catch(() => null); loadedMtime = helpers.getStatMtime(stat); + const diskValue = await file.readFile(this.encoding); + savedDoc = EditorState.create({ doc: diskValue }).doc; + if (!cacheExists) { + value = diskValue; + } } else if (!cacheExists && !fileExists) { window.log("error", "unable to load file"); throw new Error("Unable to load file"); @@ -1615,27 +1618,27 @@ export default class EditorFile { const isUnsaved = this.isUnsaved; this.markChanged = false; - this.session.setValue(value); - this.markLoaded({ mtime: loadedMtime, isUnsaved }); + this.session = EditorState.create({ doc: value }); + this.__cmSessionReady = false; + this.markLoaded({ mtime: loadedMtime, isUnsaved, savedDoc }); this.markChanged = true; this.loaded = true; this.loading = false; const { activeFile, emit } = editorManager; - if (activeFile.id === this.id) { - this.setReadOnly(false); + if (activeFile?.id === this.id) { + this.setReadOnly(editable === false); + emit("file-loaded", this); } setTimeout(() => { this.#emit("load", createFileEvent(this)); - emit("file-loaded", this); if (cursorPos) { restoreSelection(editor, cursorPos); } if (scrollTop || scrollLeft) { setScrollPosition(editor, scrollTop, scrollLeft); } - if (editable !== undefined) this.editable = editable; restoreFolds(editor, folds); }, 0); } catch (error) { diff --git a/src/lib/saveState.js b/src/lib/saveState.js index 94b0ffffb..784304d47 100644 --- a/src/lib/saveState.js +++ b/src/lib/saveState.js @@ -33,6 +33,7 @@ export default () => { cursorPos = null; } } + cursorPos = collapseSelectionForRestore(cursorPos); // Scroll per file: // - Active file uses live scroll from EditorView @@ -96,3 +97,20 @@ export default () => { localStorage.files = JSON.stringify(filesToSave); localStorage.folders = JSON.stringify(folders); }; + +function collapseSelectionForRestore(selection) { + if (!selection?.ranges?.length) return selection; + + const mainIndex = + selection.mainIndex >= 0 && selection.mainIndex < selection.ranges.length + ? selection.mainIndex + : 0; + const main = selection.ranges[mainIndex]; + const head = Number.isFinite(main?.to) ? main.to : (main?.from ?? 0); + const cursor = Math.max(0, head | 0); + + return { + ranges: [{ from: cursor, to: cursor }], + mainIndex: 0, + }; +} From ea93f73d199fceb0d0af6d4f9ac515e4604e8f56 Mon Sep 17 00:00:00 2001 From: Raunak Raj <71929976+bajrangCoder@users.noreply.github.com> Date: Tue, 9 Jun 2026 14:04:47 +0530 Subject: [PATCH 2/2] fix --- src/lib/editorFile.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/lib/editorFile.js b/src/lib/editorFile.js index 736e36ce2..5c1cef0f7 100644 --- a/src/lib/editorFile.js +++ b/src/lib/editorFile.js @@ -937,7 +937,7 @@ export default class EditorFile { this.hasDiskConflict = false; this.#hasVersionMetadata = true; this.#savedDoc = - savedDoc || (isUnsaved ? null : this.#rawSession?.doc || null); + savedDoc ?? (isUnsaved ? null : this.#rawSession?.doc || null); this.isUnsaved = isUnsaved || this.hasUnsavedChanges(); } @@ -1629,6 +1629,9 @@ export default class EditorFile { if (activeFile?.id === this.id) { this.setReadOnly(editable === false); emit("file-loaded", this); + } else if (editable !== undefined) { + this.readOnly = !editable; + this.#editable = editable; } setTimeout(() => {