Skip to content

Commit 08f6f70

Browse files
committed
initial import
0 parents  commit 08f6f70

39 files changed

+6463
-0
lines changed

.gitattributes

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
*.swift text eol=lf
2+
*.md text eol=lf
3+
*.txt text eol=lf

.gitignore

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# git ls-files --others --exclude-from=.git/info/exclude
2+
# Lines that start with '#' are comments.
3+
# For a project mostly in C, the following would be a good set of
4+
# exclude patterns (uncomment them if you want to use them):
5+
6+
**/.DS_Store
7+
.*.sw[nop]
8+
*~
9+
10+
/build/
11+
12+
/package.resolved
13+
/Packages/
14+
.build/
15+
16+
.vscode/

LICENSE

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
BSD 3-Clause License
2+
3+
Copyright (c) 2025, Saleem Abdulrasool
4+
5+
Redistribution and use in source and binary forms, with or without
6+
modification, are permitted provided that the following conditions are met:
7+
8+
1. Redistributions of source code must retain the above copyright notice, this
9+
list of conditions and the following disclaimer.
10+
11+
2. Redistributions in binary form must reproduce the above copyright notice,
12+
this list of conditions and the following disclaimer in the documentation
13+
and/or other materials provided with the distribution.
14+
15+
3. Neither the name of the copyright holder nor the names of its
16+
contributors may be used to endorse or promote products derived from
17+
this software without specific prior written permission.
18+
19+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Package.swift

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// swift-tools-version:6.0
2+
3+
import PackageDescription
4+
5+
let _: Package =
6+
.init(name: "Cursor",
7+
platforms: [
8+
.macOS(.v14),
9+
],
10+
products: [
11+
.executable(name: "VTDemo", targets: ["VTDemo"]),
12+
],
13+
dependencies: [
14+
.package(url: "https://github.com/compnerd/swift-platform-core.git", branch: "main"),
15+
],
16+
targets: [
17+
.target(name: "Geometry"),
18+
.target(name: "Primitives"),
19+
.target(name: "VirtualTerminal", dependencies: [
20+
.target(name: "Geometry"),
21+
.target(name: "Primitives"),
22+
.product(name: "POSIXCore", package: "swift-platform-core", condition: .when(platforms: [.macOS, .linux])),
23+
.product(name: "WindowsCore", package: "swift-platform-core", condition: .when(platforms: [.windows])),
24+
]),
25+
.executableTarget(name: "VTDemo", dependencies: [
26+
.target(name: "VirtualTerminal"),
27+
]),
28+
])

README.md

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
# VirtualTerminal
2+
3+
**Modern, high-performance terminal UI library for Swift**
4+
5+
Build beautiful, fast command-line applications with native Swift. VirtualTerminal provides efficient rendering, cross-platform compatibility, and Swift 6 concurrency support—without the complexity of C bindings.
6+
7+
## Why VirtualTerminal?
8+
9+
### 🚀 **Built for Performance**
10+
- **Damage-based rendering**: Only redraw changed cells, not entire screens
11+
- **Intelligent cursor optimization**: Minimal escape sequences for movement
12+
- **Double buffering**: Smooth animations without screen tearing
13+
- **Output batching**: Batch multiple operations into fewer writes
14+
15+
### 🛡️ **Swift-Native Design**
16+
- **Memory safety**: No unsafe pointers or C interop required
17+
- **Modern concurrency**: Built on Swift 6 actors and async/await
18+
- **Type safety**: Compile-time guarantees for colors, positions, and styles
19+
- **Zero dependencies**: Pure Swift implementation
20+
21+
### 🌍 **True Cross-Platform**
22+
- **macOS, Linux, Windows**: Single codebase, platform-optimized internals
23+
- **Consistent APIs**: Write once, run everywhere
24+
- **Native input handling**: Platform-specific optimizations under the hood
25+
26+
## Quick Example
27+
28+
```swift
29+
import VirtualTerminal
30+
31+
// Create a high-performance terminal renderer
32+
let renderer = try await VTRenderer(mode: .raw)
33+
34+
// Render at 60 FPS with automatic optimization
35+
try await renderer.rendering(fps: 60) { buffer in
36+
buffer.write("Hello, World!",
37+
at: VTPosition(row: 1, column: 1),
38+
style: VTStyle(foreground: .green, attributes: [.bold]))
39+
}
40+
41+
// Handle input events with modern Swift concurrency
42+
for await event in renderer.terminal.input {
43+
switch event {
44+
case .key(let key) where key.character == "q":
45+
return // Clean exit
46+
case .resize(let size):
47+
renderer.resize(to: size)
48+
default:
49+
break
50+
}
51+
}
52+
```
53+
54+
## Core Features
55+
56+
### Efficient Rendering
57+
- **Damage detection**: Only update changed regions
58+
- **Style optimization**: Minimize escape sequence overhead
59+
- **Cursor movement**: Intelligent positioning algorithms
60+
- **Unicode support**: Proper width calculation for CJK, emoji, and symbols
61+
62+
### Modern Input Handling
63+
```swift
64+
// AsyncSequence-based input processing
65+
for await event in terminal.input {
66+
switch event {
67+
case .key(let key):
68+
handleKeyPress(key)
69+
case .mouse(let mouse):
70+
handleMouseEvent(mouse)
71+
case .resize(let size):
72+
handleResize(size)
73+
}
74+
}
75+
```
76+
77+
### Rich Styling
78+
```swift
79+
let style = VTStyle(foreground: .rgb(red: 255, green: 100, blue: 50),
80+
background: .ansi(.blue),
81+
attributes: [.bold, .italic])
82+
buffer.write("Styled text", at: position, style: style)
83+
```
84+
85+
## Installation
86+
87+
Add to your `Package.swift`:
88+
89+
```swift
90+
dependencies: [
91+
.package(url: "https://github.com/compnerd/VirtualTerminal.git", branch: "main")
92+
],
93+
targets: [
94+
.target(name: "YourCLI", dependencies: ["VirtualTerminal"])
95+
]
96+
```
97+
98+
## Requirements
99+
100+
- **Swift 6.0+**
101+
- **macOS 14+**, **Linux**, or **Windows 10+**
102+
- Terminal with basic ANSI support (any modern terminal)

Sources/Geometry/Point.swift

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// Copyright © 2025 Saleem Abdulrasool <compnerd@compnerd.org>
2+
// SPDX-License-Identifier: BSD-3-Clause
3+
4+
@frozen
5+
public struct Point {
6+
public let x: Int
7+
public let y: Int
8+
9+
public init(x: Int, y: Int) {
10+
self.x = x
11+
self.y = y
12+
}
13+
}
14+
15+
extension Point: AdditiveArithmetic {
16+
public static var zero: Point {
17+
Point(x: 0, y: 0)
18+
}
19+
20+
public static func + (_ lhs: Point, _ rhs: Point) -> Point {
21+
Point(x: lhs.x + rhs.x, y: lhs.y + rhs.y)
22+
}
23+
24+
public static func - (_ lhs: Point, _ rhs: Point) -> Point {
25+
Point(x: lhs.x - rhs.x, y: lhs.y - rhs.y)
26+
}
27+
}
28+
29+
extension Point: Codable { }
30+
31+
extension Point: CustomStringConvertible {
32+
public var description: String {
33+
"(\(x), \(y))"
34+
}
35+
}
36+
37+
extension Point: Equatable { }
38+
39+
extension Point: Hashable { }
40+
41+
extension Point: Sendable { }
42+
43+
extension Point {
44+
public static func * (_ point: Point, _ scalar: Int) -> Point {
45+
Point(x: point.x * scalar, y: point.y * scalar)
46+
}
47+
}
48+
49+
extension Point {
50+
public static func / (_ point: Point, _ scalar: Int) -> Point {
51+
return Point(x: point.x / scalar, y: point.y / scalar)
52+
}
53+
}
54+
55+
extension Point {
56+
public func distance(to point: Point) -> Double {
57+
let dx = self.x - point.x
58+
let dy = self.y - point.y
59+
return Double(dx * dx + dy * dy).squareRoot()
60+
}
61+
}
62+
63+
extension Point {
64+
public var magnitude: Double {
65+
distance(to: .zero)
66+
}
67+
}
68+
69+
extension Point {
70+
public func midpoint(to point: Point) -> Point {
71+
return Point(x: (self.x + point.x) / 2, y: (self.y + point.y) / 2)
72+
}
73+
}

Sources/Geometry/Rect.swift

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
// Copyright © 2025 Saleem Abdulrasool <compnerd@compnerd.org>
2+
// SPDX-License-Identifier: BSD-3-Clause
3+
4+
@frozen
5+
public struct Rect {
6+
public let origin: Point
7+
public let size: Size
8+
9+
public init(origin: Point, size: Size) {
10+
self.origin = origin
11+
self.size = size
12+
}
13+
}
14+
15+
extension Rect: AdditiveArithmetic {
16+
public static var zero: Rect {
17+
Rect(origin: .zero, size: .zero)
18+
}
19+
20+
public static func + (_ lhs: Rect, _ rhs: Rect) -> Rect {
21+
Rect(origin: lhs.origin + rhs.origin, size: lhs.size + rhs.size)
22+
}
23+
24+
public static func - (_ lhs: Rect, _ rhs: Rect) -> Rect {
25+
Rect(origin: lhs.origin - rhs.origin, size: lhs.size - rhs.size)
26+
}
27+
}
28+
29+
extension Rect: Codable { }
30+
31+
extension Rect: CustomStringConvertible {
32+
public var description: String {
33+
"{\(origin), \(size)}"
34+
}
35+
}
36+
37+
extension Rect: Equatable { }
38+
39+
extension Rect: Hashable { }
40+
41+
extension Rect: Sendable { }
42+
43+
extension Rect {
44+
public var isEmpty: Bool {
45+
size.width <= 0 || size.height <= 0
46+
}
47+
}
48+
49+
extension Rect {
50+
public var center: Point {
51+
Point(x: origin.x + size.width / 2, y: origin.y + size.height / 2)
52+
}
53+
}
54+
55+
extension Rect {
56+
public func contains(_ point: Point) -> Bool {
57+
return point.x >= origin.x && point.x < (origin.x + size.width) &&
58+
point.y >= origin.y && point.y < (origin.y + size.height)
59+
}
60+
61+
public func contains(_ rect: Rect) -> Bool {
62+
return rect.origin.x >= origin.x &&
63+
(rect.origin.x + rect.size.width) <= (origin.x + size.width) &&
64+
rect.origin.y >= origin.y &&
65+
(rect.origin.y + rect.size.height) <= (origin.y + size.height)
66+
}
67+
}
68+
69+
extension Rect {
70+
public func intersects(_ rect: Rect) -> Bool {
71+
return (origin.x + size.width) > rect.origin.x &&
72+
origin.x < (rect.origin.x + rect.size.width) &&
73+
(origin.y + size.height) > rect.origin.y &&
74+
origin.y < (rect.origin.y + rect.size.height)
75+
}
76+
}
77+
78+
extension Rect {
79+
public func union(_ rect: Rect) -> Rect {
80+
let x = (min: min(origin.x, rect.origin.x),
81+
max: max(origin.x + size.width, rect.origin.x + rect.size.width))
82+
let y = (min: min(origin.y, rect.origin.y),
83+
max: max(origin.y + size.height, rect.origin.y + rect.size.height))
84+
85+
return Rect(origin: Point(x: x.min, y: y.min),
86+
size: Size(width: x.max - x.min, height: y.max - y.min))
87+
}
88+
}
89+
90+
extension Rect {
91+
public func intersection(with rect: Rect) -> Rect? {
92+
let x = (min: max(origin.x, rect.origin.x),
93+
max: min(origin.x + size.width, rect.origin.x + rect.size.width))
94+
let y = (min: max(origin.y, rect.origin.y),
95+
max: min(origin.y + size.height, rect.origin.y + rect.size.height))
96+
97+
guard x.min < x.max, y.min < y.max else { return nil }
98+
return Rect(origin: Point(x: x.min, y: y.min),
99+
size: Size(width: x.max - x.min, height: y.max - y.min))
100+
}
101+
}
102+
103+
extension Rect {
104+
public func offset(by point: Point) -> Rect {
105+
return Rect(origin: origin + point, size: size)
106+
}
107+
}

0 commit comments

Comments
 (0)