@@ -204,15 +204,19 @@ const sfc = {
204204 };
205205 },
206206
207- mounted () {
207+ async mounted () {
208208 // load job data and then auto-reload periodically
209- this .loadJob ();
209+ // need to await first loadJob so this.currentJobStepsStates is initialized and can be used in hashChangeListener
210+ await this .loadJob ();
210211 this .intervalID = setInterval (this .loadJob , 1000 );
211212 document .body .addEventListener (' click' , this .closeDropdown );
213+ this .hashChangeListener ();
214+ window .addEventListener (' hashchange' , this .hashChangeListener );
212215 },
213216
214217 beforeUnmount () {
215218 document .body .removeEventListener (' click' , this .closeDropdown );
219+ window .removeEventListener (' hashchange' , this .hashChangeListener );
216220 },
217221
218222 unmounted () {
@@ -280,14 +284,16 @@ const sfc = {
280284 this .fetchPost (` ${ this .run .link } /approve` );
281285 },
282286
283- createLogLine (line , startTime ) {
287+ createLogLine (line , startTime , stepIndex ) {
284288 const div = document .createElement (' div' );
285289 div .classList .add (' job-log-line' );
290+ div .setAttribute (' id' , ` jobstep-${ stepIndex} -${ line .index } ` );
286291 div ._jobLogTime = line .timestamp ;
287292
288- const lineNumber = document .createElement (' div ' );
289- lineNumber .className = ' line-num' ;
293+ const lineNumber = document .createElement (' a ' );
294+ lineNumber .classList . add ( ' line-num' , ' muted ' ) ;
290295 lineNumber .textContent = line .index ;
296+ lineNumber .setAttribute (' href' , ` #jobstep-${ stepIndex} -${ line .index } ` );
291297 div .append (lineNumber);
292298
293299 // for "Show timestamps"
@@ -318,7 +324,7 @@ const sfc = {
318324 for (const line of logLines) {
319325 // TODO: group support: ##[group]GroupTitle , ##[endgroup]
320326 const el = this .getLogsContainer (stepIndex);
321- el .append (this .createLogLine (line, startTime));
327+ el .append (this .createLogLine (line, startTime, stepIndex ));
322328 }
323329 },
324330
@@ -429,6 +435,21 @@ const sfc = {
429435 } else {
430436 actionBodyEl .append (fullScreenEl);
431437 }
438+ },
439+ async hashChangeListener () {
440+ const selectedLogStep = window .location .hash ;
441+ if (! selectedLogStep) return ;
442+ const [_ , step , _line ] = selectedLogStep .split (' -' );
443+ if (! this .currentJobStepsStates [step]) return ;
444+ if (! this .currentJobStepsStates [step].expanded && this .currentJobStepsStates [step].cursor === null ) {
445+ this .currentJobStepsStates [step].expanded = true ;
446+ // need to await for load job if the step log is loaded for the first time
447+ // so logline can be selected by querySelector
448+ await this .loadJob ();
449+ }
450+ const logLine = this .$refs .steps .querySelector (selectedLogStep);
451+ if (! logLine) return ;
452+ logLine .querySelector (' .line-num' ).click ();
432453 }
433454 },
434455};
@@ -802,10 +823,15 @@ export function initRepositoryActionView() {
802823 display: flex;
803824}
804825
805- .job - step- section .job - step- logs .job - log- line: hover {
826+ .job - log- line: hover,
827+ .job - log- line: target {
806828 background- color: var (-- color- console - hover- bg);
807829}
808830
831+ .job - log- line: target {
832+ scroll- margin- top: 95px ;
833+ }
834+
809835/* class names 'log-time-seconds' and 'log-time-stamp' are used in the method toggleTimeDisplay */
810836.job - log- line .line - num, .log - time- seconds {
811837 width: 48px ;
@@ -814,6 +840,11 @@ export function initRepositoryActionView() {
814840 user- select: none;
815841}
816842
843+ .job - log- line: target > .line - num {
844+ color: var (-- color- primary);
845+ text- decoration: underline;
846+ }
847+
817848.log - time- seconds {
818849 padding- right: 2px ;
819850}
0 commit comments