Skip to main content

web-picker

PyPI version Python versions License Downloads

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

Companion to svg-picker, inspired by HULA: Human-In-the-Loop Software Development Agents (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 (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, 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

pip install web-picker

Or for development:

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

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:

# 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

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):

<!-- 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:

# 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

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.



License

MIT

Release files for web-picker 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for web-picker 0.2.0
File Size Uploaded
web_picker-0.2.0.tar.gz 17.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for web-picker 0.2.0
File Interpreter ABI Platform
web_picker-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 31.4 kB

Release files / web_picker-0.2.0.tar.gz

Download URL web_picker-0.2.0.tar.gz
Size 17.4 kB
Tags Source
SHA-256 checksum
How to use checksums
8452d3e6e88934d74b61709db88a944dda04b987ad1fa2572d561aba51804d64
BLAKE2b-256 checksum
How to use checksums
6a04e2ec4f2f26f2ced9791e8a36d4114e2e8771cca9df2c4992b728c42216f1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 25, 2026.

Transparency log

Release files / web_picker-0.2.0-py3-none-any.whl

Download URL web_picker-0.2.0-py3-none-any.whl
Size 14.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
466589343bda2f67befc225123a8cf8217d8e488d1e677c1f4ebcafe36117dbb
BLAKE2b-256 checksum
How to use checksums
e230b0885876434fcb258d5797c2e426b13c66bb8dd6e10369c97597395d9650
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 25, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page