Skip to content

Commit 29c59eb

Browse files
committed
fix: terminal handle edge flipping and tab number reuse
1 parent b79dbf1 commit 29c59eb

File tree

2 files changed

+87
-6
lines changed

2 files changed

+87
-6
lines changed

src/components/terminal/terminalManager.js

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,32 @@ class TerminalManager {
2424
this.terminalCounter = 0;
2525
}
2626

27+
extractTerminalNumber(name) {
28+
if (!name) return null;
29+
const match = String(name).match(/^Terminal\s+(\d+)(?:\b| - )/i);
30+
if (!match) return null;
31+
const number = Number.parseInt(match[1], 10);
32+
return Number.isInteger(number) && number > 0 ? number : null;
33+
}
34+
35+
getNextAvailableTerminalNumber() {
36+
const usedNumbers = new Set();
37+
38+
for (const terminal of this.terminals.values()) {
39+
const number = terminal?.terminalNumber;
40+
if (Number.isInteger(number) && number > 0) {
41+
usedNumbers.add(number);
42+
}
43+
}
44+
45+
let nextNumber = 1;
46+
while (usedNumbers.has(nextNumber)) {
47+
nextNumber++;
48+
}
49+
50+
return nextNumber;
51+
}
52+
2753
async getPersistedSessions() {
2854
try {
2955
const stored = helpers.parseJSON(
@@ -163,7 +189,15 @@ class TerminalManager {
163189
const isServerMode = serverMode !== false;
164190

165191
const terminalId = `terminal_${++this.terminalCounter}`;
166-
const terminalName = options.name || `Terminal ${this.terminalCounter}`;
192+
const providedName =
193+
typeof options.name === "string" ? options.name.trim() : "";
194+
const terminalNumber = providedName
195+
? this.extractTerminalNumber(providedName)
196+
: this.getNextAvailableTerminalNumber();
197+
const terminalName = providedName || `Terminal ${terminalNumber}`;
198+
const titlePrefix = terminalNumber
199+
? `Terminal ${terminalNumber}`
200+
: terminalName;
167201

168202
// Check if terminal is installed before proceeding
169203
if (isServerMode) {
@@ -228,11 +262,13 @@ class TerminalManager {
228262
terminalFile,
229263
terminalComponent,
230264
uniqueId,
265+
titlePrefix,
231266
);
232267

233268
const instance = {
234269
id: uniqueId,
235270
name: terminalName,
271+
terminalNumber,
236272
component: terminalComponent,
237273
file: terminalFile,
238274
container: terminalContainer,
@@ -430,7 +466,12 @@ class TerminalManager {
430466
* @param {TerminalComponent} terminalComponent - Terminal component
431467
* @param {string} terminalId - Terminal ID
432468
*/
433-
async setupTerminalHandlers(terminalFile, terminalComponent, terminalId) {
469+
async setupTerminalHandlers(
470+
terminalFile,
471+
terminalComponent,
472+
terminalId,
473+
titlePrefix = terminalId,
474+
) {
434475
const textarea = terminalComponent.terminal?.textarea;
435476
if (textarea) {
436477
const onFocus = () => {
@@ -583,8 +624,8 @@ class TerminalManager {
583624

584625
terminalComponent.onTitleChange = async (title) => {
585626
if (title) {
586-
// Format terminal title as "Terminal ! - title"
587-
const formattedTitle = `Terminal ${this.terminalCounter} - ${title}`;
627+
// Keep the tab prefix stable for this terminal instance.
628+
const formattedTitle = `${titlePrefix} - ${title}`;
588629
terminalFile.filename = formattedTitle;
589630

590631
if (terminalComponent.serverMode && terminalComponent.pid) {

src/components/terminal/terminalTouchSelection.js

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -225,15 +225,15 @@ export default class TerminalTouchSelection {
225225
createHandles() {
226226
this.startHandle = this.createHandle("start");
227227
this.startHandle.style.cssText += `
228-
transform: rotate(180deg) translateX(87%);
229228
border-radius: 50% 50% 50% 0;
230229
`;
230+
this.setHandleOrientation(this.startHandle, "start");
231231

232232
this.endHandle = this.createHandle("end");
233233
this.endHandle.style.cssText += `
234-
transform: rotate(90deg) translateY(-13%);
235234
border-radius: 50% 50% 50% 0;
236235
`;
236+
this.setHandleOrientation(this.endHandle, "end");
237237

238238
this.selectionOverlay.appendChild(this.startHandle);
239239
this.selectionOverlay.appendChild(this.endHandle);
@@ -866,6 +866,44 @@ export default class TerminalTouchSelection {
866866
this.endHandle.style.display = "none";
867867
}
868868

869+
getHandleBaseTransform(orientation) {
870+
if (orientation === "start") {
871+
return "rotate(180deg) translateX(87%)";
872+
}
873+
return "rotate(90deg) translateY(-13%)";
874+
}
875+
876+
setHandleOrientation(handle, orientation) {
877+
if (!handle) return;
878+
879+
const baseTransform = this.getHandleBaseTransform(orientation);
880+
const hasScale = /\bscale\(/.test(handle.style.transform || "");
881+
handle.dataset.orientation = orientation;
882+
handle.style.transform = hasScale
883+
? `${baseTransform} scale(1.2)`
884+
: baseTransform;
885+
}
886+
887+
updateHandleOrientationForViewportEdges() {
888+
const overlayRect = this.selectionOverlay.getBoundingClientRect();
889+
890+
if (this.startHandle.style.display !== "none") {
891+
this.setHandleOrientation(this.startHandle, "start");
892+
const startRect = this.startHandle.getBoundingClientRect();
893+
if (startRect.left < overlayRect.left) {
894+
this.setHandleOrientation(this.startHandle, "end");
895+
}
896+
}
897+
898+
if (this.endHandle.style.display !== "none") {
899+
this.setHandleOrientation(this.endHandle, "end");
900+
const endRect = this.endHandle.getBoundingClientRect();
901+
if (endRect.right > overlayRect.right) {
902+
this.setHandleOrientation(this.endHandle, "start");
903+
}
904+
}
905+
}
906+
869907
updateHandlePositions() {
870908
if (!this.selectionStart || !this.selectionEnd) return;
871909

@@ -902,6 +940,8 @@ export default class TerminalTouchSelection {
902940
} else {
903941
this.endHandle.style.display = "none";
904942
}
943+
944+
this.updateHandleOrientationForViewportEdges();
905945
}
906946

907947
showContextMenu() {

0 commit comments

Comments
 (0)