extension ui improvements

This commit is contained in:
Francesco Renzi
2026-01-21 21:29:35 +00:00
committed by GitHub
parent 11ca211a3a
commit 38514d5278
3 changed files with 908 additions and 198 deletions

View File

@@ -2,7 +2,7 @@
* Content Script - Debugger UI
*
* Injects the debugger pane into GitHub Actions job pages and handles
* all UI interactions.
* all UI interactions. Supports two layout modes: sidebar and bottom panel.
*/
// State
@@ -11,6 +11,12 @@ let currentFrameId = 0;
let isConnected = false;
let replHistory = [];
let replHistoryIndex = -1;
let currentLayout = 'bottom'; // 'bottom' | 'sidebar'
let currentStepElement = null; // Track current step for breakpoint indicator
// Layout constants
const BOTTOM_PANEL_HEIGHT = 350;
const SIDEBAR_WIDTH = 350;
// HTML escape helper
function escapeHtml(text) {
@@ -27,6 +33,26 @@ function stripResultIndicator(name) {
return name.replace(/\s*\[(running|success|failure|skipped|cancelled)\]$/i, '');
}
/**
* Load layout preference from storage
*/
function loadLayoutPreference() {
return new Promise((resolve) => {
chrome.storage.local.get(['debuggerLayout'], (data) => {
currentLayout = data.debuggerLayout || 'bottom';
resolve(currentLayout);
});
});
}
/**
* Save layout preference to storage
*/
function saveLayoutPreference(layout) {
currentLayout = layout;
chrome.storage.local.set({ debuggerLayout: layout });
}
/**
* Send DAP request to background script
*/
@@ -84,33 +110,93 @@ function getAllSteps() {
}
/**
* Create the debugger pane HTML
* SVG Icons
*/
function createDebuggerPaneHTML() {
const Icons = {
bug: `<svg class="octicon" viewBox="0 0 16 16" width="16" height="16">
<path fill="currentColor" d="M4.72.22a.75.75 0 0 1 1.06 0l1 1a.75.75 0 0 1-1.06 1.06l-.22-.22-.22.22a.75.75 0 0 1-1.06-1.06l1-1Z"/>
<path fill="currentColor" d="M11.28.22a.75.75 0 0 0-1.06 0l-1 1a.75.75 0 0 0 1.06 1.06l.22-.22.22.22a.75.75 0 0 0 1.06-1.06l-1-1Z"/>
<path fill="currentColor" d="M8 4a4 4 0 0 0-4 4v1h1v2.5a2.5 2.5 0 0 0 2.5 2.5h1a2.5 2.5 0 0 0 2.5-2.5V9h1V8a4 4 0 0 0-4-4Z"/>
<path fill="currentColor" d="M5 9H3.5a.5.5 0 0 0-.5.5v2a.5.5 0 0 0 .5.5H5V9ZM11 9h1.5a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-.5.5H11V9Z"/>
</svg>`,
close: `<svg viewBox="0 0 16 16" width="16" height="16">
<path fill="currentColor" d="M3.72 3.72a.75.75 0 0 1 1.06 0L8 6.94l3.22-3.22a.749.749 0 0 1 1.275.326.749.749 0 0 1-.215.734L9.06 8l3.22 3.22a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215L8 9.06l-3.22 3.22a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042L6.94 8 3.72 4.78a.75.75 0 0 1 0-1.06Z"/>
</svg>`,
layoutBottom: `<svg viewBox="0 0 16 16" width="16" height="16">
<path fill="currentColor" d="M1.75 1h12.5c.966 0 1.75.784 1.75 1.75v10.5A1.75 1.75 0 0 1 14.25 15H1.75A1.75 1.75 0 0 1 0 13.25V2.75C0 1.784.784 1 1.75 1Zm0 1.5a.25.25 0 0 0-.25.25v6h13v-6a.25.25 0 0 0-.25-.25H1.75Zm-.25 10.75c0 .138.112.25.25.25h12.5a.25.25 0 0 0 .25-.25v-3h-13v3Z"/>
</svg>`,
layoutSidebar: `<svg viewBox="0 0 16 16" width="16" height="16">
<path fill="currentColor" d="M1.75 1h12.5c.966 0 1.75.784 1.75 1.75v10.5A1.75 1.75 0 0 1 14.25 15H1.75A1.75 1.75 0 0 1 0 13.25V2.75C0 1.784.784 1 1.75 1ZM1.5 2.75v10.5c0 .138.112.25.25.25h8.5V2.5h-8.5a.25.25 0 0 0-.25.25Zm10.25 10.75h2.5a.25.25 0 0 0 .25-.25V2.75a.25.25 0 0 0-.25-.25h-2.5v11Z"/>
</svg>`,
reverseContinue: `<svg viewBox="0 0 16 16" width="16" height="16"><path fill="currentColor" d="M2 2v12h2V8.5l5 4V8.5l5 4V2.5l-5 4V2.5l-5 4V2z"/></svg>`,
stepBack: `<svg viewBox="0 0 16 16" width="16" height="16"><path fill="currentColor" d="M2 2v12h2V2H2zm3 6 7 5V3L5 8z"/></svg>`,
continue: `<svg viewBox="0 0 16 16" width="16" height="16"><path fill="currentColor" d="M4 2l10 6-10 6z"/></svg>`,
stepForward: `<svg viewBox="0 0 16 16" width="16" height="16"><path fill="currentColor" d="M12 2v12h2V2h-2zM2 3l7 5-7 5V3z"/></svg>`,
};
/**
* Create control buttons HTML
*/
function createControlButtonsHTML(compact = false) {
const btnClass = compact ? 'btn-sm' : 'btn-sm';
return `
<button class="btn ${btnClass} dap-control-btn" data-action="reverseContinue" title="Reverse Continue (go to first checkpoint)" disabled>
${Icons.reverseContinue}
</button>
<button class="btn ${btnClass} dap-control-btn" data-action="stepBack" title="Step Back" disabled>
${Icons.stepBack}
</button>
<button class="btn ${btnClass} btn-primary dap-control-btn" data-action="continue" title="Continue" disabled>
${Icons.continue}
</button>
<button class="btn ${btnClass} dap-control-btn" data-action="next" title="Step to Next" disabled>
${Icons.stepForward}
</button>
`;
}
/**
* Create the bottom panel HTML structure
*/
function createBottomPaneHTML() {
return `
<div class="dap-header d-flex flex-items-center p-2 border-bottom">
<svg class="octicon mr-2" viewBox="0 0 16 16" width="16" height="16">
<path fill="currentColor" d="M4.72.22a.75.75 0 0 1 1.06 0l1 1a.75.75 0 0 1-1.06 1.06l-.22-.22-.22.22a.75.75 0 0 1-1.06-1.06l1-1Z"/>
<path fill="currentColor" d="M11.28.22a.75.75 0 0 0-1.06 0l-1 1a.75.75 0 0 0 1.06 1.06l.22-.22.22.22a.75.75 0 0 0 1.06-1.06l-1-1Z"/>
<path fill="currentColor" d="M8 4a4 4 0 0 0-4 4v1h1v2.5a2.5 2.5 0 0 0 2.5 2.5h1a2.5 2.5 0 0 0 2.5-2.5V9h1V8a4 4 0 0 0-4-4Z"/>
<path fill="currentColor" d="M5 9H3.5a.5.5 0 0 0-.5.5v2a.5.5 0 0 0 .5.5H5V9ZM11 9h1.5a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-.5.5H11V9Z"/>
</svg>
<span class="text-bold">Debugger</span>
<span class="dap-step-info color-fg-muted ml-2">Connecting...</span>
<span class="Label dap-status-label ml-auto">CONNECTING</span>
<div class="dap-header-right ml-auto d-flex flex-items-center">
<div class="dap-controls d-flex flex-items-center mr-3">
${createControlButtonsHTML(true)}
</div>
<div class="dap-layout-toggles d-flex flex-items-center mr-2">
<button class="btn btn-sm dap-layout-btn" data-layout="sidebar" title="Sidebar layout">
${Icons.layoutSidebar}
</button>
<button class="btn btn-sm dap-layout-btn active" data-layout="bottom" title="Bottom panel layout">
${Icons.layoutBottom}
</button>
</div>
<button class="btn btn-sm dap-close-btn" title="Close debugger">
${Icons.close}
</button>
</div>
</div>
<div class="dap-content d-flex" style="height: 300px;">
<div class="dap-content d-flex">
<!-- Scopes Panel -->
<div class="dap-scopes border-right overflow-auto" style="width: 33%;">
<div class="dap-scope-header p-2 text-bold border-bottom">Variables</div>
<div class="dap-scopes border-right overflow-auto">
<div class="dap-scope-header p-2 text-bold border-bottom d-flex flex-items-center">
<span>Variables</span>
</div>
<div class="dap-scope-tree p-2">
<div class="color-fg-muted">Connect to view variables</div>
</div>
</div>
<!-- REPL Console -->
<div class="dap-repl d-flex flex-column" style="width: 67%;">
<div class="dap-repl d-flex flex-column">
<div class="dap-repl-header p-2 text-bold border-bottom">Console</div>
<div class="dap-repl-output overflow-auto flex-auto p-2 text-mono text-small">
<div class="color-fg-muted">Welcome to Actions DAP Debugger</div>
@@ -123,24 +209,60 @@ function createDebuggerPaneHTML() {
</div>
</div>
</div>
`;
}
/**
* Create the sidebar panel HTML structure
*/
function createSidebarPaneHTML() {
return `
<div class="dap-header d-flex flex-items-center p-2 border-bottom">
<span class="text-bold">Debugger</span>
<div class="dap-header-right ml-auto d-flex flex-items-center">
<div class="dap-layout-toggles d-flex flex-items-center mr-2">
<button class="btn btn-sm dap-layout-btn active" data-layout="sidebar" title="Sidebar layout">
${Icons.layoutSidebar}
</button>
<button class="btn btn-sm dap-layout-btn" data-layout="bottom" title="Bottom panel layout">
${Icons.layoutBottom}
</button>
</div>
<button class="btn btn-sm dap-close-btn" title="Close debugger">
${Icons.close}
</button>
</div>
</div>
<!-- Scopes Panel -->
<div class="dap-scopes overflow-auto border-bottom">
<div class="dap-scope-header p-2 text-bold border-bottom d-flex flex-items-center">
<span>Variables</span>
</div>
<div class="dap-scope-tree p-2">
<div class="color-fg-muted">Connect to view variables</div>
</div>
</div>
<!-- REPL Console -->
<div class="dap-repl d-flex flex-column">
<div class="dap-repl-header p-2 text-bold border-bottom">Console</div>
<div class="dap-repl-output overflow-auto flex-auto p-2 text-mono text-small">
<div class="color-fg-muted">Welcome to Actions DAP Debugger</div>
<div class="color-fg-muted">Enter expressions like: \${{ github.ref }}</div>
<div class="color-fg-muted">Or shell commands: !ls -la</div>
</div>
<div class="dap-repl-input border-top p-2">
<input type="text" class="form-control input-sm text-mono"
placeholder="Enter expression or !command" disabled>
</div>
</div>
<!-- Control buttons -->
<div class="dap-controls d-flex flex-items-center p-2 border-top">
<button class="btn btn-sm mr-2" data-action="reverseContinue" title="Reverse Continue (go to first checkpoint)" disabled>
<svg viewBox="0 0 16 16" width="16" height="16"><path fill="currentColor" d="M2 2v12h2V8.5l5 4V8.5l5 4V2.5l-5 4V2.5l-5 4V2z"/></svg>
</button>
<button class="btn btn-sm mr-2" data-action="stepBack" title="Step Back" disabled>
<svg viewBox="0 0 16 16" width="16" height="16"><path fill="currentColor" d="M2 2v12h2V2H2zm3 6 7 5V3L5 8z"/></svg>
</button>
<button class="btn btn-sm btn-primary mr-2" data-action="continue" title="Continue" disabled>
<svg viewBox="0 0 16 16" width="16" height="16"><path fill="currentColor" d="M4 2l10 6-10 6z"/></svg>
</button>
<button class="btn btn-sm mr-2" data-action="next" title="Step to Next" disabled>
<svg viewBox="0 0 16 16" width="16" height="16"><path fill="currentColor" d="M2 3l7 5-7 5V3zm7 5l5 0V2h2v12h-2V8.5l-5 0z"/></svg>
</button>
<span class="dap-step-counter color-fg-muted ml-auto text-small">
Not connected
</span>
<div class="dap-controls d-flex flex-items-center justify-content-center p-2 border-top">
${createControlButtonsHTML()}
</div>
`;
}
@@ -148,49 +270,126 @@ function createDebuggerPaneHTML() {
/**
* Inject debugger pane into the page
*/
function injectDebuggerPane() {
function injectDebuggerPane(layout = null) {
// Remove existing pane if any
const existing = document.querySelector('.dap-debugger-pane');
if (existing) existing.remove();
// Find where to inject
const stepsContainer = document.querySelector('check-steps');
if (!stepsContainer) {
console.warn('[Content] No check-steps container found');
return null;
}
const targetLayout = layout || currentLayout;
// Create pane
const pane = document.createElement('div');
pane.className = 'dap-debugger-pane mx-2 mb-2 border rounded-2';
pane.innerHTML = createDebuggerPaneHTML();
pane.className = `dap-debugger-pane dap-debugger-${targetLayout}`;
// Insert before the first real workflow step (skip "Set up job" at index 0)
const steps = stepsContainer.querySelectorAll('check-step');
const targetStep = steps.length > 1 ? steps[1] : stepsContainer.firstChild;
stepsContainer.insertBefore(pane, targetStep);
if (targetLayout === 'bottom') {
pane.innerHTML = createBottomPaneHTML();
} else {
pane.innerHTML = createSidebarPaneHTML();
}
// Append to body for fixed positioning
document.body.appendChild(pane);
// Add body padding class for bottom layout to ensure content is scrollable
if (targetLayout === 'bottom') {
document.body.classList.add('dap-bottom-panel-active');
} else {
document.body.classList.remove('dap-bottom-panel-active');
}
// Setup event handlers
setupPaneEventHandlers(pane);
debuggerPane = pane;
currentLayout = targetLayout;
// Update layout toggle button states
updateLayoutToggleButtons();
return pane;
}
/**
* Move debugger pane to before a specific step
* Switch between layouts
*/
function moveDebuggerPane(stepElement, stepName) {
if (!debuggerPane || !stepElement) return;
function switchLayout(newLayout) {
if (newLayout === currentLayout) return;
// Move the pane
stepElement.parentNode.insertBefore(debuggerPane, stepElement);
// Store current state before switching
const wasConnected = isConnected;
const replOutputContent = debuggerPane?.querySelector('.dap-repl-output')?.innerHTML;
const scopeTreeContent = debuggerPane?.querySelector('.dap-scope-tree')?.innerHTML;
// Update step info
const stepInfo = debuggerPane.querySelector('.dap-step-info');
if (stepInfo) {
stepInfo.textContent = `Paused before: ${stepName}`;
// Remove old pane
if (debuggerPane) {
debuggerPane.remove();
debuggerPane = null;
}
// Save preference and create new pane
saveLayoutPreference(newLayout);
const pane = injectDebuggerPane(newLayout);
// Update body padding class based on new layout
if (newLayout === 'bottom') {
document.body.classList.add('dap-bottom-panel-active');
} else {
document.body.classList.remove('dap-bottom-panel-active');
}
// Restore state
if (replOutputContent) {
const output = pane.querySelector('.dap-repl-output');
if (output) output.innerHTML = replOutputContent;
}
if (scopeTreeContent) {
const scopeTree = pane.querySelector('.dap-scope-tree');
if (scopeTree) scopeTree.innerHTML = scopeTreeContent;
}
// Re-enable controls if connected
if (wasConnected) {
enableControls(true);
}
// Update debug button state
const btn = document.querySelector('.dap-debug-btn');
if (btn) btn.classList.add('selected');
// Update layout toggle button states
updateLayoutToggleButtons();
}
/**
* Update layout toggle button active states
*/
function updateLayoutToggleButtons() {
if (!debuggerPane) return;
debuggerPane.querySelectorAll('.dap-layout-btn').forEach((btn) => {
const layout = btn.dataset.layout;
btn.classList.toggle('active', layout === currentLayout);
});
}
/**
* Close the debugger pane
*/
function closeDebuggerPane() {
// Clear breakpoint indicator
clearBreakpointIndicator();
// Remove body padding class
document.body.classList.remove('dap-bottom-panel-active');
if (debuggerPane) {
debuggerPane.remove();
debuggerPane = null;
}
// Update debug button state
const btn = document.querySelector('.dap-debug-btn');
if (btn) btn.classList.remove('selected');
}
/**
@@ -215,6 +414,22 @@ function setupPaneEventHandlers(pane) {
});
});
// Layout toggle buttons
pane.querySelectorAll('.dap-layout-btn').forEach((btn) => {
btn.addEventListener('click', () => {
const layout = btn.dataset.layout;
if (layout && layout !== currentLayout) {
switchLayout(layout);
}
});
});
// Close button
const closeBtn = pane.querySelector('.dap-close-btn');
if (closeBtn) {
closeBtn.addEventListener('click', closeDebuggerPane);
}
// REPL input
const input = pane.querySelector('.dap-repl-input input');
if (input) {
@@ -299,7 +514,7 @@ function appendOutput(text, type) {
function enableControls(enabled) {
if (!debuggerPane) return;
debuggerPane.querySelectorAll('.dap-controls button').forEach((btn) => {
debuggerPane.querySelectorAll('.dap-control-btn').forEach((btn) => {
btn.disabled = !enabled;
});
@@ -315,37 +530,42 @@ function enableControls(enabled) {
function updateStatus(status, extra) {
if (!debuggerPane) return;
const label = debuggerPane.querySelector('.dap-status-label');
if (label) {
label.textContent = status;
label.className = 'Label dap-status-label ml-auto ';
switch (status) {
case 'PAUSED':
label.classList.add('Label--attention');
break;
case 'RUNNING':
label.classList.add('Label--success');
break;
case 'TERMINATED':
case 'DISCONNECTED':
label.classList.add('Label--secondary');
break;
case 'ERROR':
label.classList.add('Label--danger');
break;
default:
label.classList.add('Label--secondary');
}
// Update step info text
const stepInfo = debuggerPane.querySelector('.dap-step-info');
if (stepInfo && extra) {
stepInfo.textContent = extra;
}
}
// Update step counter if extra info provided
if (extra) {
const counter = debuggerPane.querySelector('.dap-step-counter');
if (counter) {
counter.textContent = extra;
}
/**
* Update breakpoint indicator - highlights the current step
*/
function updateBreakpointIndicator(stepElement) {
// Clear previous indicator
clearBreakpointIndicator();
if (!stepElement) return;
// Add indicator class to current step
stepElement.classList.add('dap-current-step');
currentStepElement = stepElement;
// Scroll step into view if needed
stepElement.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
}
/**
* Clear breakpoint indicator from all steps
*/
function clearBreakpointIndicator() {
if (currentStepElement) {
currentStepElement.classList.remove('dap-current-step');
currentStepElement = null;
}
// Also clear any other steps that might have the class
document.querySelectorAll('.dap-current-step').forEach((el) => {
el.classList.remove('dap-current-step');
});
}
/**
@@ -396,7 +616,7 @@ function createTreeNode(name, variablesReference, isExpandable, value) {
// Expand icon
const expandIcon = document.createElement('span');
expandIcon.className = 'dap-expand-icon';
expandIcon.textContent = isExpandable ? '\u25B6' : ' '; // or space
expandIcon.textContent = isExpandable ? '\u25B6' : ' '; // triangleright or space
content.appendChild(expandIcon);
// Name
@@ -433,7 +653,7 @@ async function toggleTreeNode(node) {
if (children) {
// Toggle visibility
children.hidden = !children.hidden;
expandIcon.textContent = children.hidden ? '\u25B6' : '\u25BC'; // ▶ or ▼
expandIcon.textContent = children.hidden ? '\u25B6' : '\u25BC'; // triangleright or triangledown
return;
}
@@ -461,10 +681,10 @@ async function toggleTreeNode(node) {
}
node.appendChild(childContainer);
expandIcon.textContent = '\u25BC'; //
expandIcon.textContent = '\u25BC'; // triangledown
} catch (error) {
console.error('[Content] Failed to load variables:', error);
expandIcon.textContent = '\u25B6'; //
expandIcon.textContent = '\u25B6'; // triangleright
}
}
@@ -487,7 +707,6 @@ async function handleStoppedEvent(body) {
currentFrameId = currentFrame.id;
// Strip result indicator from step name for DOM lookup
// e.g., "Run tests [running]" -> "Run tests"
const rawStepName = stripResultIndicator(currentFrame.name);
let stepElement = findStepByName(rawStepName);
@@ -497,14 +716,15 @@ async function handleStoppedEvent(body) {
stepElement = findStepByNumber(currentFrame.line + 1);
}
// Update breakpoint indicator
if (stepElement) {
moveDebuggerPane(stepElement, rawStepName);
updateBreakpointIndicator(stepElement);
}
// Update step counter
const counter = debuggerPane?.querySelector('.dap-step-counter');
if (counter) {
counter.textContent = `Step ${currentFrame.line || currentFrame.id} of ${stackTrace.stackFrames.length}`;
// Update step info in header
const stepInfo = debuggerPane?.querySelector('.dap-step-info');
if (stepInfo) {
stepInfo.textContent = `Paused before: ${rawStepName}`;
}
// Load scopes
@@ -531,13 +751,9 @@ function handleOutputEvent(body) {
*/
function handleTerminatedEvent() {
isConnected = false;
updateStatus('TERMINATED');
updateStatus('TERMINATED', 'Session ended');
enableControls(false);
const stepInfo = debuggerPane?.querySelector('.dap-step-info');
if (stepInfo) {
stepInfo.textContent = 'Session ended';
}
clearBreakpointIndicator();
}
/**
@@ -552,25 +768,24 @@ async function loadCurrentDebugState() {
const currentFrame = stackTrace.stackFrames[0];
currentFrameId = currentFrame.id;
// Move pane to current step
// Strip result indicator from step name for DOM lookup
const rawStepName = stripResultIndicator(currentFrame.name);
let stepElement = findStepByName(rawStepName);
if (!stepElement && currentFrame.line > 0) {
// Fallback: use step number from Line property
// Add 1 to account for "Set up job" which is always step 1 in GitHub UI but not in DAP
stepElement = findStepByNumber(currentFrame.line + 1);
}
// Update breakpoint indicator
if (stepElement) {
moveDebuggerPane(stepElement, rawStepName);
updateBreakpointIndicator(stepElement);
}
// Update step counter
const counter = debuggerPane.querySelector('.dap-step-counter');
if (counter) {
counter.textContent = `Step ${currentFrame.line || currentFrame.id} of ${stackTrace.stackFrames.length}`;
// Update step info in header
const stepInfo = debuggerPane.querySelector('.dap-step-info');
if (stepInfo) {
stepInfo.textContent = `Paused before: ${rawStepName}`;
}
// Load scopes
@@ -590,11 +805,7 @@ function handleStatusChange(status) {
switch (status) {
case 'connected':
isConnected = true;
updateStatus('CONNECTED');
const stepInfo = debuggerPane?.querySelector('.dap-step-info');
if (stepInfo) {
stepInfo.textContent = 'Waiting for debug event...';
}
updateStatus('CONNECTED', 'Waiting for debug event...');
break;
case 'paused':
@@ -606,20 +817,22 @@ function handleStatusChange(status) {
case 'running':
isConnected = true;
updateStatus('RUNNING');
updateStatus('RUNNING', 'Running...');
enableControls(false);
break;
case 'disconnected':
isConnected = false;
updateStatus('DISCONNECTED');
updateStatus('DISCONNECTED', 'Disconnected');
enableControls(false);
clearBreakpointIndicator();
break;
case 'error':
isConnected = false;
updateStatus('ERROR');
updateStatus('ERROR', 'Connection error');
enableControls(false);
clearBreakpointIndicator();
break;
}
}
@@ -656,7 +869,14 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
* Inject debug button into GitHub Actions UI header
*/
function injectDebugButton() {
const container = document.querySelector('.js-check-run-search');
// Try multiple possible container selectors
let container = document.querySelector('.js-check-run-search');
// Alternative: look for the header area with search box
if (!container) {
container = document.querySelector('.PageLayout-content .Box-header');
}
if (!container || container.querySelector('.dap-debug-btn-container')) {
return; // Already injected or container not found
}
@@ -665,25 +885,20 @@ function injectDebugButton() {
buttonContainer.className = 'ml-2 dap-debug-btn-container';
buttonContainer.innerHTML = `
<button type="button" class="btn btn-sm dap-debug-btn" title="Toggle DAP Debugger">
<svg viewBox="0 0 16 16" width="16" height="16" class="octicon mr-1" style="vertical-align: text-bottom;">
<path fill="currentColor" d="M4.72.22a.75.75 0 0 1 1.06 0l1 1a.75.75 0 0 1-1.06 1.06l-.22-.22-.22.22a.75.75 0 0 1-1.06-1.06l1-1Z"/>
<path fill="currentColor" d="M11.28.22a.75.75 0 0 0-1.06 0l-1 1a.75.75 0 0 0 1.06 1.06l.22-.22.22.22a.75.75 0 0 0 1.06-1.06l-1-1Z"/>
<path fill="currentColor" d="M8 4a4 4 0 0 0-4 4v1h1v2.5a2.5 2.5 0 0 0 2.5 2.5h1a2.5 2.5 0 0 0 2.5-2.5V9h1V8a4 4 0 0 0-4-4Z"/>
<path fill="currentColor" d="M5 9H3.5a.5.5 0 0 0-.5.5v2a.5.5 0 0 0 .5.5H5V9ZM11 9h1.5a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-.5.5H11V9Z"/>
</svg>
Debug
${Icons.bug}
<span class="ml-1 dap-debug-btn-text">Debug</span>
</button>
`;
const button = buttonContainer.querySelector('button');
button.addEventListener('click', () => {
button.addEventListener('click', async () => {
let pane = document.querySelector('.dap-debugger-pane');
if (pane) {
// Toggle visibility
pane.hidden = !pane.hidden;
button.classList.toggle('selected', !pane.hidden);
// Close pane
closeDebuggerPane();
} else {
// Create and show pane
// Load preference and create pane
await loadLayoutPreference();
pane = injectDebuggerPane();
if (pane) {
button.classList.add('selected');
@@ -705,9 +920,12 @@ function injectDebugButton() {
/**
* Initialize content script
*/
function init() {
async function init() {
console.log('[Content] Actions DAP Debugger content script loaded');
// Load layout preference
await loadLayoutPreference();
// Check if we're on a job page
const steps = getAllSteps();
if (steps.length === 0) {
@@ -731,8 +949,6 @@ function init() {
// Check current connection status
chrome.runtime.sendMessage({ type: 'get-status' }, async (response) => {
if (response && response.status) {
handleStatusChange(response.status);
// If already connected/paused, auto-show the debugger pane
if (response.status === 'paused' || response.status === 'connected') {
const pane = document.querySelector('.dap-debugger-pane');
@@ -742,6 +958,8 @@ function init() {
if (btn) btn.classList.add('selected');
}
handleStatusChange(response.status);
// If already paused, load the current debug state
if (response.status === 'paused') {
await loadCurrentDebugState();