@chisquare-tech/origine-ngx-apps-code 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { inject, Injectable, signal, computed, effect, untracked, Directive, ElementRef, input, output, HostListener, ChangeDetectionStrategy, Component } from '@angular/core';
|
|
2
|
+
import { inject, Injectable, signal, computed, effect, untracked, Directive, ElementRef, input, output, HostListener, ChangeDetectionStrategy, Component, DestroyRef } from '@angular/core';
|
|
3
3
|
import { ApiService } from '@thescaffold/ngx-core';
|
|
4
4
|
import { map, forkJoin, of } from 'rxjs';
|
|
5
5
|
import * as i1 from '@angular/forms';
|
|
6
6
|
import { FormsModule } from '@angular/forms';
|
|
7
7
|
import { ActivatedRoute, RouterLink, Router } from '@angular/router';
|
|
8
|
+
import { NgTemplateOutlet } from '@angular/common';
|
|
8
9
|
import { map as map$1, catchError } from 'rxjs/operators';
|
|
9
10
|
import { marked } from 'marked';
|
|
10
11
|
|
|
@@ -95,10 +96,14 @@ class CodeApi {
|
|
|
95
96
|
return this.api.get(`${this.repoPath(project, name)}/commit/${enc(sha)}`).pipe(map((r) => data(r)));
|
|
96
97
|
}
|
|
97
98
|
/** One file's hunks from base to head ("" base: the head's first parent). */
|
|
98
|
-
diff(project, name, head, path, base = '') {
|
|
99
|
+
diff(project, name, head, path, base = '', opts = {}) {
|
|
99
100
|
const p = { head, path };
|
|
100
101
|
if (base)
|
|
101
102
|
p['base'] = base;
|
|
103
|
+
if (opts.context)
|
|
104
|
+
p['context'] = String(opts.context);
|
|
105
|
+
if (opts.ignoreSpace)
|
|
106
|
+
p['w'] = 'ignore';
|
|
102
107
|
return this.api.get(`${this.repoPath(project, name)}/diff`, p).pipe(map((r) => data(r)));
|
|
103
108
|
}
|
|
104
109
|
compare(project, name, base, head) {
|
|
@@ -153,6 +158,10 @@ class CodeApi {
|
|
|
153
158
|
comment(project, name, n, c) {
|
|
154
159
|
return this.api.post(`${this.repoPath(project, name)}/pulls/${n}/comments`, c).pipe(map((r) => data(r)));
|
|
155
160
|
}
|
|
161
|
+
/** Discards one of the caller's pending review comments. */
|
|
162
|
+
discardComment(project, name, n, id) {
|
|
163
|
+
return this.api.delete(`${this.repoPath(project, name)}/pulls/${n}/comments/${enc(id)}`);
|
|
164
|
+
}
|
|
156
165
|
review(project, name, n, state, body = '') {
|
|
157
166
|
return this.api.post(`${this.repoPath(project, name)}/pulls/${n}/reviews`, { state, body }).pipe(map((r) => data(r)));
|
|
158
167
|
}
|
|
@@ -414,22 +423,41 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImpo
|
|
|
414
423
|
args: ['document:click', ['$event']]
|
|
415
424
|
}] } });
|
|
416
425
|
|
|
417
|
-
|
|
418
|
-
|
|
426
|
+
/** Past this many changed lines a file waits for "Load diff" (DEV-19 large diffs). */
|
|
427
|
+
const LARGE = 600;
|
|
428
|
+
/** Context steps for "Show more context". */
|
|
429
|
+
const CONTEXT = [3, 25, 100];
|
|
430
|
+
// One file of a diff (prototype prDiffs): its header with +/− and Viewed, the hunks highlighted,
|
|
431
|
+
// unified or split, and, on a pull request, the review threads on its lines and a comment form on
|
|
432
|
+
// any line number ("Add single comment" or part of a review). The diff loads when the file scrolls
|
|
433
|
+
// near the screen; a very large one waits to be asked for.
|
|
419
434
|
class DiffView {
|
|
420
435
|
constructor() {
|
|
421
436
|
this.api = inject(CodeApi);
|
|
422
437
|
this.host = inject(CodeHost);
|
|
438
|
+
this.el = inject((ElementRef));
|
|
439
|
+
this.destroyRef = inject(DestroyRef);
|
|
423
440
|
this.repo = input.required(...(ngDevMode ? [{ debugName: "repo" }] : /* istanbul ignore next */ []));
|
|
424
441
|
this.file = input.required(...(ngDevMode ? [{ debugName: "file" }] : /* istanbul ignore next */ []));
|
|
425
442
|
this.head = input.required(...(ngDevMode ? [{ debugName: "head" }] : /* istanbul ignore next */ []));
|
|
426
443
|
this.base = input('', ...(ngDevMode ? [{ debugName: "base" }] : /* istanbul ignore next */ []));
|
|
427
444
|
this.threads = input([], ...(ngDevMode ? [{ debugName: "threads" }] : /* istanbul ignore next */ []));
|
|
428
445
|
this.canComment = input(false, ...(ngDevMode ? [{ debugName: "canComment" }] : /* istanbul ignore next */ []));
|
|
446
|
+
this.mode = input('unified', ...(ngDevMode ? [{ debugName: "mode" }] : /* istanbul ignore next */ []));
|
|
447
|
+
this.ignoreSpace = input(false, ...(ngDevMode ? [{ debugName: "ignoreSpace" }] : /* istanbul ignore next */ []));
|
|
448
|
+
/** The file is marked viewed (pull requests); null hides the Viewed button. */
|
|
449
|
+
this.viewed = input(null, ...(ngDevMode ? [{ debugName: "viewed" }] : /* istanbul ignore next */ []));
|
|
450
|
+
/** The caller has a pending review on this pull request. */
|
|
451
|
+
this.reviewing = input(false, ...(ngDevMode ? [{ debugName: "reviewing" }] : /* istanbul ignore next */ []));
|
|
429
452
|
this.comment = output();
|
|
430
453
|
this.reply = output();
|
|
431
454
|
this.resolve = output();
|
|
455
|
+
this.discard = output();
|
|
456
|
+
this.viewedChange = output();
|
|
432
457
|
this.open = signal(true, ...(ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []));
|
|
458
|
+
this.visible = signal(false, ...(ngDevMode ? [{ debugName: "visible" }] : /* istanbul ignore next */ []));
|
|
459
|
+
this.waiting = signal(false, ...(ngDevMode ? [{ debugName: "waiting" }] : /* istanbul ignore next */ []));
|
|
460
|
+
this.context = signal(CONTEXT[0], ...(ngDevMode ? [{ debugName: "context" }] : /* istanbul ignore next */ []));
|
|
433
461
|
this.diff = signal(null, ...(ngDevMode ? [{ debugName: "diff" }] : /* istanbul ignore next */ []));
|
|
434
462
|
this.error = signal('', ...(ngDevMode ? [{ debugName: "error" }] : /* istanbul ignore next */ []));
|
|
435
463
|
this.drafting = signal('', ...(ngDevMode ? [{ debugName: "drafting" }] : /* istanbul ignore next */ []));
|
|
@@ -437,25 +465,82 @@ class DiffView {
|
|
|
437
465
|
this.replies = signal({}, ...(ngDevMode ? [{ debugName: "replies" }] : /* istanbul ignore next */ []));
|
|
438
466
|
this.people = computed(() => new People(this.host.people()), ...(ngDevMode ? [{ debugName: "people" }] : /* istanbul ignore next */ []));
|
|
439
467
|
this.threadsHere = computed(() => this.threads().filter((t) => t.path === this.file().path), ...(ngDevMode ? [{ debugName: "threadsHere" }] : /* istanbul ignore next */ []));
|
|
468
|
+
this.viewable = computed(() => this.viewed() !== null, ...(ngDevMode ? [{ debugName: "viewable" }] : /* istanbul ignore next */ []));
|
|
469
|
+
this.me = computed(() => this.host.meId(), ...(ngDevMode ? [{ debugName: "me" }] : /* istanbul ignore next */ []));
|
|
470
|
+
this.splitRows = computed(() => (this.diff()?.hunks ?? []).map((h) => pair(h.lines)), ...(ngDevMode ? [{ debugName: "splitRows" }] : /* istanbul ignore next */ []));
|
|
440
471
|
this.ago = (s) => ago(s);
|
|
472
|
+
this.asked = false;
|
|
473
|
+
effect(() => {
|
|
474
|
+
// viewed files fold away (and unfold when unmarked)
|
|
475
|
+
const v = this.viewed();
|
|
476
|
+
if (v !== null)
|
|
477
|
+
untracked(() => this.open.set(!v));
|
|
478
|
+
});
|
|
441
479
|
effect(() => {
|
|
442
|
-
|
|
443
|
-
|
|
480
|
+
// what the diff depends on; it loads once the file is near the screen and open
|
|
481
|
+
this.head();
|
|
482
|
+
this.base();
|
|
483
|
+
this.ignoreSpace();
|
|
484
|
+
this.context();
|
|
444
485
|
const f = this.file();
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
486
|
+
if (!this.visible() || !this.open() || f.binary)
|
|
487
|
+
return;
|
|
488
|
+
untracked(() => {
|
|
489
|
+
if (!this.asked && f.additions + f.deletions > LARGE) {
|
|
490
|
+
this.waiting.set(true);
|
|
491
|
+
return;
|
|
492
|
+
}
|
|
493
|
+
this.load();
|
|
494
|
+
});
|
|
451
495
|
});
|
|
452
496
|
}
|
|
497
|
+
ngAfterViewInit() {
|
|
498
|
+
if (typeof IntersectionObserver === 'undefined') {
|
|
499
|
+
this.visible.set(true);
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
502
|
+
const io = new IntersectionObserver((entries) => {
|
|
503
|
+
if (entries.some((e) => e.isIntersecting)) {
|
|
504
|
+
this.visible.set(true);
|
|
505
|
+
io.disconnect();
|
|
506
|
+
}
|
|
507
|
+
}, { rootMargin: '600px 0px' });
|
|
508
|
+
io.observe(this.el.nativeElement);
|
|
509
|
+
this.destroyRef.onDestroy(() => io.disconnect());
|
|
510
|
+
}
|
|
511
|
+
load() {
|
|
512
|
+
const p = this.host.project();
|
|
513
|
+
const f = this.file();
|
|
514
|
+
if (!p || !this.head() || f.binary)
|
|
515
|
+
return;
|
|
516
|
+
this.asked = true;
|
|
517
|
+
this.waiting.set(false);
|
|
518
|
+
this.error.set('');
|
|
519
|
+
this.api.diff(p.id, this.repo(), this.head(), f.path, this.base(), { context: this.context(), ignoreSpace: this.ignoreSpace() }).subscribe({
|
|
520
|
+
next: (d) => this.diff.set(d),
|
|
521
|
+
error: (e) => this.error.set(apiMessage(e, 'Could not load this diff.')),
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
toggleOpen() {
|
|
525
|
+
this.open.set(!this.open());
|
|
526
|
+
}
|
|
527
|
+
moreContext() {
|
|
528
|
+
const next = CONTEXT.find((c) => c > this.context());
|
|
529
|
+
if (next)
|
|
530
|
+
this.context.set(next);
|
|
531
|
+
}
|
|
453
532
|
key(l) {
|
|
454
533
|
return `${l.kind === 'delete' ? 'old' : 'new'}:${this.lineOf(l)}`;
|
|
455
534
|
}
|
|
456
535
|
lineOf(l) {
|
|
457
536
|
return (l.kind === 'delete' ? l.old : l.new) ?? 0;
|
|
458
537
|
}
|
|
538
|
+
/** The lines of a split row that threads and drafts hang from (a context line once). */
|
|
539
|
+
rowLines(r) {
|
|
540
|
+
if (r.left && r.right && r.left === r.right)
|
|
541
|
+
return [r.left];
|
|
542
|
+
return [r.left, r.right].filter((l) => !!l);
|
|
543
|
+
}
|
|
459
544
|
threadsAt(l) {
|
|
460
545
|
const side = l.kind === 'delete' ? 'old' : 'new';
|
|
461
546
|
const n = this.lineOf(l);
|
|
@@ -467,11 +552,11 @@ class DiffView {
|
|
|
467
552
|
this.drafting.set(this.key(l));
|
|
468
553
|
this.draftBody.set('');
|
|
469
554
|
}
|
|
470
|
-
send(l) {
|
|
555
|
+
send(l, pending) {
|
|
471
556
|
const body = this.draftBody().trim();
|
|
472
557
|
if (!body)
|
|
473
558
|
return;
|
|
474
|
-
this.comment.emit({ path: this.file().path, side: l.kind === 'delete' ? 'old' : 'new', line: this.lineOf(l), body });
|
|
559
|
+
this.comment.emit({ path: this.file().path, side: l.kind === 'delete' ? 'old' : 'new', line: this.lineOf(l), body, pending });
|
|
475
560
|
this.drafting.set('');
|
|
476
561
|
this.draftBody.set('');
|
|
477
562
|
}
|
|
@@ -486,130 +571,254 @@ class DiffView {
|
|
|
486
571
|
this.setReply(id, '');
|
|
487
572
|
}
|
|
488
573
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: DiffView, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
489
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.12", type: DiffView, isStandalone: true, selector: "o-diff", inputs: { repo: { classPropertyName: "repo", publicName: "repo", isSignal: true, isRequired: true, transformFunction: null }, file: { classPropertyName: "file", publicName: "file", isSignal: true, isRequired: true, transformFunction: null }, head: { classPropertyName: "head", publicName: "head", isSignal: true, isRequired: true, transformFunction: null }, base: { classPropertyName: "base", publicName: "base", isSignal: true, isRequired: false, transformFunction: null }, threads: { classPropertyName: "threads", publicName: "threads", isSignal: true, isRequired: false, transformFunction: null }, canComment: { classPropertyName: "canComment", publicName: "canComment", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { comment: "comment", reply: "reply", resolve: "resolve" }, ngImport: i0, template: `
|
|
490
|
-
<div class="o-card" [attr.data-testid]="'diff-' + file().path">
|
|
574
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.12", type: DiffView, isStandalone: true, selector: "o-diff", inputs: { repo: { classPropertyName: "repo", publicName: "repo", isSignal: true, isRequired: true, transformFunction: null }, file: { classPropertyName: "file", publicName: "file", isSignal: true, isRequired: true, transformFunction: null }, head: { classPropertyName: "head", publicName: "head", isSignal: true, isRequired: true, transformFunction: null }, base: { classPropertyName: "base", publicName: "base", isSignal: true, isRequired: false, transformFunction: null }, threads: { classPropertyName: "threads", publicName: "threads", isSignal: true, isRequired: false, transformFunction: null }, canComment: { classPropertyName: "canComment", publicName: "canComment", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, ignoreSpace: { classPropertyName: "ignoreSpace", publicName: "ignoreSpace", isSignal: true, isRequired: false, transformFunction: null }, viewed: { classPropertyName: "viewed", publicName: "viewed", isSignal: true, isRequired: false, transformFunction: null }, reviewing: { classPropertyName: "reviewing", publicName: "reviewing", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { comment: "comment", reply: "reply", resolve: "resolve", discard: "discard", viewedChange: "viewedChange" }, ngImport: i0, template: `
|
|
575
|
+
<div class="o-card" [attr.id]="'file-' + file().path" [attr.data-testid]="'diff-' + file().path">
|
|
491
576
|
<div class="o-ch" style="background:var(--o-well)">
|
|
492
|
-
<button type="button" class="o-btn o-ghost o-xs" (click)="
|
|
577
|
+
<button type="button" class="o-btn o-ghost o-xs" (click)="toggleOpen()" [attr.aria-expanded]="open()" [attr.aria-label]="(open() ? 'Collapse ' : 'Expand ') + file().path">{{ open() ? '▾' : '▸' }}</button>
|
|
493
578
|
<span class="o-mono o-sm o-ell">{{ file().oldPath && file().oldPath !== file().path ? file().oldPath + ' → ' : '' }}{{ file().path }}</span>
|
|
494
579
|
<span class="o-grn-t o-sm">+{{ file().additions }}</span><span class="o-red-t o-sm">−{{ file().deletions }}</span>
|
|
495
580
|
@if (threadsHere().length) {<span class="o-faint o-sm">💬 {{ threadsHere().length }}</span>}
|
|
496
|
-
<span class="o-r"
|
|
581
|
+
<span class="o-r o-flex" style="gap:6px">
|
|
582
|
+
@if (open() && diff() && context() < 100 && !file().binary) {<button type="button" class="o-btn o-xs" (click)="moreContext()" [attr.aria-label]="'Show more context in ' + file().path">More context</button>}
|
|
583
|
+
<ng-content />
|
|
584
|
+
@if (viewable()) {
|
|
585
|
+
<button type="button" class="o-btn o-xs viewed" role="checkbox" [attr.aria-checked]="viewed()" [attr.aria-label]="'Viewed ' + file().path" (click)="viewedChange.emit(!viewed())"><span class="o-cbx" [class.o-on]="viewed()" style="width:12px;height:12px"></span>Viewed</button>
|
|
586
|
+
}
|
|
587
|
+
</span>
|
|
497
588
|
</div>
|
|
498
589
|
@if (open()) {
|
|
499
590
|
@if (file().binary) {
|
|
500
591
|
<div class="o-empty o-sm">A binary file.</div>
|
|
592
|
+
} @else if (waiting()) {
|
|
593
|
+
<div class="o-empty o-sm o-col" style="gap:8px;align-items:center">
|
|
594
|
+
<span>{{ file().additions + file().deletions }} changed lines.</span>
|
|
595
|
+
<button type="button" class="o-btn o-sm" (click)="waiting.set(false); load()">Load diff</button>
|
|
596
|
+
</div>
|
|
501
597
|
} @else if (error()) {
|
|
502
598
|
<div class="o-note o-red" role="alert">{{ error() }}</div>
|
|
503
599
|
} @else if (diff(); as d) {
|
|
504
600
|
@if (d.truncated) {<div class="o-note">This diff is too large to show in full.</div>}
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
@for (t of threadsAt(l); track t.id) {
|
|
516
|
-
<div class="o-thread" [attr.data-testid]="'thread-' + t.id">
|
|
517
|
-
<div class="o-cb o-col" style="gap:6px;padding:10px 12px">
|
|
518
|
-
@for (c of t.comments; track c.id) {
|
|
519
|
-
<span class="o-sm"><b>{{ people().name(c.authorId) }}</b><span class="o-faint"> · {{ ago(c.createdAt) }}{{ c.pending ? ' · pending' : '' }}</span></span>
|
|
520
|
-
<span style="white-space:pre-wrap">{{ c.body }}</span>
|
|
521
|
-
}
|
|
601
|
+
@if (mode() === 'split') {
|
|
602
|
+
<div class="o-code split" style="overflow-x:auto" role="table" [attr.aria-label]="'Changes to ' + file().path + ', side by side'">
|
|
603
|
+
@for (h of d.hunks; track $index; let hi = $index) {
|
|
604
|
+
<div class="hunk o-mono full" role="row">{{ h.header }}</div>
|
|
605
|
+
@for (r of splitRows()[hi]; track $index) {
|
|
606
|
+
@if (r.left; as l) {
|
|
607
|
+
<div class="o-ln side-l" role="row" [class.o-del]="l.kind === 'delete'" [attr.data-testid]="'split-old-' + l.old">
|
|
608
|
+
<span class="o-n" role="rowheader" [class.cmt]="canComment()" (click)="startComment(l)" [attr.title]="canComment() ? 'Comment on this line' : null">{{ l.old }}</span>
|
|
609
|
+
<span class="o-sr-only">{{ l.kind === 'delete' ? 'removed' : '' }}</span>
|
|
610
|
+
<span class="o-t" role="cell" [innerHTML]="l.html || l.text"></span>
|
|
522
611
|
</div>
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
612
|
+
} @else {<div class="o-ln side-l empty-side" aria-hidden="true"><span class="o-n"></span><span class="o-t"></span></div>}
|
|
613
|
+
@if (r.right; as l) {
|
|
614
|
+
<div class="o-ln" role="row" [class.o-add]="l.kind === 'add'" [attr.data-testid]="'split-new-' + l.new">
|
|
615
|
+
<span class="o-n" role="rowheader" [class.cmt]="canComment()" (click)="startComment(l)" [attr.title]="canComment() ? 'Comment on this line' : null">{{ l.new }}</span>
|
|
616
|
+
<span class="o-sr-only">{{ l.kind === 'add' ? 'added' : '' }}</span>
|
|
617
|
+
<span class="o-t" role="cell" [innerHTML]="l.html || l.text"></span>
|
|
618
|
+
</div>
|
|
619
|
+
} @else {<div class="o-ln empty-side" aria-hidden="true"><span class="o-n"></span><span class="o-t"></span></div>}
|
|
620
|
+
@for (l of rowLines(r); track $index) {
|
|
621
|
+
@for (t of threadsAt(l); track t.id) {<div class="full"><ng-container *ngTemplateOutlet="threadTpl; context: { $implicit: t }" /></div>}
|
|
622
|
+
@if (drafting() === key(l)) {<div class="full"><ng-container *ngTemplateOutlet="draftTpl; context: { $implicit: l }" /></div>}
|
|
623
|
+
}
|
|
531
624
|
}
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
625
|
+
} @empty {<div class="o-empty o-sm full">{{ ignoreSpace() ? 'Only whitespace changed.' : 'No changes to show.' }}</div>}
|
|
626
|
+
</div>
|
|
627
|
+
} @else {
|
|
628
|
+
<div class="o-code" style="overflow-x:auto" role="table" [attr.aria-label]="'Changes to ' + file().path">
|
|
629
|
+
@for (h of d.hunks; track $index) {
|
|
630
|
+
<div class="hunk o-mono" role="row">{{ h.header }}</div>
|
|
631
|
+
@for (l of h.lines; track $index) {
|
|
632
|
+
<div class="o-ln" role="row" [class.o-add]="l.kind === 'add'" [class.o-del]="l.kind === 'delete'">
|
|
633
|
+
<span class="o-n" role="rowheader" [class.cmt]="canComment()" (click)="startComment(l)" [attr.title]="canComment() ? 'Comment on this line' : null">{{ l.kind === 'delete' ? l.old : l.new }}</span>
|
|
634
|
+
<span class="o-s" aria-hidden="true">{{ l.kind === 'add' ? '+' : l.kind === 'delete' ? '−' : ' ' }}</span>
|
|
635
|
+
<span class="o-sr-only">{{ l.kind === 'add' ? 'added' : l.kind === 'delete' ? 'removed' : '' }}</span>
|
|
636
|
+
<span class="o-t" role="cell" [innerHTML]="l.html || l.text"></span>
|
|
637
|
+
</div>
|
|
638
|
+
@for (t of threadsAt(l); track t.id) {<ng-container *ngTemplateOutlet="threadTpl; context: { $implicit: t }" />}
|
|
639
|
+
@if (drafting() === key(l)) {<ng-container *ngTemplateOutlet="draftTpl; context: { $implicit: l }" />}
|
|
537
640
|
}
|
|
641
|
+
} @empty {
|
|
642
|
+
<div class="o-empty o-sm">{{ ignoreSpace() ? 'Only whitespace changed.' : 'No changes to show.' }}</div>
|
|
538
643
|
}
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
}
|
|
542
|
-
</div>
|
|
644
|
+
</div>
|
|
645
|
+
}
|
|
543
646
|
} @else {
|
|
544
647
|
<div class="o-empty o-sm">Loading…</div>
|
|
545
648
|
}
|
|
546
649
|
}
|
|
547
650
|
</div>
|
|
548
|
-
|
|
651
|
+
|
|
652
|
+
<ng-template #threadTpl let-t>
|
|
653
|
+
<div class="o-thread" [attr.data-testid]="'thread-' + t.id">
|
|
654
|
+
<div class="o-cb o-col" style="gap:6px;padding:10px 12px">
|
|
655
|
+
@for (c of t.comments; track c.id) {
|
|
656
|
+
<span class="o-sm"><b>{{ people().name(c.authorId) }}</b><span class="o-faint"> · {{ ago(c.createdAt) }}</span>
|
|
657
|
+
@if (c.pending) {<span class="o-chip o-amb" style="margin-left:6px">Pending</span>
|
|
658
|
+
@if (c.authorId === me()) {<button type="button" class="o-lnk" style="margin-left:6px;color:var(--o-mut)" (click)="discard.emit(c.id)" [attr.aria-label]="'Discard pending comment: ' + c.body">Discard</button>}
|
|
659
|
+
}
|
|
660
|
+
</span>
|
|
661
|
+
<span style="white-space:pre-wrap">{{ c.body }}</span>
|
|
662
|
+
}
|
|
663
|
+
</div>
|
|
664
|
+
@if (canComment()) {
|
|
665
|
+
<div class="o-cf">
|
|
666
|
+
<input class="o-in o-sm" placeholder="Reply…" aria-label="Reply" [ngModel]="replies()[t.id] ?? ''" (ngModelChange)="setReply(t.id, $event)" (keydown.enter)="sendReply(t.id)" />
|
|
667
|
+
<button type="button" class="o-btn o-sm" (click)="sendReply(t.id)">Reply</button>
|
|
668
|
+
<button type="button" class="o-btn o-sm" (click)="resolve.emit({ threadId: t.id, resolved: !t.resolved })">{{ t.resolved ? 'Unresolve' : 'Resolve' }}</button>
|
|
669
|
+
</div>
|
|
670
|
+
}
|
|
671
|
+
</div>
|
|
672
|
+
</ng-template>
|
|
673
|
+
|
|
674
|
+
<ng-template #draftTpl let-l>
|
|
675
|
+
<div class="o-thread"><div class="o-cb o-col" style="gap:8px;padding:10px 12px">
|
|
676
|
+
<textarea class="o-in" rows="2" [placeholder]="'Leave a comment on line ' + lineOf(l)" [attr.aria-label]="'Comment on line ' + lineOf(l)" [ngModel]="draftBody()" (ngModelChange)="draftBody.set($event)" data-testid="line-comment"></textarea>
|
|
677
|
+
<div class="o-flex o-wrap"><span class="o-faint o-xs">Markdown works.</span>
|
|
678
|
+
<button type="button" class="o-btn o-sm o-r" (click)="drafting.set('')">Cancel</button>
|
|
679
|
+
<button type="button" class="o-btn o-sm" [disabled]="!draftBody().trim()" (click)="send(l, false)" data-testid="line-comment-add">Add single comment</button>
|
|
680
|
+
<button type="button" class="o-btn o-sm o-pri" [disabled]="!draftBody().trim()" (click)="send(l, true)" data-testid="line-comment-review">{{ reviewing() ? 'Add review comment' : 'Start a review' }}</button>
|
|
681
|
+
</div>
|
|
682
|
+
</div></div>
|
|
683
|
+
</ng-template>
|
|
684
|
+
`, isInline: true, styles: [":host ::ng-deep [class^=tk-k]{color:var(--o-tk-k)}:host ::ng-deep [class^=tk-s],:host ::ng-deep [class^=tk-l]{color:var(--o-tk-s)}:host ::ng-deep [class^=tk-c]{color:var(--o-tk-c);font-style:italic}:host ::ng-deep [class^=tk-m]{color:var(--o-tk-n)}:host ::ng-deep .tk-nf,:host ::ng-deep .tk-nx,:host ::ng-deep .tk-fm{color:var(--o-tk-f)}:host ::ng-deep .tk-nb,:host ::ng-deep .tk-nt,:host ::ng-deep .tk-bp{color:var(--o-tk-p)}:host ::ng-deep .tk-gi{color:var(--o-add)}:host ::ng-deep .tk-gd{color:var(--o-bad-tx)}\n", ".o-ln .o-n{cursor:default;width:44px}.o-ln .o-n.cmt{cursor:pointer}.o-ln .o-n.cmt:hover{color:var(--o-acc-tx)}.hunk{color:var(--o-dim);background:var(--o-well);padding:2px 12px;font-size:11px}.split{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr)}.split .side-l{border-right:1px solid var(--o-line-soft)}.split .full{grid-column:1 / -1}.split .o-ln .o-t{white-space:pre-wrap;word-break:break-all}.empty-side{background:var(--o-well)}.viewed{display:inline-flex;align-items:center;gap:6px}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
549
685
|
}
|
|
550
686
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: DiffView, decorators: [{
|
|
551
687
|
type: Component,
|
|
552
|
-
args: [{ selector: 'o-diff', imports: [FormsModule], changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
553
|
-
<div class="o-card" [attr.data-testid]="'diff-' + file().path">
|
|
688
|
+
args: [{ selector: 'o-diff', imports: [FormsModule, NgTemplateOutlet], changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
689
|
+
<div class="o-card" [attr.id]="'file-' + file().path" [attr.data-testid]="'diff-' + file().path">
|
|
554
690
|
<div class="o-ch" style="background:var(--o-well)">
|
|
555
|
-
<button type="button" class="o-btn o-ghost o-xs" (click)="
|
|
691
|
+
<button type="button" class="o-btn o-ghost o-xs" (click)="toggleOpen()" [attr.aria-expanded]="open()" [attr.aria-label]="(open() ? 'Collapse ' : 'Expand ') + file().path">{{ open() ? '▾' : '▸' }}</button>
|
|
556
692
|
<span class="o-mono o-sm o-ell">{{ file().oldPath && file().oldPath !== file().path ? file().oldPath + ' → ' : '' }}{{ file().path }}</span>
|
|
557
693
|
<span class="o-grn-t o-sm">+{{ file().additions }}</span><span class="o-red-t o-sm">−{{ file().deletions }}</span>
|
|
558
694
|
@if (threadsHere().length) {<span class="o-faint o-sm">💬 {{ threadsHere().length }}</span>}
|
|
559
|
-
<span class="o-r"
|
|
695
|
+
<span class="o-r o-flex" style="gap:6px">
|
|
696
|
+
@if (open() && diff() && context() < 100 && !file().binary) {<button type="button" class="o-btn o-xs" (click)="moreContext()" [attr.aria-label]="'Show more context in ' + file().path">More context</button>}
|
|
697
|
+
<ng-content />
|
|
698
|
+
@if (viewable()) {
|
|
699
|
+
<button type="button" class="o-btn o-xs viewed" role="checkbox" [attr.aria-checked]="viewed()" [attr.aria-label]="'Viewed ' + file().path" (click)="viewedChange.emit(!viewed())"><span class="o-cbx" [class.o-on]="viewed()" style="width:12px;height:12px"></span>Viewed</button>
|
|
700
|
+
}
|
|
701
|
+
</span>
|
|
560
702
|
</div>
|
|
561
703
|
@if (open()) {
|
|
562
704
|
@if (file().binary) {
|
|
563
705
|
<div class="o-empty o-sm">A binary file.</div>
|
|
706
|
+
} @else if (waiting()) {
|
|
707
|
+
<div class="o-empty o-sm o-col" style="gap:8px;align-items:center">
|
|
708
|
+
<span>{{ file().additions + file().deletions }} changed lines.</span>
|
|
709
|
+
<button type="button" class="o-btn o-sm" (click)="waiting.set(false); load()">Load diff</button>
|
|
710
|
+
</div>
|
|
564
711
|
} @else if (error()) {
|
|
565
712
|
<div class="o-note o-red" role="alert">{{ error() }}</div>
|
|
566
713
|
} @else if (diff(); as d) {
|
|
567
714
|
@if (d.truncated) {<div class="o-note">This diff is too large to show in full.</div>}
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
@for (t of threadsAt(l); track t.id) {
|
|
579
|
-
<div class="o-thread" [attr.data-testid]="'thread-' + t.id">
|
|
580
|
-
<div class="o-cb o-col" style="gap:6px;padding:10px 12px">
|
|
581
|
-
@for (c of t.comments; track c.id) {
|
|
582
|
-
<span class="o-sm"><b>{{ people().name(c.authorId) }}</b><span class="o-faint"> · {{ ago(c.createdAt) }}{{ c.pending ? ' · pending' : '' }}</span></span>
|
|
583
|
-
<span style="white-space:pre-wrap">{{ c.body }}</span>
|
|
584
|
-
}
|
|
715
|
+
@if (mode() === 'split') {
|
|
716
|
+
<div class="o-code split" style="overflow-x:auto" role="table" [attr.aria-label]="'Changes to ' + file().path + ', side by side'">
|
|
717
|
+
@for (h of d.hunks; track $index; let hi = $index) {
|
|
718
|
+
<div class="hunk o-mono full" role="row">{{ h.header }}</div>
|
|
719
|
+
@for (r of splitRows()[hi]; track $index) {
|
|
720
|
+
@if (r.left; as l) {
|
|
721
|
+
<div class="o-ln side-l" role="row" [class.o-del]="l.kind === 'delete'" [attr.data-testid]="'split-old-' + l.old">
|
|
722
|
+
<span class="o-n" role="rowheader" [class.cmt]="canComment()" (click)="startComment(l)" [attr.title]="canComment() ? 'Comment on this line' : null">{{ l.old }}</span>
|
|
723
|
+
<span class="o-sr-only">{{ l.kind === 'delete' ? 'removed' : '' }}</span>
|
|
724
|
+
<span class="o-t" role="cell" [innerHTML]="l.html || l.text"></span>
|
|
585
725
|
</div>
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
726
|
+
} @else {<div class="o-ln side-l empty-side" aria-hidden="true"><span class="o-n"></span><span class="o-t"></span></div>}
|
|
727
|
+
@if (r.right; as l) {
|
|
728
|
+
<div class="o-ln" role="row" [class.o-add]="l.kind === 'add'" [attr.data-testid]="'split-new-' + l.new">
|
|
729
|
+
<span class="o-n" role="rowheader" [class.cmt]="canComment()" (click)="startComment(l)" [attr.title]="canComment() ? 'Comment on this line' : null">{{ l.new }}</span>
|
|
730
|
+
<span class="o-sr-only">{{ l.kind === 'add' ? 'added' : '' }}</span>
|
|
731
|
+
<span class="o-t" role="cell" [innerHTML]="l.html || l.text"></span>
|
|
732
|
+
</div>
|
|
733
|
+
} @else {<div class="o-ln empty-side" aria-hidden="true"><span class="o-n"></span><span class="o-t"></span></div>}
|
|
734
|
+
@for (l of rowLines(r); track $index) {
|
|
735
|
+
@for (t of threadsAt(l); track t.id) {<div class="full"><ng-container *ngTemplateOutlet="threadTpl; context: { $implicit: t }" /></div>}
|
|
736
|
+
@if (drafting() === key(l)) {<div class="full"><ng-container *ngTemplateOutlet="draftTpl; context: { $implicit: l }" /></div>}
|
|
737
|
+
}
|
|
594
738
|
}
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
739
|
+
} @empty {<div class="o-empty o-sm full">{{ ignoreSpace() ? 'Only whitespace changed.' : 'No changes to show.' }}</div>}
|
|
740
|
+
</div>
|
|
741
|
+
} @else {
|
|
742
|
+
<div class="o-code" style="overflow-x:auto" role="table" [attr.aria-label]="'Changes to ' + file().path">
|
|
743
|
+
@for (h of d.hunks; track $index) {
|
|
744
|
+
<div class="hunk o-mono" role="row">{{ h.header }}</div>
|
|
745
|
+
@for (l of h.lines; track $index) {
|
|
746
|
+
<div class="o-ln" role="row" [class.o-add]="l.kind === 'add'" [class.o-del]="l.kind === 'delete'">
|
|
747
|
+
<span class="o-n" role="rowheader" [class.cmt]="canComment()" (click)="startComment(l)" [attr.title]="canComment() ? 'Comment on this line' : null">{{ l.kind === 'delete' ? l.old : l.new }}</span>
|
|
748
|
+
<span class="o-s" aria-hidden="true">{{ l.kind === 'add' ? '+' : l.kind === 'delete' ? '−' : ' ' }}</span>
|
|
749
|
+
<span class="o-sr-only">{{ l.kind === 'add' ? 'added' : l.kind === 'delete' ? 'removed' : '' }}</span>
|
|
750
|
+
<span class="o-t" role="cell" [innerHTML]="l.html || l.text"></span>
|
|
751
|
+
</div>
|
|
752
|
+
@for (t of threadsAt(l); track t.id) {<ng-container *ngTemplateOutlet="threadTpl; context: { $implicit: t }" />}
|
|
753
|
+
@if (drafting() === key(l)) {<ng-container *ngTemplateOutlet="draftTpl; context: { $implicit: l }" />}
|
|
600
754
|
}
|
|
755
|
+
} @empty {
|
|
756
|
+
<div class="o-empty o-sm">{{ ignoreSpace() ? 'Only whitespace changed.' : 'No changes to show.' }}</div>
|
|
601
757
|
}
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
}
|
|
605
|
-
</div>
|
|
758
|
+
</div>
|
|
759
|
+
}
|
|
606
760
|
} @else {
|
|
607
761
|
<div class="o-empty o-sm">Loading…</div>
|
|
608
762
|
}
|
|
609
763
|
}
|
|
610
764
|
</div>
|
|
611
|
-
|
|
612
|
-
|
|
765
|
+
|
|
766
|
+
<ng-template #threadTpl let-t>
|
|
767
|
+
<div class="o-thread" [attr.data-testid]="'thread-' + t.id">
|
|
768
|
+
<div class="o-cb o-col" style="gap:6px;padding:10px 12px">
|
|
769
|
+
@for (c of t.comments; track c.id) {
|
|
770
|
+
<span class="o-sm"><b>{{ people().name(c.authorId) }}</b><span class="o-faint"> · {{ ago(c.createdAt) }}</span>
|
|
771
|
+
@if (c.pending) {<span class="o-chip o-amb" style="margin-left:6px">Pending</span>
|
|
772
|
+
@if (c.authorId === me()) {<button type="button" class="o-lnk" style="margin-left:6px;color:var(--o-mut)" (click)="discard.emit(c.id)" [attr.aria-label]="'Discard pending comment: ' + c.body">Discard</button>}
|
|
773
|
+
}
|
|
774
|
+
</span>
|
|
775
|
+
<span style="white-space:pre-wrap">{{ c.body }}</span>
|
|
776
|
+
}
|
|
777
|
+
</div>
|
|
778
|
+
@if (canComment()) {
|
|
779
|
+
<div class="o-cf">
|
|
780
|
+
<input class="o-in o-sm" placeholder="Reply…" aria-label="Reply" [ngModel]="replies()[t.id] ?? ''" (ngModelChange)="setReply(t.id, $event)" (keydown.enter)="sendReply(t.id)" />
|
|
781
|
+
<button type="button" class="o-btn o-sm" (click)="sendReply(t.id)">Reply</button>
|
|
782
|
+
<button type="button" class="o-btn o-sm" (click)="resolve.emit({ threadId: t.id, resolved: !t.resolved })">{{ t.resolved ? 'Unresolve' : 'Resolve' }}</button>
|
|
783
|
+
</div>
|
|
784
|
+
}
|
|
785
|
+
</div>
|
|
786
|
+
</ng-template>
|
|
787
|
+
|
|
788
|
+
<ng-template #draftTpl let-l>
|
|
789
|
+
<div class="o-thread"><div class="o-cb o-col" style="gap:8px;padding:10px 12px">
|
|
790
|
+
<textarea class="o-in" rows="2" [placeholder]="'Leave a comment on line ' + lineOf(l)" [attr.aria-label]="'Comment on line ' + lineOf(l)" [ngModel]="draftBody()" (ngModelChange)="draftBody.set($event)" data-testid="line-comment"></textarea>
|
|
791
|
+
<div class="o-flex o-wrap"><span class="o-faint o-xs">Markdown works.</span>
|
|
792
|
+
<button type="button" class="o-btn o-sm o-r" (click)="drafting.set('')">Cancel</button>
|
|
793
|
+
<button type="button" class="o-btn o-sm" [disabled]="!draftBody().trim()" (click)="send(l, false)" data-testid="line-comment-add">Add single comment</button>
|
|
794
|
+
<button type="button" class="o-btn o-sm o-pri" [disabled]="!draftBody().trim()" (click)="send(l, true)" data-testid="line-comment-review">{{ reviewing() ? 'Add review comment' : 'Start a review' }}</button>
|
|
795
|
+
</div>
|
|
796
|
+
</div></div>
|
|
797
|
+
</ng-template>
|
|
798
|
+
`, styles: [":host ::ng-deep [class^=tk-k]{color:var(--o-tk-k)}:host ::ng-deep [class^=tk-s],:host ::ng-deep [class^=tk-l]{color:var(--o-tk-s)}:host ::ng-deep [class^=tk-c]{color:var(--o-tk-c);font-style:italic}:host ::ng-deep [class^=tk-m]{color:var(--o-tk-n)}:host ::ng-deep .tk-nf,:host ::ng-deep .tk-nx,:host ::ng-deep .tk-fm{color:var(--o-tk-f)}:host ::ng-deep .tk-nb,:host ::ng-deep .tk-nt,:host ::ng-deep .tk-bp{color:var(--o-tk-p)}:host ::ng-deep .tk-gi{color:var(--o-add)}:host ::ng-deep .tk-gd{color:var(--o-bad-tx)}\n", ".o-ln .o-n{cursor:default;width:44px}.o-ln .o-n.cmt{cursor:pointer}.o-ln .o-n.cmt:hover{color:var(--o-acc-tx)}.hunk{color:var(--o-dim);background:var(--o-well);padding:2px 12px;font-size:11px}.split{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr)}.split .side-l{border-right:1px solid var(--o-line-soft)}.split .full{grid-column:1 / -1}.split .o-ln .o-t{white-space:pre-wrap;word-break:break-all}.empty-side{background:var(--o-well)}.viewed{display:inline-flex;align-items:center;gap:6px}\n"] }]
|
|
799
|
+
}], ctorParameters: () => [], propDecorators: { repo: [{ type: i0.Input, args: [{ isSignal: true, alias: "repo", required: true }] }], file: [{ type: i0.Input, args: [{ isSignal: true, alias: "file", required: true }] }], head: [{ type: i0.Input, args: [{ isSignal: true, alias: "head", required: true }] }], base: [{ type: i0.Input, args: [{ isSignal: true, alias: "base", required: false }] }], threads: [{ type: i0.Input, args: [{ isSignal: true, alias: "threads", required: false }] }], canComment: [{ type: i0.Input, args: [{ isSignal: true, alias: "canComment", required: false }] }], mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], ignoreSpace: [{ type: i0.Input, args: [{ isSignal: true, alias: "ignoreSpace", required: false }] }], viewed: [{ type: i0.Input, args: [{ isSignal: true, alias: "viewed", required: false }] }], reviewing: [{ type: i0.Input, args: [{ isSignal: true, alias: "reviewing", required: false }] }], comment: [{ type: i0.Output, args: ["comment"] }], reply: [{ type: i0.Output, args: ["reply"] }], resolve: [{ type: i0.Output, args: ["resolve"] }], discard: [{ type: i0.Output, args: ["discard"] }], viewedChange: [{ type: i0.Output, args: ["viewedChange"] }] } });
|
|
800
|
+
/** Pairs a hunk's lines side by side: context on both sides, a run of removals beside the run of additions that follows it. */
|
|
801
|
+
function pair(lines) {
|
|
802
|
+
const rows = [];
|
|
803
|
+
let i = 0;
|
|
804
|
+
while (i < lines.length) {
|
|
805
|
+
const l = lines[i];
|
|
806
|
+
if (l.kind === 'context') {
|
|
807
|
+
rows.push({ left: l, right: l });
|
|
808
|
+
i++;
|
|
809
|
+
continue;
|
|
810
|
+
}
|
|
811
|
+
const dels = [];
|
|
812
|
+
const adds = [];
|
|
813
|
+
while (i < lines.length && lines[i].kind === 'delete')
|
|
814
|
+
dels.push(lines[i++]);
|
|
815
|
+
while (i < lines.length && lines[i].kind === 'add')
|
|
816
|
+
adds.push(lines[i++]);
|
|
817
|
+
for (let k = 0; k < Math.max(dels.length, adds.length); k++)
|
|
818
|
+
rows.push({ left: dels[k], right: adds[k] });
|
|
819
|
+
}
|
|
820
|
+
return rows;
|
|
821
|
+
}
|
|
613
822
|
|
|
614
823
|
// /{org}/{project}/code (prototype "code_repos"): the project's repositories.
|
|
615
824
|
class ReposPage {
|
|
@@ -1686,7 +1895,7 @@ class CommitPage extends RepoPage {
|
|
|
1686
1895
|
<div class="o-card"><div class="o-empty">Loading…</div></div>
|
|
1687
1896
|
}
|
|
1688
1897
|
</div>
|
|
1689
|
-
`, isInline: true, dependencies: [{ kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: DiffView, selector: "o-diff", inputs: ["repo", "file", "head", "base", "threads", "canComment"], outputs: ["comment", "reply", "resolve"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
1898
|
+
`, isInline: true, dependencies: [{ kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: DiffView, selector: "o-diff", inputs: ["repo", "file", "head", "base", "threads", "canComment", "mode", "ignoreSpace", "viewed", "reviewing"], outputs: ["comment", "reply", "resolve", "discard", "viewedChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
1690
1899
|
}
|
|
1691
1900
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: CommitPage, decorators: [{
|
|
1692
1901
|
type: Component,
|
|
@@ -2176,6 +2385,17 @@ class PullPage extends RepoPage {
|
|
|
2176
2385
|
this.initials = initials;
|
|
2177
2386
|
this.dot = checkDot;
|
|
2178
2387
|
this.threads = computed(() => this.timeline().threads, ...(ngDevMode ? [{ debugName: "threads" }] : /* istanbul ignore next */ []));
|
|
2388
|
+
this.mode = signal(readPref('origine.diff.mode') === 'split' ? 'split' : 'unified', ...(ngDevMode ? [{ debugName: "mode" }] : /* istanbul ignore next */ []));
|
|
2389
|
+
this.hideWs = signal(false, ...(ngDevMode ? [{ debugName: "hideWs" }] : /* istanbul ignore next */ []));
|
|
2390
|
+
/** Files marked viewed, kept per pull request head in this browser (a new push resets them). */
|
|
2391
|
+
this.viewed = signal({}, ...(ngDevMode ? [{ debugName: "viewed" }] : /* istanbul ignore next */ []));
|
|
2392
|
+
this.viewedCount = computed(() => (this.cmp()?.files ?? []).filter((f) => this.viewed()[f.path]).length, ...(ngDevMode ? [{ debugName: "viewedCount" }] : /* istanbul ignore next */ []));
|
|
2393
|
+
/** The caller's pending review comments (shown only to them until the review is submitted). */
|
|
2394
|
+
this.myPending = computed(() => {
|
|
2395
|
+
const me = this.host.meId();
|
|
2396
|
+
const t = this.timeline();
|
|
2397
|
+
return [...t.comments, ...t.threads.flatMap((th) => th.comments)].filter((c, i, all) => c.pending && c.authorId === me && all.findIndex((x) => x.id === c.id) === i);
|
|
2398
|
+
}, ...(ngDevMode ? [{ debugName: "myPending" }] : /* istanbul ignore next */ []));
|
|
2179
2399
|
this.openThreads = computed(() => this.threads().filter((t) => !t.resolved && !t.outdated).length, ...(ngDevMode ? [{ debugName: "openThreads" }] : /* istanbul ignore next */ []));
|
|
2180
2400
|
this.tabs = computed(() => {
|
|
2181
2401
|
const p = this.pr();
|
|
@@ -2194,8 +2414,12 @@ class PullPage extends RepoPage {
|
|
|
2194
2414
|
this.approvedByMe = computed(() => (this.pr()?.reviews ?? []).some((r) => r.authorId === this.host.meId() && r.state === 'approved' && !r.stale), ...(ngDevMode ? [{ debugName: "approvedByMe" }] : /* istanbul ignore next */ []));
|
|
2195
2415
|
this.reviewKinds = computed(() => {
|
|
2196
2416
|
const need = this.pr()?.gate.rows.find((r) => /required by branch protection|requires/.test(r.text + (r.meta ?? '')));
|
|
2417
|
+
const comment = { id: 'commented', label: 'Comment', hint: 'General feedback without approving.' };
|
|
2418
|
+
// the author (or a role that cannot push) still posts their pending comments as a review
|
|
2419
|
+
if (!this.pr()?.canReview)
|
|
2420
|
+
return [{ ...comment, hint: 'Post your pending comments.' }];
|
|
2197
2421
|
return [
|
|
2198
|
-
|
|
2422
|
+
comment,
|
|
2199
2423
|
{ id: 'approved', label: 'Approve', hint: need ? `Counts toward the approvals ${this.pr()?.target} requires.` : 'Approve these changes.' },
|
|
2200
2424
|
{ id: 'changes_requested', label: 'Request changes', hint: 'Blocks merge until you approve.' },
|
|
2201
2425
|
];
|
|
@@ -2271,7 +2495,32 @@ class PullPage extends RepoPage {
|
|
|
2271
2495
|
this.run(this.api.comment(this.projectId(), this.name(), this.number, { body }), 'Could not add the comment.', () => this.draft.set(''));
|
|
2272
2496
|
}
|
|
2273
2497
|
lineComment(c) {
|
|
2274
|
-
this.run(this.api.comment(this.projectId(), this.name(), this.number, { body: c.body, path: c.path, side: c.side, line: c.line }), 'Could not add the comment.')
|
|
2498
|
+
this.run(this.api.comment(this.projectId(), this.name(), this.number, { body: c.body, path: c.path, side: c.side, line: c.line, pending: c.pending }), 'Could not add the comment.', () => { if (c.pending)
|
|
2499
|
+
this.note.set('Added to your review. Submit it from Review changes to post your comments.'); });
|
|
2500
|
+
}
|
|
2501
|
+
discard(id) {
|
|
2502
|
+
this.run(this.api.discardComment(this.projectId(), this.name(), this.number, id), 'Could not discard the comment.');
|
|
2503
|
+
}
|
|
2504
|
+
setMode(m) {
|
|
2505
|
+
this.mode.set(m);
|
|
2506
|
+
writePref('origine.diff.mode', m);
|
|
2507
|
+
}
|
|
2508
|
+
viewedKey() {
|
|
2509
|
+
return `origine.viewed.${this.projectId()}.${this.name()}.${this.number}.${this.pr()?.headSha ?? ''}`;
|
|
2510
|
+
}
|
|
2511
|
+
isViewed(path) {
|
|
2512
|
+
return !!this.viewed()[path];
|
|
2513
|
+
}
|
|
2514
|
+
setViewed(path, on) {
|
|
2515
|
+
const v = { ...this.viewed(), [path]: on };
|
|
2516
|
+
if (!on)
|
|
2517
|
+
delete v[path];
|
|
2518
|
+
this.viewed.set(v);
|
|
2519
|
+
writePref(this.viewedKey(), JSON.stringify(v));
|
|
2520
|
+
}
|
|
2521
|
+
jump(path) {
|
|
2522
|
+
const el = this.el.nativeElement.querySelector(`[id="file-${CSS.escape(path)}"]`);
|
|
2523
|
+
el?.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
|
2275
2524
|
}
|
|
2276
2525
|
reply(r) {
|
|
2277
2526
|
this.run(this.api.comment(this.projectId(), this.name(), this.number, { body: r.body, threadId: r.threadId }), 'Could not reply.');
|
|
@@ -2310,7 +2559,16 @@ class PullPage extends RepoPage {
|
|
|
2310
2559
|
const project = this.projectId();
|
|
2311
2560
|
this.api.pull(project, this.name(), this.number).subscribe({
|
|
2312
2561
|
next: (p) => {
|
|
2562
|
+
const headChanged = this.pr()?.headSha !== p.headSha;
|
|
2313
2563
|
this.pr.set(p);
|
|
2564
|
+
if (headChanged) {
|
|
2565
|
+
try {
|
|
2566
|
+
this.viewed.set(JSON.parse(readPref(this.viewedKey()) || '{}'));
|
|
2567
|
+
}
|
|
2568
|
+
catch {
|
|
2569
|
+
this.viewed.set({});
|
|
2570
|
+
}
|
|
2571
|
+
}
|
|
2314
2572
|
if (first) {
|
|
2315
2573
|
this.method.set(p.mergeMethods[0] ?? 'merge');
|
|
2316
2574
|
this.delBranch.set(p.deleteBranchOnMerge);
|
|
@@ -2341,17 +2599,18 @@ class PullPage extends RepoPage {
|
|
|
2341
2599
|
<div class="o-tabs" role="tablist">
|
|
2342
2600
|
@for (t of tabs(); track t.id) {<button type="button" role="tab" class="o-tab" [class.o-on]="tab() === t.id" [attr.aria-selected]="tab() === t.id" (click)="pick(t.id)">{{ t.label }}@if (t.n !== undefined) {<span class="o-n">{{ t.n }}</span>}</button>}
|
|
2343
2601
|
<span class="o-r"></span>
|
|
2344
|
-
@if (p.state === 'open' && p.canReview) {
|
|
2602
|
+
@if (p.state === 'open' && (p.canReview || myPending().length)) {
|
|
2345
2603
|
<div class="anchor">
|
|
2346
2604
|
<button type="button" class="o-btn o-sm o-pri" (click)="reviewOpen.set(!reviewOpen())" [attr.aria-expanded]="reviewOpen()" aria-haspopup="dialog" data-testid="review-open">Review changes ⌄</button>
|
|
2347
2605
|
@if (reviewOpen()) {
|
|
2348
2606
|
<div class="o-menu" role="dialog" aria-label="Finish your review" style="right:0;top:36px;width:380px;padding:14px;display:flex;flex-direction:column;gap:10px">
|
|
2349
2607
|
<b>Finish your review</b>
|
|
2608
|
+
<span class="o-faint o-sm" data-testid="review-pending-note">{{ myPending().length ? myPending().length + ' pending ' + (myPending().length === 1 ? 'comment' : 'comments') + ' included' : 'No pending comments' }}</span>
|
|
2350
2609
|
<textarea class="o-in" rows="3" placeholder="Leave a comment" aria-label="Review comment" style="height:auto;padding:8px 10px" [ngModel]="revBody()" (ngModelChange)="revBody.set($event)"></textarea>
|
|
2351
2610
|
@for (k of reviewKinds(); track k.id) {
|
|
2352
2611
|
<button type="button" class="o-opt" style="padding:8px 10px" [class.o-on]="revKind() === k.id" (click)="revKind.set(k.id)"><span class="o-rad" [class.o-on]="revKind() === k.id"></span><span class="o-col" style="gap:1px"><b class="o-b5">{{ k.label }}</b><span class="o-faint o-sm">{{ k.hint }}</span></span></button>
|
|
2353
2612
|
}
|
|
2354
|
-
<div class="o-flex"><button type="button" class="o-btn o-sm o-pri o-r" (click)="submitReview()" [disabled]="busy() || (revKind() !== 'approved' && !revBody().trim())" data-testid="review-submit">Submit review</button></div>
|
|
2613
|
+
<div class="o-flex"><button type="button" class="o-btn o-sm o-pri o-r" (click)="submitReview()" [disabled]="busy() || (revKind() !== 'approved' && !revBody().trim() && !(revKind() === 'commented' && myPending().length))" data-testid="review-submit">Submit review</button></div>
|
|
2355
2614
|
</div>
|
|
2356
2615
|
}
|
|
2357
2616
|
</div>
|
|
@@ -2435,10 +2694,28 @@ class PullPage extends RepoPage {
|
|
|
2435
2694
|
</div>
|
|
2436
2695
|
}
|
|
2437
2696
|
@case ('files') {
|
|
2438
|
-
<div class="o-flex o-wrap"
|
|
2439
|
-
|
|
2440
|
-
<
|
|
2441
|
-
|
|
2697
|
+
<div class="o-flex o-wrap">
|
|
2698
|
+
<div class="o-seg" role="group" aria-label="Diff view"><button type="button" [class.o-on]="mode() === 'unified'" [attr.aria-pressed]="mode() === 'unified'" (click)="setMode('unified')">Unified</button><button type="button" [class.o-on]="mode() === 'split'" [attr.aria-pressed]="mode() === 'split'" (click)="setMode('split')" data-testid="diff-split">Split</button></div>
|
|
2699
|
+
<button type="button" class="o-btn o-sm" [class.o-on]="hideWs()" [attr.aria-pressed]="hideWs()" (click)="hideWs.set(!hideWs())" data-testid="hide-ws">Hide whitespace</button>
|
|
2700
|
+
@if (myPending().length) {<span class="o-chip o-amb" data-testid="pending-count">{{ myPending().length }} pending {{ myPending().length === 1 ? 'comment' : 'comments' }}</span>}
|
|
2701
|
+
<span class="o-r o-faint o-sm" data-testid="viewed-note">{{ viewedCount() }} of {{ cmp()?.files?.length ?? 0 }} files viewed · {{ openThreads() }} unresolved {{ openThreads() === 1 ? 'conversation' : 'conversations' }}</span>
|
|
2702
|
+
</div>
|
|
2703
|
+
<div class="files-split">
|
|
2704
|
+
@if ((cmp()?.files?.length ?? 0) > 1) {
|
|
2705
|
+
<div class="o-card files-tree" role="navigation" aria-label="Files">
|
|
2706
|
+
@for (f of cmp()?.files ?? []; track f.path) {
|
|
2707
|
+
<button type="button" class="o-row o-click o-sm" style="width:100%;border:none;background:none;text-align:left" (click)="jump(f.path)"><span class="o-cbx" [class.o-on]="isViewed(f.path)" aria-hidden="true"></span><span class="o-mono o-ell" style="flex:1">{{ f.path }}</span><span class="o-grn-t o-xs">+{{ f.additions }}</span></button>
|
|
2708
|
+
}
|
|
2709
|
+
</div>
|
|
2710
|
+
}
|
|
2711
|
+
<div class="o-col" style="gap:14px;min-width:0">
|
|
2712
|
+
@for (f of cmp()?.files ?? []; track f.path) {
|
|
2713
|
+
<o-diff [repo]="name()" [file]="f" [head]="p.headSha" [base]="p.mergeBase" [threads]="threads()" [canComment]="p.state === 'open'" [mode]="mode()" [ignoreSpace]="hideWs()"
|
|
2714
|
+
[viewed]="isViewed(f.path)" [reviewing]="myPending().length > 0" (viewedChange)="setViewed(f.path, $event)"
|
|
2715
|
+
(comment)="lineComment($event)" (reply)="reply($event)" (resolve)="resolve($event)" (discard)="discard($event)" />
|
|
2716
|
+
} @empty {<div class="o-card"><div class="o-empty">{{ cmp() ? 'No changes.' : 'Loading…' }}</div></div>}
|
|
2717
|
+
</div>
|
|
2718
|
+
</div>
|
|
2442
2719
|
}
|
|
2443
2720
|
@case ('checks') {
|
|
2444
2721
|
<div class="o-card">
|
|
@@ -2471,7 +2748,7 @@ class PullPage extends RepoPage {
|
|
|
2471
2748
|
<div class="o-card"><div class="o-empty">Loading…</div></div>
|
|
2472
2749
|
}
|
|
2473
2750
|
</div>
|
|
2474
|
-
`, isInline: true, styles: [".badge{padding:3px 10px;border-radius:12px;font-size:12px;font-weight:500;color:var(--o-acc-fg)}.anchor{position:relative;align-self:center}.merge{display:flex;align-items:center;position:relative}.merge .go{height:32px;padding:0 14px;border-radius:7px 0 0 7px;border:none;font-size:13px;font-weight:500;color:var(--o-acc-fg);background:var(--o-ok);cursor:pointer}.merge .mn{height:32px;padding:0 10px;border:none;border-radius:0 7px 7px 0;font-size:12px;color:var(--o-acc-fg);background:var(--o-ok);border-left:1px solid rgba(255,255,255,.25);cursor:pointer}.merge button:disabled{background:var(--o-line-mid);cursor:not-allowed}.tl{display:flex;flex-direction:column;gap:10px;padding-left:14px;border-left:2px solid var(--o-line);margin-left:14px}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: DiffView, selector: "o-diff", inputs: ["repo", "file", "head", "base", "threads", "canComment"], outputs: ["comment", "reply", "resolve"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
2751
|
+
`, isInline: true, styles: [".badge{padding:3px 10px;border-radius:12px;font-size:12px;font-weight:500;color:var(--o-acc-fg)}.anchor{position:relative;align-self:center}.merge{display:flex;align-items:center;position:relative}.merge .go{height:32px;padding:0 14px;border-radius:7px 0 0 7px;border:none;font-size:13px;font-weight:500;color:var(--o-acc-fg);background:var(--o-ok);cursor:pointer}.merge .mn{height:32px;padding:0 10px;border:none;border-radius:0 7px 7px 0;font-size:12px;color:var(--o-acc-fg);background:var(--o-ok);border-left:1px solid rgba(255,255,255,.25);cursor:pointer}.merge button:disabled{background:var(--o-line-mid);cursor:not-allowed}.tl{display:flex;flex-direction:column;gap:10px;padding-left:14px;border-left:2px solid var(--o-line);margin-left:14px}.files-split{display:grid;grid-template-columns:210px minmax(0,1fr);gap:14px;align-items:start}.files-split:has(>:only-child){grid-template-columns:minmax(0,1fr)}.files-tree{position:sticky;top:8px;max-height:calc(100vh - 120px);overflow:auto}@media(max-width:900px){.files-split{grid-template-columns:minmax(0,1fr)}.files-tree{display:none}}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: DiffView, selector: "o-diff", inputs: ["repo", "file", "head", "base", "threads", "canComment", "mode", "ignoreSpace", "viewed", "reviewing"], outputs: ["comment", "reply", "resolve", "discard", "viewedChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
2475
2752
|
}
|
|
2476
2753
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: PullPage, decorators: [{
|
|
2477
2754
|
type: Component,
|
|
@@ -2492,17 +2769,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImpo
|
|
|
2492
2769
|
<div class="o-tabs" role="tablist">
|
|
2493
2770
|
@for (t of tabs(); track t.id) {<button type="button" role="tab" class="o-tab" [class.o-on]="tab() === t.id" [attr.aria-selected]="tab() === t.id" (click)="pick(t.id)">{{ t.label }}@if (t.n !== undefined) {<span class="o-n">{{ t.n }}</span>}</button>}
|
|
2494
2771
|
<span class="o-r"></span>
|
|
2495
|
-
@if (p.state === 'open' && p.canReview) {
|
|
2772
|
+
@if (p.state === 'open' && (p.canReview || myPending().length)) {
|
|
2496
2773
|
<div class="anchor">
|
|
2497
2774
|
<button type="button" class="o-btn o-sm o-pri" (click)="reviewOpen.set(!reviewOpen())" [attr.aria-expanded]="reviewOpen()" aria-haspopup="dialog" data-testid="review-open">Review changes ⌄</button>
|
|
2498
2775
|
@if (reviewOpen()) {
|
|
2499
2776
|
<div class="o-menu" role="dialog" aria-label="Finish your review" style="right:0;top:36px;width:380px;padding:14px;display:flex;flex-direction:column;gap:10px">
|
|
2500
2777
|
<b>Finish your review</b>
|
|
2778
|
+
<span class="o-faint o-sm" data-testid="review-pending-note">{{ myPending().length ? myPending().length + ' pending ' + (myPending().length === 1 ? 'comment' : 'comments') + ' included' : 'No pending comments' }}</span>
|
|
2501
2779
|
<textarea class="o-in" rows="3" placeholder="Leave a comment" aria-label="Review comment" style="height:auto;padding:8px 10px" [ngModel]="revBody()" (ngModelChange)="revBody.set($event)"></textarea>
|
|
2502
2780
|
@for (k of reviewKinds(); track k.id) {
|
|
2503
2781
|
<button type="button" class="o-opt" style="padding:8px 10px" [class.o-on]="revKind() === k.id" (click)="revKind.set(k.id)"><span class="o-rad" [class.o-on]="revKind() === k.id"></span><span class="o-col" style="gap:1px"><b class="o-b5">{{ k.label }}</b><span class="o-faint o-sm">{{ k.hint }}</span></span></button>
|
|
2504
2782
|
}
|
|
2505
|
-
<div class="o-flex"><button type="button" class="o-btn o-sm o-pri o-r" (click)="submitReview()" [disabled]="busy() || (revKind() !== 'approved' && !revBody().trim())" data-testid="review-submit">Submit review</button></div>
|
|
2783
|
+
<div class="o-flex"><button type="button" class="o-btn o-sm o-pri o-r" (click)="submitReview()" [disabled]="busy() || (revKind() !== 'approved' && !revBody().trim() && !(revKind() === 'commented' && myPending().length))" data-testid="review-submit">Submit review</button></div>
|
|
2506
2784
|
</div>
|
|
2507
2785
|
}
|
|
2508
2786
|
</div>
|
|
@@ -2586,10 +2864,28 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImpo
|
|
|
2586
2864
|
</div>
|
|
2587
2865
|
}
|
|
2588
2866
|
@case ('files') {
|
|
2589
|
-
<div class="o-flex o-wrap"
|
|
2590
|
-
|
|
2591
|
-
<
|
|
2592
|
-
|
|
2867
|
+
<div class="o-flex o-wrap">
|
|
2868
|
+
<div class="o-seg" role="group" aria-label="Diff view"><button type="button" [class.o-on]="mode() === 'unified'" [attr.aria-pressed]="mode() === 'unified'" (click)="setMode('unified')">Unified</button><button type="button" [class.o-on]="mode() === 'split'" [attr.aria-pressed]="mode() === 'split'" (click)="setMode('split')" data-testid="diff-split">Split</button></div>
|
|
2869
|
+
<button type="button" class="o-btn o-sm" [class.o-on]="hideWs()" [attr.aria-pressed]="hideWs()" (click)="hideWs.set(!hideWs())" data-testid="hide-ws">Hide whitespace</button>
|
|
2870
|
+
@if (myPending().length) {<span class="o-chip o-amb" data-testid="pending-count">{{ myPending().length }} pending {{ myPending().length === 1 ? 'comment' : 'comments' }}</span>}
|
|
2871
|
+
<span class="o-r o-faint o-sm" data-testid="viewed-note">{{ viewedCount() }} of {{ cmp()?.files?.length ?? 0 }} files viewed · {{ openThreads() }} unresolved {{ openThreads() === 1 ? 'conversation' : 'conversations' }}</span>
|
|
2872
|
+
</div>
|
|
2873
|
+
<div class="files-split">
|
|
2874
|
+
@if ((cmp()?.files?.length ?? 0) > 1) {
|
|
2875
|
+
<div class="o-card files-tree" role="navigation" aria-label="Files">
|
|
2876
|
+
@for (f of cmp()?.files ?? []; track f.path) {
|
|
2877
|
+
<button type="button" class="o-row o-click o-sm" style="width:100%;border:none;background:none;text-align:left" (click)="jump(f.path)"><span class="o-cbx" [class.o-on]="isViewed(f.path)" aria-hidden="true"></span><span class="o-mono o-ell" style="flex:1">{{ f.path }}</span><span class="o-grn-t o-xs">+{{ f.additions }}</span></button>
|
|
2878
|
+
}
|
|
2879
|
+
</div>
|
|
2880
|
+
}
|
|
2881
|
+
<div class="o-col" style="gap:14px;min-width:0">
|
|
2882
|
+
@for (f of cmp()?.files ?? []; track f.path) {
|
|
2883
|
+
<o-diff [repo]="name()" [file]="f" [head]="p.headSha" [base]="p.mergeBase" [threads]="threads()" [canComment]="p.state === 'open'" [mode]="mode()" [ignoreSpace]="hideWs()"
|
|
2884
|
+
[viewed]="isViewed(f.path)" [reviewing]="myPending().length > 0" (viewedChange)="setViewed(f.path, $event)"
|
|
2885
|
+
(comment)="lineComment($event)" (reply)="reply($event)" (resolve)="resolve($event)" (discard)="discard($event)" />
|
|
2886
|
+
} @empty {<div class="o-card"><div class="o-empty">{{ cmp() ? 'No changes.' : 'Loading…' }}</div></div>}
|
|
2887
|
+
</div>
|
|
2888
|
+
</div>
|
|
2593
2889
|
}
|
|
2594
2890
|
@case ('checks') {
|
|
2595
2891
|
<div class="o-card">
|
|
@@ -2622,11 +2918,28 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImpo
|
|
|
2622
2918
|
<div class="o-card"><div class="o-empty">Loading…</div></div>
|
|
2623
2919
|
}
|
|
2624
2920
|
</div>
|
|
2625
|
-
`, styles: [".badge{padding:3px 10px;border-radius:12px;font-size:12px;font-weight:500;color:var(--o-acc-fg)}.anchor{position:relative;align-self:center}.merge{display:flex;align-items:center;position:relative}.merge .go{height:32px;padding:0 14px;border-radius:7px 0 0 7px;border:none;font-size:13px;font-weight:500;color:var(--o-acc-fg);background:var(--o-ok);cursor:pointer}.merge .mn{height:32px;padding:0 10px;border:none;border-radius:0 7px 7px 0;font-size:12px;color:var(--o-acc-fg);background:var(--o-ok);border-left:1px solid rgba(255,255,255,.25);cursor:pointer}.merge button:disabled{background:var(--o-line-mid);cursor:not-allowed}.tl{display:flex;flex-direction:column;gap:10px;padding-left:14px;border-left:2px solid var(--o-line);margin-left:14px}\n"] }]
|
|
2921
|
+
`, styles: [".badge{padding:3px 10px;border-radius:12px;font-size:12px;font-weight:500;color:var(--o-acc-fg)}.anchor{position:relative;align-self:center}.merge{display:flex;align-items:center;position:relative}.merge .go{height:32px;padding:0 14px;border-radius:7px 0 0 7px;border:none;font-size:13px;font-weight:500;color:var(--o-acc-fg);background:var(--o-ok);cursor:pointer}.merge .mn{height:32px;padding:0 10px;border:none;border-radius:0 7px 7px 0;font-size:12px;color:var(--o-acc-fg);background:var(--o-ok);border-left:1px solid rgba(255,255,255,.25);cursor:pointer}.merge button:disabled{background:var(--o-line-mid);cursor:not-allowed}.tl{display:flex;flex-direction:column;gap:10px;padding-left:14px;border-left:2px solid var(--o-line);margin-left:14px}.files-split{display:grid;grid-template-columns:210px minmax(0,1fr);gap:14px;align-items:start}.files-split:has(>:only-child){grid-template-columns:minmax(0,1fr)}.files-tree{position:sticky;top:8px;max-height:calc(100vh - 120px);overflow:auto}@media(max-width:900px){.files-split{grid-template-columns:minmax(0,1fr)}.files-tree{display:none}}\n"] }]
|
|
2626
2922
|
}], ctorParameters: () => [], propDecorators: { outside: [{
|
|
2627
2923
|
type: HostListener,
|
|
2628
2924
|
args: ['document:click', ['$event']]
|
|
2629
2925
|
}] } });
|
|
2926
|
+
// Per-viewer conveniences (the diff view, files viewed); storage can be missing or blocked.
|
|
2927
|
+
function readPref(key) {
|
|
2928
|
+
try {
|
|
2929
|
+
return localStorage.getItem(key) ?? '';
|
|
2930
|
+
}
|
|
2931
|
+
catch {
|
|
2932
|
+
return '';
|
|
2933
|
+
}
|
|
2934
|
+
}
|
|
2935
|
+
function writePref(key, value) {
|
|
2936
|
+
try {
|
|
2937
|
+
localStorage.setItem(key, value);
|
|
2938
|
+
}
|
|
2939
|
+
catch {
|
|
2940
|
+
/* private window or blocked storage: the setting lasts for this page only */
|
|
2941
|
+
}
|
|
2942
|
+
}
|
|
2630
2943
|
|
|
2631
2944
|
const BLANK = { id: '', pattern: '', blockDirectPush: true, blockForcePush: true, blockDeletion: true, requiredApprovals: 1, dismissStaleApprovals: true, requireUpToDate: false, requiredChecks: [] };
|
|
2632
2945
|
// /{org}/{project}/code/{repo}/settings?section= (prototype "code_settings"): general settings and
|
|
@@ -2965,5 +3278,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImpo
|
|
|
2965
3278
|
* Generated bundle index. Do not edit.
|
|
2966
3279
|
*/
|
|
2967
3280
|
|
|
2968
|
-
export { BranchesPage, CodeApi, CodeHost, CommitPage, CommitsPage, CurrentRepo, CurrentRepoPage, DiffView, FilePage, NewRepoPage, People, PullPage, PullsPage, RefPicker, RepoHomePage, RepoPage, RepoSettingsPage, ReposPage, TOKEN_CSS, ago, apiMessage, checkDot, copy, dayLabel, initials, langColour, shortDate, size };
|
|
3281
|
+
export { BranchesPage, CodeApi, CodeHost, CommitPage, CommitsPage, CurrentRepo, CurrentRepoPage, DiffView, FilePage, NewRepoPage, People, PullPage, PullsPage, RefPicker, RepoHomePage, RepoPage, RepoSettingsPage, ReposPage, TOKEN_CSS, ago, apiMessage, checkDot, copy, dayLabel, initials, langColour, pair, shortDate, size };
|
|
2969
3282
|
//# sourceMappingURL=chisquare-tech-origine-ngx-apps-code.mjs.map
|