mirror of
https://github.com/actions/runner.git
synced 2026-01-23 04:51:23 +08:00
extension ui improvements
This commit is contained in:
@@ -1,25 +1,142 @@
|
||||
/**
|
||||
* Content Script Styles
|
||||
*
|
||||
*
|
||||
* Matches GitHub's Primer design system for seamless integration.
|
||||
* Uses CSS custom properties for light/dark mode support.
|
||||
*
|
||||
* Supports two layout modes:
|
||||
* - Bottom panel: Fixed at bottom of viewport (like browser DevTools)
|
||||
* - Sidebar: Fixed on right side of viewport
|
||||
*/
|
||||
|
||||
/* Debugger Pane Container */
|
||||
/* ==========================================================================
|
||||
Base Debugger Pane Styles
|
||||
========================================================================== */
|
||||
|
||||
.dap-debugger-pane {
|
||||
background-color: var(--bgColor-default, #0d1117);
|
||||
border-color: var(--borderColor-default, #30363d) !important;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif;
|
||||
font-size: 14px;
|
||||
z-index: 999;
|
||||
box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
/* Header */
|
||||
.dap-header {
|
||||
/* ==========================================================================
|
||||
Bottom Panel Layout
|
||||
========================================================================== */
|
||||
|
||||
/* Add padding to body when bottom panel is open so all content is accessible */
|
||||
body.dap-bottom-panel-active {
|
||||
padding-bottom: 360px !important; /* Slightly more than panel height for breathing room */
|
||||
}
|
||||
|
||||
.dap-debugger-bottom {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 350px;
|
||||
border-top: 1px solid var(--borderColor-default, #30363d);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.dap-debugger-bottom .dap-header {
|
||||
background-color: var(--bgColor-muted, #161b22);
|
||||
flex-shrink: 0;
|
||||
padding: 8px 12px;
|
||||
}
|
||||
|
||||
.dap-header .octicon {
|
||||
color: var(--fgColor-muted, #8b949e);
|
||||
.dap-debugger-bottom .dap-content {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.dap-debugger-bottom .dap-scopes {
|
||||
width: 33%;
|
||||
min-width: 200px;
|
||||
max-width: 400px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-right: 1px solid var(--borderColor-default, #30363d);
|
||||
}
|
||||
|
||||
.dap-debugger-bottom .dap-repl {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.dap-debugger-bottom .dap-controls {
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Sidebar Layout
|
||||
========================================================================== */
|
||||
|
||||
.dap-debugger-sidebar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 350px;
|
||||
border-left: 1px solid var(--borderColor-default, #30363d);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
box-shadow: -2px 0 10px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.dap-debugger-sidebar .dap-header {
|
||||
background-color: var(--bgColor-muted, #161b22);
|
||||
flex-shrink: 0;
|
||||
padding: 8px 12px;
|
||||
}
|
||||
|
||||
.dap-debugger-sidebar .dap-scopes {
|
||||
flex: 0 0 auto;
|
||||
max-height: 40%;
|
||||
min-height: 150px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-bottom: 1px solid var(--borderColor-default, #30363d);
|
||||
}
|
||||
|
||||
.dap-debugger-sidebar .dap-repl {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.dap-debugger-sidebar .dap-controls {
|
||||
background-color: var(--bgColor-muted, #161b22);
|
||||
flex-shrink: 0;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Header Styles
|
||||
========================================================================== */
|
||||
|
||||
.dap-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid var(--borderColor-default, #30363d);
|
||||
}
|
||||
|
||||
.dap-header .text-bold {
|
||||
font-weight: 600;
|
||||
color: var(--fgColor-default, #e6edf3);
|
||||
}
|
||||
|
||||
.dap-header-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.dap-step-info {
|
||||
@@ -27,57 +144,135 @@
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
color: var(--fgColor-muted, #8b949e);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
/* Status Labels */
|
||||
.dap-status-label {
|
||||
flex-shrink: 0;
|
||||
/* ==========================================================================
|
||||
Layout Toggle Buttons
|
||||
========================================================================== */
|
||||
|
||||
.dap-layout-toggles {
|
||||
display: flex;
|
||||
gap: 2px;
|
||||
background-color: var(--bgColor-inset, #010409);
|
||||
border-radius: 6px;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.Label--attention {
|
||||
background-color: #9e6a03 !important;
|
||||
color: #ffffff !important;
|
||||
.dap-layout-btn {
|
||||
padding: 4px 6px !important;
|
||||
background-color: transparent !important;
|
||||
border: none !important;
|
||||
color: var(--fgColor-muted, #8b949e) !important;
|
||||
border-radius: 4px !important;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.Label--success {
|
||||
background-color: #238636 !important;
|
||||
color: #ffffff !important;
|
||||
.dap-layout-btn:hover {
|
||||
background-color: var(--bgColor-neutral-muted, #6e768166) !important;
|
||||
color: var(--fgColor-default, #e6edf3) !important;
|
||||
}
|
||||
|
||||
.dap-layout-btn.active {
|
||||
background-color: var(--bgColor-accent-muted, #388bfd26) !important;
|
||||
color: var(--fgColor-accent, #58a6ff) !important;
|
||||
}
|
||||
|
||||
.dap-layout-btn svg {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Close Button
|
||||
========================================================================== */
|
||||
|
||||
.dap-close-btn {
|
||||
padding: 4px 6px !important;
|
||||
background-color: transparent !important;
|
||||
border: none !important;
|
||||
color: var(--fgColor-muted, #8b949e) !important;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.Label--danger {
|
||||
background-color: #da3633 !important;
|
||||
color: #ffffff !important;
|
||||
border: none !important;
|
||||
.dap-close-btn:hover {
|
||||
background-color: var(--bgColor-danger-muted, #da363326) !important;
|
||||
color: var(--fgColor-danger, #f85149) !important;
|
||||
}
|
||||
|
||||
.Label--secondary {
|
||||
background-color: #30363d !important;
|
||||
color: #8b949e !important;
|
||||
border: none !important;
|
||||
.dap-close-btn svg {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
/* Content Area */
|
||||
.dap-content {
|
||||
min-height: 200px;
|
||||
max-height: 400px;
|
||||
/* ==========================================================================
|
||||
Control Buttons
|
||||
========================================================================== */
|
||||
|
||||
.dap-controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
/* Scopes Panel */
|
||||
.dap-control-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0 !important;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.dap-control-btn:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.dap-control-btn svg {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
display: block;
|
||||
margin: auto !important;
|
||||
}
|
||||
|
||||
.dap-control-btn:disabled {
|
||||
opacity: 0.4;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.dap-control-btn:not(:disabled):hover {
|
||||
background-color: var(--bgColor-accent-muted, #388bfd26);
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Scopes Panel
|
||||
========================================================================== */
|
||||
|
||||
.dap-scopes {
|
||||
border-color: var(--borderColor-default, #30363d) !important;
|
||||
min-width: 150px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.dap-scope-header {
|
||||
background-color: var(--bgColor-muted, #161b22);
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
flex-shrink: 0;
|
||||
padding: 8px;
|
||||
border-bottom: 1px solid var(--borderColor-default, #30363d);
|
||||
}
|
||||
|
||||
.dap-scope-tree {
|
||||
font-size: 12px;
|
||||
line-height: 1.6;
|
||||
padding: 8px;
|
||||
overflow-y: auto;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
/* Tree Nodes */
|
||||
@@ -123,16 +318,23 @@
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
/* REPL Console */
|
||||
/* ==========================================================================
|
||||
REPL Console
|
||||
========================================================================== */
|
||||
|
||||
.dap-repl {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.dap-repl-header {
|
||||
background-color: var(--bgColor-muted, #161b22);
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
flex-shrink: 0;
|
||||
padding: 8px;
|
||||
border-bottom: 1px solid var(--borderColor-default, #30363d);
|
||||
}
|
||||
|
||||
.dap-repl-output {
|
||||
@@ -143,7 +345,7 @@
|
||||
padding: 8px;
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
min-height: 100px;
|
||||
min-height: 60px;
|
||||
}
|
||||
|
||||
.dap-output-input {
|
||||
@@ -165,6 +367,8 @@
|
||||
/* REPL Input */
|
||||
.dap-repl-input {
|
||||
flex-shrink: 0;
|
||||
padding: 8px;
|
||||
border-top: 1px solid var(--borderColor-default, #30363d);
|
||||
}
|
||||
|
||||
.dap-repl-input input {
|
||||
@@ -174,6 +378,9 @@
|
||||
border-color: var(--borderColor-default, #30363d) !important;
|
||||
color: var(--fgColor-default, #e6edf3) !important;
|
||||
width: 100%;
|
||||
padding: 6px 8px;
|
||||
border-radius: 6px;
|
||||
border: 1px solid var(--borderColor-default, #30363d);
|
||||
}
|
||||
|
||||
.dap-repl-input input:focus {
|
||||
@@ -191,51 +398,112 @@
|
||||
color: var(--fgColor-muted, #8b949e);
|
||||
}
|
||||
|
||||
/* Control Buttons */
|
||||
.dap-controls {
|
||||
background-color: var(--bgColor-muted, #161b22);
|
||||
/* ==========================================================================
|
||||
Breakpoint Indicator
|
||||
========================================================================== */
|
||||
|
||||
/* Current step indicator - shown on the step where debugger is paused
|
||||
Breakpoints are BEFORE steps, so we indicate the boundary above the step */
|
||||
check-step.dap-current-step {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.dap-controls button {
|
||||
min-width: 32px;
|
||||
height: 28px;
|
||||
/* Highlight the step row with a subtle background and red accent line at TOP */
|
||||
check-step.dap-current-step > details,
|
||||
check-step.dap-current-step > div {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
check-step.dap-current-step > details > summary,
|
||||
check-step.dap-current-step > div:first-child {
|
||||
background: linear-gradient(90deg, rgba(248, 81, 73, 0.08) 0%, rgba(248, 81, 73, 0.02) 100%) !important;
|
||||
border-top: 2px solid #f85149 !important;
|
||||
}
|
||||
|
||||
/* Red accent bar at top of step - horizontal line showing "breakpoint here" */
|
||||
check-step.dap-current-step::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
height: 2px;
|
||||
background: linear-gradient(90deg, #f85149 0%, #da3633 100%);
|
||||
}
|
||||
|
||||
/* Triangle pointer at top of step - points to the boundary between steps */
|
||||
check-step.dap-current-step::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
right: -8px;
|
||||
top: 0;
|
||||
transform: translateY(-50%);
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-top: 8px solid transparent;
|
||||
border-bottom: 8px solid transparent;
|
||||
border-left: 10px solid #f85149;
|
||||
z-index: 100;
|
||||
filter: drop-shadow(2px 0 2px rgba(0, 0, 0, 0.2));
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Debug Button in Header
|
||||
========================================================================== */
|
||||
|
||||
.dap-debug-btn-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.dap-debug-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0 8px;
|
||||
gap: 4px;
|
||||
font-weight: 500;
|
||||
padding: 5px 12px;
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
.dap-controls button svg {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
.dap-debug-btn-text {
|
||||
font-size: var(--text-body-size-medium, .875rem);
|
||||
}
|
||||
|
||||
.dap-controls button:disabled {
|
||||
opacity: 0.4;
|
||||
cursor: not-allowed;
|
||||
.dap-debug-btn .octicon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.dap-controls button:not(:disabled):hover {
|
||||
.dap-debug-btn.selected {
|
||||
background-color: var(--bgColor-accent-muted, #388bfd26);
|
||||
border-color: var(--borderColor-accent-emphasis, #388bfd);
|
||||
color: var(--fgColor-accent, #58a6ff);
|
||||
}
|
||||
|
||||
.dap-step-counter {
|
||||
flex-shrink: 0;
|
||||
.dap-debug-btn:hover:not(:disabled) {
|
||||
background-color: var(--bgColor-neutral-muted, #6e768166);
|
||||
}
|
||||
|
||||
/* Utility Classes (in case GitHub's aren't loaded) */
|
||||
/* ==========================================================================
|
||||
Utility Classes
|
||||
========================================================================== */
|
||||
|
||||
.d-flex { display: flex; }
|
||||
.flex-column { flex-direction: column; }
|
||||
.flex-items-center { align-items: center; }
|
||||
.flex-auto { flex: 1 1 auto; }
|
||||
.justify-content-center { justify-content: center; }
|
||||
|
||||
.p-2 { padding: 8px; }
|
||||
.px-2 { padding-left: 8px; padding-right: 8px; }
|
||||
.mx-2 { margin-left: 8px; margin-right: 8px; }
|
||||
.mb-2 { margin-bottom: 8px; }
|
||||
.ml-1 { margin-left: 4px; }
|
||||
.ml-2 { margin-left: 8px; }
|
||||
.ml-3 { margin-left: 16px; }
|
||||
.mr-2 { margin-right: 8px; }
|
||||
.mr-3 { margin-right: 16px; }
|
||||
.ml-auto { margin-left: auto; }
|
||||
|
||||
.border { border: 1px solid var(--borderColor-default, #30363d); }
|
||||
@@ -253,11 +521,19 @@
|
||||
.color-fg-danger { color: var(--fgColor-danger, #f85149); }
|
||||
.color-fg-default { color: var(--fgColor-default, #e6edf3); }
|
||||
|
||||
/* Light mode overrides */
|
||||
/* ==========================================================================
|
||||
Light Mode Overrides
|
||||
========================================================================== */
|
||||
|
||||
@media (prefers-color-scheme: light) {
|
||||
.dap-debugger-pane {
|
||||
background-color: var(--bgColor-default, #ffffff);
|
||||
border-color: var(--borderColor-default, #d0d7de) !important;
|
||||
box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.dap-debugger-sidebar {
|
||||
box-shadow: -2px 0 10px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.dap-header,
|
||||
@@ -279,6 +555,12 @@
|
||||
.color-fg-muted {
|
||||
color: var(--fgColor-muted, #656d76);
|
||||
}
|
||||
|
||||
check-step.dap-current-step > details > summary,
|
||||
check-step.dap-current-step > div:first-child {
|
||||
background: linear-gradient(90deg, rgba(248, 81, 73, 0.1) 0%, rgba(248, 81, 73, 0.02) 100%) !important;
|
||||
border-top: 2px solid #f85149 !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Respect GitHub's color mode data attribute */
|
||||
@@ -286,6 +568,12 @@
|
||||
html[data-color-mode="light"] .dap-debugger-pane {
|
||||
background-color: #ffffff;
|
||||
border-color: #d0d7de !important;
|
||||
box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
[data-color-mode="light"] .dap-debugger-sidebar,
|
||||
html[data-color-mode="light"] .dap-debugger-sidebar {
|
||||
box-shadow: -2px 0 10px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
[data-color-mode="light"] .dap-header,
|
||||
@@ -306,27 +594,17 @@ html[data-color-mode="light"] .dap-repl-input input {
|
||||
background-color: #f6f8fa !important;
|
||||
}
|
||||
|
||||
/* Debug Button in Header */
|
||||
.dap-debug-btn-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
[data-color-mode="light"] .dap-layout-toggles,
|
||||
html[data-color-mode="light"] .dap-layout-toggles {
|
||||
background-color: #eaeef2;
|
||||
}
|
||||
|
||||
.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);
|
||||
[data-color-mode="light"] check-step.dap-current-step > details > summary,
|
||||
[data-color-mode="light"] check-step.dap-current-step > div:first-child,
|
||||
html[data-color-mode="light"] check-step.dap-current-step > details > summary,
|
||||
html[data-color-mode="light"] check-step.dap-current-step > div:first-child {
|
||||
background: linear-gradient(90deg, rgba(248, 81, 73, 0.1) 0%, rgba(248, 81, 73, 0.02) 100%) !important;
|
||||
border-top: 2px solid #f85149 !important;
|
||||
}
|
||||
|
||||
/* Light mode for debug button */
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user