Metadata-Version: 2.5
Name: web-picker
Version: 0.2.0
Summary: A native GUI tool for AI agents to ask humans to visually pick one of N HTML variants
Project-URL: Homepage, https://github.com/human-picker/web-picker
Project-URL: Source, https://github.com/human-picker/web-picker
Project-URL: Issues, https://github.com/human-picker/web-picker/issues
Author-email: RinKokawa <rin@rinco.cc>
License-Expression: MIT
License-File: LICENSE
Keywords: ai-agent,compare,gui,html,human-in-the-loop,picker,preview,pyside6,qt,web
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Win32 (MS Windows)
Classifier: Environment :: X11 Applications :: Qt
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: Microsoft :: Windows
Classifier: Operating System :: OS Independent
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development
Classifier: Topic :: Utilities
Requires-Python: >=3.9
Requires-Dist: pyside6>=6.5
Provides-Extra: dev
Requires-Dist: build>=1.0; extra == 'dev'
Requires-Dist: pytest>=7.0; extra == 'dev'
Requires-Dist: ruff>=0.1; extra == 'dev'
Requires-Dist: twine>=4.0; extra == 'dev'
Description-Content-Type: text/markdown

# web-picker

[![PyPI version](https://img.shields.io/pypi/v/web-picker)](https://pypi.org/project/web-picker/)
[![Python versions](https://img.shields.io/pypi/pyversions/web-picker)](https://pypi.org/project/web-picker/)
[![License](https://img.shields.io/pypi/l/web-picker)](https://github.com/human-picker/web-picker/blob/main/LICENSE)
[![Downloads](https://img.shields.io/pypi/dm/web-picker)](https://pypistats.org/packages/web-picker)

**A Human-in-the-Loop Visual Comparator for AI Agents**

> Companion to [svg-picker](https://pypi.org/project/svg-picker/), inspired by [HULA: Human-In-the-Loop Software Development Agents](https://arxiv.org/abs/2411.12924) (ICSE SEIP 2025)

---

## What Is This?

When AI agents write web code, they often propose multiple visual approaches in text — *"should the hero be a left-aligned image with caption, or a centered headline with gradient, or a video background?"* Describing these in markdown is hard for the human to evaluate.

**web-picker turns those text descriptions into actual rendered previews.** The AI writes 2-9 candidate HTML files, calls `web-picker a.html b.html c.html`, and a native window opens showing every candidate side-by-side. The human clicks the one they prefer (or presses `1`/`2`/`3`). The picked file path is printed to stdout, which the AI reads to continue with the chosen design.

**The human provides visual judgment. The AI handles everything else.**

---

## Background: Why "Human-in-the-Loop"?

The paper [HULA: Human-In-the-Loop Software Development Agents](https://arxiv.org/abs/2411.12924) (Takerngsaksiri et al., ICSE SEIP 2025) demonstrates a critical insight:

> Existing LLM-based coding agents rarely incorporate human feedback at intermediate stages. When humans can intervene during plan generation and code writing — not just review final output — development time and effort decrease significantly.

web-picker applies this principle to a specific, narrow task: **HTML variant selection**. It is the visual-design counterpart to [svg-picker](https://pypi.org/project/svg-picker/), which solves icon selection. Together they form a minimal toolkit for AI agents to consult humans on small, reversible design decisions without dragging them into a full review loop.

---

## How It Works

```
User/CI:  web-picker opt1.html opt2.html opt3.html
            │
            ▼
   ┌────────────────────────────────┐
   │  Native window (PySide6)       │
   │  ┌─────────┐ ┌─────────┐ ┌────┐│
   │  │  opt1   │ │  opt2   │ │op3 ││
   │  │  HTML   │ │  HTML   │ │HTML││
   │  │ preview │ │ preview │ │prv ││
   │  └─────────┘ └─────────┘ └────┘│
   │  ← Human clicks / presses 1-3  │
   │       (highlights the card)    │
   │  ← Then clicks Confirm or ↩    │
   └──────────────────┬─────────────┘
                      │
                      ▼
        Picked path → stdout → AI reads it
```

Two-step pick: clicking a card (or pressing `1`-`9`) **highlights** it; only clicking **Confirm** (or pressing `Enter`) commits the choice. This gives hesitant users a moment to look, then change their mind, before committing. The page itself is **view-only** — iframe interactivity is intentionally sacrificed so a click on the card surface always means "select this one".

---

## Features

- **Native GUI** — PySide6 window, no browser popup
- **HTML rendering** — each option rendered via embedded Chromium (`QWebEngineView`)
- **Adaptive grid** — 1-9 options laid out to maximize per-card area
- **Draggable resize** — drag the handles between cards to make any preview wider/taller
- **Open in real browser** — double-click a card to open it in your system default browser for full-size inspection
- **Two-step pick** — click a card to highlight, then Confirm (or `Enter`) to commit; users can change their mind before committing
- **Keyboard shortcuts** — `1`-`9` to highlight, `Enter` to confirm, `Esc` to cancel
- **Cancellation signal** — closing the window writes `[web-picker] cancelled: ...` to stderr
- **Themable** — `cream` / `sky` / `dark` via `--theme`
- **One-step install** — `pip install web-picker`, single command
- **Zero config** — no API keys, no servers, no infrastructure

---

## Install

```bash
pip install web-picker
```

Or for development:
```bash
pip install -e .
```

**Troubleshooting**: if you see `QtWebEngineWidgets is not available in this install`, run `pip install PySide6-Addons`. Some minimal PySide6 installs ship only the Essentials subset.

---

## Usage

```bash
web-picker <file1.html> [file2.html ...]    # 1-9 files
```

### Options

| Flag | Description |
|---|---|
| `-t`, `--theme <name>` | Background theme. Choices: `cream` (default), `sky`, `dark` |
| `--width <px>` | Override window width in pixels (default: auto-fit screen) |
| `--height <px>` | Override window height in pixels (default: auto-fit screen) |
| `--maximize` | Open the window maximized to fill the screen (cannot combine with `--width`/`--height`) |
| `--slider-handle <px>` | Zoom-slider knob width in pixels (default: `0` = Qt default; recommended `14`-`24` for trackpad/touch) |

#### Default Theme via `.env`

Don't want to type `--theme dark` every time? Drop a `.env` in the directory you launch `web-picker` from:

```env
# Uncomment to override the default theme
# WEB_PICKER_THEME = sky
```

Precedence: `--theme` CLI flag > `$WEB_PICKER_THEME` shell variable > `.env` file > built-in `cream`.

The `.env` file is created automatically on first launch with the options commented out.

### Examples

```bash
web-picker hero-a.html hero-b.html hero-c.html     # 3 hero variants
web-picker landing.html                             # confirm a single design
web-picker card.html card-dark.html card-outline.html card-flat.html --theme dark
web-picker hero-a.html hero-b.html --maximize       # fill the screen for easier preview
web-picker hero-a.html hero-b.html --width 1920 --height 1080   # pin to a specific size
web-picker hero-a.html hero-b.html hero-c.html --slider-handle 20 # chunky zoom knobs for trackpad use
```

### The HTML Input Contract

Each file passed to `web-picker` MUST be a **complete, standalone HTML document** — `<!DOCTYPE html>` through `</html>`. The agent is responsible for writing them; web-picker does no rendering magic.

Relative paths (CSS, images, fonts) work fine because each file is loaded via `file://`. Inline styles, external CDNs, and even `<script>` blocks are all permitted and rendered as-is. Each option is a real browser tab — animations, hover effects, the works.

A minimal example (each file is a full HTML page):

```html
<!-- hero-a.html -->
<!DOCTYPE html>
<html><body style="margin:0; font-family:sans-serif">
  <div style="height:100vh; display:grid; place-items:center; background:#1e3a8a; color:white">
    <h1>Welcome to Acme</h1>
  </div>
</body></html>
```

### Layout Strategy

| Options | Grid |
|---|---|
| 1 | 1×1 |
| 2 | 1×2 |
| 3 | 1×3 |
| 4 | 2×2 |
| 5-6 | 2×3 |
| 7-9 | 3×3 |

More than 9 options is rejected — the human can't meaningfully compare that many at once, and 10+ `QWebEngineView` instances will exhaust your RAM.

### Steps

1. Window opens, every option rendered side-by-side
2. **Drag** the handles between cards to resize any preview — if a card feels too narrow, pull it wider to inspect the detail
3. **Double-click** a card to open it in your system default browser (useful when an embedded preview is too small to judge typography or animations)
4. **Click** a card (or press its number key `1`-`9`) to highlight it — a purple border marks your current selection
5. **Click another card** to change your selection, or click **Confirm** (top-right) / press `Enter` to commit
6. Window closes; the picked file's absolute path is on stdout
7. **Close the window** (X) or press `Esc` to cancel — a `[web-picker] cancelled: ...` line is written to stderr

---

## For AI Agents

### As a Claude Code Skill

Place this file as `~/.claude/skills/web-picker.md`:

```markdown
# web-picker

Compare 2-9 HTML variants and let the human visually pick one.

Usage: web-picker <file1.html> [file2.html ...]

The human clicks a card (or presses 1-9) to highlight, then clicks
Confirm (or presses Enter) to commit. The picked file's absolute
path is printed to stdout. If the window is closed without confirming,
a "[web-picker] cancelled: ..." line is written to stderr — read stderr
to distinguish cancel from crash.
```

### Programmatic Usage

```python
import subprocess

result = subprocess.run(
    ["web-picker", "hero-a.html", "hero-b.html", "hero-c.html"],
    capture_output=True, text=True,
)

if result.returncode != 0:
    raise RuntimeError(f"web-picker crashed: {result.stderr}")

if "[web-picker] cancelled" in result.stderr:
    # 用户主动关闭窗口,没选
    print("User cancelled without picking")
else:
    # 正常完成 —— result.stdout 是被选中的文件绝对路径
    chosen_path = result.stdout.strip()
    print(f"User picked: {chosen_path}")
```

---

## Comparison

| | web-picker | svg-picker | HULA (Atlassian) |
|---|---|---|---|
| Decision type | HTML variant (1 of N) | SVG icon (N of M) | Full software dev |
| Visual surface | Embedded web pages | Icon thumbnails | Plan + code review |
| Scope | Single tool, single task | Single tool, single task | Full agent framework |
| Human role | Visual design judge | Visual icon judge | Plan + code reviewer |
| Deployment | `pip install` | `pip install` | Jira plugin |
| Target | AI agents | AI agents | Human engineers |

web-picker and svg-picker share the same philosophy: **the human only intervenes on narrow, reversible, visual decisions** — everything else stays with the agent.

---

## Related Work

- [HULA: Human-In-the-Loop Software Development Agents](https://arxiv.org/abs/2411.12924) — ICSE SEIP 2025
- [svg-picker](https://pypi.org/project/svg-picker/) — sibling tool for icon selection
- [acte](https://github.com/j66n/acte) — Framework for GUI-like Agent Tools
- [OpenUI](https://github.com/thesysdev/openui) — Open Standard for Generative UI

---

## License

MIT
