vite-plugin-specter 0.6.0 → 0.6.1

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/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # Specter
2
2
 
3
- **Hover any element on your dev site. See its styles. Copy to AI.**
3
+ **Point at any element on your dev site. Mark what to change — with exact values and a source anchor — and hand it to your AI.**
4
4
 
5
- Specter is a Vite plugin that adds an element inspector overlay to your dev server. Toggle it on, hover any element to see its styles, component name, and spacing. Copy with one shortcut and paste into your AI assistant for one-shot edits. Specter is automatically stripped from production builds.
5
+ Specter is a Vite plugin that overlays an element inspector on your dev server. Hover any element to see its styles, component name, and spacing; measure gaps Figma-style; mark elements as **Specs** with a plain-language note; then copy them all to your AI assistant each one carries the exact current values and a greppable source anchor, so there's no "which element?" and no "what's the current value?". Optionally, push your Specs straight into Claude Code with `/spectify`. Specter is automatically stripped from production builds.
6
6
 
7
7
  ## Requirements
8
8
 
@@ -46,51 +46,71 @@ If you ever need Specter gone entirely, remove `specter()` from the Vite config
46
46
 
47
47
  ### Toggle on/off
48
48
 
49
- Press **Ctrl+Option+Z** to enter Specter mode. A small zap icon appears at the bottom-left. Press again (or Esc) to exit.
49
+ Press **Ctrl+Option+Z** to activate Specter. A small zap icon appears at the bottom-left. Press again (or **Esc**) to hide it — your Specs are kept and reappear when you reactivate.
50
50
 
51
- ### Inspect an element
51
+ ### Three modes
52
52
 
53
- Once active, just **hover** any element. A tooltip shows:
53
+ Specter has three modes; switching mode only changes *what's shown on screen* — you can mark a Spec in any of them.
54
+
55
+ | Mode | Enter | Shows |
56
+ |------|-------|-------|
57
+ | **Properties** (default) | — | Styles tooltip on hover |
58
+ | **Measure** | tap **Option** | Figma-style spacing to neighbors |
59
+ | **Comment** | **C** | Just an outline — for clean design review |
60
+
61
+ ### Inspect an element (Properties mode)
62
+
63
+ **Hover** any element. A tooltip shows:
54
64
 
55
65
  - HTML tag + React/Vue component name + dimensions
56
66
  - Text content (truncated)
57
67
  - Font: family, weight, size, line-height
58
- - Color (with hex) and background
68
+ - Color (with hex) and background — flagged `(hover)` if the value comes from a `:hover` rule
59
69
  - Padding, margin, border-radius (if non-zero)
60
70
  - Display + gap + flex direction
61
71
 
62
- ### Copy to clipboard
72
+ ### Measure spacing (Measure mode)
73
+
74
+ Tap **Option** to switch to Measure mode. Hover any element to see distances to its surrounding neighbors. Press **M** while hovering to **pin** that element, then hover another to measure the gap or inset between the two; **M** again unpins. Tap **Option** again to return to Properties.
63
75
 
64
- Press **Cmd+C** while hovering. The element info is copied in an AI-optimized format. Paste it into Claude, Cursor, or any AI assistant and say what you want changed.
76
+ ### Mark a Spec (and optionally annotate)
65
77
 
66
- ### Measure mode
78
+ Press **P** while hovering an element to mark it as a **Spec**. A note box opens on the element — type the change you want ("reduce width to 320", "tighten padding to 8px") and press **Enter**, or just leave it empty for a plain mark. Numbered badges appear on each marked element. Mark as many as you like across the page.
67
79
 
68
- Tap **Option** to switch to Measure mode. Hover any element to see distance measurements to its surrounding neighbors (like Figma's spacing view). Tap **Option** again to return to Properties mode.
80
+ ### Comment mode
69
81
 
70
- ### Pin an element
82
+ Press **C** for Comment mode: the props/measure overlays hide and you get just a hover outline — good for talking through a design without visual noise. Specs still capture full properties underneath, so a Comment-mode Spec copies exactly like any other.
71
83
 
72
- In Measure mode, press **M** while hovering to pin that element. Then hover any other element to measure the gap or inset between the two. Press **Cmd+C** to copy the measurement. Press **Esc** to clear the pin.
84
+ ### Specs side panel
73
85
 
74
- ### Multi-select
86
+ Press **L** to open the Specs review panel. It lists every Spec; hover a row to scroll to and highlight its element; edit or delete notes inline. Specs survive a reload (saved to `localStorage` per URL).
75
87
 
76
- Press **P** while hovering an element to pick it. Numbered badges appear on each picked element. Press **Cmd+C** to copy all selected elements at once. Press **Esc** to clear the selection.
88
+ ### Copy to your AI
77
89
 
78
- ### Annotate a change (skip the re-explaining)
90
+ Press **Cmd+C**:
79
91
 
80
- Press **N** while hovering an element to attach a change note to it. A small field opens on the element — type what you want ("reduce width to 320", "tighten padding to 8px"), press **Enter** to save. The element is added to the selection and its badge shows a chip with your note. Annotate as many issues across the page as you like, then press **Cmd+C** once.
92
+ - **With Specs marked** copies **all** of them at once. Each block leads with your `✏️ CHANGE:` note (if any).
93
+ - **With nothing marked** → copies the hovered element's properties (or, in Measure mode, the measurement).
81
94
 
82
- Each element's copied block now leads with your instruction:
95
+ The copied format is lean and AI-ready — the current values plus a `find:` line that anchors the element in your source (test-id → id → aria-label → unique text → class → CSS path), so the assistant greps straight to the code:
83
96
 
84
97
  ```
85
- [Specter 1/3]
86
- ✏️ CHANGE: reduce width to 320, tighten vertical padding
87
- <button>.btn-primary Button 200×48
88
- font: Inter 600 14/20
89
- padding: 12px 20px
90
- ...
98
+ [Specter 1/2]
99
+ ✏️ CHANGE: tighten the vertical padding to 8px
100
+ <button> .btn-primary "Book a demo" 200×48
101
+ find: .btn-primary
102
+ font: Inter 600 14/20 · color: #fff · bg: #4f46e5 · padding: 12px 20px · radius: 10px · display: inline-flex
103
+
104
+ ---
105
+
106
+ [Specter 2/2]
107
+ ✏️ CHANGE: make this text darker — it fails contrast
108
+ <p> .hero-sub "Analytics that…" 560×57
109
+ find: .hero-sub
110
+ font: Inter 400 19/28.5 · color: #c7cbd1
91
111
  ```
92
112
 
93
- So when you paste into your AI assistant, it already knows both *what* to change and *which element* no follow-up typing. Notes are ephemeral (cleared on **Esc** or when you toggle Specter off).
113
+ Paste into Claude, Cursor, or any AI assistant — the instructions travel with the elements, so there's nothing left to explain.
94
114
 
95
115
  ## Shortcut reference
96
116
 
@@ -98,36 +118,39 @@ So when you paste into your AI assistant, it already knows both *what* to change
98
118
  |--------|----------|
99
119
  | Toggle Specter | **Ctrl+Option+Z** |
100
120
  | Inspect element | Hover (while active) |
101
- | Copy properties | **Cmd+C** |
102
- | Switch to Measure mode | **Option** (tap) |
103
- | Pin element for measuring | **M** (in Measure mode) |
104
- | Pick element (multi-select) | **P** (in Properties mode) |
105
- | Annotate a change | **N** (in Properties mode) |
106
- | Clear pin / clear selection | **Esc** |
107
- | Exit Specter | **Esc** (when nothing selected/pinned) |
121
+ | Mark a Spec (+ optional note) | **P** |
122
+ | Copy (all Specs, or hovered element) | **Cmd+C** |
123
+ | Measure mode (toggle) | **Option** (tap) |
124
+ | Pin / unpin for measuring | **M** (in Measure mode) |
125
+ | Comment mode (toggle) | **C** |
126
+ | Specs panel (toggle) | **L** |
127
+ | Close panel / hide Specter | **Esc** |
108
128
 
109
- ## Workflow with AI assistants
129
+ ## Optional: push Specs straight to Claude Code (`/spectify`)
130
+
131
+ Instead of copy-paste, Specter can auto-sync your Specs to a local bridge that Claude Code reads on demand. Enable it in your Vite config:
132
+
133
+ ```ts
134
+ specter({ claudeBridge: true })
135
+ ```
110
136
 
111
- 1. Toggle Specter (Ctrl+Option+Z)
112
- 2. Hover the element you want to change
113
- 3. Either press **Cmd+C** to copy its properties, or press **N** to annotate the exact change you want first
114
- 4. Repeat **N** on every element you want to change, then **Cmd+C** once to copy them all
115
- 5. Paste into your AI assistant's chat — the instructions travel with the elements, so there's nothing left to explain
137
+ With it on, every Spec you mark auto-syncs (watch for the sync dot in the panel) to a local bridge at `http://127.0.0.1:8787`. Run the bridge (`node node_modules/vite-plugin-specter/mcp-bridge/server.mjs`, or register it in your project's `.mcp.json` so your IDE launches it), then in Claude Code run **`/spectify`** — it pulls whatever's currently synced and implements it. See [`mcp-bridge/`](https://github.com/setugk/vite-plugin-specter/tree/main/mcp-bridge) for setup. One bridge can serve several projects; `/spectify <port>` scopes to one.
116
138
 
117
- No more "which element?", no more "what's the current value?", and now no more re-typing the change. One paste gives your assistant everything it needs.
139
+ `claudeBridge` is **off by default** the shipped plugin never opens a connection unless you enable it.
118
140
 
119
141
  ## Configuration
120
142
 
121
143
  ```ts
122
- specter() // Default shortcuts
123
- specter({ shortcuts: { activate: 'ctrl+shift+i' } }) // Custom activate shortcut
144
+ specter() // defaults
145
+ specter({ shortcuts: { activate: 'ctrl+shift+i' } }) // custom activate shortcut
146
+ specter({ claudeBridge: true }) // enable the Claude Code bridge
124
147
  ```
125
148
 
126
149
  The `activate` shortcut accepts any combination of `ctrl`, `alt`, `shift`, `meta`/`cmd`, and a key. Default is `ctrl+alt+z`.
127
150
 
128
151
  ## Optional: `data-style` attribute
129
152
 
130
- Tag elements with `data-style="keyName"` to show the style object key in the inspector:
153
+ Tag elements with `data-style="keyName"` to surface the style-object key in the inspector and the copied output:
131
154
 
132
155
  ```jsx
133
156
  <h1 data-style="pageTitle">Welcome</h1>
package/dist/client.js CHANGED
@@ -14,6 +14,7 @@
14
14
  var TIP_BG = '#292B32';
15
15
  var GREEN = '#22C55E';
16
16
  var LABEL = '#8B8D94';
17
+ var BADGE_IDLE = '#6B6D75'; // panel row-number badges sit greyscale; the hovered row's badge colors to PURPLE
17
18
  var MONO = "'JetBrains Mono', 'SF Mono', 'Fira Code', monospace";
18
19
  var ZAP = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"></polygon></svg>';
19
20
  var PENCIL = '<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z"></path><path d="m15 5 4 4"></path></svg>';
@@ -1326,9 +1327,10 @@
1326
1327
  badge.textContent = String(i + 1);
1327
1328
  Object.assign(badge.style, {
1328
1329
  flexShrink: '0', width: '20px', height: '20px', borderRadius: '999px',
1329
- background: PURPLE, color: '#fff', fontSize: '11px', fontWeight: '700',
1330
- border: '2px solid #fff', boxSizing: 'border-box',
1330
+ background: BADGE_IDLE, color: '#fff', fontSize: '11px', fontWeight: '700',
1331
+ border: '1px solid #fff', boxSizing: 'border-box',
1331
1332
  display: 'flex', alignItems: 'center', justifyContent: 'center',
1333
+ transition: 'background 0.12s ease',
1332
1334
  });
1333
1335
  var content = document.createElement('div');
1334
1336
  Object.assign(content.style, { flex: '1', minWidth: '0', display: 'flex', flexDirection: 'column', gap: '6px', paddingRight: '30px' });
@@ -1450,12 +1452,14 @@
1450
1452
  note.addEventListener('click', function (e) { if (spec.note) { e.stopPropagation(); spec._expanded = !spec._expanded; renderPanel(); } });
1451
1453
  row.addEventListener('mouseenter', function () {
1452
1454
  row.style.background = 'rgba(255,255,255,0.05)';
1455
+ badge.style.background = PURPLE;
1453
1456
  editBtn.style.display = delBtn.style.display = 'flex';
1454
1457
  actions.style.background = 'linear-gradient(to right, rgba(52,54,60,0) 0, rgba(52,54,60,1) 22px)';
1455
1458
  if (visible) { highlightSpec = spec; revealSpec(spec); }
1456
1459
  });
1457
1460
  row.addEventListener('mouseleave', function () {
1458
1461
  row.style.background = 'transparent';
1462
+ badge.style.background = BADGE_IDLE;
1459
1463
  editBtn.style.display = delBtn.style.display = 'none';
1460
1464
  actions.style.background = 'transparent';
1461
1465
  if (highlightSpec === spec) highlightSpec = null;
@@ -14,6 +14,7 @@
14
14
  var TIP_BG = '#292B32';
15
15
  var GREEN = '#22C55E';
16
16
  var LABEL = '#8B8D94';
17
+ var BADGE_IDLE = '#6B6D75'; // panel row-number badges sit greyscale; the hovered row's badge colors to PURPLE
17
18
  var MONO = "'JetBrains Mono', 'SF Mono', 'Fira Code', monospace";
18
19
  var ZAP = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"></polygon></svg>';
19
20
  var PENCIL = '<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z"></path><path d="m15 5 4 4"></path></svg>';
@@ -1326,9 +1327,10 @@
1326
1327
  badge.textContent = String(i + 1);
1327
1328
  Object.assign(badge.style, {
1328
1329
  flexShrink: '0', width: '20px', height: '20px', borderRadius: '999px',
1329
- background: PURPLE, color: '#fff', fontSize: '11px', fontWeight: '700',
1330
- border: '2px solid #fff', boxSizing: 'border-box',
1330
+ background: BADGE_IDLE, color: '#fff', fontSize: '11px', fontWeight: '700',
1331
+ border: '1px solid #fff', boxSizing: 'border-box',
1331
1332
  display: 'flex', alignItems: 'center', justifyContent: 'center',
1333
+ transition: 'background 0.12s ease',
1332
1334
  });
1333
1335
  var content = document.createElement('div');
1334
1336
  Object.assign(content.style, { flex: '1', minWidth: '0', display: 'flex', flexDirection: 'column', gap: '6px', paddingRight: '30px' });
@@ -1450,12 +1452,14 @@
1450
1452
  note.addEventListener('click', function (e) { if (spec.note) { e.stopPropagation(); spec._expanded = !spec._expanded; renderPanel(); } });
1451
1453
  row.addEventListener('mouseenter', function () {
1452
1454
  row.style.background = 'rgba(255,255,255,0.05)';
1455
+ badge.style.background = PURPLE;
1453
1456
  editBtn.style.display = delBtn.style.display = 'flex';
1454
1457
  actions.style.background = 'linear-gradient(to right, rgba(52,54,60,0) 0, rgba(52,54,60,1) 22px)';
1455
1458
  if (visible) { highlightSpec = spec; revealSpec(spec); }
1456
1459
  });
1457
1460
  row.addEventListener('mouseleave', function () {
1458
1461
  row.style.background = 'transparent';
1462
+ badge.style.background = BADGE_IDLE;
1459
1463
  editBtn.style.display = delBtn.style.display = 'none';
1460
1464
  actions.style.background = 'transparent';
1461
1465
  if (highlightSpec === spec) highlightSpec = null;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "manifest_version": 3,
3
3
  "name": "Specter",
4
- "version": "0.6.0",
4
+ "version": "0.6.1",
5
5
  "description": "Element inspector for AI-assisted development. Ctrl+Option+Z to activate.",
6
6
  "icons": {
7
7
  "16": "icons/icon16.png",
@@ -14,6 +14,7 @@
14
14
  var TIP_BG = '#292B32';
15
15
  var GREEN = '#22C55E';
16
16
  var LABEL = '#8B8D94';
17
+ var BADGE_IDLE = '#6B6D75'; // panel row-number badges sit greyscale; the hovered row's badge colors to PURPLE
17
18
  var MONO = "'JetBrains Mono', 'SF Mono', 'Fira Code', monospace";
18
19
  var ZAP = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"></polygon></svg>';
19
20
  var PENCIL = '<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z"></path><path d="m15 5 4 4"></path></svg>';
@@ -1326,9 +1327,10 @@
1326
1327
  badge.textContent = String(i + 1);
1327
1328
  Object.assign(badge.style, {
1328
1329
  flexShrink: '0', width: '20px', height: '20px', borderRadius: '999px',
1329
- background: PURPLE, color: '#fff', fontSize: '11px', fontWeight: '700',
1330
- border: '2px solid #fff', boxSizing: 'border-box',
1330
+ background: BADGE_IDLE, color: '#fff', fontSize: '11px', fontWeight: '700',
1331
+ border: '1px solid #fff', boxSizing: 'border-box',
1331
1332
  display: 'flex', alignItems: 'center', justifyContent: 'center',
1333
+ transition: 'background 0.12s ease',
1332
1334
  });
1333
1335
  var content = document.createElement('div');
1334
1336
  Object.assign(content.style, { flex: '1', minWidth: '0', display: 'flex', flexDirection: 'column', gap: '6px', paddingRight: '30px' });
@@ -1450,12 +1452,14 @@
1450
1452
  note.addEventListener('click', function (e) { if (spec.note) { e.stopPropagation(); spec._expanded = !spec._expanded; renderPanel(); } });
1451
1453
  row.addEventListener('mouseenter', function () {
1452
1454
  row.style.background = 'rgba(255,255,255,0.05)';
1455
+ badge.style.background = PURPLE;
1453
1456
  editBtn.style.display = delBtn.style.display = 'flex';
1454
1457
  actions.style.background = 'linear-gradient(to right, rgba(52,54,60,0) 0, rgba(52,54,60,1) 22px)';
1455
1458
  if (visible) { highlightSpec = spec; revealSpec(spec); }
1456
1459
  });
1457
1460
  row.addEventListener('mouseleave', function () {
1458
1461
  row.style.background = 'transparent';
1462
+ badge.style.background = BADGE_IDLE;
1459
1463
  editBtn.style.display = delBtn.style.display = 'none';
1460
1464
  actions.style.background = 'transparent';
1461
1465
  if (highlightSpec === spec) highlightSpec = null;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "manifest_version": 2,
3
3
  "name": "Specter",
4
- "version": "0.6.0",
4
+ "version": "0.6.1",
5
5
  "description": "Element inspector for AI-assisted development. Ctrl+Option+Z to activate.",
6
6
  "browser_specific_settings": {
7
7
  "gecko": {
package/dist/index.cjs CHANGED
@@ -46,6 +46,7 @@ function getClientScript(options) {
46
46
  var TIP_BG = '#292B32';
47
47
  var GREEN = '#22C55E';
48
48
  var LABEL = '#8B8D94';
49
+ var BADGE_IDLE = '#6B6D75'; // panel row-number badges sit greyscale; the hovered row's badge colors to PURPLE
49
50
  var MONO = "'JetBrains Mono', 'SF Mono', 'Fira Code', monospace";
50
51
  var ZAP = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"></polygon></svg>';
51
52
  var PENCIL = '<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z"></path><path d="m15 5 4 4"></path></svg>';
@@ -1358,9 +1359,10 @@ function getClientScript(options) {
1358
1359
  badge.textContent = String(i + 1);
1359
1360
  Object.assign(badge.style, {
1360
1361
  flexShrink: '0', width: '20px', height: '20px', borderRadius: '999px',
1361
- background: PURPLE, color: '#fff', fontSize: '11px', fontWeight: '700',
1362
- border: '2px solid #fff', boxSizing: 'border-box',
1362
+ background: BADGE_IDLE, color: '#fff', fontSize: '11px', fontWeight: '700',
1363
+ border: '1px solid #fff', boxSizing: 'border-box',
1363
1364
  display: 'flex', alignItems: 'center', justifyContent: 'center',
1365
+ transition: 'background 0.12s ease',
1364
1366
  });
1365
1367
  var content = document.createElement('div');
1366
1368
  Object.assign(content.style, { flex: '1', minWidth: '0', display: 'flex', flexDirection: 'column', gap: '6px', paddingRight: '30px' });
@@ -1482,12 +1484,14 @@ function getClientScript(options) {
1482
1484
  note.addEventListener('click', function (e) { if (spec.note) { e.stopPropagation(); spec._expanded = !spec._expanded; renderPanel(); } });
1483
1485
  row.addEventListener('mouseenter', function () {
1484
1486
  row.style.background = 'rgba(255,255,255,0.05)';
1487
+ badge.style.background = PURPLE;
1485
1488
  editBtn.style.display = delBtn.style.display = 'flex';
1486
1489
  actions.style.background = 'linear-gradient(to right, rgba(52,54,60,0) 0, rgba(52,54,60,1) 22px)';
1487
1490
  if (visible) { highlightSpec = spec; revealSpec(spec); }
1488
1491
  });
1489
1492
  row.addEventListener('mouseleave', function () {
1490
1493
  row.style.background = 'transparent';
1494
+ badge.style.background = BADGE_IDLE;
1491
1495
  editBtn.style.display = delBtn.style.display = 'none';
1492
1496
  actions.style.background = 'transparent';
1493
1497
  if (highlightSpec === spec) highlightSpec = null;
package/dist/index.js CHANGED
@@ -19,6 +19,7 @@ function getClientScript(options) {
19
19
  var TIP_BG = '#292B32';
20
20
  var GREEN = '#22C55E';
21
21
  var LABEL = '#8B8D94';
22
+ var BADGE_IDLE = '#6B6D75'; // panel row-number badges sit greyscale; the hovered row's badge colors to PURPLE
22
23
  var MONO = "'JetBrains Mono', 'SF Mono', 'Fira Code', monospace";
23
24
  var ZAP = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"></polygon></svg>';
24
25
  var PENCIL = '<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z"></path><path d="m15 5 4 4"></path></svg>';
@@ -1331,9 +1332,10 @@ function getClientScript(options) {
1331
1332
  badge.textContent = String(i + 1);
1332
1333
  Object.assign(badge.style, {
1333
1334
  flexShrink: '0', width: '20px', height: '20px', borderRadius: '999px',
1334
- background: PURPLE, color: '#fff', fontSize: '11px', fontWeight: '700',
1335
- border: '2px solid #fff', boxSizing: 'border-box',
1335
+ background: BADGE_IDLE, color: '#fff', fontSize: '11px', fontWeight: '700',
1336
+ border: '1px solid #fff', boxSizing: 'border-box',
1336
1337
  display: 'flex', alignItems: 'center', justifyContent: 'center',
1338
+ transition: 'background 0.12s ease',
1337
1339
  });
1338
1340
  var content = document.createElement('div');
1339
1341
  Object.assign(content.style, { flex: '1', minWidth: '0', display: 'flex', flexDirection: 'column', gap: '6px', paddingRight: '30px' });
@@ -1455,12 +1457,14 @@ function getClientScript(options) {
1455
1457
  note.addEventListener('click', function (e) { if (spec.note) { e.stopPropagation(); spec._expanded = !spec._expanded; renderPanel(); } });
1456
1458
  row.addEventListener('mouseenter', function () {
1457
1459
  row.style.background = 'rgba(255,255,255,0.05)';
1460
+ badge.style.background = PURPLE;
1458
1461
  editBtn.style.display = delBtn.style.display = 'flex';
1459
1462
  actions.style.background = 'linear-gradient(to right, rgba(52,54,60,0) 0, rgba(52,54,60,1) 22px)';
1460
1463
  if (visible) { highlightSpec = spec; revealSpec(spec); }
1461
1464
  });
1462
1465
  row.addEventListener('mouseleave', function () {
1463
1466
  row.style.background = 'transparent';
1467
+ badge.style.background = BADGE_IDLE;
1464
1468
  editBtn.style.display = delBtn.style.display = 'none';
1465
1469
  actions.style.background = 'transparent';
1466
1470
  if (highlightSpec === spec) highlightSpec = null;
@@ -14,6 +14,7 @@
14
14
  var TIP_BG = '#292B32';
15
15
  var GREEN = '#22C55E';
16
16
  var LABEL = '#8B8D94';
17
+ var BADGE_IDLE = '#6B6D75'; // panel row-number badges sit greyscale; the hovered row's badge colors to PURPLE
17
18
  var MONO = "'JetBrains Mono', 'SF Mono', 'Fira Code', monospace";
18
19
  var ZAP = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"></polygon></svg>';
19
20
  var PENCIL = '<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z"></path><path d="m15 5 4 4"></path></svg>';
@@ -1326,9 +1327,10 @@
1326
1327
  badge.textContent = String(i + 1);
1327
1328
  Object.assign(badge.style, {
1328
1329
  flexShrink: '0', width: '20px', height: '20px', borderRadius: '999px',
1329
- background: PURPLE, color: '#fff', fontSize: '11px', fontWeight: '700',
1330
- border: '2px solid #fff', boxSizing: 'border-box',
1330
+ background: BADGE_IDLE, color: '#fff', fontSize: '11px', fontWeight: '700',
1331
+ border: '1px solid #fff', boxSizing: 'border-box',
1331
1332
  display: 'flex', alignItems: 'center', justifyContent: 'center',
1333
+ transition: 'background 0.12s ease',
1332
1334
  });
1333
1335
  var content = document.createElement('div');
1334
1336
  Object.assign(content.style, { flex: '1', minWidth: '0', display: 'flex', flexDirection: 'column', gap: '6px', paddingRight: '30px' });
@@ -1450,12 +1452,14 @@
1450
1452
  note.addEventListener('click', function (e) { if (spec.note) { e.stopPropagation(); spec._expanded = !spec._expanded; renderPanel(); } });
1451
1453
  row.addEventListener('mouseenter', function () {
1452
1454
  row.style.background = 'rgba(255,255,255,0.05)';
1455
+ badge.style.background = PURPLE;
1453
1456
  editBtn.style.display = delBtn.style.display = 'flex';
1454
1457
  actions.style.background = 'linear-gradient(to right, rgba(52,54,60,0) 0, rgba(52,54,60,1) 22px)';
1455
1458
  if (visible) { highlightSpec = spec; revealSpec(spec); }
1456
1459
  });
1457
1460
  row.addEventListener('mouseleave', function () {
1458
1461
  row.style.background = 'transparent';
1462
+ badge.style.background = BADGE_IDLE;
1459
1463
  editBtn.style.display = delBtn.style.display = 'none';
1460
1464
  actions.style.background = 'transparent';
1461
1465
  if (highlightSpec === spec) highlightSpec = null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vite-plugin-specter",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "description": "Inspect elements and Figma-style measure spacing in your vibe-coded Vite projects. Give your AI exactly what it needs to make the right change.",
5
5
  "author": "Setu Kathawate <dev@setugk.com>",
6
6
  "homepage": "https://github.com/setugk/vite-plugin-specter#readme",