vibetunnel/ios/VibeTunnel/Models/TerminalData.swift
Peter Steinberger baaaa5a033 fix: CI and linting issues across all platforms
- Fix code signing in Mac and iOS test workflows
- Fix all SwiftFormat and SwiftLint issues
- Fix ESLint issues in web code
- Remove force casts and unwrapping in Swift code
- Update build scripts to use correct file paths
2025-06-23 19:40:53 +02:00

162 lines
5 KiB
Swift

import Foundation
/// Represents different types of events that can occur in a terminal session.
///
/// TerminalEvent encapsulates the various event types from the Asciinema
/// streaming format, including headers, output, resize events, and session exits.
enum TerminalEvent {
/// Header event containing session metadata.
case header(AsciinemaHeader)
/// Terminal output event with timestamp and text data.
case output(timestamp: Double, data: String)
/// Terminal resize event with timestamp and dimensions.
case resize(timestamp: Double, dimensions: String)
/// Session exit event with exit code and session ID.
case exit(code: Int, sessionId: String)
/// Creates a terminal event from a JSON line.
///
/// - Parameter line: A JSON-formatted string representing an event.
/// - Returns: A parsed TerminalEvent, or nil if parsing fails.
///
/// This initializer handles multiple event formats:
/// - Asciinema header objects
/// - Array-based events for output/resize
/// - Exit events with status information
init?(from line: String) {
guard let data = line.data(using: .utf8) else { return nil }
// Try to parse as header first
if let header = try? JSONDecoder().decode(AsciinemaHeader.self, from: data) {
self = .header(header)
return
}
// Try to parse as array event
guard let array = try? JSONSerialization.jsonObject(with: data) as? [Any] else {
return nil
}
// Check for exit event: ["exit", exitCode, sessionId]
if array.count == 3,
let exitString = array[0] as? String,
exitString == "exit",
let exitCode = array[1] as? Int,
let sessionId = array[2] as? String
{
self = .exit(code: exitCode, sessionId: sessionId)
return
}
// Parse normal events: [timestamp, "type", "data"]
guard array.count >= 3,
let timestamp = array[0] as? Double,
let typeString = array[1] as? String,
let eventData = array[2] as? String
else {
return nil
}
switch typeString {
case "o":
self = .output(timestamp: timestamp, data: eventData)
case "r":
self = .resize(timestamp: timestamp, dimensions: eventData)
default:
return nil
}
}
}
/// Header structure for Asciinema format streams.
///
/// Contains metadata about the terminal session including
/// dimensions, timing, and environment information.
struct AsciinemaHeader: Codable {
let version: Int
let width: Int
let height: Int
let timestamp: Double?
let duration: Double?
let command: String?
let title: String?
let env: [String: String]?
}
/// Represents input to be sent to the terminal.
///
/// TerminalInput handles both regular text input and special
/// key sequences like arrow keys, control combinations, etc.
struct TerminalInput: Codable {
let text: String
/// Special key sequences that can be sent to the terminal.
enum SpecialKey: String {
// MARK: - Arrow Keys
/// Up arrow key (ANSI escape sequence).
case arrowUp = "\u{001B}[A"
/// Down arrow key (ANSI escape sequence).
case arrowDown = "\u{001B}[B"
/// Right arrow key (ANSI escape sequence).
case arrowRight = "\u{001B}[C"
/// Left arrow key (ANSI escape sequence).
case arrowLeft = "\u{001B}[D"
// MARK: - Special Keys
/// Escape key.
case escape = "\u{001B}"
/// Enter/Return key (carriage return).
case enter = "\r"
/// Tab key.
case tab = "\t"
// MARK: - Control Keys
/// Control-C (interrupt signal).
case ctrlC = "\u{0003}"
/// Control-D (end of transmission).
case ctrlD = "\u{0004}"
/// Control-Z (suspend signal).
case ctrlZ = "\u{001A}"
/// Control-L (clear screen).
case ctrlL = "\u{000C}"
/// Control-A (move to beginning of line).
case ctrlA = "\u{0001}"
/// Control-E (move to end of line).
case ctrlE = "\u{0005}"
// MARK: - Web Compatibility
/// Control-Enter combination (web frontend compatibility).
case ctrlEnter = "ctrl_enter"
/// Shift-Enter combination (web frontend compatibility).
case shiftEnter = "shift_enter"
}
/// Creates a terminal input from a special key.
///
/// - Parameter specialKey: The special key to send.
init(specialKey: SpecialKey) {
self.text = specialKey.rawValue
}
/// Creates a terminal input from regular text.
///
/// - Parameter text: The text to send to the terminal.
init(text: String) {
self.text = text
}
}
/// Represents a terminal resize request.
///
/// Used to notify the server when the terminal dimensions change.
struct TerminalResize: Codable {
let cols: Int
let rows: Int
}