Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -96,3 +96,56 @@ Calling AX or screen-capture APIs from an embedded library still requires the ho
- **Screen Recording** — for `WindowCapture` screenshots.

Grant these under **System Settings → Privacy & Security** against your app's bundle identifier, the same as for the standalone `CuaDriver.app`.

## Cursor customization

`AgentCursorRenderer.shared` drives the visual style applied to every click animation. Set it at app startup from the `@MainActor` context — changes take effect on the next rendered frame.

### Custom gradient colors

```swift
import CuaDriverCore

await MainActor.run {
AgentCursorRenderer.shared.style = AgentCursorStyle(
strokeGradientStops: [
AgentCursorGradientStop(color: NSColor(hex: "#A855F7")!, location: 0),
AgentCursorGradientStop(color: NSColor(hex: "#6366F1")!, location: 1),
],
bloomColor: NSColor(hex: "#A855F7")!
)
}
```

### Custom PNG / SVG cursor image

Replace the default arrow with any image `NSImage` can load (PNG, JPEG, PDF, SVG on macOS 12+):

```swift
import CuaDriverCore

await MainActor.run {
let img = NSImage(contentsOf: Bundle.main.url(forResource: "cursor", withExtension: "png")!)
AgentCursorRenderer.shared.style = AgentCursorStyle(image: img)
}
```

The image is drawn at `shapeSize × shapeSize` points (default 22 pt), centered on the cursor position and rotated to track the motion heading. To suppress the bloom halo behind it, pass `bloomCenterAlpha: 0`.

### Revert to default

```swift
await MainActor.run {
AgentCursorRenderer.shared.style = .default
}
```

### All style knobs

| Parameter | Type | Default | Description |
|---|---|---|---|
| `image` | `NSImage?` | `nil` | Custom cursor image (replaces arrow when non-nil) |
| `strokeGradientStops` | `[AgentCursorGradientStop]` | ice-blue → cyan → mint | Arrow fill gradient |
| `bloomColor` | `NSColor` | cyan `#5EC0E8` | Bloom halo and focus-rect color |
| `bloomCenterAlpha` | `CGFloat` | `0.55` | Center opacity of the bloom glow |
| `shapeSize` | `CGFloat` | `22` | Width and height of the cursor shape (points) |
16 changes: 16 additions & 0 deletions docs/content/docs/cua-driver/reference/mcp-tools.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,22 @@ Toggle the overlay. Persists to config.

Tune the Bezier-arc + spring motion knobs. All fields are optional; omitted fields keep their current value.

### set_agent_cursor_style

Customize the cursor's visual appearance. All fields optional; omitted fields keep their current value. Changes persist to config across restarts.

**Arguments:**

- `gradient_colors` (array of string, optional): CSS hex color strings (`#RRGGBB` or `#RGB`) defining the arrow fill gradient from tip to tail. Pass an empty array `[]` to revert to the default.
- `bloom_color` (string, optional): CSS hex color for the bloom halo and the focus-rect highlight around clicked elements. Pass `""` to revert.
- `image_path` (string, optional): Absolute or `~`-rooted path to a PNG, JPEG, PDF, or SVG file. When set, replaces the default procedural arrow. Pass `""` to revert to the arrow.

```json
{"gradient_colors": ["#A855F7", "#6366F1"], "bloom_color": "#A855F7"}
{"image_path": "~/cursors/my-cursor.png"}
{"gradient_colors": [], "bloom_color": "", "image_path": ""}
```

**Arguments:**

- `start_handle` (number, optional): Start-handle fraction in [0, 1]. Default 0.3.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,10 @@ public struct AgentCursorConfig: Codable, Sendable, Equatable {
/// five sibling fields fighting for space with `enabled`.
public var motion: Motion

/// Persisted cursor style overrides. Applied to `AgentCursorRenderer.shared`
/// at daemon startup via `AgentCursor.shared.apply(config:)`.
public var style: Style

public struct Motion: Codable, Sendable, Equatable {
/// Start-handle fraction along the straight line, in `[0, 1]`.
public var startHandle: Double
Expand Down Expand Up @@ -220,12 +224,55 @@ public struct AgentCursorConfig: Codable, Sendable, Equatable {
public static let `default` = Motion()
}

/// Persisted visual style overrides. Only non-nil fields are applied;
/// nil fields fall back to `AgentCursorStyle.default`.
public struct Style: Codable, Sendable, Equatable {
/// Gradient color stops as CSS hex strings (#RRGGBB / #RGB).
/// When set, replaces the default ice-blue→cyan→mint gradient.
public var gradientColors: [String]?

/// Bloom halo color as a CSS hex string. When set, also tints the
/// focus-rect highlight drawn around clicked AX elements.
public var bloomColor: String?

/// Absolute or `~`-rooted path to a PNG, JPEG, PDF, or SVG file
/// that replaces the default procedural arrow shape.
public var imagePath: String?

public init(
gradientColors: [String]? = nil,
bloomColor: String? = nil,
imagePath: String? = nil
) {
self.gradientColors = gradientColors
self.bloomColor = bloomColor
self.imagePath = imagePath
}

public static let `default` = Style()
}

private enum CodingKeys: String, CodingKey {
case enabled
case motion
case style
}

public init(
enabled: Bool = true,
motion: Motion = .default
motion: Motion = .default,
style: Style = .default
) {
self.enabled = enabled
self.motion = motion
self.style = style
}

public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.enabled = (try? container.decode(Bool.self, forKey: .enabled)) ?? true
self.motion = (try? container.decode(Motion.self, forKey: .motion)) ?? .default
self.style = (try? container.decode(Style.self, forKey: .style)) ?? .default
}

public static let `default` = AgentCursorConfig()
Expand Down
Loading
Loading