Skip to content

Commit 5d0eccc

Browse files
committed
fix
1 parent a0ca89e commit 5d0eccc

File tree

2 files changed

+40
-18
lines changed

2 files changed

+40
-18
lines changed

web_src/js/components/ActionRunStatus.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ import {SvgIcon} from '../svg.ts';
77

88
withDefaults(defineProps<{
99
status: 'success' | 'skipped' | 'waiting' | 'blocked' | 'running' | 'failure' | 'cancelled' | 'unknown',
10-
size: number,
11-
className: string,
10+
size?: number,
11+
className?: string,
1212
localeStatus?: string,
1313
}>(), {
1414
size: 16,

web_src/js/components/RepoActionView.vue

Lines changed: 38 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@ function parseLineCommand(line: LogLine): LogLineCommand | null {
3838
return null;
3939
}
4040
41+
function isLogElementInViewport(el: HTMLElement): boolean {
42+
const rect = el.getBoundingClientRect();
43+
return rect.top >= 0 && rect.bottom <= window.innerHeight; // only check height but not width
44+
}
45+
4146
const sfc = {
4247
name: 'RepoActionView',
4348
components: {
@@ -142,9 +147,14 @@ const sfc = {
142147
},
143148
144149
methods: {
145-
// get the active container element, either the `job-step-logs` or the `job-log-list` in the `job-log-group`
146-
getLogsContainer(stepIndex: number) {
147-
const el = this.$refs.logs[stepIndex];
150+
// get the job step logs container ('.job-step-logs')
151+
getJobStepLogsContainer(stepIndex: number): HTMLElement {
152+
return this.$refs.logs[stepIndex];
153+
},
154+
155+
// get the active logs container element, either the `job-step-logs` or the `job-log-list` in the `job-log-group`
156+
getActiveLogsContainer(stepIndex: number): HTMLElement {
157+
const el = this.getJobStepLogsContainer(stepIndex);
148158
return el._stepLogsActiveContainer ?? el;
149159
},
150160
// begin a log group
@@ -217,14 +227,15 @@ const sfc = {
217227
);
218228
},
219229
220-
appendLogs(stepIndex: number, startTime: number, logLines: LogLine[]) {
221-
// position of the client view relative to the website top
222-
const clientHeight = document.documentElement.clientHeight + window.scrollY;
223-
// height of the logs container relative to the website top
224-
const logsContainerHeight = this.$refs.stepsContainer.getBoundingClientRect().bottom + window.scrollY;
230+
shouldAutoScroll(stepIndex: number): boolean {
231+
const el = this.getJobStepLogsContainer(stepIndex);
232+
if (!el.lastChild) return false;
233+
return isLogElementInViewport(el.lastChild);
234+
},
225235
236+
appendLogs(stepIndex: number, startTime: number, logLines: LogLine[]) {
226237
for (const line of logLines) {
227-
const el = this.getLogsContainer(stepIndex);
238+
const el = this.getActiveLogsContainer(stepIndex);
228239
const cmd = parseLineCommand(line);
229240
if (cmd?.name === 'group') {
230241
this.beginLogGroup(stepIndex, startTime, line, cmd);
@@ -235,12 +246,6 @@ const sfc = {
235246
}
236247
el.append(this.createLogLine(stepIndex, startTime, line));
237248
}
238-
239-
// scrolls to the bottom if job is running and the bottom of the logs container is visible
240-
if (!this.run.done && logLines.length && clientHeight >= logsContainerHeight) {
241-
const newLogsContainerHeight = this.$refs.stepsContainer.getBoundingClientRect().bottom + window.scrollY;
242-
window.scrollTo({top: clientHeight + (newLogsContainerHeight - logsContainerHeight), behavior: 'smooth'});
243-
}
244249
},
245250
246251
async deleteArtifact(name: string) {
@@ -289,13 +294,30 @@ const sfc = {
289294
this.currentJobStepsStates[i] = {cursor: null, expanded: false};
290295
}
291296
}
297+
298+
// find the step indexes that need to auto-scroll
299+
const autoScrollStepIndexes = new Map<number, boolean>();
300+
for (const logs of job.logs.stepsLog ?? []) {
301+
if (autoScrollStepIndexes.has(logs.step)) continue;
302+
autoScrollStepIndexes.set(logs.step, this.shouldAutoScroll(logs.step));
303+
}
304+
292305
// append logs to the UI
293306
for (const logs of job.logs.stepsLog ?? []) {
294307
// save the cursor, it will be passed to backend next time
295308
this.currentJobStepsStates[logs.step].cursor = logs.cursor;
296309
this.appendLogs(logs.step, logs.started, logs.lines);
297310
}
298311
312+
// auto-scroll to the last log line of the last step
313+
let autoScrollJobStepElement: HTMLElement;
314+
for (let stepIndex = 0; stepIndex < this.currentJob.steps.length; stepIndex++) {
315+
if (!autoScrollStepIndexes.get(stepIndex)) continue;
316+
autoScrollJobStepElement = this.getJobStepLogsContainer(stepIndex);
317+
}
318+
autoScrollJobStepElement?.lastElementChild.scrollIntoView({behavior: 'smooth', block: 'nearest'});
319+
320+
// clear the interval timer if the job is done
299321
if (this.run.done && this.intervalID) {
300322
clearInterval(this.intervalID);
301323
this.intervalID = null;
@@ -478,7 +500,7 @@ export function initRepositoryActionView() {
478500
</div>
479501
</div>
480502

481-
<div class="action-view-right" ref="stepsContainer">
503+
<div class="action-view-right">
482504
<div class="job-info-header">
483505
<div class="job-info-header-left gt-ellipsis">
484506
<h3 class="job-info-header-title gt-ellipsis">

0 commit comments

Comments
 (0)