web-picker 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,41 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+ workflow_dispatch:
8
+
9
+ jobs:
10
+ build:
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - uses: actions/checkout@v4
14
+ - uses: actions/setup-python@v5
15
+ with:
16
+ python-version: "3.13"
17
+ - name: Install build
18
+ run: python -m pip install --upgrade build
19
+ - name: Build sdist and wheel
20
+ run: python -m build
21
+ - name: Upload artifacts
22
+ uses: actions/upload-artifact@v4
23
+ with:
24
+ name: dist
25
+ path: dist/
26
+
27
+ publish:
28
+ needs: build
29
+ runs-on: ubuntu-latest
30
+ if: startsWith(github.ref, 'refs/tags/v')
31
+ environment:
32
+ name: pypi
33
+ url: https://pypi.org/p/web-picker
34
+ permissions:
35
+ id-token: write
36
+ steps:
37
+ - uses: actions/download-artifact@v4
38
+ with:
39
+ name: dist
40
+ path: dist/
41
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,15 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.egg-info/
4
+ dist/
5
+ build/
6
+ .eggs/
7
+ *.egg
8
+ .pytest_cache/
9
+ .tox/
10
+ .mypy_cache/
11
+ .ruff_cache/
12
+ .env
13
+ .venv
14
+ venv/
15
+ test-fixtures/
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 RinKokawa
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,280 @@
1
+ Metadata-Version: 2.5
2
+ Name: web-picker
3
+ Version: 0.2.0
4
+ Summary: A native GUI tool for AI agents to ask humans to visually pick one of N HTML variants
5
+ Project-URL: Homepage, https://github.com/human-picker/web-picker
6
+ Project-URL: Source, https://github.com/human-picker/web-picker
7
+ Project-URL: Issues, https://github.com/human-picker/web-picker/issues
8
+ Author-email: RinKokawa <rin@rinco.cc>
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: ai-agent,compare,gui,html,human-in-the-loop,picker,preview,pyside6,qt,web
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Environment :: Win32 (MS Windows)
14
+ Classifier: Environment :: X11 Applications :: Qt
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Operating System :: Microsoft :: Windows
18
+ Classifier: Operating System :: OS Independent
19
+ Classifier: Operating System :: POSIX :: Linux
20
+ Classifier: Programming Language :: Python :: 3
21
+ Classifier: Programming Language :: Python :: 3.9
22
+ Classifier: Programming Language :: Python :: 3.10
23
+ Classifier: Programming Language :: Python :: 3.11
24
+ Classifier: Programming Language :: Python :: 3.12
25
+ Classifier: Programming Language :: Python :: 3.13
26
+ Classifier: Topic :: Software Development
27
+ Classifier: Topic :: Utilities
28
+ Requires-Python: >=3.9
29
+ Requires-Dist: pyside6>=6.5
30
+ Provides-Extra: dev
31
+ Requires-Dist: build>=1.0; extra == 'dev'
32
+ Requires-Dist: pytest>=7.0; extra == 'dev'
33
+ Requires-Dist: ruff>=0.1; extra == 'dev'
34
+ Requires-Dist: twine>=4.0; extra == 'dev'
35
+ Description-Content-Type: text/markdown
36
+
37
+ # web-picker
38
+
39
+ [![PyPI version](https://img.shields.io/pypi/v/web-picker)](https://pypi.org/project/web-picker/)
40
+ [![Python versions](https://img.shields.io/pypi/pyversions/web-picker)](https://pypi.org/project/web-picker/)
41
+ [![License](https://img.shields.io/pypi/l/web-picker)](https://github.com/human-picker/web-picker/blob/main/LICENSE)
42
+ [![Downloads](https://img.shields.io/pypi/dm/web-picker)](https://pypistats.org/packages/web-picker)
43
+
44
+ **A Human-in-the-Loop Visual Comparator for AI Agents**
45
+
46
+ > 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)
47
+
48
+ ---
49
+
50
+ ## What Is This?
51
+
52
+ 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.
53
+
54
+ **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.
55
+
56
+ **The human provides visual judgment. The AI handles everything else.**
57
+
58
+ ---
59
+
60
+ ## Background: Why "Human-in-the-Loop"?
61
+
62
+ 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:
63
+
64
+ > 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.
65
+
66
+ 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.
67
+
68
+ ---
69
+
70
+ ## How It Works
71
+
72
+ ```
73
+ User/CI: web-picker opt1.html opt2.html opt3.html
74
+ │
75
+ ▼
76
+ ┌────────────────────────────────┐
77
+ │ Native window (PySide6) │
78
+ │ ┌─────────┐ ┌─────────┐ ┌────┐│
79
+ │ │ opt1 │ │ opt2 │ │op3 ││
80
+ │ │ HTML │ │ HTML │ │HTML││
81
+ │ │ preview │ │ preview │ │prv ││
82
+ │ └─────────┘ └─────────┘ └────┘│
83
+ │ ← Human clicks / presses 1-3 │
84
+ │ (highlights the card) │
85
+ │ ← Then clicks Confirm or ↩ │
86
+ └──────────────────┬─────────────┘
87
+ │
88
+ ▼
89
+ Picked path → stdout → AI reads it
90
+ ```
91
+
92
+ 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".
93
+
94
+ ---
95
+
96
+ ## Features
97
+
98
+ - **Native GUI** — PySide6 window, no browser popup
99
+ - **HTML rendering** — each option rendered via embedded Chromium (`QWebEngineView`)
100
+ - **Adaptive grid** — 1-9 options laid out to maximize per-card area
101
+ - **Draggable resize** — drag the handles between cards to make any preview wider/taller
102
+ - **Open in real browser** — double-click a card to open it in your system default browser for full-size inspection
103
+ - **Two-step pick** — click a card to highlight, then Confirm (or `Enter`) to commit; users can change their mind before committing
104
+ - **Keyboard shortcuts** — `1`-`9` to highlight, `Enter` to confirm, `Esc` to cancel
105
+ - **Cancellation signal** — closing the window writes `[web-picker] cancelled: ...` to stderr
106
+ - **Themable** — `cream` / `sky` / `dark` via `--theme`
107
+ - **One-step install** — `pip install web-picker`, single command
108
+ - **Zero config** — no API keys, no servers, no infrastructure
109
+
110
+ ---
111
+
112
+ ## Install
113
+
114
+ ```bash
115
+ pip install web-picker
116
+ ```
117
+
118
+ Or for development:
119
+ ```bash
120
+ pip install -e .
121
+ ```
122
+
123
+ **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.
124
+
125
+ ---
126
+
127
+ ## Usage
128
+
129
+ ```bash
130
+ web-picker <file1.html> [file2.html ...] # 1-9 files
131
+ ```
132
+
133
+ ### Options
134
+
135
+ | Flag | Description |
136
+ |---|---|
137
+ | `-t`, `--theme <name>` | Background theme. Choices: `cream` (default), `sky`, `dark` |
138
+ | `--width <px>` | Override window width in pixels (default: auto-fit screen) |
139
+ | `--height <px>` | Override window height in pixels (default: auto-fit screen) |
140
+ | `--maximize` | Open the window maximized to fill the screen (cannot combine with `--width`/`--height`) |
141
+ | `--slider-handle <px>` | Zoom-slider knob width in pixels (default: `0` = Qt default; recommended `14`-`24` for trackpad/touch) |
142
+
143
+ #### Default Theme via `.env`
144
+
145
+ Don't want to type `--theme dark` every time? Drop a `.env` in the directory you launch `web-picker` from:
146
+
147
+ ```env
148
+ # Uncomment to override the default theme
149
+ # WEB_PICKER_THEME = sky
150
+ ```
151
+
152
+ Precedence: `--theme` CLI flag > `$WEB_PICKER_THEME` shell variable > `.env` file > built-in `cream`.
153
+
154
+ The `.env` file is created automatically on first launch with the options commented out.
155
+
156
+ ### Examples
157
+
158
+ ```bash
159
+ web-picker hero-a.html hero-b.html hero-c.html # 3 hero variants
160
+ web-picker landing.html # confirm a single design
161
+ web-picker card.html card-dark.html card-outline.html card-flat.html --theme dark
162
+ web-picker hero-a.html hero-b.html --maximize # fill the screen for easier preview
163
+ web-picker hero-a.html hero-b.html --width 1920 --height 1080 # pin to a specific size
164
+ web-picker hero-a.html hero-b.html hero-c.html --slider-handle 20 # chunky zoom knobs for trackpad use
165
+ ```
166
+
167
+ ### The HTML Input Contract
168
+
169
+ 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.
170
+
171
+ 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.
172
+
173
+ A minimal example (each file is a full HTML page):
174
+
175
+ ```html
176
+ <!-- hero-a.html -->
177
+ <!DOCTYPE html>
178
+ <html><body style="margin:0; font-family:sans-serif">
179
+ <div style="height:100vh; display:grid; place-items:center; background:#1e3a8a; color:white">
180
+ <h1>Welcome to Acme</h1>
181
+ </div>
182
+ </body></html>
183
+ ```
184
+
185
+ ### Layout Strategy
186
+
187
+ | Options | Grid |
188
+ |---|---|
189
+ | 1 | 1×1 |
190
+ | 2 | 1×2 |
191
+ | 3 | 1×3 |
192
+ | 4 | 2×2 |
193
+ | 5-6 | 2×3 |
194
+ | 7-9 | 3×3 |
195
+
196
+ More than 9 options is rejected — the human can't meaningfully compare that many at once, and 10+ `QWebEngineView` instances will exhaust your RAM.
197
+
198
+ ### Steps
199
+
200
+ 1. Window opens, every option rendered side-by-side
201
+ 2. **Drag** the handles between cards to resize any preview — if a card feels too narrow, pull it wider to inspect the detail
202
+ 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)
203
+ 4. **Click** a card (or press its number key `1`-`9`) to highlight it — a purple border marks your current selection
204
+ 5. **Click another card** to change your selection, or click **Confirm** (top-right) / press `Enter` to commit
205
+ 6. Window closes; the picked file's absolute path is on stdout
206
+ 7. **Close the window** (X) or press `Esc` to cancel — a `[web-picker] cancelled: ...` line is written to stderr
207
+
208
+ ---
209
+
210
+ ## For AI Agents
211
+
212
+ ### As a Claude Code Skill
213
+
214
+ Place this file as `~/.claude/skills/web-picker.md`:
215
+
216
+ ```markdown
217
+ # web-picker
218
+
219
+ Compare 2-9 HTML variants and let the human visually pick one.
220
+
221
+ Usage: web-picker <file1.html> [file2.html ...]
222
+
223
+ The human clicks a card (or presses 1-9) to highlight, then clicks
224
+ Confirm (or presses Enter) to commit. The picked file's absolute
225
+ path is printed to stdout. If the window is closed without confirming,
226
+ a "[web-picker] cancelled: ..." line is written to stderr — read stderr
227
+ to distinguish cancel from crash.
228
+ ```
229
+
230
+ ### Programmatic Usage
231
+
232
+ ```python
233
+ import subprocess
234
+
235
+ result = subprocess.run(
236
+ ["web-picker", "hero-a.html", "hero-b.html", "hero-c.html"],
237
+ capture_output=True, text=True,
238
+ )
239
+
240
+ if result.returncode != 0:
241
+ raise RuntimeError(f"web-picker crashed: {result.stderr}")
242
+
243
+ if "[web-picker] cancelled" in result.stderr:
244
+ # 用户主动关闭窗口,没选
245
+ print("User cancelled without picking")
246
+ else:
247
+ # 正常完成 —— result.stdout 是被选中的文件绝对路径
248
+ chosen_path = result.stdout.strip()
249
+ print(f"User picked: {chosen_path}")
250
+ ```
251
+
252
+ ---
253
+
254
+ ## Comparison
255
+
256
+ | | web-picker | svg-picker | HULA (Atlassian) |
257
+ |---|---|---|---|
258
+ | Decision type | HTML variant (1 of N) | SVG icon (N of M) | Full software dev |
259
+ | Visual surface | Embedded web pages | Icon thumbnails | Plan + code review |
260
+ | Scope | Single tool, single task | Single tool, single task | Full agent framework |
261
+ | Human role | Visual design judge | Visual icon judge | Plan + code reviewer |
262
+ | Deployment | `pip install` | `pip install` | Jira plugin |
263
+ | Target | AI agents | AI agents | Human engineers |
264
+
265
+ web-picker and svg-picker share the same philosophy: **the human only intervenes on narrow, reversible, visual decisions** — everything else stays with the agent.
266
+
267
+ ---
268
+
269
+ ## Related Work
270
+
271
+ - [HULA: Human-In-the-Loop Software Development Agents](https://arxiv.org/abs/2411.12924) — ICSE SEIP 2025
272
+ - [svg-picker](https://pypi.org/project/svg-picker/) — sibling tool for icon selection
273
+ - [acte](https://github.com/j66n/acte) — Framework for GUI-like Agent Tools
274
+ - [OpenUI](https://github.com/thesysdev/openui) — Open Standard for Generative UI
275
+
276
+ ---
277
+
278
+ ## License
279
+
280
+ MIT
@@ -0,0 +1,244 @@
1
+ # web-picker
2
+
3
+ [![PyPI version](https://img.shields.io/pypi/v/web-picker)](https://pypi.org/project/web-picker/)
4
+ [![Python versions](https://img.shields.io/pypi/pyversions/web-picker)](https://pypi.org/project/web-picker/)
5
+ [![License](https://img.shields.io/pypi/l/web-picker)](https://github.com/human-picker/web-picker/blob/main/LICENSE)
6
+ [![Downloads](https://img.shields.io/pypi/dm/web-picker)](https://pypistats.org/packages/web-picker)
7
+
8
+ **A Human-in-the-Loop Visual Comparator for AI Agents**
9
+
10
+ > 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)
11
+
12
+ ---
13
+
14
+ ## What Is This?
15
+
16
+ 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.
17
+
18
+ **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.
19
+
20
+ **The human provides visual judgment. The AI handles everything else.**
21
+
22
+ ---
23
+
24
+ ## Background: Why "Human-in-the-Loop"?
25
+
26
+ 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:
27
+
28
+ > 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.
29
+
30
+ 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.
31
+
32
+ ---
33
+
34
+ ## How It Works
35
+
36
+ ```
37
+ User/CI: web-picker opt1.html opt2.html opt3.html
38
+ │
39
+ ▼
40
+ ┌────────────────────────────────┐
41
+ │ Native window (PySide6) │
42
+ │ ┌─────────┐ ┌─────────┐ ┌────┐│
43
+ │ │ opt1 │ │ opt2 │ │op3 ││
44
+ │ │ HTML │ │ HTML │ │HTML││
45
+ │ │ preview │ │ preview │ │prv ││
46
+ │ └─────────┘ └─────────┘ └────┘│
47
+ │ ← Human clicks / presses 1-3 │
48
+ │ (highlights the card) │
49
+ │ ← Then clicks Confirm or ↩ │
50
+ └──────────────────┬─────────────┘
51
+ │
52
+ ▼
53
+ Picked path → stdout → AI reads it
54
+ ```
55
+
56
+ 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".
57
+
58
+ ---
59
+
60
+ ## Features
61
+
62
+ - **Native GUI** — PySide6 window, no browser popup
63
+ - **HTML rendering** — each option rendered via embedded Chromium (`QWebEngineView`)
64
+ - **Adaptive grid** — 1-9 options laid out to maximize per-card area
65
+ - **Draggable resize** — drag the handles between cards to make any preview wider/taller
66
+ - **Open in real browser** — double-click a card to open it in your system default browser for full-size inspection
67
+ - **Two-step pick** — click a card to highlight, then Confirm (or `Enter`) to commit; users can change their mind before committing
68
+ - **Keyboard shortcuts** — `1`-`9` to highlight, `Enter` to confirm, `Esc` to cancel
69
+ - **Cancellation signal** — closing the window writes `[web-picker] cancelled: ...` to stderr
70
+ - **Themable** — `cream` / `sky` / `dark` via `--theme`
71
+ - **One-step install** — `pip install web-picker`, single command
72
+ - **Zero config** — no API keys, no servers, no infrastructure
73
+
74
+ ---
75
+
76
+ ## Install
77
+
78
+ ```bash
79
+ pip install web-picker
80
+ ```
81
+
82
+ Or for development:
83
+ ```bash
84
+ pip install -e .
85
+ ```
86
+
87
+ **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.
88
+
89
+ ---
90
+
91
+ ## Usage
92
+
93
+ ```bash
94
+ web-picker <file1.html> [file2.html ...] # 1-9 files
95
+ ```
96
+
97
+ ### Options
98
+
99
+ | Flag | Description |
100
+ |---|---|
101
+ | `-t`, `--theme <name>` | Background theme. Choices: `cream` (default), `sky`, `dark` |
102
+ | `--width <px>` | Override window width in pixels (default: auto-fit screen) |
103
+ | `--height <px>` | Override window height in pixels (default: auto-fit screen) |
104
+ | `--maximize` | Open the window maximized to fill the screen (cannot combine with `--width`/`--height`) |
105
+ | `--slider-handle <px>` | Zoom-slider knob width in pixels (default: `0` = Qt default; recommended `14`-`24` for trackpad/touch) |
106
+
107
+ #### Default Theme via `.env`
108
+
109
+ Don't want to type `--theme dark` every time? Drop a `.env` in the directory you launch `web-picker` from:
110
+
111
+ ```env
112
+ # Uncomment to override the default theme
113
+ # WEB_PICKER_THEME = sky
114
+ ```
115
+
116
+ Precedence: `--theme` CLI flag > `$WEB_PICKER_THEME` shell variable > `.env` file > built-in `cream`.
117
+
118
+ The `.env` file is created automatically on first launch with the options commented out.
119
+
120
+ ### Examples
121
+
122
+ ```bash
123
+ web-picker hero-a.html hero-b.html hero-c.html # 3 hero variants
124
+ web-picker landing.html # confirm a single design
125
+ web-picker card.html card-dark.html card-outline.html card-flat.html --theme dark
126
+ web-picker hero-a.html hero-b.html --maximize # fill the screen for easier preview
127
+ web-picker hero-a.html hero-b.html --width 1920 --height 1080 # pin to a specific size
128
+ web-picker hero-a.html hero-b.html hero-c.html --slider-handle 20 # chunky zoom knobs for trackpad use
129
+ ```
130
+
131
+ ### The HTML Input Contract
132
+
133
+ 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.
134
+
135
+ 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.
136
+
137
+ A minimal example (each file is a full HTML page):
138
+
139
+ ```html
140
+ <!-- hero-a.html -->
141
+ <!DOCTYPE html>
142
+ <html><body style="margin:0; font-family:sans-serif">
143
+ <div style="height:100vh; display:grid; place-items:center; background:#1e3a8a; color:white">
144
+ <h1>Welcome to Acme</h1>
145
+ </div>
146
+ </body></html>
147
+ ```
148
+
149
+ ### Layout Strategy
150
+
151
+ | Options | Grid |
152
+ |---|---|
153
+ | 1 | 1×1 |
154
+ | 2 | 1×2 |
155
+ | 3 | 1×3 |
156
+ | 4 | 2×2 |
157
+ | 5-6 | 2×3 |
158
+ | 7-9 | 3×3 |
159
+
160
+ More than 9 options is rejected — the human can't meaningfully compare that many at once, and 10+ `QWebEngineView` instances will exhaust your RAM.
161
+
162
+ ### Steps
163
+
164
+ 1. Window opens, every option rendered side-by-side
165
+ 2. **Drag** the handles between cards to resize any preview — if a card feels too narrow, pull it wider to inspect the detail
166
+ 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)
167
+ 4. **Click** a card (or press its number key `1`-`9`) to highlight it — a purple border marks your current selection
168
+ 5. **Click another card** to change your selection, or click **Confirm** (top-right) / press `Enter` to commit
169
+ 6. Window closes; the picked file's absolute path is on stdout
170
+ 7. **Close the window** (X) or press `Esc` to cancel — a `[web-picker] cancelled: ...` line is written to stderr
171
+
172
+ ---
173
+
174
+ ## For AI Agents
175
+
176
+ ### As a Claude Code Skill
177
+
178
+ Place this file as `~/.claude/skills/web-picker.md`:
179
+
180
+ ```markdown
181
+ # web-picker
182
+
183
+ Compare 2-9 HTML variants and let the human visually pick one.
184
+
185
+ Usage: web-picker <file1.html> [file2.html ...]
186
+
187
+ The human clicks a card (or presses 1-9) to highlight, then clicks
188
+ Confirm (or presses Enter) to commit. The picked file's absolute
189
+ path is printed to stdout. If the window is closed without confirming,
190
+ a "[web-picker] cancelled: ..." line is written to stderr — read stderr
191
+ to distinguish cancel from crash.
192
+ ```
193
+
194
+ ### Programmatic Usage
195
+
196
+ ```python
197
+ import subprocess
198
+
199
+ result = subprocess.run(
200
+ ["web-picker", "hero-a.html", "hero-b.html", "hero-c.html"],
201
+ capture_output=True, text=True,
202
+ )
203
+
204
+ if result.returncode != 0:
205
+ raise RuntimeError(f"web-picker crashed: {result.stderr}")
206
+
207
+ if "[web-picker] cancelled" in result.stderr:
208
+ # 用户主动关闭窗口,没选
209
+ print("User cancelled without picking")
210
+ else:
211
+ # 正常完成 —— result.stdout 是被选中的文件绝对路径
212
+ chosen_path = result.stdout.strip()
213
+ print(f"User picked: {chosen_path}")
214
+ ```
215
+
216
+ ---
217
+
218
+ ## Comparison
219
+
220
+ | | web-picker | svg-picker | HULA (Atlassian) |
221
+ |---|---|---|---|
222
+ | Decision type | HTML variant (1 of N) | SVG icon (N of M) | Full software dev |
223
+ | Visual surface | Embedded web pages | Icon thumbnails | Plan + code review |
224
+ | Scope | Single tool, single task | Single tool, single task | Full agent framework |
225
+ | Human role | Visual design judge | Visual icon judge | Plan + code reviewer |
226
+ | Deployment | `pip install` | `pip install` | Jira plugin |
227
+ | Target | AI agents | AI agents | Human engineers |
228
+
229
+ web-picker and svg-picker share the same philosophy: **the human only intervenes on narrow, reversible, visual decisions** — everything else stays with the agent.
230
+
231
+ ---
232
+
233
+ ## Related Work
234
+
235
+ - [HULA: Human-In-the-Loop Software Development Agents](https://arxiv.org/abs/2411.12924) — ICSE SEIP 2025
236
+ - [svg-picker](https://pypi.org/project/svg-picker/) — sibling tool for icon selection
237
+ - [acte](https://github.com/j66n/acte) — Framework for GUI-like Agent Tools
238
+ - [OpenUI](https://github.com/thesysdev/openui) — Open Standard for Generative UI
239
+
240
+ ---
241
+
242
+ ## License
243
+
244
+ MIT