Skip to content

Commit c25632c

Browse files
Merge branch 'main' into lsp
2 parents d2fec9f + 017a83f commit c25632c

File tree

17 files changed

+804
-122
lines changed

17 files changed

+804
-122
lines changed

config.xml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@
3737
<edit-config file="app/src/main/AndroidManifest.xml" mode="merge" target="/manifest/application">
3838
<application android:networkSecurityConfig="@xml/network_security_config" />
3939
<application android:hardwareAccelerated="true" />
40-
<application android:requestLegacyExternalStorage="true" />
4140
<application android:largeHeap="true" />
4241
<application android:requestLegacyExternalStorage="true"/>
4342
</edit-config>

package-lock.json

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/components/terminal/terminal.js

Lines changed: 101 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ export default class TerminalComponent {
3131
scrollOnUserInput: true,
3232
rows: options.rows || 24,
3333
cols: options.cols || 80,
34+
port: options.port || 8767,
3435
fontSize: terminalSettings.fontSize,
3536
fontFamily: terminalSettings.fontFamily,
3637
fontWeight: terminalSettings.fontWeight,
@@ -128,7 +129,7 @@ export default class TerminalComponent {
128129
let lastKnownScrollPosition = 0;
129130
let isResizing = false;
130131
let resizeCount = 0;
131-
const RESIZE_DEBOUNCE = 150;
132+
const RESIZE_DEBOUNCE = 100;
132133
const MAX_RAPID_RESIZES = 3;
133134

134135
// Store original dimensions for comparison
@@ -170,8 +171,37 @@ export default class TerminalComponent {
170171
await this.resizeTerminal(size.cols, size.rows);
171172
}
172173

173-
// Preserve scroll position for content-heavy terminals
174-
this.preserveViewportPosition(lastKnownScrollPosition);
174+
// Handle keyboard resize cursor positioning
175+
const heightRatio = size.rows / originalRows;
176+
if (
177+
heightRatio < 0.75 &&
178+
this.terminal.buffer &&
179+
this.terminal.buffer.active
180+
) {
181+
// Keyboard resize detected - ensure cursor is visible
182+
const buffer = this.terminal.buffer.active;
183+
const cursorY = buffer.cursorY;
184+
const cursorViewportPos = buffer.baseY + cursorY;
185+
const viewportTop = buffer.viewportY;
186+
const viewportBottom = viewportTop + this.terminal.rows - 1;
187+
188+
if (
189+
cursorViewportPos <= viewportTop + 1 ||
190+
cursorViewportPos >= viewportBottom - 1
191+
) {
192+
const targetScroll = Math.max(
193+
0,
194+
Math.min(
195+
buffer.length - this.terminal.rows,
196+
cursorViewportPos - Math.floor(this.terminal.rows * 0.25),
197+
),
198+
);
199+
this.terminal.scrollToLine(targetScroll);
200+
}
201+
} else {
202+
// Regular resize - preserve scroll position
203+
this.preserveViewportPosition(lastKnownScrollPosition);
204+
}
175205

176206
// Update stored dimensions
177207
originalRows = size.rows;
@@ -437,8 +467,9 @@ export default class TerminalComponent {
437467
height: 100%;
438468
position: relative;
439469
background: ${this.options.theme.background};
440-
border-radius: 4px;
441470
overflow: hidden;
471+
padding: 0.25rem;
472+
box-sizing: border-box;
442473
`;
443474

444475
return this.container;
@@ -455,6 +486,9 @@ export default class TerminalComponent {
455486

456487
this.container = container;
457488

489+
// Apply terminal background color to container to match theme
490+
this.container.style.background = this.options.theme.background;
491+
458492
try {
459493
try {
460494
this.terminal.loadAddon(this.webglAddon);
@@ -535,13 +569,16 @@ export default class TerminalComponent {
535569
rows: this.terminal.rows,
536570
};
537571

538-
const response = await fetch("http://localhost:8767/terminals", {
539-
method: "POST",
540-
headers: {
541-
"Content-Type": "application/json",
572+
const response = await fetch(
573+
`http://localhost:${this.options.port}/terminals`,
574+
{
575+
method: "POST",
576+
headers: {
577+
"Content-Type": "application/json",
578+
},
579+
body: JSON.stringify(requestBody),
542580
},
543-
body: JSON.stringify(requestBody),
544-
});
581+
);
545582

546583
if (!response.ok) {
547584
throw new Error(`HTTP error! status: ${response.status}`);
@@ -573,7 +610,7 @@ export default class TerminalComponent {
573610

574611
this.pid = pid;
575612

576-
const wsUrl = `ws://localhost:8767/terminals/${pid}`;
613+
const wsUrl = `ws://localhost:${this.options.port}/terminals/${pid}`;
577614

578615
this.websocket = new WebSocket(wsUrl);
579616

@@ -627,13 +664,16 @@ export default class TerminalComponent {
627664
if (!this.pid || !this.serverMode) return;
628665

629666
try {
630-
await fetch(`http://localhost:8767/terminals/${this.pid}/resize`, {
631-
method: "POST",
632-
headers: {
633-
"Content-Type": "application/json",
667+
await fetch(
668+
`http://localhost:${this.options.port}/terminals/${this.pid}/resize`,
669+
{
670+
method: "POST",
671+
headers: {
672+
"Content-Type": "application/json",
673+
},
674+
body: JSON.stringify({ cols, rows }),
634675
},
635-
body: JSON.stringify({ cols, rows }),
636-
});
676+
);
637677
} catch (error) {
638678
console.error("Failed to resize terminal:", error);
639679
}
@@ -653,7 +693,18 @@ export default class TerminalComponent {
653693
* @param {string} data - Data to write
654694
*/
655695
write(data) {
656-
this.terminal.write(data);
696+
if (
697+
this.serverMode &&
698+
this.isConnected &&
699+
this.websocket &&
700+
this.websocket.readyState === WebSocket.OPEN
701+
) {
702+
// Send data through WebSocket instead of direct write
703+
this.websocket.send(data);
704+
} else {
705+
// For local mode or disconnected terminals, write directly
706+
this.terminal.write(data);
707+
}
657708
}
658709

659710
/**
@@ -675,6 +726,32 @@ export default class TerminalComponent {
675726
* Focus terminal
676727
*/
677728
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+
678755
this.terminal.focus();
679756
}
680757

@@ -896,9 +973,12 @@ export default class TerminalComponent {
896973

897974
if (this.pid && this.serverMode) {
898975
try {
899-
await fetch(`http://localhost:8767/terminals/${this.pid}/terminate`, {
900-
method: "POST",
901-
});
976+
await fetch(
977+
`http://localhost:${this.options.port}/terminals/${this.pid}/terminate`,
978+
{
979+
method: "POST",
980+
},
981+
);
902982
} catch (error) {
903983
console.error("Failed to terminate terminal:", error);
904984
}

src/components/terminal/terminalManager.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,11 @@ class TerminalManager {
223223
terminalId,
224224
);
225225

226+
// Set up custom title for installation terminal
227+
terminalFile.setCustomTitle(
228+
() => "Installing Terminal Environment...",
229+
);
230+
226231
const instance = {
227232
id: terminalId,
228233
name: terminalName,
@@ -343,6 +348,15 @@ class TerminalManager {
343348
// Format terminal title as "Terminal ! - title"
344349
const formattedTitle = `Terminal ${this.terminalCounter} - ${title}`;
345350
terminalFile.filename = formattedTitle;
351+
352+
// Refresh the header subtitle if this terminal is active
353+
if (
354+
editorManager.activeFile &&
355+
editorManager.activeFile.id === terminalFile.id
356+
) {
357+
// Force refresh of the header subtitle
358+
terminalFile.setCustomTitle(getTerminalTitle);
359+
}
346360
}
347361
};
348362

@@ -366,6 +380,17 @@ class TerminalManager {
366380
terminalFile._terminalId = terminalId;
367381
terminalFile.terminalComponent = terminalComponent;
368382
terminalFile._resizeObserver = resizeObserver;
383+
384+
// Set up custom title function for terminal
385+
const getTerminalTitle = () => {
386+
if (terminalComponent.pid) {
387+
return `PID: ${terminalComponent.pid}`;
388+
}
389+
// fallback to terminal name
390+
return `${terminalId}`;
391+
};
392+
393+
terminalFile.setCustomTitle(getTerminalTitle);
369394
}
370395

371396
/**
@@ -447,6 +472,7 @@ class TerminalManager {
447472
background: #1e1e1e;
448473
overflow: hidden;
449474
position: relative;
475+
padding: 0.25rem;
450476
}
451477
`;
452478
}

src/components/terminal/terminalTouchSelection.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,11 @@ export default class TerminalTouchSelection {
240240
return;
241241
}
242242

243+
// Check if touch is near screen edge (likely Android back gesture)
244+
if (this.isEdgeGesture(touch)) {
245+
return;
246+
}
247+
243248
// Clear any existing tap-hold timeout
244249
if (this.tapHoldTimeout) {
245250
clearTimeout(this.tapHoldTimeout);
@@ -269,6 +274,21 @@ export default class TerminalTouchSelection {
269274
const touch = event.touches[0];
270275
const deltaX = Math.abs(touch.clientX - this.touchStartPos.x);
271276
const deltaY = Math.abs(touch.clientY - this.touchStartPos.y);
277+
const horizontalDelta = touch.clientX - this.touchStartPos.x;
278+
279+
// Check if this looks like a back gesture (started near edge and moving horizontally inward)
280+
if (
281+
this.isEdgeGesture(this.initialTouchPos) &&
282+
Math.abs(horizontalDelta) > deltaY &&
283+
deltaX > this.options.moveThreshold
284+
) {
285+
// This looks like a back gesture, cancel selection
286+
if (this.tapHoldTimeout) {
287+
clearTimeout(this.tapHoldTimeout);
288+
this.tapHoldTimeout = null;
289+
}
290+
return;
291+
}
272292

273293
// If significant movement, cancel tap-hold
274294
if (
@@ -1141,6 +1161,26 @@ export default class TerminalTouchSelection {
11411161
return Math.sqrt(dx * dx + dy * dy);
11421162
}
11431163

1164+
/**
1165+
* Check if touch is likely an Android back gesture (starts near screen edge)
1166+
*/
1167+
isEdgeGesture(touch) {
1168+
const edgeThreshold = 30; // pixels from screen edge
1169+
const screenWidth = window.innerWidth;
1170+
1171+
// Check if touch starts near left edge (most common for back gesture)
1172+
if (touch.clientX <= edgeThreshold) {
1173+
return true;
1174+
}
1175+
1176+
// Check if touch starts near right edge (for RTL languages or right-handed back gesture)
1177+
if (touch.clientX >= screenWidth - edgeThreshold) {
1178+
return true;
1179+
}
1180+
1181+
return false;
1182+
}
1183+
11441184
destroy() {
11451185
// Clear selection
11461186
this.forceClearSelection();

src/lang/id-id.json

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@
126126
"animation": "Animasi",
127127
"backup": "Cadangkan",
128128
"restore": "Mengembalikan",
129+
"allFileAccess": "Semua akses berkas",
129130
"backup successful": "Berhasil membuat cadangan",
130131
"invalid backup file": "Berkas cadangan tidak valid",
131132
"add path": "Tambah jalur",
@@ -410,16 +411,16 @@
410411
"enable_plugin": "Aktifkan Plugin ini",
411412
"disable_plugin": "Nonaktifkan Plugin ini",
412413
"open_source": "Sumber Terbuka",
413-
"terminal settings": "Terminal Settings",
414-
"font ligatures": "Font Ligatures",
415-
"letter spacing": "Letter Spacing",
416-
"terminal:tab stop width": "Tab Stop Width",
417-
"terminal:scrollback": "Scrollback Lines",
418-
"terminal:cursor blink": "Cursor Blink",
419-
"terminal:font weight": "Font Weight",
420-
"terminal:cursor inactive style": "Cursor Inactive Style",
421-
"terminal:cursor style": "Cursor Style",
422-
"terminal:font family": "Font Family",
423-
"terminal:convert eol": "Convert EOL",
414+
"terminal settings": "Pengaturan Terminal",
415+
"font ligatures": "Ligatur Huruf",
416+
"letter spacing": "Jarak Antar Huruf",
417+
"terminal:tab stop width": "Lebar Hentian Tab",
418+
"terminal:scrollback": "Garis Gulir Balik",
419+
"terminal:cursor blink": "Kursor Berkedip",
420+
"terminal:font weight": "Berat Huruf",
421+
"terminal:cursor inactive style": "Gaya Kursor Tidak Aktif",
422+
"terminal:cursor style": "Gaya Kursor",
423+
"terminal:font family": "Keluarga Huruf",
424+
"terminal:convert eol": "Konversi Akhir Baris",
424425
"terminal": "Terminal"
425426
}

0 commit comments

Comments
 (0)