Files
playwriter/extension-kapture/panel.js
Tommy D. Rossi ec02a90812 adding examples
2025-11-13 19:42:44 +01:00

305 lines
9.5 KiB
JavaScript

// DevTools Panel UI with Real Connection and Data
// Debug what you're actually getting
console.log('inspectedWindow.tabId:', chrome.devtools.inspectedWindow.tabId);
// Let's also check what chrome.tabs.query gives us for comparison
chrome.tabs.query({}, (tabs) => {
console.log('All tabs:', tabs);
// Find the tab with the same ID
const inspectedTab = tabs.find(tab => tab.id === chrome.devtools.inspectedWindow.tabId);
console.log('Inspected tab details:', inspectedTab);
});
// Also check the inspected window URL
console.log('inspectedWindow.url:', chrome.devtools.inspectedWindow.url);
const tabId = chrome.devtools.inspectedWindow.tabId;
let selectedMessageIndex = -1;
let messages = [];
let consoleLogCount = 0;
let port = null;
// Initialize UI
function initializeUI() {
// Connect to background script
port = chrome.runtime.connect({ name: 'panel' });
// Listen for state updates
port.onMessage.addListener((msg) => {
if (msg.type === 'state' && msg.tabId === tabId) {
updateUI(msg.connected, msg.status);
} else if (msg.type === 'messages' && msg.tabId === tabId) {
// Update messages from background
messages = msg.messages || [];
renderMessages();
} else if (msg.type === 'consoleCount' && msg.tabId === tabId) {
// Update console count from background
consoleLogCount = msg.count || 0;
updateConsoleCount();
}
});
// Subscribe to state updates for this tab
port.postMessage({ type: 'subscribe', tabId });
// Event listeners
document.getElementById('toggle').addEventListener('change', handleToggleChange);
document.getElementById('clear-logs').addEventListener('click', handleClearLogs);
document.getElementById('clear-messages').addEventListener('click', handleClearMessages);
document.getElementById('messages-list').addEventListener('click', handleMessageClick);
document.addEventListener('keydown', handleKeyDown);
// Resize handle
initializeResizeHandle();
}
// Update UI based on connection state
function updateUI(connected, status = 'disconnected') {
const toggle = document.getElementById('toggle');
const toggleContainer = toggle.parentElement;
const statusEl = document.getElementById('status');
const statusText = statusEl.querySelector('.status-text');
const tabInfo = document.getElementById('tab-info');
// Remove existing classes
statusEl.classList.remove('connected', 'disconnected', 'retrying');
toggleContainer.classList.remove('connected', 'disconnected', 'retrying');
switch (status) {
case 'connected':
toggle.checked = true;
toggle.disabled = false;
statusEl.classList.add('connected');
toggleContainer.classList.add('connected');
statusText.textContent = 'Connected';
tabInfo.textContent = `Tab: ${tabId} - Connected`;
break;
case 'retrying':
toggle.checked = true;
toggle.disabled = false;
statusEl.classList.add('retrying');
toggleContainer.classList.add('retrying');
statusText.textContent = 'Retrying...';
tabInfo.textContent = `Tab: ${tabId} - Reconnecting`;
break;
case 'disconnected':
default:
toggle.checked = false;
toggle.disabled = false;
statusEl.classList.add('disconnected');
toggleContainer.classList.add('disconnected');
statusText.textContent = 'Disconnected';
tabInfo.textContent = 'Tab: Not connected';
break;
}
}
// Render messages
function renderMessages() {
const messagesList = document.getElementById('messages-list');
const messagesContainer = document.querySelector('.messages-container');
// Toggle class based on whether we have messages
messagesContainer.classList.toggle('has-messages', messages.length > 0);
if (messages.length === 0) {
return;
}
messagesList.innerHTML = '';
messages.forEach((msg, index) => {
const messageEl = document.createElement('div');
messageEl.className = 'message';
if (index === selectedMessageIndex) {
messageEl.classList.add('selected');
}
const arrow = msg.direction === 'outgoing' ? '&#x2B06' : '&#x2B07'; // Up arrow for outgoing, down arrow for incoming
const arrowClass = msg.direction === 'outgoing' ? 'outgoing' : 'incoming';
// Show the raw JSON data
const dataText = JSON.stringify(msg.data);
messageEl.innerHTML = `
<div class="message-data">
<span class="message-arrow ${arrowClass}">${arrow}</span>
${dataText}
</div>
<div class="message-time">${formatTime(msg.timestamp)}</div>
`;
messageEl.dataset.index = index;
messagesList.appendChild(messageEl);
});
// Scroll to bottom
messagesList.scrollTop = messagesList.scrollHeight;
}
// Format timestamp
function formatTime(date) {
// Convert string to Date if needed
const dateObj = typeof date === 'string' ? new Date(date) : date;
return dateObj.toLocaleTimeString('en-US', {
hour12: false,
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
});
}
// Handle message click
function handleMessageClick(e) {
const messageEl = e.target.closest('.message');
if (!messageEl) return;
const index = parseInt(messageEl.dataset.index);
selectMessage(index);
}
// Select message
function selectMessage(index) {
selectedMessageIndex = index;
// Update selected state
document.querySelectorAll('.message').forEach((el, i) => {
el.classList.toggle('selected', i === index);
});
// Show detail view
const detailContainer = document.getElementById('detail-container');
const detailContent = document.getElementById('detail-content');
if (index >= 0 && index < messages.length) {
detailContainer.classList.add('visible');
const message = messages[index];
const data = message.data;
// Check if this is a screenshot response
if (data.type === 'response' && data.success && data.result && data.result.data &&
data.result.mimeType && data.result.mimeType.startsWith('image/')) {
// Create image preview for screenshot
const img = document.createElement('img');
img.src = `data:${data.result.mimeType};base64,${data.result.data}`;
img.style.cssText = 'max-width: 100%; height: auto; cursor: pointer; display: block; border: 1px solid #e0e0e0; border-radius: 4px;';
img.title = 'Click to open in new tab';
// Open in new tab on click
img.addEventListener('click', () => {
window.open(img.src, '_blank');
});
// Show image and data
detailContent.innerHTML = '';
detailContent.appendChild(img);
// Add the rest of the data below the image
const dataInfo = document.createElement('pre');
dataInfo.textContent = JSON.stringify(data.result, null, 2);
dataInfo.style.cssText = 'margin-top: 10px; font-size: 12px; overflow: auto;';
detailContent.appendChild(dataInfo);
} else {
// Show only the actual message data, not the wrapper
detailContent.innerHTML = '';
const pre = document.createElement('pre');
pre.style.cssText = 'margin: 0; font-size: 12px; overflow: auto;';
pre.textContent = JSON.stringify(message.data, null, 2);
detailContent.appendChild(pre);
}
} else {
detailContainer.classList.remove('visible');
}
}
// Handle keyboard navigation
function handleKeyDown(e) {
if (e.key === 'ArrowUp' && selectedMessageIndex > 0) {
selectMessage(selectedMessageIndex - 1);
e.preventDefault();
} else if (e.key === 'ArrowDown' && selectedMessageIndex < messages.length - 1) {
selectMessage(selectedMessageIndex + 1);
e.preventDefault();
} else if ((e.metaKey || e.ctrlKey) && e.key === 'k') {
// Clear messages
handleClearMessages();
e.preventDefault();
}
}
// Handle toggle change
function handleToggleChange(e) {
const checked = e.target.checked;
chrome.runtime.sendMessage(
{
type: checked ? 'connect' : 'disconnect',
tabId: tabId
},
(response) => {
if (response?.error) {
console.error('Toggle error:', response.error);
}
}
);
}
// Handle clear logs
function handleClearLogs() {
// Request background to clear console logs
port.postMessage({ type: 'clearConsoleLogs', tabId });
}
// Handle clear messages
function handleClearMessages() {
// Request background to clear messages
port.postMessage({ type: 'clearMessages', tabId });
// Clear the detail view
const detailContainer = document.getElementById('detail-container');
detailContainer.classList.remove('visible');
selectedMessageIndex = -1;
}
// Update console count
function updateConsoleCount() {
document.getElementById('console-count').textContent = `Console: ${consoleLogCount}`;
}
// Initialize resize handle
function initializeResizeHandle() {
const resizeHandle = document.getElementById('resize-handle');
const detailContainer = document.getElementById('detail-container');
let isResizing = false;
let startY = 0;
let startHeight = 0;
resizeHandle.addEventListener('mousedown', (e) => {
isResizing = true;
startY = e.clientY;
startHeight = detailContainer.offsetHeight;
document.body.style.cursor = 'ns-resize';
e.preventDefault();
});
document.addEventListener('mousemove', (e) => {
if (!isResizing) return;
const deltaY = startY - e.clientY;
const newHeight = Math.min(Math.max(100, startHeight + deltaY), 500);
detailContainer.style.height = `${newHeight}px`;
});
document.addEventListener('mouseup', () => {
isResizing = false;
document.body.style.cursor = '';
});
}
// Initialize when DOM is ready
document.addEventListener('DOMContentLoaded', initializeUI);