mirror of
https://github.com/actions/runner.git
synced 2026-01-20 19:41:40 +08:00
wip
This commit is contained in:
@@ -305,3 +305,33 @@ html[data-color-mode="light"] .dap-repl-output,
|
|||||||
html[data-color-mode="light"] .dap-repl-input input {
|
html[data-color-mode="light"] .dap-repl-input input {
|
||||||
background-color: #f6f8fa !important;
|
background-color: #f6f8fa !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Debug Button in Header */
|
||||||
|
.dap-debug-btn-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dap-debug-btn {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dap-debug-btn.selected {
|
||||||
|
background-color: var(--bgColor-accent-muted, #388bfd26);
|
||||||
|
border-color: var(--borderColor-accent-emphasis, #388bfd);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dap-debug-btn:hover:not(:disabled) {
|
||||||
|
background-color: var(--bgColor-neutral-muted, #6e768166);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Light mode for debug button */
|
||||||
|
[data-color-mode="light"] .dap-debug-btn.selected,
|
||||||
|
html[data-color-mode="light"] .dap-debug-btn.selected {
|
||||||
|
background-color: #ddf4ff;
|
||||||
|
border-color: #54aeff;
|
||||||
|
}
|
||||||
|
|||||||
@@ -19,6 +19,14 @@ function escapeHtml(text) {
|
|||||||
return div.innerHTML;
|
return div.innerHTML;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Strip result indicator suffix from step name
|
||||||
|
* e.g., "Run tests [running]" -> "Run tests"
|
||||||
|
*/
|
||||||
|
function stripResultIndicator(name) {
|
||||||
|
return name.replace(/\s*\[(running|success|failure|skipped|cancelled)\]$/i, '');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send DAP request to background script
|
* Send DAP request to background script
|
||||||
*/
|
*/
|
||||||
@@ -236,7 +244,9 @@ async function handleReplKeydown(e) {
|
|||||||
frameId: currentFrameId,
|
frameId: currentFrameId,
|
||||||
context: command.startsWith('!') ? 'repl' : 'watch',
|
context: command.startsWith('!') ? 'repl' : 'watch',
|
||||||
});
|
});
|
||||||
if (response.result) {
|
// Only show result if it's NOT an exit code summary
|
||||||
|
// (shell command output is already streamed via output events)
|
||||||
|
if (response.result && !/^\(exit code: -?\d+\)$/.test(response.result)) {
|
||||||
appendOutput(response.result, 'result');
|
appendOutput(response.result, 'result');
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -359,15 +369,26 @@ async function loadScopes(frameId) {
|
|||||||
scopesContainer.innerHTML = '<div class="color-fg-muted">Loading...</div>';
|
scopesContainer.innerHTML = '<div class="color-fg-muted">Loading...</div>';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
console.log('[Content] Loading scopes for frame:', frameId);
|
||||||
const response = await sendDapRequest('scopes', { frameId });
|
const response = await sendDapRequest('scopes', { frameId });
|
||||||
|
console.log('[Content] Scopes response:', response);
|
||||||
|
|
||||||
scopesContainer.innerHTML = '';
|
scopesContainer.innerHTML = '';
|
||||||
|
|
||||||
|
if (!response.scopes || response.scopes.length === 0) {
|
||||||
|
scopesContainer.innerHTML = '<div class="color-fg-muted">No scopes available</div>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (const scope of response.scopes) {
|
for (const scope of response.scopes) {
|
||||||
const node = createTreeNode(scope.name, scope.variablesReference, true);
|
console.log('[Content] Creating tree node for scope:', scope.name, 'variablesRef:', scope.variablesReference);
|
||||||
|
// Only mark as expandable if variablesReference > 0
|
||||||
|
const isExpandable = scope.variablesReference > 0;
|
||||||
|
const node = createTreeNode(scope.name, scope.variablesReference, isExpandable);
|
||||||
scopesContainer.appendChild(node);
|
scopesContainer.appendChild(node);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
console.error('[Content] Failed to load scopes:', error);
|
||||||
scopesContainer.innerHTML = `<div class="color-fg-danger">Error: ${escapeHtml(error.message)}</div>`;
|
scopesContainer.innerHTML = `<div class="color-fg-danger">Error: ${escapeHtml(error.message)}</div>`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -476,24 +497,30 @@ async function handleStoppedEvent(body) {
|
|||||||
const currentFrame = stackTrace.stackFrames[0];
|
const currentFrame = stackTrace.stackFrames[0];
|
||||||
currentFrameId = currentFrame.id;
|
currentFrameId = currentFrame.id;
|
||||||
|
|
||||||
// Find the step element and move pane
|
// Strip result indicator from step name for DOM lookup
|
||||||
const stepName = currentFrame.name;
|
// e.g., "Run tests [running]" -> "Run tests"
|
||||||
const stepElement = findStepByName(stepName);
|
const rawStepName = stripResultIndicator(currentFrame.name);
|
||||||
|
let stepElement = findStepByName(rawStepName);
|
||||||
|
|
||||||
|
if (!stepElement) {
|
||||||
|
// Fallback: use step index
|
||||||
|
// Note: GitHub Actions UI shows "Set up job" at index 0, which is not a real workflow step
|
||||||
|
// DAP uses 1-based frame IDs, so frame ID 1 maps to UI step index 1 (skipping "Set up job")
|
||||||
|
const steps = getAllSteps();
|
||||||
|
const adjustedIndex = currentFrame.id; // 1-based, happens to match after skipping "Set up job"
|
||||||
|
if (adjustedIndex > 0 && adjustedIndex < steps.length) {
|
||||||
|
stepElement = steps[adjustedIndex];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (stepElement) {
|
if (stepElement) {
|
||||||
moveDebuggerPane(stepElement, stepName);
|
moveDebuggerPane(stepElement, rawStepName);
|
||||||
} else {
|
|
||||||
// Try to find by frame id as step index
|
|
||||||
const steps = getAllSteps();
|
|
||||||
if (steps[currentFrame.id]) {
|
|
||||||
moveDebuggerPane(steps[currentFrame.id], stepName);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update step counter
|
// Update step counter
|
||||||
const counter = debuggerPane?.querySelector('.dap-step-counter');
|
const counter = debuggerPane?.querySelector('.dap-step-counter');
|
||||||
if (counter) {
|
if (counter) {
|
||||||
counter.textContent = `Step ${currentFrame.id + 1} of ${stackTrace.stackFrames.length}`;
|
counter.textContent = `Step ${currentFrame.id} of ${stackTrace.stackFrames.length}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load scopes
|
// Load scopes
|
||||||
@@ -599,6 +626,56 @@ 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');
|
||||||
|
if (!container || container.querySelector('.dap-debug-btn-container')) {
|
||||||
|
return; // Already injected or container not found
|
||||||
|
}
|
||||||
|
|
||||||
|
const buttonContainer = document.createElement('div');
|
||||||
|
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
|
||||||
|
</button>
|
||||||
|
`;
|
||||||
|
|
||||||
|
const button = buttonContainer.querySelector('button');
|
||||||
|
button.addEventListener('click', () => {
|
||||||
|
let pane = document.querySelector('.dap-debugger-pane');
|
||||||
|
if (pane) {
|
||||||
|
// Toggle visibility
|
||||||
|
pane.hidden = !pane.hidden;
|
||||||
|
button.classList.toggle('selected', !pane.hidden);
|
||||||
|
} else {
|
||||||
|
// Create and show pane
|
||||||
|
pane = injectDebuggerPane();
|
||||||
|
if (pane) {
|
||||||
|
button.classList.add('selected');
|
||||||
|
// Check connection status after creating pane
|
||||||
|
chrome.runtime.sendMessage({ type: 'get-status' }, (response) => {
|
||||||
|
if (response && response.status) {
|
||||||
|
handleStatusChange(response.status);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Insert at the beginning of the container
|
||||||
|
container.insertBefore(buttonContainer, container.firstChild);
|
||||||
|
console.log('[Content] Debug button injected');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize content script
|
* Initialize content script
|
||||||
*/
|
*/
|
||||||
@@ -614,21 +691,30 @@ function init() {
|
|||||||
const steps = getAllSteps();
|
const steps = getAllSteps();
|
||||||
if (steps.length > 0) {
|
if (steps.length > 0) {
|
||||||
observer.disconnect();
|
observer.disconnect();
|
||||||
console.log('[Content] Steps found, injecting debugger pane');
|
console.log('[Content] Steps found, injecting debug button');
|
||||||
injectDebuggerPane();
|
injectDebugButton();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
observer.observe(document.body, { childList: true, subtree: true });
|
observer.observe(document.body, { childList: true, subtree: true });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inject debugger pane
|
// Inject debug button in header (user can click to show debugger pane)
|
||||||
injectDebuggerPane();
|
injectDebugButton();
|
||||||
|
|
||||||
// Check current connection status
|
// Check current connection status
|
||||||
chrome.runtime.sendMessage({ type: 'get-status' }, (response) => {
|
chrome.runtime.sendMessage({ type: 'get-status' }, (response) => {
|
||||||
if (response && response.status) {
|
if (response && response.status) {
|
||||||
handleStatusChange(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');
|
||||||
|
if (!pane) {
|
||||||
|
injectDebuggerPane();
|
||||||
|
const btn = document.querySelector('.dap-debug-btn');
|
||||||
|
if (btn) btn.classList.add('selected');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user