authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-07-16 16:17:03+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-07-16 16:17:03+02:00
log5d68fca8f210c137eee04da30ed25b5fa7e432d9
tree532ac40efe5719e2d35e300543787d978d7a3ef3
parent2a33a18716854b8d07378008cf3101ad999209ae
signature Signed by PGP key B5690EEEBB952194

Fix iOS terminal Enter input encoding (#4043)


3 files changed, 22 insertions(+), 4 deletions(-)

apps/mobile/modules/t3-terminal/ios/T3TerminalModule.swift+1-1
...@@ -7,7 +7,7 @@ public class T3TerminalModule: Module {...@@ -7,7 +7,7 @@ public class T3TerminalModule: Module {
7 // Bumped when native hardware-keyboard handling changes; surfaced in the JS debug7 // Bumped when native hardware-keyboard handling changes; surfaced in the JS debug
8 // logs so a stale native binary is distinguishable from a broken key pipeline.8 // logs so a stale native binary is distinguishable from a broken key pipeline.
9 Constants([9 Constants([
10 "hardwareKeyRevision": 2,10 "hardwareKeyRevision": 3,
11 ])11 ])
1212
13 View(T3TerminalView.self) {13 View(T3TerminalView.self) {
apps/mobile/modules/t3-terminal/ios/T3TerminalView.swift+19-2
...@@ -119,6 +119,21 @@ private enum TerminalHardwareKeyEncoder {...@@ -119,6 +119,21 @@ private enum TerminalHardwareKeyEncoder {
119 }119 }
120}120}
121121
122private enum TerminalInputSequence {
123 /// Terminal Enter is carriage return. Sending line feed instead is Ctrl+J,
124 /// which raw-mode TUIs may interpret as the literal J key.
125 static let carriageReturn = "\r"
126
127 static func normalizingReturn(_ input: String) -> String {
128 switch input {
129 case "\n", "\r\n":
130 return carriageReturn
131 default:
132 return input
133 }
134 }
135}
136
122private final class TerminalInputField: UITextField {137private final class TerminalInputField: UITextField {
123 var onDeleteBackward: (() -> Void)?138 var onDeleteBackward: (() -> Void)?
124 var onInsert: ((String) -> Void)?139 var onInsert: ((String) -> Void)?
...@@ -352,7 +367,9 @@ public final class T3TerminalView: ExpoView, UITextFieldDelegate {...@@ -352,7 +367,9 @@ public final class T3TerminalView: ExpoView, UITextFieldDelegate {
352367
353 public func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {368 public func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
354 if !string.isEmpty {369 if !string.isEmpty {
355 emitInput(string)370 // Some software keyboards deliver Return through this delegate instead of
371 // textFieldShouldReturn, so normalize that path too.
372 emitInput(TerminalInputSequence.normalizingReturn(string))
356 return false373 return false
357 }374 }
358375
...@@ -360,7 +377,7 @@ public final class T3TerminalView: ExpoView, UITextFieldDelegate {...@@ -360,7 +377,7 @@ public final class T3TerminalView: ExpoView, UITextFieldDelegate {
360 }377 }
361378
362 public func textFieldShouldReturn(_ textField: UITextField) -> Bool {379 public func textFieldShouldReturn(_ textField: UITextField) -> Bool {
363 emitInput("\n")380 emitInput(TerminalInputSequence.carriageReturn)
364 textField.text = ""381 textField.text = ""
365 return false382 return false
366 }383 }
apps/mobile/src/features/terminal/NativeTerminalSurface.tsx+2-1
...@@ -145,7 +145,8 @@ const FallbackTerminalSurface = memo(function FallbackTerminalSurface(props: Ter...@@ -145,7 +145,8 @@ const FallbackTerminalSurface = memo(function FallbackTerminalSurface(props: Ter
145 onSubmitEditing={(event) => {145 onSubmitEditing={(event) => {
146 const text = event.nativeEvent.text;146 const text = event.nativeEvent.text;
147 if (text.length > 0) {147 if (text.length > 0) {
148 props.onInput(`${text}\n`);148 // Terminal Enter is CR. LF is Ctrl+J and raw-mode TUIs can treat it as J.
149 props.onInput(`${text}\r`);
149 }150 }
150 }}151 }}
151 />152 />