Add paste button to mobile terminal keyboard

This commit is contained in:
Kaede Akino committed 2026-09-12 10:37:39 +08:00
1 parent 591be6909e
commit fe9dc6e20f
4 files changed
+38 -8

No files matched your search

@@ -5,12 +5,17 @@ struct MobileTerminalKeyboard: View {
static let barHeight: CGFloat = 44
@ObservedObject var state: TerminalViewState
let paste: () -> Void
@State private var control: TerminalPublicStickyActivation = .inactive
@State private var alt: TerminalPublicStickyActivation = .inactive
@State private var shift = MobileTerminalShiftState()
init(state: TerminalViewState, initialShift: MobileTerminalShiftState = MobileTerminalShiftState()) {
init(
state: TerminalViewState, initialShift: MobileTerminalShiftState = MobileTerminalShiftState(),
paste: @escaping () -> Void
) {
self.state = state
self.paste = paste
_shift = State(initialValue: initialShift)
}
@@ -37,7 +42,11 @@ struct MobileTerminalKeyboard: View {
.frame(maxWidth: .infinity)
Divider().padding(.vertical, 8)
keyboardToggle.padding(.horizontal, 8)
HStack(spacing: 0) {
pasteButton
keyboardToggle
}
.padding(.horizontal, 8)
}
.buttonStyle(.plain)
.font(.system(.callout, design: .monospaced))
@@ -62,6 +71,15 @@ struct MobileTerminalKeyboard: View {
.accessibilityAddTraits(shift.activation == .inactive ? [] : .isSelected)
}
private var pasteButton: some View {
Button(action: paste) {
Label("Paste", systemImage: "doc.on.clipboard")
.labelStyle(.iconOnly)
.frame(width: 44, height: 44)
.contentShape(.rect)
}
}
private var keyboardToggle: some View {
Button {
if state.isFocused {
@@ -45,7 +45,10 @@ struct MobileWorkspaceView: View {
workspace.terminal.applyPreferences(value)
}
.onTapGesture { workspace.terminal.requestFocus() }
MobileTerminalKeyboard(state: workspace.terminal)
MobileTerminalKeyboard(state: workspace.terminal) {
guard let text = UIPasteboard.general.string else { return }
workspace.insertTerminalText(text)
}
}.opacity(showsJourney ? 0 : 1)
.allowsHitTesting(!showsJourney).accessibilityHidden(showsJourney)
if showsJourney { connectionJourney.background(.background) }
+6 -4
View File
@@ -15,11 +15,12 @@ import struct TermindDomain.Host
struct MobileIconRenderingTests {
@Test func terminalKeyboardKeepsItsCompactHeight() {
#expect(UIImage(systemName: "play.rectangle") != nil)
#expect(UIImage(systemName: "doc.on.clipboard") != nil)
#expect(MobileTerminalKeyboard.barHeight == 44)
for width in [CGFloat(320), 390, 430] {
for dynamicTypeSize in [DynamicTypeSize.large, .accessibility3] {
let controller = UIHostingController(
rootView: MobileTerminalKeyboard(state: TerminalViewState())
rootView: MobileTerminalKeyboard(state: TerminalViewState(), paste: {})
.environment(\.dynamicTypeSize, dynamicTypeSize))
let size = controller.sizeThatFits(in: CGSize(width: width, height: 800))
#expect(abs(size.height - 44) < 0.5)
@@ -126,7 +127,7 @@ struct MobileIconRenderingTests {
try await capture("terminal-keyboard-\(suffix)", language: language, scheme: scheme) {
VStack(spacing: 0) {
Spacer()
MobileTerminalKeyboard(state: TerminalViewState())
MobileTerminalKeyboard(state: TerminalViewState(), paste: {})
}
.environment(\.locale, locale)
}
@@ -135,7 +136,8 @@ struct MobileIconRenderingTests {
Spacer()
MobileTerminalKeyboard(
state: TerminalViewState(),
initialShift: MobileTerminalShiftState(activation: .locked))
initialShift: MobileTerminalShiftState(activation: .locked),
paste: {})
}
.environment(\.locale, locale)
}
@@ -170,7 +172,7 @@ struct MobileIconRenderingTests {
try await capture("terminal-keyboard-large-type", language: language, scheme: .light) {
VStack(spacing: 0) {
Spacer()
MobileTerminalKeyboard(state: TerminalViewState())
MobileTerminalKeyboard(state: TerminalViewState(), paste: {})
}
.environment(\.locale, locale).environment(\.dynamicTypeSize, .accessibility3)
}
+8 -1
View File
@@ -530,13 +530,20 @@ struct ArchitectureBoundaryTests {
#expect(historyDivider.lowerBound < nextMatch.lowerBound)
#expect(workspace.contains(".dropDestination(for: URL.self)"))
#expect(workspace.contains(".toolbar(.hidden, for: .tabBar)"))
#expect(workspace.contains("guard let text = UIPasteboard.general.string else { return }"))
#expect(workspace.contains("workspace.insertTerminalText(text)"))
#expect(root.contains("private func closeWorkspace(_ id: UUID)"))
#expect(!workspace.contains("Circle().fill(workspace.connected"))
let keyboard = try String(
contentsOf: sourceRoot.appending(path: "../TFI/Features/Terminal/MobileTerminalKeyboard.swift"),
encoding: .utf8)
#expect(keyboard.contains("ScrollView(.horizontal"))
#expect(keyboard.contains("keyboardToggle.padding(.horizontal, 8)"))
let pasteButton = try #require(keyboard.range(of: "pasteButton"))
let keyboardToggle = try #require(keyboard.range(of: "keyboardToggle"))
#expect(pasteButton.lowerBound < keyboardToggle.lowerBound)
#expect(keyboard.contains("Button(action: paste)"))
#expect(keyboard.contains("Label(\"Paste\", systemImage: \"doc.on.clipboard\")"))
#expect(keyboard.contains(".padding(.horizontal, 8)"))
#expect(keyboard.contains("static let barHeight: CGFloat = 44"))
#expect(keyboard.contains(".buttonStyle(.plain)"))
#expect(!keyboard.contains(".buttonStyle(.bordered)"))