vite-plugin-source-locator 1.1.2 → 1.1.4

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
@@ -42,11 +42,16 @@ No `main.tsx` wiring required. The plugin auto-injects the client overlay in dev
42
42
 
43
43
  ## Pick Mode
44
44
 
45
- 1. Click the badge (bottom-right): **Source Locator (auto) — click to pick**
45
+ 1. Click the badge (bottom-right): **Locator**
46
46
  2. Hover elements — blue highlight + file paths in tooltip
47
47
  3. Click to open source — cycles **TSX → CSS → TSX** when both exist
48
48
  4. **Esc** — cancel pick mode
49
- 5. **Shift+L** — cycle IDE (session only, resets on refresh)
49
+
50
+ | Shortcut | Action |
51
+ |----------|--------|
52
+ | Click | Open TSX (or cycle TSX/CSS) |
53
+ | **Shift+C** | Open CSS/style directly |
54
+ | Esc | Cancel pick |
50
55
 
51
56
  ## Exports
52
57
 
@@ -89,7 +94,6 @@ import { initSourceLocator } from 'vite-plugin-source-locator/client'
89
94
  initSourceLocator({
90
95
  endpoint: '/__open-in-editor',
91
96
  attribute: 'data-source',
92
- ides: ['auto', 'cursor', 'vscode', 'webstorm'],
93
97
  theme: {
94
98
  background: '#ffffff',
95
99
  text: '#000000',
@@ -132,7 +136,6 @@ import { initSourceLocator } from 'vite-plugin-source-locator/client'
132
136
  initSourceLocator({
133
137
  endpoint: '/__open-in-editor',
134
138
  attribute: 'data-source',
135
- ides: ['auto', 'cursor', 'vscode', 'webstorm'],
136
139
  theme: 'blue',
137
140
  })
138
141
  ```
@@ -149,7 +152,7 @@ By default, `ides` includes `'auto'` as the first entry. In `auto` mode, the plu
149
152
  2. Running editor process (VS Code, Cursor, WebStorm, etc.)
150
153
  3. `VISUAL` / `EDITOR` environment variable
151
154
 
152
- No CLI on `PATH` is required if the editor is already running.
155
+ The plugin resolves known app-bundle CLI paths automatically (e.g. VS Code on macOS). If detection still fails, install the editor shell command or set `LAUNCH_EDITOR` to the full CLI path.
153
156
 
154
157
  ```typescript
155
158
  // auto-detect open IDE (default)
@@ -157,16 +160,13 @@ sourceLocator()
157
160
 
158
161
  // VS Code only
159
162
  sourceLocator({ ides: ['vscode'] })
160
-
161
- // auto + manual override with Shift+L (session only)
162
- sourceLocator({ ides: ['auto', 'vscode'] })
163
163
  ```
164
164
 
165
- The first entry in `ides` is the default. Shift+L cycles through the list in memory (resets on page refresh).
165
+ The `ides` option controls which editors the server may open. The client always uses auto-detection.
166
166
 
167
167
  ### Explicit editor
168
168
 
169
- When not using `auto`, the editor CLI should be on your `PATH`:
169
+ When not using `auto`, the plugin resolves common install paths first, then falls back to the CLI name on `PATH`:
170
170
 
171
171
  | IDE | CLI command |
172
172
  |-----|-------------|
@@ -174,10 +174,12 @@ When not using `auto`, the editor CLI should be on your `PATH`:
174
174
  | VS Code | `code` |
175
175
  | WebStorm | `webstorm` |
176
176
 
177
- Override with environment variables:
177
+ If opening fails with `ENOENT`, install the shell command in your editor (VS Code: **Shell Command: Install 'code' command in PATH**) or set a full path:
178
178
 
179
- - `LAUNCH_EDITOR=code`
180
- - `REACT_EDITOR=code`
179
+ ```
180
+ LAUNCH_EDITOR=/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code
181
+ REACT_EDITOR=/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code
182
+ ```
181
183
 
182
184
  ## Adding a New IDE
183
185
 
@@ -1,8 +1,7 @@
1
- import type { LocatorIde, LocatorThemeInput } from '../shared/index.js';
1
+ import type { LocatorThemeInput } from '../shared/index.js';
2
2
  export type ClientConfig = {
3
3
  endpoint: string;
4
4
  attribute: string;
5
- ides: LocatorIde[];
6
5
  theme?: LocatorThemeInput;
7
6
  };
8
7
  export declare function startPickController(root: ShadowRoot, host: Element, config: ClientConfig): () => void;
@@ -1,4 +1,4 @@
1
- import { nextClickTarget, nextIde, parseSourceLocation, resolveTheme } from '../shared/index.js';
1
+ import { DEFAULT_IDE, nextClickTarget, parseSourceLocation, resolveTheme } from '../shared/index.js';
2
2
  import { findCssSource } from './css-source.js';
3
3
  import { createLocatorOverlayUi } from './overlay.js';
4
4
  const EMPTY_SOURCES = Object.freeze({
@@ -12,13 +12,13 @@ function getSourceEl(target, attribute, host) {
12
12
  const el = target.closest(`[${attribute}]`);
13
13
  return el instanceof HTMLElement ? el : null;
14
14
  }
15
- async function openSourceInEditor(source, config, activeIde) {
15
+ async function openSourceInEditor(source, config) {
16
16
  const loc = parseSourceLocation(source);
17
17
  const params = new URLSearchParams({
18
18
  file: loc.file,
19
19
  line: loc.line,
20
20
  col: loc.col,
21
- ide: activeIde,
21
+ ide: DEFAULT_IDE,
22
22
  });
23
23
  await fetch(`${config.endpoint}?${params.toString()}`);
24
24
  }
@@ -33,8 +33,7 @@ function resolveOpenSource(sources) {
33
33
  export function startPickController(root, host, config) {
34
34
  let pickMode = false;
35
35
  let sources = EMPTY_SOURCES;
36
- let activeIde = config.ides[0] ?? 'auto';
37
- const ui = createLocatorOverlayUi(root, () => setPickMode(!pickMode), resolveTheme(config.theme), () => activeIde);
36
+ const ui = createLocatorOverlayUi(root, () => setPickMode(!pickMode), resolveTheme(config.theme));
38
37
  function setPickMode(active) {
39
38
  pickMode = active;
40
39
  ui.setPickActive(active);
@@ -52,7 +51,7 @@ export function startPickController(root, host, config) {
52
51
  return;
53
52
  }
54
53
  syncSources(el);
55
- ui.showSourceTooltip(el, sources.tsx, sources.css, sources.clickTarget, x, y);
54
+ ui.showSourceTooltip(el, sources.tsx, sources.css, x, y);
56
55
  };
57
56
  const onMouseMove = (e) => {
58
57
  if (!pickMode) {
@@ -61,17 +60,23 @@ export function startPickController(root, host, config) {
61
60
  }
62
61
  updateHover(e.target, e.clientX, e.clientY);
63
62
  };
64
- const onKeyDown = (e) => {
63
+ const onKeyDown = async (e) => {
65
64
  if (e.key === 'Escape' && pickMode) {
66
65
  setPickMode(false);
67
66
  return;
68
67
  }
69
- if (!e.shiftKey || e.key !== 'L')
68
+ if (e.shiftKey && e.key === 'C' && pickMode) {
69
+ const el = ui.getActiveEl();
70
+ if (!(el instanceof HTMLElement))
71
+ return;
72
+ syncSources(el);
73
+ if (!sources.css) {
74
+ ui.flashMessage('No CSS for this element');
75
+ return;
76
+ }
77
+ await openSourceInEditor(sources.css, config);
70
78
  return;
71
- activeIde = nextIde(activeIde, config.ides);
72
- if (!pickMode)
73
- ui.refreshBadgeLabel();
74
- ui.flashMessage(`IDE: ${activeIde}`);
79
+ }
75
80
  };
76
81
  const onClick = async (e) => {
77
82
  if (!pickMode || host.contains(e.target))
@@ -86,9 +91,9 @@ export function startPickController(root, host, config) {
86
91
  ui.flashMessage('No source for this element');
87
92
  return;
88
93
  }
89
- await openSourceInEditor(openSource, config, activeIde);
94
+ await openSourceInEditor(openSource, config);
90
95
  sources = { ...sources, clickTarget: nextClickTarget(sources.clickTarget, !!sources.css) };
91
- ui.showSourceTooltip(el, sources.tsx, sources.css, sources.clickTarget, e.clientX, e.clientY);
96
+ ui.showSourceTooltip(el, sources.tsx, sources.css, e.clientX, e.clientY);
92
97
  };
93
98
  document.addEventListener('keydown', onKeyDown);
94
99
  document.addEventListener('mousemove', onMouseMove);
@@ -1,10 +1,12 @@
1
1
  import { startPickController } from './controller.js';
2
+ import { HOST_LAYOUT } from './overlay-styles.js';
2
3
  const HOST_ID = 'source-locator-host';
3
4
  export function initSourceLocator(config) {
4
5
  if (window.__sourceLocator || document.getElementById(HOST_ID))
5
6
  return;
6
7
  const host = document.createElement('div');
7
8
  host.id = HOST_ID;
9
+ Object.assign(host.style, HOST_LAYOUT);
8
10
  const root = host.attachShadow({ mode: 'open' });
9
11
  document.body.appendChild(host);
10
12
  const disposeController = startPickController(root, host, config);
@@ -3,18 +3,25 @@ export declare const UI_IDS: {
3
3
  readonly tooltip: "source-locator-tooltip";
4
4
  readonly highlight: "source-locator-highlight";
5
5
  };
6
+ export declare const HOST_LAYOUT: {
7
+ readonly position: "fixed";
8
+ readonly inset: "0";
9
+ readonly zIndex: "99999";
10
+ readonly pointerEvents: "none";
11
+ };
6
12
  export declare const LAYOUT: {
7
13
  readonly badge: {
8
14
  readonly position: "fixed";
9
15
  readonly bottom: "12px";
10
16
  readonly right: "12px";
11
- readonly padding: "8px 12px";
12
- readonly borderRadius: "999px";
13
- readonly fontSize: "11px";
17
+ readonly padding: "10px 14px";
18
+ readonly borderRadius: "8px";
19
+ readonly fontSize: "12px";
14
20
  readonly fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace";
15
21
  readonly zIndex: "99999";
16
22
  readonly cursor: "pointer";
17
- readonly boxShadow: "0 4px 16px rgba(0, 0, 0, 0.35)";
23
+ readonly pointerEvents: "auto";
24
+ readonly boxShadow: "0 2px 12px rgba(0, 0, 0, 0.12), 0 0 0 1px rgba(0, 0, 0, 0.08)";
18
25
  };
19
26
  readonly tooltip: {
20
27
  readonly position: "fixed";
@@ -3,18 +3,25 @@ export const UI_IDS = {
3
3
  tooltip: 'source-locator-tooltip',
4
4
  highlight: 'source-locator-highlight',
5
5
  };
6
+ export const HOST_LAYOUT = {
7
+ position: 'fixed',
8
+ inset: '0',
9
+ zIndex: '99999',
10
+ pointerEvents: 'none',
11
+ };
6
12
  export const LAYOUT = {
7
13
  badge: {
8
14
  position: 'fixed',
9
15
  bottom: '12px',
10
16
  right: '12px',
11
- padding: '8px 12px',
12
- borderRadius: '999px',
13
- fontSize: '11px',
17
+ padding: '10px 14px',
18
+ borderRadius: '8px',
19
+ fontSize: '12px',
14
20
  fontFamily: 'ui-monospace, SFMono-Regular, Menlo, monospace',
15
21
  zIndex: '99999',
16
22
  cursor: 'pointer',
17
- boxShadow: '0 4px 16px rgba(0, 0, 0, 0.35)',
23
+ pointerEvents: 'auto',
24
+ boxShadow: '0 2px 12px rgba(0, 0, 0, 0.12), 0 0 0 1px rgba(0, 0, 0, 0.08)',
18
25
  },
19
26
  tooltip: {
20
27
  position: 'fixed',
@@ -1,9 +1,8 @@
1
- import type { ClickTarget, LocatorIde, LocatorTheme } from '../shared/index.js';
2
- export declare function createLocatorOverlayUi(root: ShadowRoot, onTogglePick: () => void, theme: LocatorTheme, getActiveIde: () => LocatorIde): {
1
+ import type { LocatorTheme } from '../shared/index.js';
2
+ export declare function createLocatorOverlayUi(root: ShadowRoot, onTogglePick: () => void, theme: LocatorTheme): {
3
3
  mountBadge: () => void;
4
4
  setPickActive: (active: boolean) => void;
5
- refreshBadgeLabel: () => void;
6
- showSourceTooltip: (el: Element, tsxSource: string | undefined, cssSource: string | undefined, clickTarget: ClickTarget, x: number, y: number) => void;
5
+ showSourceTooltip: (el: Element, tsxSource: string | undefined, cssSource: string | undefined, x: number, y: number) => void;
7
6
  flashMessage: (text: string) => void;
8
7
  removeTooltip: () => void;
9
8
  getActiveEl: () => Element | null;
@@ -6,7 +6,7 @@ const TOOLTIP_CURSOR_OFFSET = 16;
6
6
  const FLASH_DURATION_MS = 1500;
7
7
  const FLASH_HORIZONTAL_OFFSET = 80;
8
8
  const FLASH_BOTTOM_OFFSET = 80;
9
- export function createLocatorOverlayUi(root, onTogglePick, theme, getActiveIde) {
9
+ export function createLocatorOverlayUi(root, onTogglePick, theme) {
10
10
  let activeEl = null;
11
11
  let flashTimeout = null;
12
12
  let badgeEl = null;
@@ -49,8 +49,8 @@ export function createLocatorOverlayUi(root, onTogglePick, theme, getActiveIde)
49
49
  if (el)
50
50
  showHighlight(el);
51
51
  };
52
- const showSourceTooltip = (el, tsxSource, cssSource, clickTarget, x, y) => {
53
- showTooltip(buildTooltipText(tsxSource, cssSource, clickTarget), el, x, y);
52
+ const showSourceTooltip = (el, tsxSource, cssSource, x, y) => {
53
+ showTooltip(buildTooltipText(tsxSource, cssSource), el, x, y);
54
54
  };
55
55
  const flashMessage = (text) => {
56
56
  if (flashTimeout)
@@ -65,25 +65,22 @@ export function createLocatorOverlayUi(root, onTogglePick, theme, getActiveIde)
65
65
  return;
66
66
  badgeEl.style.background = active ? theme.badgeActiveBackground : theme.badgeBackground;
67
67
  badgeEl.style.color = active ? theme.badgeActiveText : theme.badgeText;
68
+ badgeEl.style.border = `1px solid ${active ? theme.badgeActiveBorder : theme.badgeBorder}`;
68
69
  };
69
70
  const setPickActive = (active) => {
70
71
  document.body.style.cursor = active ? 'crosshair' : '';
71
72
  if (!badgeEl)
72
73
  return;
73
- badgeEl.textContent = badgeLabel(active, getActiveIde());
74
+ badgeEl.textContent = badgeLabel(active);
74
75
  applyBadgeColors(active);
75
76
  if (!active)
76
77
  removeTooltip();
77
78
  };
78
- const refreshBadgeLabel = () => {
79
- if (badgeEl)
80
- badgeEl.textContent = badgeLabel(false, getActiveIde());
81
- };
82
79
  const mountBadge = () => {
83
80
  badgeEl = document.createElement('button');
84
81
  badgeEl.id = UI_IDS.badge;
85
82
  badgeEl.type = 'button';
86
- badgeEl.textContent = badgeLabel(false, getActiveIde());
83
+ badgeEl.textContent = badgeLabel(false);
87
84
  Object.assign(badgeEl.style, LAYOUT.badge, {
88
85
  background: theme.badgeBackground,
89
86
  color: theme.badgeText,
@@ -98,7 +95,6 @@ export function createLocatorOverlayUi(root, onTogglePick, theme, getActiveIde)
98
95
  return {
99
96
  mountBadge,
100
97
  setPickActive,
101
- refreshBadgeLabel,
102
98
  showSourceTooltip,
103
99
  flashMessage,
104
100
  removeTooltip,
@@ -1,2 +1 @@
1
- import type { LocatorIde } from '../shared/index.js';
2
- export declare function badgeLabel(picking: boolean, activeIde: LocatorIde): string;
1
+ export declare function badgeLabel(picking: boolean): string;
@@ -1,7 +1,5 @@
1
- const BADGE_PICKING_HINT = 'Esc to cancel';
2
- const BADGE_IDLE_HINT = 'click to pick';
3
- export function badgeLabel(picking, activeIde) {
1
+ export function badgeLabel(picking) {
4
2
  if (picking)
5
- return `Pick element (${activeIde}) ${BADGE_PICKING_HINT}`;
6
- return `Source Locator (${activeIde}) — ${BADGE_IDLE_HINT}`;
3
+ return 'PickingEsc';
4
+ return 'Locator';
7
5
  }
@@ -1,2 +1 @@
1
- import type { ClickTarget } from '../shared/index.js';
2
- export declare function buildTooltipText(tsxSource: string | undefined, cssSource: string | undefined, clickTarget: ClickTarget): string;
1
+ export declare function buildTooltipText(tsxSource: string | undefined, cssSource: string | undefined): string;
@@ -1,9 +1,5 @@
1
1
  import { parseSourceLocation } from '../shared/index.js';
2
2
  const DEFAULT_LINE = '1';
3
- const CLICK_PROMPT_ORDER = {
4
- tsx: ['TSX', 'CSS'],
5
- css: ['CSS', 'TSX'],
6
- };
7
3
  function formatSourceLabel(source, prefix) {
8
4
  const { file, line } = parseSourceLocation(source);
9
5
  const name = file.split('/').pop() ?? file;
@@ -12,7 +8,7 @@ function formatSourceLabel(source, prefix) {
12
8
  return `${prefix}: ${label}`;
13
9
  return label;
14
10
  }
15
- export function buildTooltipText(tsxSource, cssSource, clickTarget) {
11
+ export function buildTooltipText(tsxSource, cssSource) {
16
12
  const lines = [];
17
13
  if (tsxSource)
18
14
  lines.push(formatSourceLabel(tsxSource, 'TSX'));
@@ -22,7 +18,6 @@ export function buildTooltipText(tsxSource, cssSource, clickTarget) {
22
18
  lines.push('Click → open TSX');
23
19
  return lines.join('\n');
24
20
  }
25
- const [first, second] = CLICK_PROMPT_ORDER[clickTarget];
26
- lines.push(`Click → open ${first}`, `Click again → open ${second}`);
21
+ lines.push('Click → open TSX', 'Shift+C CSS');
27
22
  return lines.join('\n');
28
23
  }
@@ -13,7 +13,6 @@ export declare const DEFAULT_IDE: LocatorIde;
13
13
  export declare const IDE_ORDER: LocatorIde[];
14
14
  export declare function isLocatorIde(value: string): value is LocatorIde;
15
15
  export declare function resolveIde(value: string, allowed: LocatorIde[]): LocatorIde;
16
- export declare function nextIde(current: LocatorIde, order?: LocatorIde[]): LocatorIde;
17
16
  export declare function parseSourceLocation(raw: string): SourceLocation;
18
17
  export declare function formatSourceLocation(loc: SourceLocation): string;
19
18
  export declare function nextClickTarget(current: ClickTarget, hasCss: boolean): ClickTarget;
@@ -14,11 +14,6 @@ export function resolveIde(value, allowed) {
14
14
  return fallback;
15
15
  return value;
16
16
  }
17
- export function nextIde(current, order = IDE_ORDER) {
18
- const index = order.indexOf(current);
19
- const nextIndex = (index + 1) % order.length;
20
- return order[nextIndex] ?? DEFAULT_IDE;
21
- }
22
17
  export function parseSourceLocation(raw) {
23
18
  const parts = raw.split(':');
24
19
  if (parts.length < 3)
@@ -11,6 +11,7 @@ export type LocatorTheme = {
11
11
  badgeActiveBackground: string;
12
12
  badgeActiveText: string;
13
13
  badgeBorder: string;
14
+ badgeActiveBorder: string;
14
15
  tooltipBackground: string;
15
16
  tooltipText: string;
16
17
  tooltipBorder: string;
@@ -1,9 +1,10 @@
1
1
  const PRESET_COLORS = {
2
2
  default: { background: '#0f172a', text: '#f8fafc', accent: '#38bdf8' },
3
- light: { background: '#ffffff', text: '#0f172a', accent: '#2563eb' },
3
+ light: { background: '#ffffff', text: '#111827', accent: '#2563eb' },
4
4
  dark: { background: '#000000', text: '#ffffff', accent: '#a3a3a3' },
5
5
  blue: { background: '#1e3a8a', text: '#eff6ff', accent: '#60a5fa' },
6
6
  };
7
+ const LIGHT_BADGE_BORDER = '#d1d5db';
7
8
  const HEX_COLOR_LENGTH = 7;
8
9
  const RGB_CHANNEL_MAX = 255;
9
10
  const HIGHLIGHT_ALPHA = 0.12;
@@ -16,14 +17,31 @@ function withAlpha(hex, alpha) {
16
17
  .padStart(2, '0');
17
18
  return `${hex}${channel}`;
18
19
  }
19
- function buildTheme(colors) {
20
+ function buildTheme(colors, preset) {
20
21
  const { background, text, accent } = colors;
22
+ if (preset === 'light') {
23
+ return {
24
+ badgeBackground: background,
25
+ badgeText: text,
26
+ badgeActiveBackground: background,
27
+ badgeActiveText: accent,
28
+ badgeBorder: LIGHT_BADGE_BORDER,
29
+ badgeActiveBorder: accent,
30
+ tooltipBackground: background,
31
+ tooltipText: text,
32
+ tooltipBorder: LIGHT_BADGE_BORDER,
33
+ highlightBorder: accent,
34
+ highlightBackground: withAlpha(accent, HIGHLIGHT_ALPHA),
35
+ highlightShadow: withAlpha(background, SHADOW_ALPHA),
36
+ };
37
+ }
21
38
  return {
22
39
  badgeBackground: background,
23
40
  badgeText: accent,
24
41
  badgeActiveBackground: accent,
25
42
  badgeActiveText: background,
26
43
  badgeBorder: accent,
44
+ badgeActiveBorder: accent,
27
45
  tooltipBackground: background,
28
46
  tooltipText: text,
29
47
  tooltipBorder: accent,
@@ -33,10 +51,10 @@ function buildTheme(colors) {
33
51
  };
34
52
  }
35
53
  export function resolveTheme(input) {
36
- const base = PRESET_COLORS.default;
54
+ const lightBase = PRESET_COLORS.light;
37
55
  if (!input)
38
- return buildTheme(base);
56
+ return buildTheme(lightBase, 'light');
39
57
  if (typeof input === 'string')
40
- return buildTheme(PRESET_COLORS[input] ?? base);
41
- return buildTheme({ ...base, ...input });
58
+ return buildTheme(PRESET_COLORS[input] ?? lightBase, input);
59
+ return buildTheme({ ...lightBase, ...input }, 'light');
42
60
  }
@@ -0,0 +1,5 @@
1
+ import type { LocatorIde } from '../shared/index.js';
2
+ export declare function toLaunchEditorName(ide: Exclude<LocatorIde, 'auto'>): string;
3
+ export declare function resolveCliPath(command: string): string;
4
+ export declare function formatLaunchEditorCommand(command: string): string;
5
+ export declare function resolveAutoEditor(): string | null;
@@ -0,0 +1,53 @@
1
+ import { existsSync } from 'node:fs';
2
+ import guessEditor from 'launch-editor/guess.js';
3
+ const IDE_LAUNCH_NAMES = {
4
+ vscode: 'code',
5
+ cursor: 'cursor',
6
+ webstorm: 'webstorm',
7
+ };
8
+ function buildCliCandidates() {
9
+ if (process.platform === 'win32') {
10
+ const localAppData = process.env.LOCALAPPDATA ?? '';
11
+ const programFiles = process.env.ProgramFiles ?? 'C:\\Program Files';
12
+ return {
13
+ code: [
14
+ `${localAppData}\\Programs\\Microsoft VS Code\\bin\\code.cmd`,
15
+ `${programFiles}\\Microsoft VS Code\\bin\\code.cmd`,
16
+ 'code',
17
+ ],
18
+ cursor: [`${localAppData}\\Programs\\cursor\\Cursor.exe`, 'cursor'],
19
+ webstorm: [`${programFiles}\\JetBrains\\WebStorm\\bin\\webstorm64.exe`, 'webstorm'],
20
+ };
21
+ }
22
+ return {
23
+ code: ['/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code', 'code'],
24
+ cursor: ['/Applications/Cursor.app/Contents/Resources/app/bin/cursor', 'cursor'],
25
+ webstorm: ['/Applications/WebStorm.app/Contents/MacOS/webstorm', 'webstorm'],
26
+ };
27
+ }
28
+ const CLI_CANDIDATES = buildCliCandidates();
29
+ export function toLaunchEditorName(ide) {
30
+ return IDE_LAUNCH_NAMES[ide];
31
+ }
32
+ export function resolveCliPath(command) {
33
+ if ((command.includes('/') || command.includes('\\')) && existsSync(command)) {
34
+ return command;
35
+ }
36
+ const candidates = CLI_CANDIDATES[command] ?? [command];
37
+ const bundlePath = candidates.find((path) => (path.includes('/') || path.includes('\\')) && existsSync(path));
38
+ if (bundlePath)
39
+ return bundlePath;
40
+ return command;
41
+ }
42
+ export function formatLaunchEditorCommand(command) {
43
+ if (command.includes(' ')) {
44
+ return `"${command}"`;
45
+ }
46
+ return command;
47
+ }
48
+ export function resolveAutoEditor() {
49
+ const [editor] = guessEditor();
50
+ if (!editor)
51
+ return null;
52
+ return resolveCliPath(editor);
53
+ }
@@ -1,11 +1,18 @@
1
1
  import launch from 'launch-editor';
2
2
  import { resolveIde } from '../shared/index.js';
3
+ import { formatLaunchEditorCommand, resolveAutoEditor, resolveCliPath, toLaunchEditorName } from './editor-cli.js';
3
4
  export function openInEditor(loc, ideParam, allowed) {
4
5
  const ide = resolveIde(ideParam, allowed);
5
6
  const spec = `${loc.file}:${loc.line}:${loc.col}`;
6
7
  if (ide === 'auto') {
8
+ const resolved = resolveAutoEditor();
9
+ if (resolved) {
10
+ launch(spec, formatLaunchEditorCommand(resolved));
11
+ return;
12
+ }
7
13
  launch(spec);
8
14
  return;
9
15
  }
10
- launch(spec, ide);
16
+ const command = resolveCliPath(toLaunchEditorName(ide));
17
+ launch(spec, formatLaunchEditorCommand(command));
11
18
  }
@@ -38,7 +38,6 @@ function sourceLocator(options = {}) {
38
38
  const clientConfig = {
39
39
  endpoint: config.endpoint,
40
40
  attribute: config.attribute,
41
- ides: config.ides,
42
41
  theme: config.theme,
43
42
  };
44
43
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vite-plugin-source-locator",
3
- "version": "1.1.2",
3
+ "version": "1.1.4",
4
4
  "description": "Dev-only Vite plugin: click UI elements to jump to source in your IDE",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -32,7 +32,7 @@
32
32
  "prepublishOnly": "npm run lint && npm run build && npm run test"
33
33
  },
34
34
  "peerDependencies": {
35
- "vite": "^5.0.0"
35
+ "vite": "^5.0.0 || ^6.0.0"
36
36
  },
37
37
  "dependencies": {
38
38
  "@babel/core": "^7.29.7",