|
1 | 1 | <script lang="ts"> |
2 | 2 | import {SvgIcon} from '../svg.ts'; |
3 | 3 | import ActionRunStatus from './ActionRunStatus.vue'; |
4 | | -import {defineComponent, nextTick, type PropType} from 'vue'; |
| 4 | +import {defineComponent, type PropType} from 'vue'; |
5 | 5 | import {createElementFromAttrs, toggleElem} from '../utils/dom.ts'; |
6 | 6 | import {formatDatetime} from '../utils/time.ts'; |
7 | 7 | import {renderAnsi} from '../render/ansi.ts'; |
@@ -104,7 +104,6 @@ export default defineComponent({ |
104 | 104 | // internal state |
105 | 105 | loadingAbortController: null as AbortController | null, |
106 | 106 | intervalID: null as IntervalId | null, |
107 | | - mutationObserver: null as MutationObserver | null, // Observer for auto-expand/auto-scroll functionality |
108 | 107 | currentJobStepsStates: [] as Array<Record<string, any>>, |
109 | 108 | artifacts: [] as Array<Record<string, any>>, |
110 | 109 | menuVisible: false, |
@@ -186,72 +185,11 @@ export default defineComponent({ |
186 | 185 | document.body.addEventListener('click', this.closeDropdown); |
187 | 186 | this.hashChangeListener(); |
188 | 187 | window.addEventListener('hashchange', this.hashChangeListener); |
189 | | -
|
190 | | - // === Auto Expand + Auto Scroll Fix (for Issue #35570) === |
191 | | - // Ensure Vue has updated DOM for steps after initial load |
192 | | - await nextTick(); |
193 | | -
|
194 | | - // Set up observer on the steps container (safer and more efficient than document.body) |
195 | | - const stepsContainer = (this.$refs.steps as HTMLElement); |
196 | | - if (stepsContainer && typeof MutationObserver !== 'undefined') { |
197 | | - this.mutationObserver = new MutationObserver((mutations) => { |
198 | | - for (const m of mutations) { |
199 | | - // Auto-scroll new log lines as they appear |
200 | | - if (m.type === 'childList') { |
201 | | - for (const n of m.addedNodes) { |
202 | | - if (n.nodeType === 1 && (n as Element).classList.contains('job-log-line')) { |
203 | | - if (this.optionAlwaysAutoScroll) { |
204 | | - try { (n as Element).scrollIntoView({ behavior: 'smooth', block: 'end' }); } |
205 | | - catch { (n as Element).scrollIntoView(); } |
206 | | - } |
207 | | - } |
208 | | - } |
209 | | - } |
210 | | -
|
211 | | - // Auto-expand running steps when their class changes |
212 | | - if (m.type === 'attributes' && m.attributeName === 'class') { |
213 | | - const t = m.target as Element; |
214 | | - if (t.classList && t.classList.contains('job-step-summary')) { |
215 | | - const stepAttr = t.getAttribute('data-step'); |
216 | | - if (!stepAttr) continue; |
217 | | - const stepIndex = Number(stepAttr); |
218 | | - // If expand-running option is on and step is expandable but not selected, open it via state |
219 | | - if (this.optionAlwaysExpandRunning && |
220 | | - t.classList.contains('step-expandable') && |
221 | | - !t.classList.contains('selected')) { |
222 | | - // Update state inside nextTick to ensure Vue has finished rendering |
223 | | - nextTick(() => { |
224 | | - if (!this.currentJobStepsStates[stepIndex]?.expanded) { |
225 | | - // Update internal state so logs are immediately loaded |
226 | | - this.currentJobStepsStates[stepIndex].expanded = true; |
227 | | - this.loadJob(); |
228 | | - } |
229 | | - }); |
230 | | - } |
231 | | - } |
232 | | - } |
233 | | - } |
234 | | - }); |
235 | | -
|
236 | | - // Observe only the steps container subtree (minimized observation area) |
237 | | - this.mutationObserver.observe(stepsContainer, { |
238 | | - childList: true, |
239 | | - subtree: true, |
240 | | - attributes: true, |
241 | | - attributeFilter: ['class'], |
242 | | - }); |
243 | | - } |
244 | | - // === End Fix === |
245 | 188 | }, |
246 | 189 |
|
247 | 190 | beforeUnmount() { |
248 | 191 | document.body.removeEventListener('click', this.closeDropdown); |
249 | 192 | window.removeEventListener('hashchange', this.hashChangeListener); |
250 | | - // Clean up MutationObserver to prevent memory leaks |
251 | | - if (this.mutationObserver) { |
252 | | - this.mutationObserver.disconnect(); |
253 | | - this.mutationObserver = null; |
254 | | - } |
255 | 193 | }, |
256 | 194 |
|
257 | 195 | unmounted() { |
@@ -422,6 +360,19 @@ export default defineComponent({ |
422 | 360 | } |
423 | 361 | } |
424 | 362 |
|
| 363 | + // Auto-expand running steps if option is enabled (fix for Issue #35570) |
| 364 | + for (let i = 0; i < this.currentJob.steps.length; i++) { |
| 365 | + const step = this.currentJob.steps[i]; |
| 366 | + const state = this.currentJobStepsStates[i]; |
| 367 | + if ( |
| 368 | + this.optionAlwaysExpandRunning && |
| 369 | + step.status === 'running' && |
| 370 | + !state.expanded |
| 371 | + ) { |
| 372 | + state.expanded = true; |
| 373 | + } |
| 374 | + } |
| 375 | +
|
425 | 376 | // find the step indexes that need to auto-scroll |
426 | 377 | const autoScrollStepIndexes = new Map<number, boolean>(); |
427 | 378 | for (const logs of job.logs.stepsLog ?? []) { |
@@ -630,7 +581,7 @@ export default defineComponent({ |
630 | 581 | </div> |
631 | 582 | <div class="job-step-container" ref="steps" v-if="currentJob.steps.length"> |
632 | 583 | <div class="job-step-section" v-for="(jobStep, i) in currentJob.steps" :key="i"> |
633 | | - <div class="job-step-summary" :data-step="i" @click.stop="isExpandable(jobStep.status) && toggleStepLogs(i)" :class="[currentJobStepsStates[i].expanded ? 'selected' : '', isExpandable(jobStep.status) && 'step-expandable']"> |
| 584 | + <div class="job-step-summary" @click.stop="isExpandable(jobStep.status) && toggleStepLogs(i)" :class="[currentJobStepsStates[i].expanded ? 'selected' : '', isExpandable(jobStep.status) && 'step-expandable']"> |
634 | 585 | <!-- If the job is done and the job step log is loaded for the first time, show the loading icon |
635 | 586 | currentJobStepsStates[i].cursor === null means the log is loaded for the first time |
636 | 587 | --> |
|
0 commit comments