Skip to content

Commit c43d312

Browse files
committed
v0.1.2: screenshots, --x/--y/--screen flags, pre-wrap text, window-id stderr
5 screenshots in media/ showing real agent-UI patterns: deploy approval, PR picker, config form, destructive confirmation, triage. All content generic (no company/team refs), rendered via the real A2UI mode. New flags --x/--y (points from top-left of target screen) and --screen (NSScreen index). Window ID emitted to stderr as '[wid] N' for screencapture -l workflows. CSS fix: Text components now preserve newlines (white-space: pre-wrap).
1 parent 83b7f31 commit c43d312

8 files changed

Lines changed: 54 additions & 19 deletions

File tree

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,17 @@
22

33
All notable changes to webview-cli. Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
44

5+
## [0.1.2] — 2026-04-17
6+
7+
### Added
8+
- `--x`, `--y`, `--screen` CLI flags for precise window positioning (top-left origin, point-based, 0-indexed screen)
9+
- Window ID is emitted to stderr as `[wid] <id>` — enables `screencapture -l <id>` for clean captures
10+
- CSS: multi-line text in `Text` components now preserves newlines (`white-space: pre-wrap`)
11+
- Five screenshots in `media/` showing real agent-UI patterns (deploy approval, PR picker, config form, destructive confirmation, triage)
12+
13+
### Changed
14+
- Diagnostic stderr output on startup: screen list + chosen screen + final window rect. Useful for automation and debugging.
15+
516
## [0.1.1] — 2026-04-17
617

718
### Added

README.md

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
</p>
66

77
<p align="center">
8-
<img src="media/hero.gif" alt="An AI agent spawns webview-cli, a native macOS window pops up in under 200ms, user submits a form, structured JSON goes back to the agent" width="640">
8+
<img src="media/hero-deploy.png" alt="Deploy approval window — an agent asks for production deploy approval with a native macOS UI" width="560">
99
</p>
1010

1111
<p align="center">
@@ -133,38 +133,40 @@ esac
133133
<tr>
134134
<td width="50%" valign="top">
135135

136-
### Deploy approval
136+
### Pick from options
137137

138-
Show the diff, context, a comment field. One click, agent continues with structured result plus the note going into the deploy log.
138+
Agent enumerates candidates (PRs, branches, files). User picks one with radio buttons. Agent proceeds with the choice.
139139

140-
[`examples/hero-deploy-approval.jsonl`](examples/hero-deploy-approval.jsonl)
140+
<img src="media/select-pr.png" alt="PR picker with 5 options" width="100%">
141141

142142
</td>
143143
<td width="50%" valign="top">
144144

145-
### Pick from options
145+
### Multi-field config
146146

147-
Agent enumerates candidates (PRs, branches, files). User picks one with radio buttons. Agent proceeds with the choice.
147+
Text inputs + selects + checkboxes in one native form. Better than six `read -p` prompts in a row.
148148

149-
[`examples/`](examples/)
149+
<img src="media/form-config.png" alt="Multi-field config form" width="100%">
150150

151151
</td>
152152
</tr>
153153
<tr>
154154
<td width="50%" valign="top">
155155

156-
### Multi-field config
156+
### Destructive confirmation
157157

158-
Text inputs + selects + checkboxes in one native form. Better than six `read -p` prompts in a row.
158+
Irreversible action with context, safety checkbox, and a danger-variant button. Prevents the "yes 40 times in a row" mistake.
159+
160+
<img src="media/confirm-destructive.png" alt="Destructive confirmation with safety check" width="100%">
159161

160162
</td>
161163
<td width="50%" valign="top">
162164

163-
### Custom HTML via `agent://`
165+
### Triage / classification
164166

165-
When the A2UI catalog isn't enough — charts, diffs, diagrams — pipe base64-encoded HTML on stdin. In-memory scheme handler serves it, no HTTP server.
167+
Priority dropdown, complexity radio, and notes in one native form. Agent gets structured metadata back to act on.
166168

167-
[`docs/protocol.md#agent-scheme`](docs/protocol.md)
169+
<img src="media/triage.png" alt="Triage form" width="100%">
168170

169171
</td>
170172
</tr>

media/confirm-destructive.png

106 KB
Loading

media/form-config.png

106 KB
Loading

media/hero-deploy.png

110 KB
Loading

media/select-pr.png

112 KB
Loading

media/triage.png

106 KB
Loading

src/main.swift

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ struct Config {
1010
var height: Int = 768
1111
var timeout: Int = 0
1212
var a2ui: Bool = false
13+
var x: Int? = nil // nil = center on screen
14+
var y: Int? = nil
15+
var screen: Int = 0 // NSScreen index; 0 = main
1316
}
1417

1518
func parseArgs() -> Config? {
@@ -35,6 +38,15 @@ func parseArgs() -> Config? {
3538
config.timeout = Int(args[i]) ?? 0
3639
case "--a2ui":
3740
config.a2ui = true
41+
case "--x":
42+
i += 1; guard i < args.count else { return nil }
43+
config.x = Int(args[i])
44+
case "--y":
45+
i += 1; guard i < args.count else { return nil }
46+
config.y = Int(args[i])
47+
case "--screen":
48+
i += 1; guard i < args.count else { return nil }
49+
config.screen = Int(args[i]) ?? 0
3850
case "--help", "-h":
3951
printUsage(); exit(0)
4052
default:
@@ -231,12 +243,21 @@ class AppCoordinator: NSObject, WKScriptMessageHandler, WKNavigationDelegate, NS
231243
webView = WKWebView(frame: .zero, configuration: webConfig)
232244
webView.navigationDelegate = self
233245

234-
let screenFrame = NSScreen.main?.visibleFrame ?? NSRect(x: 0, y: 0, width: 1024, height: 768)
235-
let windowRect = NSRect(
236-
x: (screenFrame.width - CGFloat(config.width)) / 2 + screenFrame.origin.x,
237-
y: (screenFrame.height - CGFloat(config.height)) / 2 + screenFrame.origin.y,
238-
width: CGFloat(config.width), height: CGFloat(config.height)
239-
)
246+
// Select the target screen (0 = main, 1+ = secondary displays in NSScreen.screens order)
247+
let screens = NSScreen.screens
248+
for (i, s) in screens.enumerated() {
249+
writeStderr("[screen \(i)] frame=\(Int(s.frame.origin.x)),\(Int(s.frame.origin.y)) \(Int(s.frame.width))x\(Int(s.frame.height))\(s == NSScreen.main ? " MAIN" : "")")
250+
}
251+
let targetScreen = (config.screen >= 0 && config.screen < screens.count) ? screens[config.screen] : (NSScreen.main ?? screens.first!)
252+
let screenFrame = targetScreen.visibleFrame
253+
writeStderr("[chosen screen \(config.screen)] visibleFrame=\(Int(screenFrame.origin.x)),\(Int(screenFrame.origin.y)) \(Int(screenFrame.width))x\(Int(screenFrame.height))")
254+
// AppKit y=0 is BOTTOM of the global space. --y in CLI is from TOP of the target screen.
255+
let winX: CGFloat = config.x.map { CGFloat($0) + screenFrame.origin.x }
256+
?? (screenFrame.width - CGFloat(config.width)) / 2 + screenFrame.origin.x
257+
let winY: CGFloat = config.y.map { screenFrame.maxY - CGFloat($0) - CGFloat(config.height) }
258+
?? (screenFrame.height - CGFloat(config.height)) / 2 + screenFrame.origin.y
259+
let windowRect = NSRect(x: winX, y: winY, width: CGFloat(config.width), height: CGFloat(config.height))
260+
writeStderr("[window rect] x=\(Int(winX)) y=\(Int(winY)) w=\(config.width) h=\(config.height)")
240261

241262
window = NSWindow(
242263
contentRect: windowRect,
@@ -260,6 +281,7 @@ class AppCoordinator: NSObject, WKScriptMessageHandler, WKNavigationDelegate, NS
260281

261282
window.makeKeyAndOrderFront(nil)
262283
NSApp.activate(ignoringOtherApps: true)
284+
writeStderr("[wid] \(window.windowNumber)")
263285

264286
if config.timeout > 0 {
265287
timeoutTimer = Timer.scheduledTimer(withTimeInterval: TimeInterval(config.timeout), repeats: false) { [weak self] _ in
@@ -489,7 +511,7 @@ body {
489511
background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
490512
padding: 1.5rem;
491513
}
492-
.a2ui-text { line-height: 1.5; letter-spacing: -0.01em; }
514+
.a2ui-text { line-height: 1.5; letter-spacing: -0.01em; white-space: pre-wrap; }
493515
.a2ui-text.h1 { font-size: 1.75rem; font-weight: 700; letter-spacing: -0.02em; }
494516
.a2ui-text.h2 { font-size: 1.35rem; font-weight: 600; letter-spacing: -0.02em; }
495517
.a2ui-text.h3 { font-size: 1.05rem; font-weight: 600; letter-spacing: -0.01em; }

0 commit comments

Comments
 (0)