webtap-tool 0.3.0__tar.gz → 0.5.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.

Potentially problematic release.


This version of webtap-tool might be problematic. Click here for more details.

Files changed (65) hide show
  1. {webtap_tool-0.3.0 → webtap_tool-0.5.0}/CHANGELOG.md +63 -0
  2. {webtap_tool-0.3.0 → webtap_tool-0.5.0}/PKG-INFO +2 -2
  3. webtap_tool-0.5.0/extension/content.js +97 -0
  4. webtap_tool-0.5.0/extension/manifest.json +22 -0
  5. webtap_tool-0.5.0/extension/sidepanel.html +317 -0
  6. webtap_tool-0.5.0/extension/sidepanel.js +625 -0
  7. {webtap_tool-0.3.0 → webtap_tool-0.5.0}/pyproject.toml +12 -2
  8. webtap_tool-0.5.0/src/webtap/api.py +591 -0
  9. {webtap_tool-0.3.0 → webtap_tool-0.5.0}/src/webtap/app.py +15 -9
  10. {webtap_tool-0.3.0 → webtap_tool-0.5.0}/src/webtap/cdp/session.py +101 -1
  11. {webtap_tool-0.3.0 → webtap_tool-0.5.0}/src/webtap/commands/DEVELOPER_GUIDE.md +108 -22
  12. {webtap_tool-0.3.0 → webtap_tool-0.5.0}/src/webtap/commands/TIPS.md +24 -1
  13. webtap_tool-0.5.0/src/webtap/commands/_builders.py +265 -0
  14. {webtap_tool-0.3.0 → webtap_tool-0.5.0}/src/webtap/commands/body.py +1 -2
  15. {webtap_tool-0.3.0 → webtap_tool-0.5.0}/src/webtap/commands/connection.py +1 -2
  16. {webtap_tool-0.3.0 → webtap_tool-0.5.0}/src/webtap/commands/console.py +1 -2
  17. {webtap_tool-0.3.0 → webtap_tool-0.5.0}/src/webtap/commands/events.py +1 -2
  18. {webtap_tool-0.3.0 → webtap_tool-0.5.0}/src/webtap/commands/fetch.py +1 -2
  19. {webtap_tool-0.3.0 → webtap_tool-0.5.0}/src/webtap/commands/filters.py +95 -62
  20. {webtap_tool-0.3.0 → webtap_tool-0.5.0}/src/webtap/commands/inspect.py +1 -2
  21. webtap_tool-0.5.0/src/webtap/commands/javascript.py +102 -0
  22. {webtap_tool-0.3.0 → webtap_tool-0.5.0}/src/webtap/commands/navigation.py +1 -2
  23. {webtap_tool-0.3.0 → webtap_tool-0.5.0}/src/webtap/commands/network.py +11 -7
  24. webtap_tool-0.5.0/src/webtap/commands/selections.py +129 -0
  25. {webtap_tool-0.3.0 → webtap_tool-0.5.0}/src/webtap/commands/server.py +19 -0
  26. {webtap_tool-0.3.0 → webtap_tool-0.5.0}/src/webtap/filters.py +116 -56
  27. webtap_tool-0.5.0/src/webtap/services/dom.py +512 -0
  28. {webtap_tool-0.3.0 → webtap_tool-0.5.0}/src/webtap/services/main.py +14 -0
  29. webtap_tool-0.3.0/extension/manifest.json +0 -12
  30. webtap_tool-0.3.0/extension/popup.html +0 -181
  31. webtap_tool-0.3.0/extension/popup.js +0 -314
  32. webtap_tool-0.3.0/src/webtap/api.py +0 -282
  33. webtap_tool-0.3.0/src/webtap/commands/_builders.py +0 -127
  34. webtap_tool-0.3.0/src/webtap/commands/_errors.py +0 -108
  35. webtap_tool-0.3.0/src/webtap/commands/javascript.py +0 -87
  36. {webtap_tool-0.3.0 → webtap_tool-0.5.0}/.gitignore +0 -0
  37. {webtap_tool-0.3.0 → webtap_tool-0.5.0}/ARCHITECTURE.md +0 -0
  38. {webtap_tool-0.3.0 → webtap_tool-0.5.0}/README.md +0 -0
  39. {webtap_tool-0.3.0 → webtap_tool-0.5.0}/data/filters.json +0 -0
  40. {webtap_tool-0.3.0 → webtap_tool-0.5.0}/llms.txt +0 -0
  41. {webtap_tool-0.3.0 → webtap_tool-0.5.0}/src/webtap/VISION.md +0 -0
  42. {webtap_tool-0.3.0 → webtap_tool-0.5.0}/src/webtap/__init__.py +0 -0
  43. {webtap_tool-0.3.0 → webtap_tool-0.5.0}/src/webtap/cdp/README.md +0 -0
  44. {webtap_tool-0.3.0 → webtap_tool-0.5.0}/src/webtap/cdp/__init__.py +0 -0
  45. {webtap_tool-0.3.0 → webtap_tool-0.5.0}/src/webtap/cdp/query.py +0 -0
  46. {webtap_tool-0.3.0 → webtap_tool-0.5.0}/src/webtap/cdp/schema/README.md +0 -0
  47. {webtap_tool-0.3.0 → webtap_tool-0.5.0}/src/webtap/cdp/schema/cdp_protocol.json +0 -0
  48. {webtap_tool-0.3.0 → webtap_tool-0.5.0}/src/webtap/cdp/schema/cdp_version.json +0 -0
  49. {webtap_tool-0.3.0 → webtap_tool-0.5.0}/src/webtap/commands/__init__.py +0 -0
  50. {webtap_tool-0.3.0 → webtap_tool-0.5.0}/src/webtap/commands/_tips.py +0 -0
  51. {webtap_tool-0.3.0 → webtap_tool-0.5.0}/src/webtap/commands/_utils.py +0 -0
  52. {webtap_tool-0.3.0 → webtap_tool-0.5.0}/src/webtap/commands/launch.py +0 -0
  53. {webtap_tool-0.3.0 → webtap_tool-0.5.0}/src/webtap/commands/setup.py +0 -0
  54. {webtap_tool-0.3.0 → webtap_tool-0.5.0}/src/webtap/services/README.md +0 -0
  55. {webtap_tool-0.3.0 → webtap_tool-0.5.0}/src/webtap/services/__init__.py +0 -0
  56. {webtap_tool-0.3.0 → webtap_tool-0.5.0}/src/webtap/services/body.py +0 -0
  57. {webtap_tool-0.3.0 → webtap_tool-0.5.0}/src/webtap/services/console.py +0 -0
  58. {webtap_tool-0.3.0 → webtap_tool-0.5.0}/src/webtap/services/fetch.py +0 -0
  59. {webtap_tool-0.3.0 → webtap_tool-0.5.0}/src/webtap/services/network.py +0 -0
  60. {webtap_tool-0.3.0 → webtap_tool-0.5.0}/src/webtap/services/setup/__init__.py +0 -0
  61. {webtap_tool-0.3.0 → webtap_tool-0.5.0}/src/webtap/services/setup/chrome.py +0 -0
  62. {webtap_tool-0.3.0 → webtap_tool-0.5.0}/src/webtap/services/setup/desktop.py +0 -0
  63. {webtap_tool-0.3.0 → webtap_tool-0.5.0}/src/webtap/services/setup/extension.py +0 -0
  64. {webtap_tool-0.3.0 → webtap_tool-0.5.0}/src/webtap/services/setup/filters.py +0 -0
  65. {webtap_tool-0.3.0 → webtap_tool-0.5.0}/src/webtap/services/setup/platform.py +0 -0
@@ -15,15 +15,78 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
15
15
 
16
16
  ### Removed
17
17
 
18
+ ## [0.5.0] - 2025-10-09
19
+
20
+ ### Added
21
+ - **Real-time SSE streaming**: API endpoint `/events` for Server-Sent Events broadcasting full state to extension
22
+ - **Element selection**: CDP-native `Overlay.setInspectMode` with visual badges on page
23
+ - New `DOMService` for element inspection via CDP
24
+ - `selections()` command for viewing selected elements with preview and data extraction
25
+ - `content.js` for badge rendering on page
26
+ - Thread-safe selection processing via background ThreadPoolExecutor
27
+ - Auto-clear selections on disconnect and frame navigation
28
+ - **Error state management**: Persistent error banner in extension with dismiss endpoint `/errors/dismiss`
29
+ - **Broadcast queue**: Cross-thread signaling from WebSocket thread → FastAPI event loop via `asyncio.Queue`
30
+ - **Progress indicators**: Extension shows pending count during async element selection
31
+ - **Auto-refresh**: Page list updates on tab activate/create/remove/move events
32
+ - **CDP event callbacks**: `register_event_callback()` system for real-time event handling
33
+ - **js() element integration**: New `selection` parameter to run JavaScript on selected elements
34
+
35
+ ### Changed
36
+ - **Extension architecture**: Migrated from popup (popup.html/popup.js) to side panel (sidepanel.html/sidepanel.js)
37
+ - **State propagation**: Replaced 2-second polling with SSE streaming (<100ms latency vs 2s worst-case)
38
+ - **API server**: Hybrid REST + SSE architecture with broadcast processor and graceful SSE client shutdown
39
+ - **Page list**: Increased size from 6 to 15 rows, auto-highlights connected page
40
+ - **CDP session**: Increased ping timeout from 10s to 20s, added 1-second debounce on event broadcasts
41
+ - **Response builders**: Consolidated `_errors.py` functionality into `_builders.py` with enhanced helpers
42
+ - **Cleanup flow**: `app.cleanup()` now calls `service.disconnect()` for proper state cleanup
43
+ - **Thread safety**: All blocking CDP calls in API endpoints wrapped in `asyncio.to_thread()`
44
+ - **Queue initialization**: Uses `threading.Event` for synchronization instead of sleep
45
+
46
+ ### Removed
47
+ - **popup.html** and **popup.js**: Replaced by side panel architecture
48
+ - **_errors.py**: Merged into `_builders.py` for cleaner response builder consolidation
49
+ - **2-second polling**: Extension no longer polls `/status` endpoint
50
+
51
+ ## [0.4.0] - 2025-09-28
52
+
53
+ ### Added
54
+ - Filter mode support: `include` and `exclude` modes for fine-grained request filtering
55
+ - TypedDict for filter configuration ensuring type safety
56
+ - Filter tip in `network()` command showing example usage
57
+ - Markdown table display for filter categories replacing code blocks
58
+ - CDP resource types documentation in filter command
59
+
60
+ ### Changed
61
+ - **BREAKING**: Filter categories now require explicit `mode` field ("include" or "exclude")
62
+ - **BREAKING**: Updated to ReplKit2 v0.12.0 - using `mcp_config` instead of `fastmcp` parameter
63
+ - Filter core returns data (`get_categories_summary()`) instead of formatted strings
64
+ - Presentation logic moved from FilterManager to command layer
65
+ - Server command uses assistant prefill to prevent LLM commentary
66
+
67
+ ### Fixed
68
+ - Alert element in network command now uses correct `message` field instead of `content`
69
+ - Type annotations for optional parameters properly use `str | None`
70
+
71
+ ### Removed
72
+ - Backward compatibility for filters without mode field
73
+ - Emoji icons in filter display, using plain text indicators instead
74
+
18
75
  ## [0.3.0] - 2025-09-19
19
76
 
20
77
  ### Added
78
+ - New `server` command for explicit API server management (start/stop/restart/status)
79
+ - Server command implemented as MCP prompt returning markdown status information
21
80
 
22
81
  ### Changed
82
+ - **BREAKING**: API server no longer starts automatically - must be started explicitly with `server('start')`
83
+ - API server management is now explicit and user-controlled
23
84
 
24
85
  ### Fixed
25
86
 
26
87
  ### Removed
88
+ - Automatic API server startup on webtap launch
89
+ - `/release` endpoint from API (no longer needed with explicit server management)
27
90
 
28
91
  ## [0.2.4] - 2025-09-19
29
92
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: webtap-tool
3
- Version: 0.3.0
3
+ Version: 0.5.0
4
4
  Summary: Terminal-based web page inspector for AI debugging sessions
5
5
  Author-email: Fredrik Angelsen <fredrikangelsen@gmail.com>
6
6
  Classifier: Development Status :: 3 - Alpha
@@ -21,7 +21,7 @@ Requires-Dist: platformdirs>=4.4.0
21
21
  Requires-Dist: protobuf>=6.32.0
22
22
  Requires-Dist: pyjwt>=2.10.1
23
23
  Requires-Dist: pyyaml>=6.0.2
24
- Requires-Dist: replkit2[all]>=0.11.0
24
+ Requires-Dist: replkit2[all]>=0.12.0
25
25
  Requires-Dist: requests>=2.32.4
26
26
  Requires-Dist: uvicorn>=0.35.0
27
27
  Requires-Dist: websocket-client>=1.8.0
@@ -0,0 +1,97 @@
1
+ // WebTap Badge Renderer - Displays element selection badges only
2
+ // All element selection logic moved to CDP backend
3
+
4
+ console.log("[WebTap] Badge renderer loaded");
5
+
6
+ // Track rendered badges - Map<selectionId, badgeElement>
7
+ const renderedBadges = new Map();
8
+
9
+ // Listen for badge updates from side panel
10
+ chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
11
+ console.log("[WebTap] Received message:", msg.action);
12
+
13
+ if (msg.action === "ping") {
14
+ // Health check - respond immediately
15
+ sendResponse({ ready: true });
16
+ } else if (msg.action === "updateBadges") {
17
+ updateBadges(msg.selections);
18
+ sendResponse({ success: true });
19
+ } else if (msg.action === "clearBadges") {
20
+ clearAllBadges();
21
+ sendResponse({ success: true });
22
+ }
23
+
24
+ return true; // Keep channel open for async response
25
+ });
26
+
27
+ /**
28
+ * Update badges incrementally - add new, remove deleted
29
+ */
30
+ function updateBadges(selections) {
31
+ const selectionIds = new Set(Object.keys(selections));
32
+
33
+ // Add new badges
34
+ Object.entries(selections).forEach(([id, data]) => {
35
+ if (!renderedBadges.has(id) && data.badge) {
36
+ const badge = createBadge(id, data.badge.x, data.badge.y);
37
+ renderedBadges.set(id, badge);
38
+ }
39
+ });
40
+
41
+ // Remove badges for deleted selections
42
+ renderedBadges.forEach((badge, id) => {
43
+ if (!selectionIds.has(id)) {
44
+ badge.remove();
45
+ renderedBadges.delete(id);
46
+ }
47
+ });
48
+
49
+ console.log(`[WebTap] Badges updated: ${renderedBadges.size} active`);
50
+ }
51
+
52
+ /**
53
+ * Create a badge element at specified position
54
+ */
55
+ function createBadge(id, x, y) {
56
+ const badge = document.createElement("div");
57
+ badge.className = "webtap-badge";
58
+ badge.dataset.selectionId = id;
59
+ badge.textContent = `#${id}`;
60
+
61
+ // Position with scroll offset
62
+ badge.style.cssText = `
63
+ position: absolute;
64
+ top: ${y + window.scrollY}px;
65
+ left: ${x}px;
66
+ background: #4CAF50;
67
+ color: white;
68
+ padding: 4px 8px;
69
+ border-radius: 4px;
70
+ font: bold 12px monospace;
71
+ z-index: 2147483647;
72
+ pointer-events: none;
73
+ box-shadow: 0 2px 4px rgba(0,0,0,0.3);
74
+ transition: opacity 0.2s;
75
+ `;
76
+
77
+ document.body.appendChild(badge);
78
+ return badge;
79
+ }
80
+
81
+ /**
82
+ * Clear all badges from page
83
+ */
84
+ function clearAllBadges() {
85
+ renderedBadges.forEach((badge) => {
86
+ if (badge.parentNode) {
87
+ badge.parentNode.removeChild(badge);
88
+ }
89
+ });
90
+ renderedBadges.clear();
91
+ console.log("[WebTap] All badges cleared");
92
+ }
93
+
94
+ // Clear badges on navigation
95
+ window.addEventListener("beforeunload", () => {
96
+ clearAllBadges();
97
+ });
@@ -0,0 +1,22 @@
1
+ {
2
+ "manifest_version": 3,
3
+ "name": "WebTap DevTools",
4
+ "version": "0.2.0",
5
+ "description": "Chrome DevTools Protocol debugger via WebTap",
6
+ "permissions": ["activeTab", "tabs", "scripting", "sidePanel"],
7
+ "side_panel": {
8
+ "default_path": "sidepanel.html"
9
+ },
10
+ "action": {
11
+ "default_title": "WebTap - Open side panel"
12
+ },
13
+ "host_permissions": ["http://localhost:8765/*", "<all_urls>"],
14
+ "content_scripts": [
15
+ {
16
+ "matches": ["<all_urls>"],
17
+ "js": ["content.js"],
18
+ "run_at": "document_idle",
19
+ "all_frames": false
20
+ }
21
+ ]
22
+ }
@@ -0,0 +1,317 @@
1
+ <!doctype html>
2
+ <html>
3
+ <head>
4
+ <style>
5
+ body {
6
+ margin: 0;
7
+ padding: 12px;
8
+ font-family:
9
+ system-ui,
10
+ -apple-system,
11
+ sans-serif;
12
+ font-size: 13px;
13
+ }
14
+ h3 {
15
+ margin: 0 0 12px 0;
16
+ font-size: 15px;
17
+ }
18
+ h4 {
19
+ margin: 12px 0 8px 0;
20
+ font-size: 13px;
21
+ }
22
+ select {
23
+ width: 100%;
24
+ padding: 6px;
25
+ margin-bottom: 8px;
26
+ border: 1px solid #ccc;
27
+ border-radius: 4px;
28
+ font-size: 12px;
29
+ }
30
+ button {
31
+ padding: 8px;
32
+ border: 1px solid #ccc;
33
+ border-radius: 4px;
34
+ background: white;
35
+ cursor: pointer;
36
+ font-size: 12px;
37
+ }
38
+ button:hover {
39
+ background: #f0f0f0;
40
+ }
41
+ button:disabled {
42
+ opacity: 0.5;
43
+ cursor: not-allowed;
44
+ }
45
+ .button-row {
46
+ display: flex;
47
+ gap: 5px;
48
+ margin-bottom: 8px;
49
+ }
50
+ .button-row button {
51
+ flex: 1;
52
+ }
53
+ #status {
54
+ padding: 8px;
55
+ font-size: 12px;
56
+ background: #f5f5f5;
57
+ border-radius: 4px;
58
+ margin-bottom: 8px;
59
+ }
60
+ .error {
61
+ color: #d00;
62
+ }
63
+ .connected {
64
+ color: #080;
65
+ font-weight: bold;
66
+ }
67
+ .divider {
68
+ border-top: 1px solid #ddd;
69
+ margin: 12px 0;
70
+ }
71
+ .fetch-row {
72
+ display: flex;
73
+ align-items: center;
74
+ justify-content: space-between;
75
+ padding: 5px 0;
76
+ }
77
+ .fetch-status {
78
+ font-size: 12px;
79
+ }
80
+ .toggle {
81
+ background: #e0e0e0;
82
+ border: 1px solid #ccc;
83
+ border-radius: 4px;
84
+ padding: 4px 12px;
85
+ cursor: pointer;
86
+ }
87
+ .toggle.on {
88
+ background: #4caf50;
89
+ color: white;
90
+ }
91
+ .filter-section label {
92
+ display: block;
93
+ padding: 4px 0;
94
+ font-size: 12px;
95
+ cursor: pointer;
96
+ }
97
+ .filter-section input[type="checkbox"] {
98
+ margin-right: 6px;
99
+ }
100
+ .filter-count {
101
+ color: #666;
102
+ font-size: 11px;
103
+ margin-left: 4px;
104
+ }
105
+ .selection-list {
106
+ max-height: 150px;
107
+ overflow-y: auto;
108
+ margin: 8px 0;
109
+ border: 1px solid #ddd;
110
+ border-radius: 4px;
111
+ background: #fafafa;
112
+ }
113
+ .selection-item {
114
+ display: flex;
115
+ align-items: center;
116
+ padding: 6px 8px;
117
+ font-size: 11px;
118
+ border-bottom: 1px solid #eee;
119
+ }
120
+ .selection-item:last-child {
121
+ border-bottom: none;
122
+ }
123
+ .selection-badge {
124
+ background: #4caf50;
125
+ color: white;
126
+ padding: 2px 5px;
127
+ border-radius: 3px;
128
+ font-weight: bold;
129
+ margin-right: 8px;
130
+ font-size: 10px;
131
+ }
132
+ .selection-preview {
133
+ flex: 1;
134
+ color: #333;
135
+ overflow: hidden;
136
+ text-overflow: ellipsis;
137
+ white-space: nowrap;
138
+ }
139
+ .selection-remove {
140
+ background: #f44336;
141
+ color: white;
142
+ border: none;
143
+ padding: 2px 6px;
144
+ border-radius: 3px;
145
+ cursor: pointer;
146
+ font-size: 10px;
147
+ }
148
+ .selection-remove:hover {
149
+ background: #d32f2f;
150
+ }
151
+ .help-text {
152
+ font-size: 11px;
153
+ color: #666;
154
+ margin-top: 8px;
155
+ padding: 8px;
156
+ background: #f9f9f9;
157
+ border-radius: 4px;
158
+ border: 1px solid #e0e0e0;
159
+ }
160
+ .help-text code {
161
+ background: #e8e8e8;
162
+ padding: 2px 4px;
163
+ border-radius: 2px;
164
+ font-family: monospace;
165
+ font-size: 10px;
166
+ }
167
+ .error-banner {
168
+ display: none;
169
+ background: #ffebee;
170
+ border: 1px solid #ef5350;
171
+ border-radius: 4px;
172
+ padding: 10px;
173
+ margin-bottom: 12px;
174
+ font-size: 12px;
175
+ }
176
+ .error-banner.visible {
177
+ display: block;
178
+ }
179
+ .error-banner-header {
180
+ display: flex;
181
+ justify-content: space-between;
182
+ align-items: flex-start;
183
+ }
184
+ .error-banner-message {
185
+ color: #c62828;
186
+ flex: 1;
187
+ margin-right: 8px;
188
+ }
189
+ .error-banner-dismiss {
190
+ background: transparent;
191
+ border: none;
192
+ color: #c62828;
193
+ cursor: pointer;
194
+ padding: 0;
195
+ font-size: 16px;
196
+ line-height: 1;
197
+ font-weight: bold;
198
+ }
199
+ .error-banner-dismiss:hover {
200
+ color: #b71c1c;
201
+ }
202
+ </style>
203
+ </head>
204
+ <body>
205
+ <h3>WebTap DevTools</h3>
206
+
207
+ <div id="errorBanner" class="error-banner">
208
+ <div class="error-banner-header">
209
+ <div class="error-banner-message" id="errorMessage"></div>
210
+ <button
211
+ class="error-banner-dismiss"
212
+ id="dismissError"
213
+ title="Dismiss error"
214
+ >
215
+
216
+ </button>
217
+ </div>
218
+ </div>
219
+
220
+ <select id="pageList" size="15">
221
+ <option disabled>Loading pages...</option>
222
+ </select>
223
+
224
+ <div class="button-row">
225
+ <button id="connect">Connect</button>
226
+ <button id="disconnect">Disconnect</button>
227
+ </div>
228
+
229
+ <div id="status">Not connected</div>
230
+
231
+ <div class="divider"></div>
232
+
233
+ <div class="fetch-row">
234
+ <span class="fetch-status"
235
+ >Intercept: <span id="fetchStatus">OFF</span></span
236
+ >
237
+ <button id="fetchToggle" class="toggle">Toggle</button>
238
+ </div>
239
+
240
+ <div style="margin-top: 5px; font-size: 11px">
241
+ <label style="cursor: pointer">
242
+ <input type="checkbox" id="responseStage" style="margin-right: 4px" />
243
+ Also pause at Response stage
244
+ </label>
245
+ </div>
246
+
247
+ <div class="button-row" style="margin-top: 8px">
248
+ <button id="clear" style="width: 100%">Clear Events</button>
249
+ </div>
250
+
251
+ <div class="divider"></div>
252
+
253
+ <div class="filter-section">
254
+ <div
255
+ style="
256
+ display: flex;
257
+ justify-content: space-between;
258
+ align-items: center;
259
+ "
260
+ >
261
+ <h4 style="margin: 5px 0">Filters</h4>
262
+ <span id="filterStats" style="font-size: 11px; color: #666"
263
+ >Loading...</span
264
+ >
265
+ </div>
266
+ <div id="filterList" style="margin: 8px 0"></div>
267
+ <div class="button-row" style="margin-top: 8px">
268
+ <button id="enableAllFilters" style="font-size: 11px">
269
+ Enable All
270
+ </button>
271
+ <button id="disableAllFilters" style="font-size: 11px">
272
+ Disable All
273
+ </button>
274
+ </div>
275
+ </div>
276
+
277
+ <div class="divider"></div>
278
+
279
+ <div class="filter-section">
280
+ <h4 style="margin: 5px 0">Element Selection</h4>
281
+
282
+ <button id="startSelection" style="width: 100%; margin: 8px 0">
283
+ Start Selection Mode
284
+ </button>
285
+
286
+ <div id="selectionStatus" style="display: none">
287
+ <div
288
+ style="
289
+ display: flex;
290
+ justify-content: space-between;
291
+ align-items: center;
292
+ margin-bottom: 4px;
293
+ "
294
+ >
295
+ <span style="font-size: 12px"
296
+ >Selected: <strong id="selectionCount">0</strong> elements</span
297
+ >
298
+ <button
299
+ id="clearSelections"
300
+ style="padding: 4px 8px; font-size: 11px"
301
+ >
302
+ Clear
303
+ </button>
304
+ </div>
305
+
306
+ <div id="selectionList" class="selection-list"></div>
307
+
308
+ <div class="help-text">
309
+ Use <code>@webtap:webtap://selections</code> in Claude Code prompts to
310
+ analyze selected elements. Reference elements as #1, #2, etc.
311
+ </div>
312
+ </div>
313
+ </div>
314
+
315
+ <script src="sidepanel.js"></script>
316
+ </body>
317
+ </html>