Skip to content

Commit 32a1c68

Browse files
authored
fix: resize of terminal and some small patches (#1562)
* fix: handle null case * remove the test logs * fix: terminal resize issue and also remove weird stuff
1 parent 64f1d9d commit 32a1c68

File tree

4 files changed

+111
-111
lines changed

4 files changed

+111
-111
lines changed

src/components/terminal/terminal.js

Lines changed: 54 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ export default class TerminalComponent {
3232
rows: options.rows || 24,
3333
cols: options.cols || 80,
3434
port: options.port || 8767,
35+
renderer: options.renderer || "auto", // 'auto' | 'canvas' | 'webgl'
3536
fontSize: terminalSettings.fontSize,
3637
fontFamily: terminalSettings.fontFamily,
3738
fontWeight: terminalSettings.fontWeight,
@@ -80,7 +81,7 @@ export default class TerminalComponent {
8081
system.openInBrowser(uri);
8182
}
8283
});
83-
this.webglAddon = new WebglAddon();
84+
this.webglAddon = null;
8485

8586
// Load addons
8687
this.terminal.loadAddon(this.fitAddon);
@@ -244,36 +245,12 @@ export default class TerminalComponent {
244245
// Ensure scroll position is within valid bounds
245246
const safeScrollPosition = Math.min(targetScrollPosition, maxScroll);
246247

247-
// Only adjust if we have significant content and the position is different
248+
// Only adjust if we have significant content and the position differs
248249
if (
249250
buffer.length > this.terminal.rows &&
250-
Math.abs(buffer.viewportY - safeScrollPosition) > 2
251+
buffer.viewportY !== safeScrollPosition
251252
) {
252-
// Gradually adjust to prevent jarring movements
253-
const steps = 3;
254-
const diff = safeScrollPosition - buffer.viewportY;
255-
const stepSize = Math.ceil(Math.abs(diff) / steps);
256-
257-
let currentStep = 0;
258-
const adjustStep = () => {
259-
if (currentStep >= steps) return;
260-
261-
const currentPos = buffer.viewportY;
262-
const remaining = safeScrollPosition - currentPos;
263-
const adjustment =
264-
Math.sign(remaining) * Math.min(stepSize, Math.abs(remaining));
265-
266-
if (Math.abs(adjustment) >= 1) {
267-
this.terminal.scrollLines(adjustment);
268-
}
269-
270-
currentStep++;
271-
if (currentStep < steps && Math.abs(remaining) > 1) {
272-
setTimeout(adjustStep, 50);
273-
}
274-
};
275-
276-
setTimeout(adjustStep, 100);
253+
this.terminal.scrollToLine(safeScrollPosition);
277254
}
278255
}
279256

@@ -468,7 +445,6 @@ export default class TerminalComponent {
468445
position: relative;
469446
background: ${this.options.theme.background};
470447
overflow: hidden;
471-
padding: 0.25rem;
472448
box-sizing: border-box;
473449
`;
474450

@@ -490,32 +466,49 @@ export default class TerminalComponent {
490466
this.container.style.background = this.options.theme.background;
491467

492468
try {
493-
try {
494-
this.terminal.loadAddon(this.webglAddon);
495-
this.terminal.open(container);
496-
} catch (error) {
497-
console.error("Failed to load WebglAddon:", error);
498-
this.webglAddon.dispose();
499-
}
500-
501-
if (!this.terminal.element) {
502-
// webgl loading failed for some reason, attach with DOM renderer
503-
this.terminal.open(container);
469+
// Open first to ensure a stable renderer is attached
470+
this.terminal.open(container);
471+
472+
// Renderer selection: 'canvas' (default core), 'webgl', or 'auto'
473+
if (
474+
this.options.renderer === "webgl" ||
475+
this.options.renderer === "auto"
476+
) {
477+
try {
478+
const addon = new WebglAddon();
479+
this.terminal.loadAddon(addon);
480+
if (typeof addon.onContextLoss === "function") {
481+
addon.onContextLoss(() => this._handleWebglContextLoss());
482+
}
483+
this.webglAddon = addon;
484+
} catch (error) {
485+
console.error("Failed to enable WebGL renderer:", error);
486+
try {
487+
this.webglAddon?.dispose?.();
488+
} catch {}
489+
this.webglAddon = null; // stay on canvas
490+
}
504491
}
505492
const terminalSettings = getTerminalSettings();
506493
// Load ligatures addon if enabled
507494
if (terminalSettings.fontLigatures) {
508495
this.loadLigaturesAddon();
509496
}
510497

511-
// Wait for terminal to render then fit
512-
setTimeout(() => {
513-
this.fitAddon.fit();
514-
this.terminal.focus();
515-
516-
// Initialize touch selection after terminal is mounted
517-
this.setupTouchSelection();
518-
}, 10);
498+
// First render pass: schedule a fit + focus once the frame is ready
499+
if (typeof requestAnimationFrame === "function") {
500+
requestAnimationFrame(() => {
501+
this.fitAddon.fit();
502+
this.terminal.focus();
503+
this.setupTouchSelection();
504+
});
505+
} else {
506+
setTimeout(() => {
507+
this.fitAddon.fit();
508+
this.terminal.focus();
509+
this.setupTouchSelection();
510+
}, 0);
511+
}
519512
} catch (error) {
520513
console.error("Failed to mount terminal:", error);
521514
}
@@ -726,32 +719,6 @@ export default class TerminalComponent {
726719
* Focus terminal
727720
*/
728721
focus() {
729-
// Ensure cursor is visible before focusing to prevent half-visibility
730-
if (this.terminal.buffer && this.terminal.buffer.active) {
731-
const buffer = this.terminal.buffer.active;
732-
const cursorY = buffer.cursorY;
733-
const cursorViewportPos = buffer.baseY + cursorY;
734-
const viewportTop = buffer.viewportY;
735-
const viewportBottom = viewportTop + this.terminal.rows - 1;
736-
737-
// Check if cursor is fully visible (with margin to prevent half-visibility)
738-
const isCursorFullyVisible =
739-
cursorViewportPos >= viewportTop + 1 &&
740-
cursorViewportPos <= viewportBottom - 2;
741-
742-
// If cursor is not fully visible, scroll to make it properly visible
743-
if (!isCursorFullyVisible && buffer.length > this.terminal.rows) {
744-
const targetScroll = Math.max(
745-
0,
746-
Math.min(
747-
buffer.length - this.terminal.rows,
748-
cursorViewportPos - Math.floor(this.terminal.rows * 0.25),
749-
),
750-
);
751-
this.terminal.scrollToLine(targetScroll);
752-
}
753-
}
754-
755722
this.terminal.focus();
756723
}
757724

@@ -1018,3 +985,16 @@ export default class TerminalComponent {
1018985
onBell() {}
1019986
onProcessExit(exitData) {}
1020987
}
988+
989+
// Internal helpers for WebGL renderer lifecycle
990+
TerminalComponent.prototype._handleWebglContextLoss = function () {
991+
try {
992+
console.warn("WebGL context lost; falling back to canvas renderer");
993+
try {
994+
this.webglAddon?.dispose?.();
995+
} catch {}
996+
this.webglAddon = null;
997+
} catch (e) {
998+
console.error("Error handling WebGL context loss:", e);
999+
}
1000+
};

src/components/terminal/terminalManager.js

Lines changed: 54 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,15 @@ class TerminalManager {
4444
id: `terminal-${terminalId}`,
4545
});
4646

47-
// Terminal styles
48-
const terminalStyles = this.getTerminalStyles();
49-
const terminalStyle = tag("style", {
50-
textContent: terminalStyles,
51-
});
52-
document.body.appendChild(terminalStyle);
47+
// Terminal styles (inject once)
48+
if (!document.getElementById("acode-terminal-styles")) {
49+
const terminalStyles = this.getTerminalStyles();
50+
const terminalStyle = tag("style", {
51+
id: "acode-terminal-styles",
52+
textContent: terminalStyles,
53+
});
54+
document.body.appendChild(terminalStyle);
55+
}
5356

5457
// Create EditorFile for terminal
5558
const terminalFile = new EditorFile(terminalName, {
@@ -188,12 +191,15 @@ class TerminalManager {
188191
id: `terminal-${terminalId}`,
189192
});
190193

191-
// Terminal styles
192-
const terminalStyles = this.getTerminalStyles();
193-
const terminalStyle = tag("style", {
194-
textContent: terminalStyles,
195-
});
196-
document.body.appendChild(terminalStyle);
194+
// Terminal styles (inject once)
195+
if (!document.getElementById("acode-terminal-styles")) {
196+
const terminalStyles = this.getTerminalStyles();
197+
const terminalStyle = tag("style", {
198+
id: "acode-terminal-styles",
199+
textContent: terminalStyles,
200+
});
201+
document.body.appendChild(terminalStyle);
202+
}
197203

198204
// Create EditorFile for terminal
199205
const terminalFile = new EditorFile(terminalName, {
@@ -257,10 +263,25 @@ class TerminalManager {
257263
setupTerminalHandlers(terminalFile, terminalComponent, terminalId) {
258264
// Handle tab focus/blur
259265
terminalFile.onfocus = () => {
260-
setTimeout(() => {
266+
// Guarded fit on focus: only fit if cols/rows would change, then focus
267+
const run = () => {
268+
try {
269+
const pd = terminalComponent.fitAddon?.proposeDimensions?.();
270+
if (
271+
pd &&
272+
(pd.cols !== terminalComponent.terminal.cols ||
273+
pd.rows !== terminalComponent.terminal.rows)
274+
) {
275+
terminalComponent.fitAddon.fit();
276+
}
277+
} catch {}
261278
terminalComponent.focus();
262-
terminalComponent.fit();
263-
}, 10);
279+
};
280+
if (typeof requestAnimationFrame === "function") {
281+
requestAnimationFrame(run);
282+
} else {
283+
setTimeout(run, 0);
284+
}
264285
};
265286

266287
// Handle tab close
@@ -273,8 +294,14 @@ class TerminalManager {
273294
const RESIZE_DEBOUNCE = 200;
274295
let lastResizeTime = 0;
275296

297+
let lastWidth = 0;
298+
let lastHeight = 0;
276299
const resizeObserver = new ResizeObserver((entries) => {
277300
const now = Date.now();
301+
const entry = entries && entries[0];
302+
const cr = entry?.contentRect;
303+
const width = cr?.width ?? terminalFile.content?.clientWidth ?? 0;
304+
const height = cr?.height ?? terminalFile.content?.clientHeight ?? 0;
278305

279306
// Clear any pending resize
280307
if (resizeTimeout) {
@@ -289,24 +316,14 @@ class TerminalManager {
289316
return;
290317
}
291318

292-
// Get current terminal state
293-
const currentRows = terminalComponent.terminal.rows;
294-
const currentCols = terminalComponent.terminal.cols;
295-
296-
// Fit the terminal to new container size
297-
terminalComponent.fit();
298-
299-
// Check if dimensions actually changed after fit
300-
const newRows = terminalComponent.terminal.rows;
301-
const newCols = terminalComponent.terminal.cols;
302-
319+
// Only fit if actual size changed to reduce reflows
303320
if (
304-
Math.abs(newRows - currentRows) > 1 ||
305-
Math.abs(newCols - currentCols) > 1
321+
Math.abs(width - lastWidth) > 0.5 ||
322+
Math.abs(height - lastHeight) > 0.5
306323
) {
307-
// console.log(
308-
// `Terminal ${terminalId} resized: ${currentRows}x${currentCols} -> ${newRows}x${newCols}`,
309-
// );
324+
terminalComponent.fit();
325+
lastWidth = width;
326+
lastHeight = height;
310327
}
311328

312329
// Update last resize time
@@ -322,6 +339,8 @@ class TerminalManager {
322339
const containerElement = terminalFile.content;
323340
if (containerElement && containerElement instanceof Element) {
324341
resizeObserver.observe(containerElement);
342+
// store observer so we can disconnect on close
343+
terminalFile._resizeObserver = resizeObserver;
325344
} else {
326345
console.warn("Terminal container not available for ResizeObserver");
327346
}
@@ -472,7 +491,11 @@ class TerminalManager {
472491
background: #1e1e1e;
473492
overflow: hidden;
474493
position: relative;
494+
}
495+
496+
.terminal-content .xterm {
475497
padding: 0.25rem;
498+
box-sizing: border-box;
476499
}
477500
`;
478501
}

src/lib/installPlugin.js

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -187,9 +187,6 @@ export default async function installPlugin(
187187
const isDirEntry = !!zip.files[file].dir || /\/$/.test(correctFile);
188188

189189
// If the original path is absolute or otherwise unsafe, skip it and warn later
190-
console.log(
191-
`Skipping unsafe path: ${file} : ${isUnsafeAbsolutePath(file)}`,
192-
);
193190
if (isUnsafeAbsolutePath(file)) {
194191
ignoredUnsafeEntries.add(file);
195192
return;

src/sidebarApps/extensions/index.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -417,12 +417,12 @@ function updateHeight($el) {
417417
removeHeight($explore, $el !== $explore);
418418

419419
try {
420-
let height = $header.getBoundingClientRect().height;
421-
const tileHeight = $el.get(":scope>.tile").getBoundingClientRect().height;
420+
let height = $header?.getBoundingClientRect().height;
421+
const tileHeight = $el.get(":scope>.tile")?.getBoundingClientRect().height;
422422
if ($el === $searchResult) {
423423
height += 60;
424424
} else {
425-
height += $searchResult.getBoundingClientRect().height + tileHeight;
425+
height += $searchResult?.getBoundingClientRect().height + tileHeight;
426426
}
427427

428428
setHeight($el, height);

0 commit comments

Comments
 (0)