mirror of
https://github.com/actions/runner.git
synced 2026-01-23 04:51:23 +08:00
editing jobs
This commit is contained in:
@@ -613,3 +613,411 @@ html[data-color-mode="light"] .dap-debug-btn.selected {
|
||||
background-color: #ddf4ff;
|
||||
border-color: #54aeff;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Steps Panel
|
||||
========================================================================== */
|
||||
|
||||
.dap-steps-panel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-right: 1px solid var(--borderColor-default, #30363d);
|
||||
min-width: 200px;
|
||||
max-width: 280px;
|
||||
width: 25%;
|
||||
}
|
||||
|
||||
.dap-debugger-sidebar .dap-steps-panel {
|
||||
border-right: none;
|
||||
border-bottom: 1px solid var(--borderColor-default, #30363d);
|
||||
max-width: none;
|
||||
width: auto;
|
||||
max-height: 35%;
|
||||
min-height: 120px;
|
||||
}
|
||||
|
||||
.dap-steps-header {
|
||||
background-color: var(--bgColor-muted, #161b22);
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.dap-steps-list {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
.dap-steps-empty {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.dap-steps-footer {
|
||||
flex-shrink: 0;
|
||||
background-color: var(--bgColor-muted, #161b22);
|
||||
}
|
||||
|
||||
.dap-add-step-btn,
|
||||
.dap-export-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.dap-add-step-btn svg,
|
||||
.dap-export-btn svg {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
}
|
||||
|
||||
.dap-add-step-btn:disabled,
|
||||
.dap-export-btn:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
/* Step Items */
|
||||
.dap-step-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 4px 8px;
|
||||
font-size: 12px;
|
||||
cursor: default;
|
||||
gap: 4px;
|
||||
border-left: 3px solid transparent;
|
||||
}
|
||||
|
||||
.dap-step-item:hover {
|
||||
background-color: var(--bgColor-muted, #161b22);
|
||||
}
|
||||
|
||||
.dap-step-item.completed {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.dap-step-item.current {
|
||||
background-color: var(--bgColor-accent-muted, #388bfd26);
|
||||
border-left-color: var(--fgColor-accent, #58a6ff);
|
||||
}
|
||||
|
||||
.dap-step-item.pending[data-editable="true"] {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.dap-step-status-icon {
|
||||
flex-shrink: 0;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.dap-step-status-icon svg {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
}
|
||||
|
||||
.dap-step-status-icon.completed {
|
||||
color: var(--fgColor-success, #3fb950);
|
||||
}
|
||||
|
||||
.dap-step-status-icon.current {
|
||||
color: var(--fgColor-accent, #58a6ff);
|
||||
}
|
||||
|
||||
.dap-step-status-icon.pending {
|
||||
color: var(--fgColor-muted, #8b949e);
|
||||
}
|
||||
|
||||
.dap-step-index {
|
||||
color: var(--fgColor-muted, #8b949e);
|
||||
font-size: 11px;
|
||||
min-width: 20px;
|
||||
}
|
||||
|
||||
.dap-step-name {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
color: var(--fgColor-default, #e6edf3);
|
||||
}
|
||||
|
||||
.dap-step-badge {
|
||||
font-size: 10px;
|
||||
padding: 1px 4px;
|
||||
border-radius: 3px;
|
||||
font-weight: 500;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.dap-step-badge-added {
|
||||
background-color: var(--bgColor-success-muted, #2ea04326);
|
||||
color: var(--fgColor-success, #3fb950);
|
||||
}
|
||||
|
||||
.dap-step-badge-modified {
|
||||
background-color: var(--bgColor-attention-muted, #bb800926);
|
||||
color: var(--fgColor-attention, #d29922);
|
||||
}
|
||||
|
||||
.dap-step-type {
|
||||
color: var(--fgColor-muted, #8b949e);
|
||||
font-size: 10px;
|
||||
flex-shrink: 0;
|
||||
max-width: 80px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.dap-step-menu-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--fgColor-muted, #8b949e);
|
||||
cursor: pointer;
|
||||
padding: 2px 6px;
|
||||
border-radius: 3px;
|
||||
font-weight: bold;
|
||||
letter-spacing: 1px;
|
||||
opacity: 0;
|
||||
transition: opacity 0.1s;
|
||||
}
|
||||
|
||||
.dap-step-item:hover .dap-step-menu-btn {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.dap-step-menu-btn:hover {
|
||||
background-color: var(--bgColor-neutral-muted, #6e768166);
|
||||
color: var(--fgColor-default, #e6edf3);
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Context Menu
|
||||
========================================================================== */
|
||||
|
||||
.dap-context-menu {
|
||||
background-color: var(--bgColor-default, #0d1117);
|
||||
border: 1px solid var(--borderColor-default, #30363d);
|
||||
border-radius: 6px;
|
||||
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
|
||||
z-index: 10000;
|
||||
min-width: 140px;
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
.dap-context-menu-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 6px 12px;
|
||||
font-size: 12px;
|
||||
color: var(--fgColor-default, #e6edf3);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.dap-context-menu-item:hover {
|
||||
background-color: var(--bgColor-accent-muted, #388bfd26);
|
||||
}
|
||||
|
||||
.dap-context-menu-item.danger {
|
||||
color: var(--fgColor-danger, #f85149);
|
||||
}
|
||||
|
||||
.dap-context-menu-item.danger:hover {
|
||||
background-color: var(--bgColor-danger-muted, #da363326);
|
||||
}
|
||||
|
||||
.dap-context-menu-item svg {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
}
|
||||
|
||||
.dap-context-menu-divider {
|
||||
height: 1px;
|
||||
background-color: var(--borderColor-default, #30363d);
|
||||
margin: 4px 0;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Modal
|
||||
========================================================================== */
|
||||
|
||||
.dap-modal-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 10001;
|
||||
}
|
||||
|
||||
.dap-modal {
|
||||
background-color: var(--bgColor-default, #0d1117);
|
||||
border: 1px solid var(--borderColor-default, #30363d);
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
|
||||
width: 90%;
|
||||
max-width: 500px;
|
||||
max-height: 80vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.dap-modal-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 12px 16px;
|
||||
border-bottom: 1px solid var(--borderColor-default, #30363d);
|
||||
}
|
||||
|
||||
.dap-modal-header .text-bold {
|
||||
font-size: 14px;
|
||||
color: var(--fgColor-default, #e6edf3);
|
||||
}
|
||||
|
||||
.dap-modal-close-btn {
|
||||
background: none !important;
|
||||
border: none !important;
|
||||
color: var(--fgColor-muted, #8b949e) !important;
|
||||
padding: 4px !important;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.dap-modal-close-btn:hover {
|
||||
color: var(--fgColor-default, #e6edf3) !important;
|
||||
}
|
||||
|
||||
.dap-modal-close-btn svg {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.dap-modal-content {
|
||||
padding: 16px;
|
||||
overflow-y: auto;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.dap-modal-footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 8px;
|
||||
padding: 12px 16px;
|
||||
border-top: 1px solid var(--borderColor-default, #30363d);
|
||||
}
|
||||
|
||||
/* Form elements */
|
||||
.dap-form-group {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.dap-form-group:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.dap-label {
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: var(--fgColor-default, #e6edf3);
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.dap-modal .form-control {
|
||||
width: 100%;
|
||||
background-color: var(--bgColor-inset, #010409) !important;
|
||||
border-color: var(--borderColor-default, #30363d) !important;
|
||||
color: var(--fgColor-default, #e6edf3) !important;
|
||||
font-size: 12px;
|
||||
padding: 6px 8px;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.dap-modal .form-control:focus {
|
||||
border-color: var(--focus-outlineColor, #1f6feb) !important;
|
||||
outline: none;
|
||||
box-shadow: 0 0 0 3px rgba(31, 111, 235, 0.3);
|
||||
}
|
||||
|
||||
.dap-modal select.form-control {
|
||||
appearance: auto;
|
||||
}
|
||||
|
||||
.dap-modal textarea.form-control {
|
||||
font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
/* Export Modal */
|
||||
.dap-export-stats {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.dap-export-yaml {
|
||||
background-color: var(--bgColor-inset, #010409);
|
||||
border: 1px solid var(--borderColor-default, #30363d);
|
||||
border-radius: 6px;
|
||||
overflow: auto;
|
||||
max-height: 300px;
|
||||
}
|
||||
|
||||
.dap-yaml-content {
|
||||
margin: 0;
|
||||
padding: 12px;
|
||||
font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
|
||||
font-size: 11px;
|
||||
line-height: 1.5;
|
||||
color: var(--fgColor-default, #e6edf3);
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Light Mode Overrides for New Components
|
||||
========================================================================== */
|
||||
|
||||
[data-color-mode="light"] .dap-context-menu,
|
||||
html[data-color-mode="light"] .dap-context-menu {
|
||||
background-color: #ffffff;
|
||||
border-color: #d0d7de;
|
||||
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
[data-color-mode="light"] .dap-context-menu-item,
|
||||
html[data-color-mode="light"] .dap-context-menu-item {
|
||||
color: #1f2328;
|
||||
}
|
||||
|
||||
[data-color-mode="light"] .dap-modal,
|
||||
html[data-color-mode="light"] .dap-modal {
|
||||
background-color: #ffffff;
|
||||
border-color: #d0d7de;
|
||||
}
|
||||
|
||||
[data-color-mode="light"] .dap-modal-header .text-bold,
|
||||
html[data-color-mode="light"] .dap-modal-header .text-bold {
|
||||
color: #1f2328;
|
||||
}
|
||||
|
||||
[data-color-mode="light"] .dap-step-name,
|
||||
html[data-color-mode="light"] .dap-step-name {
|
||||
color: #1f2328;
|
||||
}
|
||||
|
||||
[data-color-mode="light"] .dap-export-yaml,
|
||||
html[data-color-mode="light"] .dap-export-yaml {
|
||||
background-color: #f6f8fa;
|
||||
}
|
||||
|
||||
[data-color-mode="light"] .dap-yaml-content,
|
||||
html[data-color-mode="light"] .dap-yaml-content {
|
||||
color: #1f2328;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,9 @@ let replHistory = [];
|
||||
let replHistoryIndex = -1;
|
||||
let currentLayout = 'bottom'; // 'bottom' | 'sidebar'
|
||||
let currentStepElement = null; // Track current step for breakpoint indicator
|
||||
let stepsList = []; // Cached steps from DAP
|
||||
let activeContextMenu = null; // Track open context menu
|
||||
let activeModal = null; // Track open modal
|
||||
|
||||
// Layout constants
|
||||
const BOTTOM_PANEL_HEIGHT = 350;
|
||||
@@ -132,6 +135,16 @@ const Icons = {
|
||||
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>`,
|
||||
plus: `<svg viewBox="0 0 16 16" width="16" height="16"><path fill="currentColor" d="M7.75 2a.75.75 0 0 1 .75.75V7h4.25a.75.75 0 0 1 0 1.5H8.5v4.25a.75.75 0 0 1-1.5 0V8.5H2.75a.75.75 0 0 1 0-1.5H7V2.75A.75.75 0 0 1 7.75 2Z"/></svg>`,
|
||||
download: `<svg viewBox="0 0 16 16" width="16" height="16"><path fill="currentColor" d="M2.75 14A1.75 1.75 0 0 1 1 12.25v-2.5a.75.75 0 0 1 1.5 0v2.5c0 .138.112.25.25.25h10.5a.25.25 0 0 0 .25-.25v-2.5a.75.75 0 0 1 1.5 0v2.5A1.75 1.75 0 0 1 13.25 14Zm-1.97-7.22a.75.75 0 0 0 1.06 1.06L7 2.69v8.56a.75.75 0 0 0 1.5 0V2.69l5.16 5.15a.75.75 0 1 0 1.06-1.06l-6.5-6.5a.75.75 0 0 0-1.06 0l-6.5 6.5Z" transform="rotate(180 8 8)"/></svg>`,
|
||||
check: `<svg viewBox="0 0 16 16" width="16" height="16"><path fill="currentColor" d="M13.78 4.22a.75.75 0 0 1 0 1.06l-7.25 7.25a.75.75 0 0 1-1.06 0L2.22 9.28a.751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018L6 10.94l6.72-6.72a.75.75 0 0 1 1.06 0Z"/></svg>`,
|
||||
play: `<svg viewBox="0 0 16 16" width="16" height="16"><path fill="currentColor" d="M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0ZM1.5 8a6.5 6.5 0 1 0 13 0 6.5 6.5 0 0 0-13 0Zm4.879-2.773 4.264 2.559a.25.25 0 0 1 0 .428l-4.264 2.559A.25.25 0 0 1 6 10.559V5.442a.25.25 0 0 1 .379-.215Z"/></svg>`,
|
||||
clock: `<svg viewBox="0 0 16 16" width="16" height="16"><path fill="currentColor" d="M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0ZM1.5 8a6.5 6.5 0 1 0 13 0 6.5 6.5 0 0 0-13 0Zm7-3.25v2.992l2.028.812a.75.75 0 0 1-.557 1.392l-2.5-1A.751.751 0 0 1 7 8.25v-3.5a.75.75 0 0 1 1.5 0Z"/></svg>`,
|
||||
pencil: `<svg viewBox="0 0 16 16" width="16" height="16"><path fill="currentColor" d="M11.013 1.427a1.75 1.75 0 0 1 2.474 0l1.086 1.086a1.75 1.75 0 0 1 0 2.474l-8.61 8.61c-.21.21-.47.364-.756.445l-3.251.93a.75.75 0 0 1-.927-.928l.929-3.25c.081-.286.235-.547.445-.758l8.61-8.61Zm.176 4.823L9.75 4.81l-6.286 6.287a.253.253 0 0 0-.064.108l-.558 1.953 1.953-.558a.253.253 0 0 0 .108-.064Zm1.238-3.763a.25.25 0 0 0-.354 0L10.811 3.75l1.439 1.44 1.263-1.263a.25.25 0 0 0 0-.354Z"/></svg>`,
|
||||
trash: `<svg viewBox="0 0 16 16" width="16" height="16"><path fill="currentColor" d="M11 1.75V3h2.25a.75.75 0 0 1 0 1.5H2.75a.75.75 0 0 1 0-1.5H5V1.75C5 .784 5.784 0 6.75 0h2.5C10.216 0 11 .784 11 1.75ZM4.496 6.675l.66 6.6a.25.25 0 0 0 .249.225h5.19a.25.25 0 0 0 .249-.225l.66-6.6a.75.75 0 0 1 1.492.149l-.66 6.6A1.748 1.748 0 0 1 10.595 15h-5.19a1.75 1.75 0 0 1-1.741-1.575l-.66-6.6a.75.75 0 1 1 1.492-.15ZM6.5 1.75V3h3V1.75a.25.25 0 0 0-.25-.25h-2.5a.25.25 0 0 0-.25.25Z"/></svg>`,
|
||||
arrowUp: `<svg viewBox="0 0 16 16" width="16" height="16"><path fill="currentColor" d="M3.47 7.78a.75.75 0 0 1 0-1.06l4.25-4.25a.75.75 0 0 1 1.06 0l4.25 4.25a.751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018L9 4.81v7.44a.75.75 0 0 1-1.5 0V4.81L4.53 7.78a.75.75 0 0 1-1.06 0Z"/></svg>`,
|
||||
arrowDown: `<svg viewBox="0 0 16 16" width="16" height="16"><path fill="currentColor" d="M13.03 8.22a.75.75 0 0 1 0 1.06l-4.25 4.25a.75.75 0 0 1-1.06 0L3.47 9.28a.751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018l2.97 2.97V3.75a.75.75 0 0 1 1.5 0v7.44l2.97-2.97a.75.75 0 0 1 1.06 0Z"/></svg>`,
|
||||
copy: `<svg viewBox="0 0 16 16" width="16" height="16"><path fill="currentColor" d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 0 1 0 1.5h-1.5a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-1.5a.75.75 0 0 1 1.5 0v1.5A1.75 1.75 0 0 1 9.25 16h-7.5A1.75 1.75 0 0 1 0 14.25ZM5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0 1 14.25 11h-7.5A1.75 1.75 0 0 1 5 9.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z"/></svg>`,
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -155,6 +168,31 @@ function createControlButtonsHTML(compact = false) {
|
||||
`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the steps panel HTML
|
||||
*/
|
||||
function createStepsPanelHTML() {
|
||||
return `
|
||||
<div class="dap-steps-panel">
|
||||
<div class="dap-steps-header p-2 text-bold border-bottom d-flex flex-items-center">
|
||||
<span>Steps</span>
|
||||
<button class="btn btn-sm dap-add-step-btn ml-auto" title="Add Step" disabled>
|
||||
${Icons.plus}
|
||||
</button>
|
||||
</div>
|
||||
<div class="dap-steps-list overflow-auto flex-auto">
|
||||
<div class="dap-steps-empty p-2 color-fg-muted">Connect to view steps</div>
|
||||
</div>
|
||||
<div class="dap-steps-footer p-2 border-top">
|
||||
<button class="btn btn-sm dap-export-btn" title="Export Changes" disabled>
|
||||
${Icons.download}
|
||||
<span class="ml-1">Export</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the bottom panel HTML structure
|
||||
*/
|
||||
@@ -185,6 +223,9 @@ function createBottomPaneHTML() {
|
||||
</div>
|
||||
|
||||
<div class="dap-content d-flex">
|
||||
<!-- Steps Panel -->
|
||||
${createStepsPanelHTML()}
|
||||
|
||||
<!-- Scopes Panel -->
|
||||
<div class="dap-scopes border-right overflow-auto">
|
||||
<div class="dap-scope-header p-2 text-bold border-bottom d-flex flex-items-center">
|
||||
@@ -236,6 +277,9 @@ function createSidebarPaneHTML() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Steps Panel -->
|
||||
${createStepsPanelHTML()}
|
||||
|
||||
<!-- 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">
|
||||
@@ -382,6 +426,10 @@ function closeDebuggerPane() {
|
||||
// Remove body padding class
|
||||
document.body.classList.remove('dap-bottom-panel-active');
|
||||
|
||||
// Close any open context menu or modal
|
||||
closeContextMenu();
|
||||
closeModal();
|
||||
|
||||
if (debuggerPane) {
|
||||
debuggerPane.remove();
|
||||
debuggerPane = null;
|
||||
@@ -392,6 +440,628 @@ function closeDebuggerPane() {
|
||||
if (btn) btn.classList.remove('selected');
|
||||
}
|
||||
|
||||
// ==========================================================================
|
||||
// Step Manipulation UI
|
||||
// ==========================================================================
|
||||
|
||||
/**
|
||||
* Get status icon for step
|
||||
*/
|
||||
function getStepStatusIcon(status) {
|
||||
switch (status) {
|
||||
case 'completed':
|
||||
return `<span class="dap-step-status-icon completed">${Icons.check}</span>`;
|
||||
case 'current':
|
||||
return `<span class="dap-step-status-icon current">${Icons.play}</span>`;
|
||||
case 'pending':
|
||||
default:
|
||||
return `<span class="dap-step-status-icon pending">${Icons.clock}</span>`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render step list from data
|
||||
*/
|
||||
function renderStepList(steps) {
|
||||
const container = debuggerPane?.querySelector('.dap-steps-list');
|
||||
if (!container) return;
|
||||
|
||||
if (!steps || steps.length === 0) {
|
||||
container.innerHTML = '<div class="dap-steps-empty p-2 color-fg-muted">No steps available</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
stepsList = steps;
|
||||
|
||||
const html = steps.map((step) => {
|
||||
const statusIcon = getStepStatusIcon(step.status);
|
||||
const changeBadge = step.change ? `<span class="dap-step-badge dap-step-badge-${step.change.toLowerCase()}">[${step.change}]</span>` : '';
|
||||
const typeLabel = step.type === 'uses' ? step.typeDetail || step.type : step.type;
|
||||
const isPending = step.status === 'pending';
|
||||
|
||||
return `
|
||||
<div class="dap-step-item ${step.status}" data-index="${step.index}" ${isPending ? 'data-editable="true"' : ''}>
|
||||
${statusIcon}
|
||||
<span class="dap-step-index">${step.index}.</span>
|
||||
<span class="dap-step-name" title="${escapeHtml(step.name)}">${escapeHtml(step.name)}</span>
|
||||
${changeBadge}
|
||||
<span class="dap-step-type">${escapeHtml(typeLabel)}</span>
|
||||
${isPending ? `<button class="dap-step-menu-btn" title="Step actions">...</button>` : ''}
|
||||
</div>
|
||||
`;
|
||||
}).join('');
|
||||
|
||||
container.innerHTML = html;
|
||||
|
||||
// Add click handlers for step menu buttons
|
||||
container.querySelectorAll('.dap-step-menu-btn').forEach((btn) => {
|
||||
btn.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
const stepItem = btn.closest('.dap-step-item');
|
||||
const index = parseInt(stepItem.dataset.index);
|
||||
showStepContextMenu(index, e);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Load steps from DAP server
|
||||
*/
|
||||
async function loadSteps() {
|
||||
try {
|
||||
const response = await sendDapRequest('evaluate', {
|
||||
expression: JSON.stringify({ cmd: 'step.list', verbose: false }),
|
||||
frameId: currentFrameId,
|
||||
context: 'repl',
|
||||
});
|
||||
|
||||
if (response.result) {
|
||||
try {
|
||||
const result = JSON.parse(response.result);
|
||||
if (result.Success && result.Result) {
|
||||
renderStepList(result.Result);
|
||||
enableStepControls(true);
|
||||
return;
|
||||
}
|
||||
} catch (parseErr) {
|
||||
// Response might not be JSON, that's ok
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: show empty state
|
||||
renderStepList([]);
|
||||
} catch (error) {
|
||||
console.error('[Content] Failed to load steps:', error);
|
||||
const container = debuggerPane?.querySelector('.dap-steps-list');
|
||||
if (container) {
|
||||
container.innerHTML = '<div class="dap-steps-empty p-2 color-fg-muted">Failed to load steps</div>';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable/disable step manipulation controls
|
||||
*/
|
||||
function enableStepControls(enabled) {
|
||||
if (!debuggerPane) return;
|
||||
|
||||
const addBtn = debuggerPane.querySelector('.dap-add-step-btn');
|
||||
const exportBtn = debuggerPane.querySelector('.dap-export-btn');
|
||||
|
||||
if (addBtn) addBtn.disabled = !enabled;
|
||||
if (exportBtn) exportBtn.disabled = !enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send step command via JSON API
|
||||
*/
|
||||
async function sendStepCommand(cmd, options = {}) {
|
||||
const command = { cmd, ...options };
|
||||
try {
|
||||
const response = await sendDapRequest('evaluate', {
|
||||
expression: JSON.stringify(command),
|
||||
frameId: currentFrameId,
|
||||
context: 'repl',
|
||||
});
|
||||
|
||||
if (response.result) {
|
||||
try {
|
||||
return JSON.parse(response.result);
|
||||
} catch (e) {
|
||||
return { Success: false, Error: 'PARSE_ERROR', Message: response.result };
|
||||
}
|
||||
}
|
||||
return { Success: false, Error: 'NO_RESPONSE', Message: 'No response from server' };
|
||||
} catch (error) {
|
||||
return { Success: false, Error: 'REQUEST_FAILED', Message: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show context menu for a step
|
||||
*/
|
||||
function showStepContextMenu(stepIndex, event) {
|
||||
closeContextMenu();
|
||||
|
||||
const step = stepsList.find((s) => s.index === stepIndex);
|
||||
if (!step || step.status !== 'pending') return;
|
||||
|
||||
const menu = document.createElement('div');
|
||||
menu.className = 'dap-context-menu';
|
||||
menu.innerHTML = `
|
||||
<div class="dap-context-menu-item" data-action="edit">
|
||||
${Icons.pencil}
|
||||
<span>Edit</span>
|
||||
</div>
|
||||
<div class="dap-context-menu-item" data-action="moveUp">
|
||||
${Icons.arrowUp}
|
||||
<span>Move Up</span>
|
||||
</div>
|
||||
<div class="dap-context-menu-item" data-action="moveDown">
|
||||
${Icons.arrowDown}
|
||||
<span>Move Down</span>
|
||||
</div>
|
||||
<div class="dap-context-menu-divider"></div>
|
||||
<div class="dap-context-menu-item danger" data-action="delete">
|
||||
${Icons.trash}
|
||||
<span>Delete</span>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Position menu
|
||||
const rect = event.target.getBoundingClientRect();
|
||||
menu.style.position = 'fixed';
|
||||
menu.style.top = `${rect.bottom + 4}px`;
|
||||
menu.style.left = `${rect.left}px`;
|
||||
|
||||
// Add event handlers
|
||||
menu.querySelectorAll('.dap-context-menu-item').forEach((item) => {
|
||||
item.addEventListener('click', async () => {
|
||||
const action = item.dataset.action;
|
||||
closeContextMenu();
|
||||
await handleStepAction(action, stepIndex);
|
||||
});
|
||||
});
|
||||
|
||||
document.body.appendChild(menu);
|
||||
activeContextMenu = menu;
|
||||
|
||||
// Close menu on outside click
|
||||
setTimeout(() => {
|
||||
document.addEventListener('click', closeContextMenuOnOutsideClick);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Close context menu
|
||||
*/
|
||||
function closeContextMenu() {
|
||||
if (activeContextMenu) {
|
||||
activeContextMenu.remove();
|
||||
activeContextMenu = null;
|
||||
}
|
||||
document.removeEventListener('click', closeContextMenuOnOutsideClick);
|
||||
}
|
||||
|
||||
/**
|
||||
* Close context menu on outside click
|
||||
*/
|
||||
function closeContextMenuOnOutsideClick(e) {
|
||||
if (activeContextMenu && !activeContextMenu.contains(e.target)) {
|
||||
closeContextMenu();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle step action from context menu
|
||||
*/
|
||||
async function handleStepAction(action, stepIndex) {
|
||||
switch (action) {
|
||||
case 'edit':
|
||||
showEditStepDialog(stepIndex);
|
||||
break;
|
||||
case 'moveUp':
|
||||
await moveStep(stepIndex, 'before', stepIndex - 1);
|
||||
break;
|
||||
case 'moveDown':
|
||||
await moveStep(stepIndex, 'after', stepIndex + 1);
|
||||
break;
|
||||
case 'delete':
|
||||
await deleteStep(stepIndex);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Move a step
|
||||
*/
|
||||
async function moveStep(fromIndex, positionType, targetIndex) {
|
||||
const position = {};
|
||||
position[positionType] = targetIndex;
|
||||
|
||||
const result = await sendStepCommand('step.move', { from: fromIndex, position });
|
||||
if (result.Success) {
|
||||
appendOutput(`Step moved successfully`, 'result');
|
||||
await loadSteps();
|
||||
} else {
|
||||
appendOutput(`Failed to move step: ${result.Message || result.Error}`, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a step
|
||||
*/
|
||||
async function deleteStep(stepIndex) {
|
||||
const step = stepsList.find((s) => s.index === stepIndex);
|
||||
if (!step) return;
|
||||
|
||||
// Simple confirmation
|
||||
if (!confirm(`Delete step "${step.name}"?`)) return;
|
||||
|
||||
const result = await sendStepCommand('step.remove', { index: stepIndex });
|
||||
if (result.Success) {
|
||||
appendOutput(`Step removed successfully`, 'result');
|
||||
await loadSteps();
|
||||
} else {
|
||||
appendOutput(`Failed to remove step: ${result.Message || result.Error}`, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Close modal
|
||||
*/
|
||||
function closeModal() {
|
||||
if (activeModal) {
|
||||
activeModal.remove();
|
||||
activeModal = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create modal wrapper
|
||||
*/
|
||||
function createModal(title, content, buttons = []) {
|
||||
closeModal();
|
||||
|
||||
const modal = document.createElement('div');
|
||||
modal.className = 'dap-modal-overlay';
|
||||
modal.innerHTML = `
|
||||
<div class="dap-modal">
|
||||
<div class="dap-modal-header">
|
||||
<span class="text-bold">${escapeHtml(title)}</span>
|
||||
<button class="btn btn-sm dap-modal-close-btn">${Icons.close}</button>
|
||||
</div>
|
||||
<div class="dap-modal-content">
|
||||
${content}
|
||||
</div>
|
||||
<div class="dap-modal-footer">
|
||||
${buttons.map((btn) => `<button class="btn btn-sm ${btn.primary ? 'btn-primary' : ''}" data-action="${btn.action}">${btn.label}</button>`).join('')}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Close on overlay click
|
||||
modal.addEventListener('click', (e) => {
|
||||
if (e.target === modal) closeModal();
|
||||
});
|
||||
|
||||
// Close button
|
||||
modal.querySelector('.dap-modal-close-btn').addEventListener('click', closeModal);
|
||||
|
||||
document.body.appendChild(modal);
|
||||
activeModal = modal;
|
||||
|
||||
return modal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show Add Step dialog
|
||||
*/
|
||||
function showAddStepDialog() {
|
||||
const content = `
|
||||
<div class="dap-form-group">
|
||||
<label class="dap-label">Step Type</label>
|
||||
<select class="form-control dap-step-type-select">
|
||||
<option value="run">Run (shell command)</option>
|
||||
<option value="uses">Uses (action)</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="dap-form-group dap-run-fields">
|
||||
<label class="dap-label">Script</label>
|
||||
<textarea class="form-control dap-script-input" rows="4" placeholder="echo 'Hello World'"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="dap-form-group dap-run-fields">
|
||||
<label class="dap-label">Shell (optional)</label>
|
||||
<select class="form-control dap-shell-select">
|
||||
<option value="">Default</option>
|
||||
<option value="bash">bash</option>
|
||||
<option value="sh">sh</option>
|
||||
<option value="pwsh">pwsh</option>
|
||||
<option value="powershell">powershell</option>
|
||||
<option value="cmd">cmd</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="dap-form-group dap-uses-fields" style="display: none;">
|
||||
<label class="dap-label">Action</label>
|
||||
<input type="text" class="form-control dap-action-input" placeholder="actions/checkout@v4">
|
||||
</div>
|
||||
|
||||
<div class="dap-form-group dap-uses-fields" style="display: none;">
|
||||
<label class="dap-label">With (key=value, one per line)</label>
|
||||
<textarea class="form-control dap-with-input" rows="3" placeholder="node-version=20 cache=npm"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="dap-form-group">
|
||||
<label class="dap-label">Name (optional)</label>
|
||||
<input type="text" class="form-control dap-name-input" placeholder="Step name">
|
||||
</div>
|
||||
|
||||
<div class="dap-form-group">
|
||||
<label class="dap-label">Position</label>
|
||||
<select class="form-control dap-position-select">
|
||||
<option value="last">At end (default)</option>
|
||||
<option value="first">At first pending position</option>
|
||||
<option value="after">After current step</option>
|
||||
</select>
|
||||
</div>
|
||||
`;
|
||||
|
||||
const modal = createModal('Add Step', content, [
|
||||
{ label: 'Cancel', action: 'cancel' },
|
||||
{ label: 'Add Step', action: 'add', primary: true },
|
||||
]);
|
||||
|
||||
// Type select handler
|
||||
const typeSelect = modal.querySelector('.dap-step-type-select');
|
||||
const runFields = modal.querySelectorAll('.dap-run-fields');
|
||||
const usesFields = modal.querySelectorAll('.dap-uses-fields');
|
||||
|
||||
typeSelect.addEventListener('change', () => {
|
||||
const isRun = typeSelect.value === 'run';
|
||||
runFields.forEach((f) => (f.style.display = isRun ? '' : 'none'));
|
||||
usesFields.forEach((f) => (f.style.display = isRun ? 'none' : ''));
|
||||
});
|
||||
|
||||
// Button handlers
|
||||
modal.querySelectorAll('[data-action]').forEach((btn) => {
|
||||
btn.addEventListener('click', async () => {
|
||||
const action = btn.dataset.action;
|
||||
if (action === 'cancel') {
|
||||
closeModal();
|
||||
return;
|
||||
}
|
||||
|
||||
if (action === 'add') {
|
||||
await handleAddStep(modal);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle add step form submission
|
||||
*/
|
||||
async function handleAddStep(modal) {
|
||||
const type = modal.querySelector('.dap-step-type-select').value;
|
||||
const name = modal.querySelector('.dap-name-input').value.trim() || undefined;
|
||||
const positionSelect = modal.querySelector('.dap-position-select').value;
|
||||
|
||||
let position = {};
|
||||
if (positionSelect === 'first') {
|
||||
position.first = true;
|
||||
} else if (positionSelect === 'after') {
|
||||
// After current step - find current step index
|
||||
const currentStep = stepsList.find((s) => s.status === 'current');
|
||||
if (currentStep) {
|
||||
position.after = currentStep.index;
|
||||
} else {
|
||||
position.last = true;
|
||||
}
|
||||
} else {
|
||||
position.last = true;
|
||||
}
|
||||
|
||||
let result;
|
||||
if (type === 'run') {
|
||||
const script = modal.querySelector('.dap-script-input').value;
|
||||
const shell = modal.querySelector('.dap-shell-select').value || undefined;
|
||||
|
||||
if (!script.trim()) {
|
||||
alert('Script is required');
|
||||
return;
|
||||
}
|
||||
|
||||
result = await sendStepCommand('step.add', {
|
||||
type: 'run',
|
||||
script,
|
||||
name,
|
||||
shell,
|
||||
position,
|
||||
});
|
||||
} else {
|
||||
const action = modal.querySelector('.dap-action-input').value.trim();
|
||||
const withText = modal.querySelector('.dap-with-input').value.trim();
|
||||
|
||||
if (!action) {
|
||||
alert('Action is required');
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse with inputs
|
||||
const withInputs = {};
|
||||
if (withText) {
|
||||
withText.split('\n').forEach((line) => {
|
||||
const [key, ...valueParts] = line.split('=');
|
||||
if (key && valueParts.length > 0) {
|
||||
withInputs[key.trim()] = valueParts.join('=').trim();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
result = await sendStepCommand('step.add', {
|
||||
type: 'uses',
|
||||
action,
|
||||
name,
|
||||
with: Object.keys(withInputs).length > 0 ? withInputs : undefined,
|
||||
position,
|
||||
});
|
||||
}
|
||||
|
||||
if (result.Success) {
|
||||
closeModal();
|
||||
appendOutput(`Step added at position ${result.Result?.index || 'end'}`, 'result');
|
||||
await loadSteps();
|
||||
} else {
|
||||
appendOutput(`Failed to add step: ${result.Message || result.Error}`, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show Edit Step dialog
|
||||
*/
|
||||
function showEditStepDialog(stepIndex) {
|
||||
const step = stepsList.find((s) => s.index === stepIndex);
|
||||
if (!step) return;
|
||||
|
||||
const isRun = step.type === 'run';
|
||||
|
||||
const content = `
|
||||
<div class="dap-form-group">
|
||||
<label class="dap-label">Step Type</label>
|
||||
<input type="text" class="form-control" value="${escapeHtml(step.type)}" disabled>
|
||||
</div>
|
||||
|
||||
${isRun ? `
|
||||
<div class="dap-form-group">
|
||||
<label class="dap-label">Script</label>
|
||||
<textarea class="form-control dap-edit-script-input" rows="4">${escapeHtml(step.typeDetail || '')}</textarea>
|
||||
</div>
|
||||
` : `
|
||||
<div class="dap-form-group">
|
||||
<label class="dap-label">Action</label>
|
||||
<input type="text" class="form-control" value="${escapeHtml(step.typeDetail || '')}" disabled>
|
||||
</div>
|
||||
`}
|
||||
|
||||
<div class="dap-form-group">
|
||||
<label class="dap-label">Name</label>
|
||||
<input type="text" class="form-control dap-edit-name-input" value="${escapeHtml(step.name)}">
|
||||
</div>
|
||||
|
||||
<div class="dap-form-group">
|
||||
<label class="dap-label">Condition (optional)</label>
|
||||
<input type="text" class="form-control dap-edit-condition-input" placeholder="success()">
|
||||
</div>
|
||||
`;
|
||||
|
||||
const modal = createModal(`Edit Step ${stepIndex}`, content, [
|
||||
{ label: 'Cancel', action: 'cancel' },
|
||||
{ label: 'Save Changes', action: 'save', primary: true },
|
||||
]);
|
||||
|
||||
// Button handlers
|
||||
modal.querySelectorAll('[data-action]').forEach((btn) => {
|
||||
btn.addEventListener('click', async () => {
|
||||
const action = btn.dataset.action;
|
||||
if (action === 'cancel') {
|
||||
closeModal();
|
||||
return;
|
||||
}
|
||||
|
||||
if (action === 'save') {
|
||||
await handleEditStep(modal, stepIndex, isRun);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle edit step form submission
|
||||
*/
|
||||
async function handleEditStep(modal, stepIndex, isRun) {
|
||||
const name = modal.querySelector('.dap-edit-name-input').value.trim() || undefined;
|
||||
const condition = modal.querySelector('.dap-edit-condition-input')?.value.trim() || undefined;
|
||||
|
||||
const options = { index: stepIndex };
|
||||
if (name) options.name = name;
|
||||
if (condition) options.if = condition;
|
||||
|
||||
if (isRun) {
|
||||
const script = modal.querySelector('.dap-edit-script-input')?.value;
|
||||
if (script) options.script = script;
|
||||
}
|
||||
|
||||
const result = await sendStepCommand('step.edit', options);
|
||||
|
||||
if (result.Success) {
|
||||
closeModal();
|
||||
appendOutput(`Step ${stepIndex} updated`, 'result');
|
||||
await loadSteps();
|
||||
} else {
|
||||
appendOutput(`Failed to edit step: ${result.Message || result.Error}`, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show Export modal
|
||||
*/
|
||||
async function showExportModal() {
|
||||
const result = await sendStepCommand('step.export', { changesOnly: false, withComments: true });
|
||||
|
||||
if (!result.Success) {
|
||||
appendOutput(`Failed to export: ${result.Message || result.Error}`, 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
const yaml = result.Message || result.Result?.yaml || '';
|
||||
const stats = result.Result || {};
|
||||
|
||||
const content = `
|
||||
<div class="dap-export-stats mb-2 color-fg-muted">
|
||||
Total steps: ${stats.totalSteps || 0} |
|
||||
Added: ${stats.addedCount || 0} |
|
||||
Modified: ${stats.modifiedCount || 0}
|
||||
</div>
|
||||
<div class="dap-export-yaml">
|
||||
<pre class="dap-yaml-content">${escapeHtml(yaml)}</pre>
|
||||
</div>
|
||||
`;
|
||||
|
||||
const modal = createModal('Export Steps', content, [
|
||||
{ label: 'Close', action: 'close' },
|
||||
{ label: 'Copy to Clipboard', action: 'copy', primary: true },
|
||||
]);
|
||||
|
||||
// Store YAML for copy
|
||||
modal.dataset.yaml = yaml;
|
||||
|
||||
// Button handlers
|
||||
modal.querySelectorAll('[data-action]').forEach((btn) => {
|
||||
btn.addEventListener('click', async () => {
|
||||
const action = btn.dataset.action;
|
||||
if (action === 'close') {
|
||||
closeModal();
|
||||
return;
|
||||
}
|
||||
|
||||
if (action === 'copy') {
|
||||
try {
|
||||
await navigator.clipboard.writeText(modal.dataset.yaml);
|
||||
btn.textContent = 'Copied!';
|
||||
setTimeout(() => {
|
||||
btn.innerHTML = `${Icons.copy} Copy to Clipboard`;
|
||||
}, 2000);
|
||||
} catch (err) {
|
||||
appendOutput('Failed to copy to clipboard', 'error');
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup event handlers for debugger pane
|
||||
*/
|
||||
@@ -435,6 +1105,18 @@ function setupPaneEventHandlers(pane) {
|
||||
if (input) {
|
||||
input.addEventListener('keydown', handleReplKeydown);
|
||||
}
|
||||
|
||||
// Add Step button
|
||||
const addStepBtn = pane.querySelector('.dap-add-step-btn');
|
||||
if (addStepBtn) {
|
||||
addStepBtn.addEventListener('click', showAddStepDialog);
|
||||
}
|
||||
|
||||
// Export button
|
||||
const exportBtn = pane.querySelector('.dap-export-btn');
|
||||
if (exportBtn) {
|
||||
exportBtn.addEventListener('click', showExportModal);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -522,6 +1204,9 @@ function enableControls(enabled) {
|
||||
if (input) {
|
||||
input.disabled = !enabled;
|
||||
}
|
||||
|
||||
// Also enable step controls when debugger is enabled
|
||||
enableStepControls(enabled);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -729,6 +1414,9 @@ async function handleStoppedEvent(body) {
|
||||
|
||||
// Load scopes
|
||||
await loadScopes(currentFrame.id);
|
||||
|
||||
// Load steps for step manipulation panel
|
||||
await loadSteps();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[Content] Failed to get stack trace:', error);
|
||||
@@ -790,6 +1478,9 @@ async function loadCurrentDebugState() {
|
||||
|
||||
// Load scopes
|
||||
await loadScopes(currentFrame.id);
|
||||
|
||||
// Load steps for step manipulation panel
|
||||
await loadSteps();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[Content] Failed to load current debug state:', error);
|
||||
|
||||
Reference in New Issue
Block a user