|
1 | 1 | let selectedElement = null; |
2 | 2 | let isPicking = false; |
| 3 | +let projectRoot = null; |
3 | 4 |
|
4 | 5 | // Initialize source-map library |
5 | 6 | sourceMap.SourceMapConsumer.initialize({ |
@@ -233,7 +234,8 @@ document.addEventListener("DOMContentLoaded", function () { |
233 | 234 | selectedElement.component.file && |
234 | 235 | selectedElement.component.file !== "detecting..." |
235 | 236 | ) { |
236 | | - addDetailsRow("File:", selectedElement.component.file); |
| 237 | + const displayFile = makePathRelativeToProject(selectedElement.component.file); |
| 238 | + addDetailsRow("File:", displayFile); |
237 | 239 | } |
238 | 240 | } |
239 | 241 |
|
@@ -303,7 +305,8 @@ document.addEventListener("DOMContentLoaded", function () { |
303 | 305 | prompt += `\n- Framework: ${selectedElement.component.framework}`; |
304 | 306 | } |
305 | 307 | if (selectedElement.component.file) { |
306 | | - prompt += `\n- File: ${selectedElement.component.file}`; |
| 308 | + const displayFile = makePathRelativeToProject(selectedElement.component.file); |
| 309 | + prompt += `\n- File: ${displayFile}`; |
307 | 310 | } |
308 | 311 | if ( |
309 | 312 | propsOption.classList.contains("enabled") && |
@@ -348,36 +351,86 @@ document.addEventListener("DOMContentLoaded", function () { |
348 | 351 | return result.serverPort || 47923; |
349 | 352 | } |
350 | 353 |
|
| 354 | + function compareVersions(v1, v2) { |
| 355 | + const parts1 = v1.split('.').map(Number); |
| 356 | + const parts2 = v2.split('.').map(Number); |
| 357 | + for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) { |
| 358 | + const part1 = parts1[i] || 0; |
| 359 | + const part2 = parts2[i] || 0; |
| 360 | + if (part1 > part2) return 1; |
| 361 | + if (part1 < part2) return -1; |
| 362 | + } |
| 363 | + return 0; |
| 364 | + } |
| 365 | + |
| 366 | + function makePathRelativeToProject(absolutePath) { |
| 367 | + if (!projectRoot || !absolutePath) return absolutePath; |
| 368 | + |
| 369 | + const normalizedPath = absolutePath.replace(/\\/g, '/'); |
| 370 | + const normalizedRoot = projectRoot.replace(/\\/g, '/'); |
| 371 | + |
| 372 | + if (normalizedPath.startsWith(normalizedRoot)) { |
| 373 | + let relative = normalizedPath.substring(normalizedRoot.length); |
| 374 | + if (relative.startsWith('/')) { |
| 375 | + relative = relative.substring(1); |
| 376 | + } |
| 377 | + return relative; |
| 378 | + } |
| 379 | + |
| 380 | + return absolutePath; |
| 381 | + } |
| 382 | + |
351 | 383 | async function checkConnectionStatus() { |
352 | 384 | const port = portInput.value.trim() || "47923"; |
| 385 | + const configResponse = await fetch(chrome.runtime.getURL('config.json')); |
| 386 | + const config = await configResponse.json(); |
| 387 | + const MINIMAL_HOST_VERSION = config.host_version_requirements?.minimal || "0.0.0"; |
| 388 | + const RECOMMENDED_HOST_VERSION = config.host_version_requirements?.recommended || "0.0.0"; |
| 389 | + |
353 | 390 | try { |
354 | | - const response = await fetch(`http://127.0.0.1:${port}/health`, { |
355 | | - method: "GET", |
356 | | - signal: AbortSignal.timeout(2000), // 2 second timeout |
357 | | - }); |
358 | | - if (response.ok) { |
359 | | - const healthData = await response.json(); |
360 | | - const version = healthData.version ? `v${healthData.version}` : ""; |
361 | | - setConnectionStatus( |
362 | | - true, |
363 | | - version ? `connected ${version}` : "connected" |
364 | | - ); |
| 391 | + const [healthResponse, projectRootResponse] = await Promise.all([ |
| 392 | + fetch(`http://127.0.0.1:${port}/health`, { |
| 393 | + method: "GET", |
| 394 | + signal: AbortSignal.timeout(2000), |
| 395 | + }), |
| 396 | + fetch(`http://127.0.0.1:${port}/project-root`, { |
| 397 | + method: "GET", |
| 398 | + signal: AbortSignal.timeout(2000), |
| 399 | + }).catch(() => null), |
| 400 | + ]); |
| 401 | + |
| 402 | + if (healthResponse.ok) { |
| 403 | + const healthData = await healthResponse.json(); |
| 404 | + const version = healthData.version || "0.0.0"; |
| 405 | + |
| 406 | + if (projectRootResponse && projectRootResponse.ok) { |
| 407 | + const data = await projectRootResponse.json(); |
| 408 | + projectRoot = data.projectRoot; |
| 409 | + } |
| 410 | + |
| 411 | + if (compareVersions(version, MINIMAL_HOST_VERSION) < 0) { |
| 412 | + setConnectionStatus("error", `unsupported version (${version})`); |
| 413 | + } else if (compareVersions(version, RECOMMENDED_HOST_VERSION) < 0) { |
| 414 | + setConnectionStatus("warning", `connected (outdated, not all features may work)`); |
| 415 | + } else { |
| 416 | + setConnectionStatus("connected", `connected v${version}`); |
| 417 | + } |
365 | 418 | } else { |
366 | | - setConnectionStatus(false, "error"); |
| 419 | + setConnectionStatus("error", "error"); |
367 | 420 | } |
368 | 421 | } catch (error) { |
369 | | - setConnectionStatus(false, "disconnected"); |
| 422 | + setConnectionStatus("error", "disconnected"); |
370 | 423 | } |
371 | 424 | } |
372 | 425 |
|
373 | | - function setConnectionStatus(connected, statusText) { |
374 | | - if (connected) { |
375 | | - statusDot.classList.remove("disconnected"); |
376 | | - connectionStatus.textContent = statusText; |
377 | | - } else { |
| 426 | + function setConnectionStatus(state, statusText) { |
| 427 | + statusDot.classList.remove("disconnected", "warning"); |
| 428 | + if (state === "error") { |
378 | 429 | statusDot.classList.add("disconnected"); |
379 | | - connectionStatus.textContent = statusText; |
| 430 | + } else if (state === "warning") { |
| 431 | + statusDot.classList.add("warning"); |
380 | 432 | } |
| 433 | + connectionStatus.textContent = statusText; |
381 | 434 | } |
382 | 435 |
|
383 | 436 | function autoResizeTextarea(textarea) { |
@@ -612,6 +665,8 @@ document.addEventListener("DOMContentLoaded", function () { |
612 | 665 | fileName = fullName.split("?")[0]; |
613 | 666 | } |
614 | 667 |
|
| 668 | + fileName = makePathRelativeToProject(fileName); |
| 669 | + |
615 | 670 | return `${fileName}:${finalLineNumber}`; |
616 | 671 | } |
617 | 672 |
|
|
0 commit comments