pixelpick 2.0.0
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.
- package/LICENSE +21 -0
- package/README.md +233 -0
- package/dist/chrome-mv3/assets/sidepanel-D7qwGDau.css +1 -0
- package/dist/chrome-mv3/background.js +1 -0
- package/dist/chrome-mv3/chunks/sidepanel-CzCbYvqW.js +13 -0
- package/dist/chrome-mv3/content-scripts/content.js +34 -0
- package/dist/chrome-mv3/icon/128.png +0 -0
- package/dist/chrome-mv3/icon/16.png +0 -0
- package/dist/chrome-mv3/icon/48.png +0 -0
- package/dist/chrome-mv3/manifest.json +1 -0
- package/dist/chrome-mv3/sidepanel.html +13 -0
- package/dist/chrome-mv3/src/constants.js +63 -0
- package/dist/chrome-mv3/src/inspector/dom-inspector.js +165 -0
- package/dist/chrome-mv3/src/inspector/highlight.js +155 -0
- package/dist/chrome-mv3/src/inspector/index.js +82 -0
- package/dist/chrome-mv3/src/inspector/picker.js +101 -0
- package/dist/chrome-mv3/src/inspector/react-detector.js +225 -0
- package/package.json +75 -0
- package/public/icon/128.png +0 -0
- package/public/icon/16.png +0 -0
- package/public/icon/48.png +0 -0
- package/src/cli/index.js +278 -0
- package/src/hooks/check-selection.mjs +123 -0
- package/src/server/index.js +571 -0
- package/src/shared/constants.js +97 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 bilune
|
|
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.
|
package/README.md
ADDED
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
# PixelPick
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/pixelpick)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
|
|
6
|
+
**Visual UI inspector for Claude Code.** Cmd+Shift+Click on any element in your browser and Claude Code automatically knows which component you're referring to.
|
|
7
|
+
|
|
8
|
+
## Why PixelPick?
|
|
9
|
+
|
|
10
|
+
Stop wasting time describing UI elements to Claude Code ("the blue button in the top right corner"). Just **point and click** -- Claude Code will automatically see what you selected.
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
You: *Cmd+Shift+Click on a button*
|
|
14
|
+
You: "make the hover transition smoother"
|
|
15
|
+
Claude: *already knows which button, its file location, and current styles*
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
### 1. Install the Chrome Extension
|
|
21
|
+
|
|
22
|
+
Load the extension from `node_modules/pixelpick/dist/chrome-mv3/`:
|
|
23
|
+
|
|
24
|
+
1. Open `chrome://extensions`
|
|
25
|
+
2. Enable **Developer mode** (toggle in top right)
|
|
26
|
+
3. Click **Load unpacked**
|
|
27
|
+
4. Select `node_modules/pixelpick/dist/chrome-mv3/`
|
|
28
|
+
|
|
29
|
+
### 2. Set Up Claude Code Integration
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npx pixelpick@latest init
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
This registers the MCP server and installs the auto-injection hook. Done.
|
|
36
|
+
|
|
37
|
+
## Usage
|
|
38
|
+
|
|
39
|
+
1. **Run your app** in Chrome (localhost only)
|
|
40
|
+
```bash
|
|
41
|
+
npm run dev # or your dev command
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
2. **Select elements** with **Cmd+Shift+Click** (Mac) or **Ctrl+Shift+Click** (Windows/Linux)
|
|
45
|
+
- Select multiple elements (up to 10) -- they accumulate automatically
|
|
46
|
+
- View your selections in the extension's side panel
|
|
47
|
+
- Remove individual selections or clear all
|
|
48
|
+
|
|
49
|
+
3. **Switch to Claude Code** and describe what you want to change
|
|
50
|
+
```
|
|
51
|
+
"make this button's padding bigger"
|
|
52
|
+
"change the color to blue"
|
|
53
|
+
"add a loading spinner"
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
4. **Claude automatically sees** all selected elements' context
|
|
57
|
+
- Selections are auto-cleared after being injected into your prompt
|
|
58
|
+
|
|
59
|
+
## What Gets Captured
|
|
60
|
+
|
|
61
|
+
### For React Components
|
|
62
|
+
- Component name (e.g., `TestButton`)
|
|
63
|
+
- Props with values
|
|
64
|
+
- File location with line:column (e.g., `src/components/Button.tsx:24:5`)
|
|
65
|
+
- Component tree (parent > child hierarchy)
|
|
66
|
+
|
|
67
|
+
### For Any Element
|
|
68
|
+
- CSS selector
|
|
69
|
+
- Computed styles (colors, spacing, typography, etc.)
|
|
70
|
+
- Tailwind classes (auto-detected)
|
|
71
|
+
- Accessibility info (role, aria-label, etc.)
|
|
72
|
+
- Bounding box (position and size)
|
|
73
|
+
|
|
74
|
+
## Framework Support
|
|
75
|
+
|
|
76
|
+
| Framework | Status | Component Detection | File Location | Props |
|
|
77
|
+
|-----------|--------|---------------------|---------------|-------|
|
|
78
|
+
| **React 18** | Full Support | Yes | Yes (line + column) | Yes |
|
|
79
|
+
| **React 19** | Supported | Yes | Partial (line only) | Yes |
|
|
80
|
+
| **Non-framework** | Supported | - | - | - |
|
|
81
|
+
| Vue, Svelte | Coming Soon | - | - | - |
|
|
82
|
+
|
|
83
|
+
### React 19 Note
|
|
84
|
+
React 19 removed precise source location metadata (`_debugSource`). File locations will show line number only (no column). This is a React limitation, not a PixelPick issue.
|
|
85
|
+
|
|
86
|
+
## Commands
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
# Setup (one-time)
|
|
90
|
+
npx pixelpick init # Register with Claude Code
|
|
91
|
+
npx pixelpick init --project # Use project scope
|
|
92
|
+
|
|
93
|
+
# Server management
|
|
94
|
+
npx pixelpick start # Start MCP server manually
|
|
95
|
+
npx pixelpick status # Check connection status
|
|
96
|
+
|
|
97
|
+
# Help
|
|
98
|
+
npx pixelpick help # Show all commands
|
|
99
|
+
npx pixelpick --version # Show version
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## How It Works
|
|
103
|
+
|
|
104
|
+
PixelPick uses a three-component architecture:
|
|
105
|
+
|
|
106
|
+
```
|
|
107
|
+
Chrome Extension <--WebSocket--> MCP Server <--MCP+Hook--> Claude Code
|
|
108
|
+
(Element UI) (Selection Buffer) (AI Assistant)
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
1. **Chrome Extension**: Captures element selections via Cmd+Shift+Click, detects React components, maintains multi-selection list, sends via WebSocket
|
|
112
|
+
2. **MCP Server**: Maintains selection buffer (up to 10 active + 50 history), exposes MCP tools, runs WebSocket + IPC servers
|
|
113
|
+
3. **UserPromptSubmit Hook**: Automatically injects all active selections on every prompt (deterministic, <500ms), auto-clears after injection
|
|
114
|
+
|
|
115
|
+
### Why This Approach?
|
|
116
|
+
|
|
117
|
+
- **Zero-config**: `npx pixelpick init` sets up everything automatically
|
|
118
|
+
- **Deterministic**: Hook fires on EVERY prompt (doesn't rely on Claude deciding to call a tool)
|
|
119
|
+
- **Framework-aware**: Extracts React component names, props, and file locations from Fiber internals
|
|
120
|
+
- **Localhost-only**: Security-focused, never runs on production URLs
|
|
121
|
+
- **Fast**: Hook checks selection in <500ms via HTTP IPC endpoint
|
|
122
|
+
|
|
123
|
+
## Troubleshooting
|
|
124
|
+
|
|
125
|
+
### Extension shows "disconnected" (yellow badge)
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
npx pixelpick status
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
If server not running:
|
|
132
|
+
- Make sure you have a Claude Code session running (MCP servers start with Claude)
|
|
133
|
+
- Or manually start: `npx pixelpick start`
|
|
134
|
+
|
|
135
|
+
### Selection not appearing in Claude Code
|
|
136
|
+
|
|
137
|
+
**Check hook is installed:**
|
|
138
|
+
```bash
|
|
139
|
+
cat ~/.claude/settings.json | grep pixelpick
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
**Re-run setup if missing:**
|
|
143
|
+
```bash
|
|
144
|
+
npx pixelpick init
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Port already in use
|
|
148
|
+
|
|
149
|
+
**Check what's using the port:**
|
|
150
|
+
```bash
|
|
151
|
+
lsof -i :9315
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
**Use different port:**
|
|
155
|
+
```bash
|
|
156
|
+
PIXELPICK_PORT=9999 npx pixelpick start
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### React component name not detected
|
|
160
|
+
|
|
161
|
+
**Possible causes:**
|
|
162
|
+
- Not a React app (PixelPick will fall back to CSS selector)
|
|
163
|
+
- Minified production build (component names are stripped)
|
|
164
|
+
- React DevTools disabled (check React Fiber is accessible)
|
|
165
|
+
|
|
166
|
+
**Debug:**
|
|
167
|
+
```javascript
|
|
168
|
+
// In browser console
|
|
169
|
+
$0 // Selected element
|
|
170
|
+
Object.keys($0).find(k => k.startsWith('__reactFiber')) // Should return fiber key
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### File location missing (React 19)
|
|
174
|
+
|
|
175
|
+
This is expected. React 19 removed `_debugSource` metadata. PixelPick will fall back to stack trace parsing, which provides line numbers only (no column).
|
|
176
|
+
|
|
177
|
+
## Environment Variables
|
|
178
|
+
|
|
179
|
+
| Variable | Default | Description |
|
|
180
|
+
|----------|---------|-------------|
|
|
181
|
+
| `PIXELPICK_PORT` | `9315` | WebSocket server port |
|
|
182
|
+
| `PIXELPICK_IPC_PORT` | `9316` | HTTP IPC server port (for hook) |
|
|
183
|
+
|
|
184
|
+
## Security
|
|
185
|
+
|
|
186
|
+
PixelPick only runs on **localhost** (127.0.0.1 and localhost). It will never activate on production websites.
|
|
187
|
+
|
|
188
|
+
The Chrome extension has minimal permissions:
|
|
189
|
+
- `activeTab`: Only active tab access
|
|
190
|
+
- `scripting`: Inject inspector on localhost only
|
|
191
|
+
- `host_permissions`: For inspector injection on any localhost port
|
|
192
|
+
|
|
193
|
+
No data is sent to external servers. Everything runs locally. See [PRIVACY.md](PRIVACY.md) for the full privacy policy.
|
|
194
|
+
|
|
195
|
+
## Advanced Usage
|
|
196
|
+
|
|
197
|
+
### Activate Picker Programmatically
|
|
198
|
+
|
|
199
|
+
Use the `pick_element` MCP tool:
|
|
200
|
+
|
|
201
|
+
```
|
|
202
|
+
You: "activate the element picker"
|
|
203
|
+
Claude: *calls pick_element tool*
|
|
204
|
+
You: *clicks on element in browser*
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### Access Selection History
|
|
208
|
+
|
|
209
|
+
```
|
|
210
|
+
You: "show me the last 5 elements I selected"
|
|
211
|
+
Claude: *calls get_selection_history tool*
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### Clear Selections
|
|
215
|
+
|
|
216
|
+
```
|
|
217
|
+
You: "clear all selections"
|
|
218
|
+
Claude: *calls clear_selection tool*
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
Note: Selections are automatically cleared after being injected into your prompt, so manual clearing is rarely needed.
|
|
222
|
+
|
|
223
|
+
## Contributing
|
|
224
|
+
|
|
225
|
+
Issues and PRs welcome at [GitHub](https://github.com/bilune/pixelpick).
|
|
226
|
+
|
|
227
|
+
## License
|
|
228
|
+
|
|
229
|
+
MIT - see [LICENSE](LICENSE) for details.
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
**Made for developers who ship fast.** Stop describing, start pointing.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@import"https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600;700&display=swap";@layer properties,theme,base,components,utilities;@layer theme{:root,:host{--font-sans:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-mono:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--color-green-700:oklch(52.7% .154 150.069);--color-blue-100:oklch(93.2% .032 255.585);--color-blue-300:oklch(80.9% .105 251.813);--color-blue-600:oklch(54.6% .245 262.881);--color-blue-700:oklch(48.8% .243 264.376);--color-gray-700:oklch(37.3% .034 259.733);--color-black:#000;--color-white:#fff;--spacing:.25rem;--container-xs:20rem;--text-xs:.75rem;--text-xs--line-height:calc(1/.75);--text-sm:.875rem;--text-sm--line-height:calc(1.25/.875);--text-base:1rem;--text-base--line-height: 1.5 ;--text-lg:1.125rem;--text-lg--line-height:calc(1.75/1.125);--font-weight-medium:500;--font-weight-semibold:600;--radius-md:.375rem;--radius-lg:.5rem;--ease-out:cubic-bezier(0,0,.2,1);--ease-in-out:cubic-bezier(.4,0,.2,1);--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4,0,.2,1);--default-font-family:var(--font-sans);--default-font-feature-settings:var(--font-sans--font-feature-settings);--default-font-variation-settings:var(--font-sans--font-variation-settings);--default-mono-font-family:var(--font-mono);--default-mono-font-feature-settings:var(--font-mono--font-feature-settings);--default-mono-font-variation-settings:var(--font-mono--font-variation-settings);--color-brand:#d97757;--color-brand-hover:#c96647;--color-dark-bg:#2d2d2d;--color-darker-bg:#1d1d1d;--color-light-bg:#3d3d3d;--color-dark-border:#4d4d4d;--color-brand-border:#e6704e;--color-light-text:#fff;--color-gray-text:#9d9d9d;--color-dim-text:#6d6d6d;--color-react-bg:#164e634d;--color-vue-bg:#14532d4d;--color-vue-text:#86efac;--color-vue-border:#16a34a;--color-svelte-bg:#7c2d124d;--color-success:#22c55e;--color-warning:#e6704e;--color-danger:#ef4444;--font-jetbrains:"JetBrains Mono",monospace}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}body{line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::-moz-placeholder{opacity:1;color:currentColor}::placeholder{opacity:1;color:currentColor}@supports (color:color-mix(in lab,red,red)){::-moz-placeholder{color:color-mix(in oklab,currentColor 50%,transparent)}::placeholder{color:color-mix(in oklab,currentColor 50%,transparent)}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){-webkit-appearance:button;-moz-appearance:button;appearance:button}::file-selector-button{-webkit-appearance:button;-moz-appearance:button;appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer utilities{.fixed{position:fixed}.inset-0{inset:calc(var(--spacing)*0)}.z-\[1000\]{z-index:1000}.container{width:100%}@media(min-width:40rem){.container{max-width:40rem}}@media(min-width:48rem){.container{max-width:48rem}}@media(min-width:64rem){.container{max-width:64rem}}@media(min-width:80rem){.container{max-width:80rem}}@media(min-width:96rem){.container{max-width:96rem}}.m-0{margin:calc(var(--spacing)*0)}.mt-2{margin-top:calc(var(--spacing)*2)}.mt-4{margin-top:calc(var(--spacing)*4)}.mb-2{margin-bottom:calc(var(--spacing)*2)}.mb-3{margin-bottom:calc(var(--spacing)*3)}.mb-4{margin-bottom:calc(var(--spacing)*4)}.mb-6{margin-bottom:calc(var(--spacing)*6)}.block{display:block}.contents{display:contents}.flex{display:flex}.grid{display:grid}.inline{display:inline}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.table{display:table}.h-2{height:calc(var(--spacing)*2)}.h-4{height:calc(var(--spacing)*4)}.h-8{height:calc(var(--spacing)*8)}.h-screen{height:100vh}.max-h-\[300px\]{max-height:300px}.min-h-screen{min-height:100vh}.w-2{width:calc(var(--spacing)*2)}.w-4{width:calc(var(--spacing)*4)}.w-8{width:calc(var(--spacing)*8)}.w-full{width:100%}.max-w-xs{max-width:var(--container-xs)}.min-w-0{min-width:calc(var(--spacing)*0)}.flex-1{flex:1}.transform{transform:var(--tw-rotate-x,)var(--tw-rotate-y,)var(--tw-rotate-z,)var(--tw-skew-x,)var(--tw-skew-y,)}.cursor-pointer{cursor:pointer}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.gap-1\.5{gap:calc(var(--spacing)*1.5)}.gap-2{gap:calc(var(--spacing)*2)}.gap-4{gap:calc(var(--spacing)*4)}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.border{border-style:var(--tw-border-style);border-width:1px}.border-2{border-style:var(--tw-border-style);border-width:2px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-l{border-left-style:var(--tw-border-style);border-left-width:1px}.border-\[\#e6704e\]{border-color:#e6704e}.border-blue-300{border-color:var(--color-blue-300)}.border-brand{border-color:var(--color-brand)}.border-brand-border{border-color:var(--color-brand-border)}.border-danger{border-color:var(--color-danger)}.border-dark-border{border-color:var(--color-dark-border)}.border-success{border-color:var(--color-success)}.border-vue-border{border-color:var(--color-vue-border)}.bg-\[\#2d2d2d\]{background-color:#2d2d2d}.bg-black\/70{background-color:#000000b3}@supports (color:color-mix(in lab,red,red)){.bg-black\/70{background-color:color-mix(in oklab,var(--color-black)70%,transparent)}}.bg-blue-100{background-color:var(--color-blue-100)}.bg-blue-600{background-color:var(--color-blue-600)}.bg-brand{background-color:var(--color-brand)}.bg-danger{background-color:var(--color-danger)}.bg-dark-bg{background-color:var(--color-dark-bg)}.bg-darker-bg{background-color:var(--color-darker-bg)}.bg-react-bg{background-color:var(--color-react-bg)}.bg-success{background-color:var(--color-success)}.bg-svelte-bg{background-color:var(--color-svelte-bg)}.bg-vue-bg{background-color:var(--color-vue-bg)}.bg-warning{background-color:var(--color-warning)}.p-2{padding:calc(var(--spacing)*2)}.p-3{padding:calc(var(--spacing)*3)}.p-4{padding:calc(var(--spacing)*4)}.p-6{padding:calc(var(--spacing)*6)}.px-1\.5{padding-inline:calc(var(--spacing)*1.5)}.px-2{padding-inline:calc(var(--spacing)*2)}.px-3{padding-inline:calc(var(--spacing)*3)}.px-4{padding-inline:calc(var(--spacing)*4)}.px-6{padding-inline:calc(var(--spacing)*6)}.py-0\.5{padding-block:calc(var(--spacing)*.5)}.py-1\.5{padding-block:calc(var(--spacing)*1.5)}.py-2{padding-block:calc(var(--spacing)*2)}.py-3{padding-block:calc(var(--spacing)*3)}.py-8{padding-block:calc(var(--spacing)*8)}.pb-3{padding-bottom:calc(var(--spacing)*3)}.font-jetbrains{font-family:var(--font-jetbrains)}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\[11px\]{font-size:11px}.text-\[13px\]{font-size:13px}.text-\[32px\]{font-size:32px}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.text-\[\#ffffff\]{color:#fff}.text-dim-text{color:var(--color-dim-text)}.text-gray-700{color:var(--color-gray-700)}.text-gray-text{color:var(--color-gray-text)}.text-light-text{color:var(--color-light-text)}.text-vue-text{color:var(--color-vue-text)}.text-white{color:var(--color-white)}.opacity-50{opacity:.5}.shadow-md{--tw-shadow:0 4px 6px -1px var(--tw-shadow-color,#0000001a),0 2px 4px -2px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-xl{--tw-shadow:0 20px 25px -5px var(--tw-shadow-color,#0000001a),0 8px 10px -6px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.filter{filter:var(--tw-blur,)var(--tw-brightness,)var(--tw-contrast,)var(--tw-grayscale,)var(--tw-hue-rotate,)var(--tw-invert,)var(--tw-saturate,)var(--tw-sepia,)var(--tw-drop-shadow,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-all{transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.ease-in-out{--tw-ease:var(--ease-in-out);transition-timing-function:var(--ease-in-out)}.ease-out{--tw-ease:var(--ease-out);transition-timing-function:var(--ease-out)}@media(hover:hover){.hover\:bg-blue-700:hover{background-color:var(--color-blue-700)}.hover\:bg-brand:hover{background-color:var(--color-brand)}.hover\:bg-light-bg:hover{background-color:var(--color-light-bg)}.hover\:text-white:hover{color:var(--color-white)}.enabled\:hover\:border-brand:enabled:hover{border-color:var(--color-brand)}.enabled\:hover\:border-brand-border:enabled:hover{border-color:var(--color-brand-border)}.enabled\:hover\:bg-brand:enabled:hover{background-color:var(--color-brand)}.enabled\:hover\:bg-brand-hover:enabled:hover{background-color:var(--color-brand-hover)}.enabled\:hover\:bg-danger:enabled:hover{background-color:var(--color-danger)}.enabled\:hover\:bg-green-700:enabled:hover{background-color:var(--color-green-700)}.enabled\:hover\:bg-light-bg:enabled:hover{background-color:var(--color-light-bg)}}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-50:disabled{opacity:.5}}*{font-family:JetBrains Mono,monospace}html,body{background-color:#2d2d2d;margin:0;padding:0}#root{min-height:100vh}@keyframes pulse{50%{opacity:.5}}@keyframes blink{0%,to{opacity:1}50%{opacity:.3}}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}@keyframes slideIn{0%{transform:translate(100%)}to{transform:translate(0)}}.picker-btn.active{animation:2s ease-in-out infinite pulse}.status-dot{animation:1.5s ease-in-out infinite blink}.settings-overlay{animation:.2s ease-out fadeIn}.settings-panel{animation:.2s ease-out slideIn}::-webkit-scrollbar{width:8px}::-webkit-scrollbar-track{background:#1d1d1d}::-webkit-scrollbar-thumb{background:#4d4d4d;border-radius:4px}::-webkit-scrollbar-thumb:hover{background:#e6704e}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}@layer properties{@supports ((-webkit-hyphens:none) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-border-style:solid;--tw-font-weight:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-outline-style:solid;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-ease:initial}}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var background=(function(){"use strict";function b(t){return t==null||typeof t=="function"?{main:t}:t}const N="ws://localhost:9315",E={HEARTBEAT_INTERVAL:2e4,RECONNECT_DELAYS:[3e3,6e3,12e3,24e3,6e4],MAX_RECONNECT_ATTEMPTS:10},p={CONNECTED:{color:"#10b981",text:""},DISCONNECTED:{color:"#f59e0b",text:"!"},ERROR:{color:"#ef4444",text:"X"}},y=b(()=>{const t=E.HEARTBEAT_INTERVAL,f=E.RECONNECT_DELAYS,h=E.MAX_RECONNECT_ATTEMPTS;let r=null,d=null,m=null,c=0;const i=p;function a(e){chrome.action.setBadgeBackgroundColor({color:e.color}),chrome.action.setBadgeText({text:e.text})}function _(){T(),d=setInterval(()=>{r?.readyState===WebSocket.OPEN&&r.send(JSON.stringify({type:"ping",timestamp:Date.now()}))},t)}function T(){d&&(clearInterval(d),d=null)}function l(){if(!(r&&(r.readyState===WebSocket.CONNECTING||r.readyState===WebSocket.OPEN))){console.log("[PixelPick] Connecting to WebSocket server..."),a(i.DISCONNECTED);try{r=new WebSocket(N),r.onopen=()=>{console.log("[PixelPick] WebSocket connected"),a(i.CONNECTED),_(),c=0},r.onmessage=e=>{try{const n=JSON.parse(e.data);switch(n.type){case"pong":break;case"activate_picker":chrome.tabs.query({active:!0,currentWindow:!0},o=>{o[0]&&chrome.tabs.sendMessage(o[0].id,{type:"activate_picker"})});break;default:console.log("[PixelPick] Unknown message from server:",n.type)}}catch(n){console.error("[PixelPick] Invalid message from server:",n)}},r.onerror=e=>{console.error("[PixelPick] WebSocket error:",e),a(i.ERROR)},r.onclose=()=>{console.log("[PixelPick] WebSocket closed"),a(i.DISCONNECTED),T(),S()}}catch(e){console.error("[PixelPick] Failed to create WebSocket:",e),a(i.ERROR),S()}}}function S(){if(c>=h){console.error("[PixelPick] Max reconnect attempts reached"),a(i.ERROR);return}const e=f[Math.min(c,f.length-1)];c++,console.log(`[PixelPick] Reconnecting in ${e}ms (attempt ${c}/${h})`),m&&clearTimeout(m),m=setTimeout(()=>{l()},e)}chrome.runtime.onMessage.addListener((e,n,o)=>{if(e.type==="selection")return r?.readyState===WebSocket.OPEN?(r.send(JSON.stringify({type:"selection",data:e.data,tabId:n.tab?.id,url:n.tab?.url})),o({success:!0})):(console.error("[PixelPick] Cannot send selection - WebSocket not connected"),o({success:!1,error:"Not connected to server"})),!0;if(e.type==="get_connection_status")return o({connected:r?.readyState===WebSocket.OPEN,readyState:r?.readyState,reconnectAttempts:c}),!0;if(e.type==="toggle_picker")return chrome.tabs.query({active:!0,currentWindow:!0},async k=>{if(!k||k.length===0){o({success:!1,error:"No active tab found"});return}const s=k[0];if(!s.url||s.url.startsWith("chrome://")||s.url.startsWith("chrome-extension://")||s.url.startsWith("edge://")){o({success:!1,error:"Cannot activate picker on browser internal pages. Open a regular webpage first."});return}if(s.status==="loading"){o({success:!1,error:"Page is still loading. Wait a moment and try again."});return}chrome.tabs.sendMessage(s.id,{type:"activate_picker"},A=>{if(chrome.runtime.lastError){const P=chrome.runtime.lastError.message;console.error("[PixelPick] Error sending activate_picker:",P),P?.includes("Receiving end does not exist")?o({success:!1,error:"PixelPick not ready on this page. Reload the extension and try again."}):o({success:!1,error:P})}else console.log("[PixelPick] Picker activated successfully"),o({success:!0})})}),!0;if(e.type==="picker_deactivated")return chrome.runtime.sendMessage({type:"picker_deactivated"}).catch(()=>{console.log("[PixelPick] Sidepanel not open, picker_deactivated not forwarded")}),!0}),l(),chrome.runtime.onStartup.addListener(()=>{console.log("[PixelPick] Service worker started"),l()}),chrome.runtime.onInstalled.addListener(()=>{console.log("[PixelPick] Extension installed"),l()}),chrome.alarms.create("keepalive",{periodInMinutes:1}),chrome.alarms.onAlarm.addListener(e=>{e.name==="keepalive"&&r?.readyState!==WebSocket.OPEN&&l()}),chrome.action.onClicked.addListener(e=>{chrome.sidePanel.open({windowId:e.windowId})})});function v(){}globalThis.browser?.runtime?.id?globalThis.browser:globalThis.chrome;function u(t,...f){}const C={debug:(...t)=>u(console.debug,...t),log:(...t)=>u(console.log,...t),warn:(...t)=>u(console.warn,...t),error:(...t)=>u(console.error,...t)};let g;try{g=y.main(),g instanceof Promise&&console.warn("The background's main() function return a promise, but it must be synchronous")}catch(t){throw C.error("The background crashed on startup!"),t}var O=g;return O})();
|