@chisquare-tech/origine-ngx-apps-code 0.4.0 → 0.6.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.
|
@@ -65,8 +65,15 @@ class CodeApi {
|
|
|
65
65
|
tree(project, name, ref, path) {
|
|
66
66
|
return this.api.get(`${this.repoPath(project, name)}/tree`, { ref, path }).pipe(map((r) => data(r)));
|
|
67
67
|
}
|
|
68
|
-
blob(project, name, ref, path) {
|
|
69
|
-
|
|
68
|
+
blob(project, name, ref, path, withText = false) {
|
|
69
|
+
const p = { ref, path };
|
|
70
|
+
if (withText)
|
|
71
|
+
p['format'] = 'text';
|
|
72
|
+
return this.api.get(`${this.repoPath(project, name)}/blob`, p).pipe(map((r) => data(r)));
|
|
73
|
+
}
|
|
74
|
+
/** A commit from the browser (DEV-13): files, base64 uploads and deletes; from a new branch it can open a pull request. */
|
|
75
|
+
commitFiles(project, name, body) {
|
|
76
|
+
return this.api.post(`${this.repoPath(project, name)}/commits`, body).pipe(map((r) => data(r)));
|
|
70
77
|
}
|
|
71
78
|
blame(project, name, ref, path) {
|
|
72
79
|
return this.api.get(`${this.repoPath(project, name)}/blame`, { ref, path }).pipe(map((r) => data(r) ?? []));
|
|
@@ -1226,6 +1233,7 @@ class RepoHomePage extends RepoPage {
|
|
|
1226
1233
|
this.path = signal('', ...(ngDevMode ? [{ debugName: "path" }] : /* istanbul ignore next */ []));
|
|
1227
1234
|
this.proto = signal('https', ...(ngDevMode ? [{ debugName: "proto" }] : /* istanbul ignore next */ []));
|
|
1228
1235
|
this.cloneOpen = signal(false, ...(ngDevMode ? [{ debugName: "cloneOpen" }] : /* istanbul ignore next */ []));
|
|
1236
|
+
this.addOpen = signal(false, ...(ngDevMode ? [{ debugName: "addOpen" }] : /* istanbul ignore next */ []));
|
|
1229
1237
|
this.copied = signal(false, ...(ngDevMode ? [{ debugName: "copied" }] : /* istanbul ignore next */ []));
|
|
1230
1238
|
this.finding = signal(false, ...(ngDevMode ? [{ debugName: "finding" }] : /* istanbul ignore next */ []));
|
|
1231
1239
|
this.fq = signal('', ...(ngDevMode ? [{ debugName: "fq" }] : /* istanbul ignore next */ []));
|
|
@@ -1354,6 +1362,17 @@ class RepoHomePage extends RepoPage {
|
|
|
1354
1362
|
</nav>
|
|
1355
1363
|
<span class="o-r"></span>
|
|
1356
1364
|
<button type="button" class="o-btn o-sm" (click)="openFinder()">Go to file <span class="o-kbd">T</span></button>
|
|
1365
|
+
@if (canWrite()) {
|
|
1366
|
+
<span class="anchor add">
|
|
1367
|
+
<button type="button" class="o-btn o-sm" (click)="addOpen.set(!addOpen())" [attr.aria-expanded]="addOpen()" aria-haspopup="menu" data-testid="add-file">Add file ⌄</button>
|
|
1368
|
+
@if (addOpen()) {
|
|
1369
|
+
<div class="o-menu" role="menu" style="right:0;top:34px;width:200px">
|
|
1370
|
+
<a role="menuitem" [routerLink]="link('new')" [queryParams]="{ ref: ref() || null, path: path() || null }" data-testid="add-new">Create new file</a>
|
|
1371
|
+
<a role="menuitem" [routerLink]="link('upload')" [queryParams]="{ ref: ref() || null, path: path() || null }" data-testid="add-upload">Upload files</a>
|
|
1372
|
+
</div>
|
|
1373
|
+
}
|
|
1374
|
+
</span>
|
|
1375
|
+
}
|
|
1357
1376
|
<span class="anchor">
|
|
1358
1377
|
<button type="button" class="o-btn o-sm o-pri" (click)="cloneOpen.set(!cloneOpen())" [attr.aria-expanded]="cloneOpen()" aria-haspopup="dialog" data-testid="clone-menu">Code ⌄</button>
|
|
1359
1378
|
@if (cloneOpen()) {
|
|
@@ -1474,6 +1493,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImpo
|
|
|
1474
1493
|
</nav>
|
|
1475
1494
|
<span class="o-r"></span>
|
|
1476
1495
|
<button type="button" class="o-btn o-sm" (click)="openFinder()">Go to file <span class="o-kbd">T</span></button>
|
|
1496
|
+
@if (canWrite()) {
|
|
1497
|
+
<span class="anchor add">
|
|
1498
|
+
<button type="button" class="o-btn o-sm" (click)="addOpen.set(!addOpen())" [attr.aria-expanded]="addOpen()" aria-haspopup="menu" data-testid="add-file">Add file ⌄</button>
|
|
1499
|
+
@if (addOpen()) {
|
|
1500
|
+
<div class="o-menu" role="menu" style="right:0;top:34px;width:200px">
|
|
1501
|
+
<a role="menuitem" [routerLink]="link('new')" [queryParams]="{ ref: ref() || null, path: path() || null }" data-testid="add-new">Create new file</a>
|
|
1502
|
+
<a role="menuitem" [routerLink]="link('upload')" [queryParams]="{ ref: ref() || null, path: path() || null }" data-testid="add-upload">Upload files</a>
|
|
1503
|
+
</div>
|
|
1504
|
+
}
|
|
1505
|
+
</span>
|
|
1506
|
+
}
|
|
1477
1507
|
<span class="anchor">
|
|
1478
1508
|
<button type="button" class="o-btn o-sm o-pri" (click)="cloneOpen.set(!cloneOpen())" [attr.aria-expanded]="cloneOpen()" aria-haspopup="dialog" data-testid="clone-menu">Code ⌄</button>
|
|
1479
1509
|
@if (cloneOpen()) {
|
|
@@ -1557,6 +1587,109 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImpo
|
|
|
1557
1587
|
args: ['document:keydown', ['$event']]
|
|
1558
1588
|
}] } });
|
|
1559
1589
|
|
|
1590
|
+
/** Whether a branch protection pattern names the branch (as code's protect.Match: * within a segment, ** across). */
|
|
1591
|
+
function matchesPattern(pattern, branch) {
|
|
1592
|
+
const seg = (p) => '^' + p.split('*').map((x) => x.replace(/[.+?^${}()|[\]\\]/g, '\\$&')).join('[^/]*') + '$';
|
|
1593
|
+
const pat = pattern.split('/');
|
|
1594
|
+
const name = branch.split('/');
|
|
1595
|
+
const go = (i, j) => {
|
|
1596
|
+
if (i === pat.length)
|
|
1597
|
+
return j === name.length;
|
|
1598
|
+
if (pat[i] === '**') {
|
|
1599
|
+
if (i === pat.length - 1)
|
|
1600
|
+
return j < name.length;
|
|
1601
|
+
for (let k = j; k <= name.length; k++)
|
|
1602
|
+
if (go(i + 1, k))
|
|
1603
|
+
return true;
|
|
1604
|
+
return false;
|
|
1605
|
+
}
|
|
1606
|
+
return j < name.length && new RegExp(seg(pat[i])).test(name[j]) && go(i + 1, j + 1);
|
|
1607
|
+
};
|
|
1608
|
+
return go(0, 0);
|
|
1609
|
+
}
|
|
1610
|
+
// The web editor's commit box (prototype code_file "Commit and open PR", DEV-13): a message, and
|
|
1611
|
+
// either the branch itself or a new branch with a pull request. A branch whose rules block direct
|
|
1612
|
+
// pushes only offers the second.
|
|
1613
|
+
class CommitBox {
|
|
1614
|
+
constructor() {
|
|
1615
|
+
this.api = inject(CodeApi);
|
|
1616
|
+
this.host = inject(CodeHost);
|
|
1617
|
+
this.repo = input.required(...(ngDevMode ? [{ debugName: "repo" }] : /* istanbul ignore next */ []));
|
|
1618
|
+
this.branch = input.required(...(ngDevMode ? [{ debugName: "branch" }] : /* istanbul ignore next */ []));
|
|
1619
|
+
this.defaultMessage = input('', ...(ngDevMode ? [{ debugName: "defaultMessage" }] : /* istanbul ignore next */ []));
|
|
1620
|
+
this.busy = input(false, ...(ngDevMode ? [{ debugName: "busy" }] : /* istanbul ignore next */ []));
|
|
1621
|
+
this.commit = output();
|
|
1622
|
+
this.cancel = output();
|
|
1623
|
+
this.message = signal('', ...(ngDevMode ? [{ debugName: "message" }] : /* istanbul ignore next */ []));
|
|
1624
|
+
this.toNew = signal(false, ...(ngDevMode ? [{ debugName: "toNew" }] : /* istanbul ignore next */ []));
|
|
1625
|
+
this.newBranch = signal('', ...(ngDevMode ? [{ debugName: "newBranch" }] : /* istanbul ignore next */ []));
|
|
1626
|
+
this.rules = signal([], ...(ngDevMode ? [{ debugName: "rules" }] : /* istanbul ignore next */ []));
|
|
1627
|
+
this.blocked = computed(() => this.rules().some((r) => r.blockDirectPush && matchesPattern(r.pattern, this.branch())), ...(ngDevMode ? [{ debugName: "blocked" }] : /* istanbul ignore next */ []));
|
|
1628
|
+
effect(() => {
|
|
1629
|
+
const m = this.defaultMessage();
|
|
1630
|
+
untracked(() => this.message.set(m));
|
|
1631
|
+
});
|
|
1632
|
+
effect(() => {
|
|
1633
|
+
const repo = this.repo();
|
|
1634
|
+
const p = this.host.project();
|
|
1635
|
+
if (!p || !repo)
|
|
1636
|
+
return;
|
|
1637
|
+
untracked(() => {
|
|
1638
|
+
this.api.protections(p.id, repo).subscribe({ next: (r) => this.rules.set(r), error: () => undefined });
|
|
1639
|
+
const me = (this.host.people().find((x) => x.id === this.host.meId())?.name || 'patch').toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '') || 'patch';
|
|
1640
|
+
this.newBranch.set(`${me}/patch-${Math.floor(Date.now() / 1000) % 100000}`);
|
|
1641
|
+
});
|
|
1642
|
+
});
|
|
1643
|
+
}
|
|
1644
|
+
send() {
|
|
1645
|
+
const toNew = this.toNew() || this.blocked();
|
|
1646
|
+
this.commit.emit({ message: this.message().trim(), newBranch: toNew ? this.newBranch().trim() : undefined });
|
|
1647
|
+
}
|
|
1648
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: CommitBox, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1649
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.12", type: CommitBox, isStandalone: true, selector: "o-commit-box", inputs: { repo: { classPropertyName: "repo", publicName: "repo", isSignal: true, isRequired: true, transformFunction: null }, branch: { classPropertyName: "branch", publicName: "branch", isSignal: true, isRequired: true, transformFunction: null }, defaultMessage: { classPropertyName: "defaultMessage", publicName: "defaultMessage", isSignal: true, isRequired: false, transformFunction: null }, busy: { classPropertyName: "busy", publicName: "busy", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { commit: "commit", cancel: "cancel" }, ngImport: i0, template: `
|
|
1650
|
+
<div class="o-col" style="gap:10px" data-testid="commit-box">
|
|
1651
|
+
@if (blocked()) {<div class="o-note o-blu">Your change is committed to a new branch with a pull request because <b>{{ branch() }}</b> is protected.</div>}
|
|
1652
|
+
<div class="o-fld"><label for="cb-msg">Commit message</label><input id="cb-msg" class="o-in" [ngModel]="message()" (ngModelChange)="message.set($event)" data-testid="commit-message" /></div>
|
|
1653
|
+
@if (!blocked()) {
|
|
1654
|
+
<label class="o-flex o-sm"><input type="radio" name="cb-where" [checked]="!toNew()" (change)="toNew.set(false)" /> Commit directly to <b class="o-mono">{{ branch() }}</b></label>
|
|
1655
|
+
<label class="o-flex o-sm"><input type="radio" name="cb-where" [checked]="toNew()" (change)="toNew.set(true)" data-testid="commit-new-branch" /> Create a new branch and open a pull request</label>
|
|
1656
|
+
}
|
|
1657
|
+
@if (toNew() || blocked()) {
|
|
1658
|
+
<div class="o-flex o-sm"><span class="o-faint">→ new branch</span><input class="o-in o-sm o-mono" style="max-width:260px" aria-label="New branch" [ngModel]="newBranch()" (ngModelChange)="newBranch.set($event)" data-testid="commit-branch" /></div>
|
|
1659
|
+
}
|
|
1660
|
+
<div class="o-flex">
|
|
1661
|
+
<button type="button" class="o-btn o-sm o-r" (click)="cancel.emit()">Cancel</button>
|
|
1662
|
+
<button type="button" class="o-btn o-sm o-pri" [disabled]="busy() || !message().trim() || ((toNew() || blocked()) && !newBranch().trim())" (click)="send()" data-testid="commit-submit">{{ toNew() || blocked() ? 'Commit and open PR' : 'Commit changes' }}</button>
|
|
1663
|
+
</div>
|
|
1664
|
+
</div>
|
|
1665
|
+
`, isInline: true, 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"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
1666
|
+
}
|
|
1667
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: CommitBox, decorators: [{
|
|
1668
|
+
type: Component,
|
|
1669
|
+
args: [{
|
|
1670
|
+
selector: 'o-commit-box',
|
|
1671
|
+
imports: [FormsModule],
|
|
1672
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
1673
|
+
template: `
|
|
1674
|
+
<div class="o-col" style="gap:10px" data-testid="commit-box">
|
|
1675
|
+
@if (blocked()) {<div class="o-note o-blu">Your change is committed to a new branch with a pull request because <b>{{ branch() }}</b> is protected.</div>}
|
|
1676
|
+
<div class="o-fld"><label for="cb-msg">Commit message</label><input id="cb-msg" class="o-in" [ngModel]="message()" (ngModelChange)="message.set($event)" data-testid="commit-message" /></div>
|
|
1677
|
+
@if (!blocked()) {
|
|
1678
|
+
<label class="o-flex o-sm"><input type="radio" name="cb-where" [checked]="!toNew()" (change)="toNew.set(false)" /> Commit directly to <b class="o-mono">{{ branch() }}</b></label>
|
|
1679
|
+
<label class="o-flex o-sm"><input type="radio" name="cb-where" [checked]="toNew()" (change)="toNew.set(true)" data-testid="commit-new-branch" /> Create a new branch and open a pull request</label>
|
|
1680
|
+
}
|
|
1681
|
+
@if (toNew() || blocked()) {
|
|
1682
|
+
<div class="o-flex o-sm"><span class="o-faint">→ new branch</span><input class="o-in o-sm o-mono" style="max-width:260px" aria-label="New branch" [ngModel]="newBranch()" (ngModelChange)="newBranch.set($event)" data-testid="commit-branch" /></div>
|
|
1683
|
+
}
|
|
1684
|
+
<div class="o-flex">
|
|
1685
|
+
<button type="button" class="o-btn o-sm o-r" (click)="cancel.emit()">Cancel</button>
|
|
1686
|
+
<button type="button" class="o-btn o-sm o-pri" [disabled]="busy() || !message().trim() || ((toNew() || blocked()) && !newBranch().trim())" (click)="send()" data-testid="commit-submit">{{ toNew() || blocked() ? 'Commit and open PR' : 'Commit changes' }}</button>
|
|
1687
|
+
</div>
|
|
1688
|
+
</div>
|
|
1689
|
+
`,
|
|
1690
|
+
}]
|
|
1691
|
+
}], ctorParameters: () => [], propDecorators: { repo: [{ type: i0.Input, args: [{ isSignal: true, alias: "repo", required: true }] }], branch: [{ type: i0.Input, args: [{ isSignal: true, alias: "branch", required: true }] }], defaultMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultMessage", required: false }] }], busy: [{ type: i0.Input, args: [{ isSignal: true, alias: "busy", required: false }] }], commit: [{ type: i0.Output, args: ["commit"] }], cancel: [{ type: i0.Output, args: ["cancel"] }] } });
|
|
1692
|
+
|
|
1560
1693
|
// /{org}/{project}/code/{repo}/blob?ref=&path= (prototype "code_file"): one file, highlighted, with
|
|
1561
1694
|
// Markdown preview, blame, raw, copy and download; a line number links to that line (#L12).
|
|
1562
1695
|
class FilePage extends RepoPage {
|
|
@@ -1583,6 +1716,17 @@ class FilePage extends RepoPage {
|
|
|
1583
1716
|
return '';
|
|
1584
1717
|
return [b.binary ? '' : `${b.lineCount} lines`, size(b.size), b.language ?? ''].filter(Boolean).join(' · ');
|
|
1585
1718
|
}, ...(ngDevMode ? [{ debugName: "meta" }] : /* istanbul ignore next */ []));
|
|
1719
|
+
this.mode = signal('', ...(ngDevMode ? [{ debugName: "mode" }] : /* istanbul ignore next */ []));
|
|
1720
|
+
this.text = signal(null, ...(ngDevMode ? [{ debugName: "text" }] : /* istanbul ignore next */ []));
|
|
1721
|
+
this.busy = signal(false, ...(ngDevMode ? [{ debugName: "busy" }] : /* istanbul ignore next */ []));
|
|
1722
|
+
this.branches = signal([], ...(ngDevMode ? [{ debugName: "branches" }] : /* istanbul ignore next */ []));
|
|
1723
|
+
/** The branch the view is on ('' for a tag or a commit, which cannot take commits). */
|
|
1724
|
+
this.branch = computed(() => {
|
|
1725
|
+
const r = this.repo();
|
|
1726
|
+
const ref = this.ref() || r?.defaultBranch || '';
|
|
1727
|
+
return this.branches().includes(ref) ? ref : '';
|
|
1728
|
+
}, ...(ngDevMode ? [{ debugName: "branch" }] : /* istanbul ignore next */ []));
|
|
1729
|
+
this.editable = computed(() => this.canWrite() && !!this.branch(), ...(ngDevMode ? [{ debugName: "editable" }] : /* istanbul ignore next */ []));
|
|
1586
1730
|
this.route.queryParamMap.subscribe((q) => {
|
|
1587
1731
|
this.ref.set(q.get('ref') ?? '');
|
|
1588
1732
|
this.path.set(q.get('path') ?? '');
|
|
@@ -1594,6 +1738,60 @@ class FilePage extends RepoPage {
|
|
|
1594
1738
|
}
|
|
1595
1739
|
onRepo(r) {
|
|
1596
1740
|
this.load(r);
|
|
1741
|
+
this.api.refs(this.projectId(), r.name, 'branch').subscribe({ next: (b) => this.branches.set(b.map((x) => x.name)), error: () => undefined });
|
|
1742
|
+
}
|
|
1743
|
+
startEdit() {
|
|
1744
|
+
const r = this.repo();
|
|
1745
|
+
if (!r)
|
|
1746
|
+
return;
|
|
1747
|
+
this.mode.set('edit');
|
|
1748
|
+
this.text.set(null);
|
|
1749
|
+
this.api.blob(this.projectId(), r.name, this.branch(), this.path(), true).subscribe({
|
|
1750
|
+
next: (b) => {
|
|
1751
|
+
if (b.text === undefined) {
|
|
1752
|
+
this.mode.set('');
|
|
1753
|
+
this.error.set('This file cannot be edited in the browser.');
|
|
1754
|
+
return;
|
|
1755
|
+
}
|
|
1756
|
+
this.blob.set(b);
|
|
1757
|
+
this.text.set(b.text);
|
|
1758
|
+
},
|
|
1759
|
+
error: (e) => { this.mode.set(''); this.error.set(apiMessage(e, 'Could not open the file.')); },
|
|
1760
|
+
});
|
|
1761
|
+
}
|
|
1762
|
+
/** Commits the edit (or the delete), onto the branch or onto a new branch with a pull request. */
|
|
1763
|
+
save(c, del = false) {
|
|
1764
|
+
const r = this.repo();
|
|
1765
|
+
const b = this.blob();
|
|
1766
|
+
if (!r || !b)
|
|
1767
|
+
return;
|
|
1768
|
+
const body = c.newBranch
|
|
1769
|
+
? { branch: c.newBranch, from: this.branch(), message: c.message, pull: {} }
|
|
1770
|
+
: { branch: this.branch(), expect: b.commit, message: c.message };
|
|
1771
|
+
if (del)
|
|
1772
|
+
body.deletes = [b.path];
|
|
1773
|
+
else
|
|
1774
|
+
body.files = { [b.path]: this.text() ?? '' };
|
|
1775
|
+
this.busy.set(true);
|
|
1776
|
+
this.error.set('');
|
|
1777
|
+
this.api.commitFiles(this.projectId(), r.name, body).subscribe({
|
|
1778
|
+
next: (res) => {
|
|
1779
|
+
this.busy.set(false);
|
|
1780
|
+
this.mode.set('');
|
|
1781
|
+
if (res.pull) {
|
|
1782
|
+
void this.router.navigate(this.link('pull/' + res.pull));
|
|
1783
|
+
return;
|
|
1784
|
+
}
|
|
1785
|
+
if (del) {
|
|
1786
|
+
void this.router.navigate(this.link(), { queryParams: { ref: res.branch, path: b.path.split('/').slice(0, -1).join('/') || null } });
|
|
1787
|
+
return;
|
|
1788
|
+
}
|
|
1789
|
+
this.note.set(`Committed ${res.commit.short} to ${res.branch}.`);
|
|
1790
|
+
void this.router.navigate(this.link('blob'), { queryParams: { ref: res.branch, path: b.path } });
|
|
1791
|
+
this.load(r);
|
|
1792
|
+
},
|
|
1793
|
+
error: (e) => { this.busy.set(false); this.error.set(apiMessage(e, 'Could not commit.')); },
|
|
1794
|
+
});
|
|
1597
1795
|
}
|
|
1598
1796
|
pickRef(ref) {
|
|
1599
1797
|
void this.router.navigate(this.link('blob'), { queryParams: { ref, path: this.path() } });
|
|
@@ -1651,6 +1849,7 @@ class FilePage extends RepoPage {
|
|
|
1651
1849
|
</nav>
|
|
1652
1850
|
</div>
|
|
1653
1851
|
@if (error()) {<div class="o-note o-red" role="alert">{{ error() }}</div>}
|
|
1852
|
+
@if (note()) {<div class="o-note" role="status">{{ note() }}</div>}
|
|
1654
1853
|
@if (blob(); as b) {
|
|
1655
1854
|
<div class="o-card">
|
|
1656
1855
|
@if (b.lastCommit; as lc) {
|
|
@@ -1670,8 +1869,26 @@ class FilePage extends RepoPage {
|
|
|
1670
1869
|
@if (b.rawUrl) {<a class="o-btn o-xs" [href]="b.rawUrl" target="_blank" rel="noopener">Raw</a>}
|
|
1671
1870
|
@if (!b.binary) {<button type="button" class="o-btn o-xs" (click)="copyText()">{{ copied() ? 'Copied' : 'Copy' }}</button>}
|
|
1672
1871
|
@if (b.downloadUrl) {<a class="o-btn o-xs" [href]="b.downloadUrl">Download</a>}
|
|
1872
|
+
@if (editable()) {
|
|
1873
|
+
@if (!b.binary && !b.truncated) {<button type="button" class="o-btn o-xs" (click)="startEdit()" [disabled]="mode() !== ''" data-testid="file-edit">Edit</button>}
|
|
1874
|
+
<button type="button" class="o-btn o-xs" (click)="mode.set('delete')" [disabled]="mode() !== ''" data-testid="file-delete">Delete</button>
|
|
1875
|
+
}
|
|
1673
1876
|
</span>
|
|
1674
1877
|
</div>
|
|
1878
|
+
@if (mode() === 'edit') {
|
|
1879
|
+
<div class="o-cb o-col" style="gap:10px;background:var(--o-well);border-bottom:1px solid var(--o-line-soft)">
|
|
1880
|
+
@if (text() === null) {<span class="o-faint o-sm">Loading the file…</span>} @else {
|
|
1881
|
+
<textarea class="o-in o-mono" rows="16" style="font-size:12px;height:auto;padding:8px 10px" aria-label="File contents" [ngModel]="text()" (ngModelChange)="text.set($event)" data-testid="file-text"></textarea>
|
|
1882
|
+
<o-commit-box [repo]="r.name" [branch]="branch()" [defaultMessage]="'Update ' + b.name" [busy]="busy()" (commit)="save($event)" (cancel)="mode.set('')" />
|
|
1883
|
+
}
|
|
1884
|
+
</div>
|
|
1885
|
+
}
|
|
1886
|
+
@if (mode() === 'delete') {
|
|
1887
|
+
<div class="o-cb o-col" style="gap:10px;background:var(--o-well);border-bottom:1px solid var(--o-line-soft)">
|
|
1888
|
+
<span class="o-sm">Delete <b class="o-mono">{{ b.path }}</b> from <b class="o-mono">{{ branch() }}</b>?</span>
|
|
1889
|
+
<o-commit-box [repo]="r.name" [branch]="branch()" [defaultMessage]="'Delete ' + b.name" [busy]="busy()" (commit)="save($event, true)" (cancel)="mode.set('')" />
|
|
1890
|
+
</div>
|
|
1891
|
+
}
|
|
1675
1892
|
@if (b.image && b.rawUrl) {
|
|
1676
1893
|
<div class="o-cb" style="text-align:center"><img [src]="b.rawUrl" [alt]="b.name" style="max-width:100%" /></div>
|
|
1677
1894
|
} @else if (b.binary || b.truncated) {
|
|
@@ -1698,11 +1915,11 @@ class FilePage extends RepoPage {
|
|
|
1698
1915
|
<div class="o-card o-empty"><p>{{ error() }}</p></div>
|
|
1699
1916
|
}
|
|
1700
1917
|
</div>
|
|
1701
|
-
`, 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{text-decoration:none}.o-btn.o-on{background:var(--o-tx);border-color:var(--o-tx);color:var(--o-acc-fg)}\n"], dependencies: [{ kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: RefPicker, selector: "o-ref-picker", inputs: ["repo", "current"], outputs: ["picked"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
1918
|
+
`, 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{text-decoration:none}.o-btn.o-on{background:var(--o-tx);border-color:var(--o-tx);color:var(--o-acc-fg)}\n"], dependencies: [{ kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: RefPicker, selector: "o-ref-picker", inputs: ["repo", "current"], outputs: ["picked"] }, { 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: "component", type: CommitBox, selector: "o-commit-box", inputs: ["repo", "branch", "defaultMessage", "busy"], outputs: ["commit", "cancel"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
1702
1919
|
}
|
|
1703
1920
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: FilePage, decorators: [{
|
|
1704
1921
|
type: Component,
|
|
1705
|
-
args: [{ selector: 'o-code-file', imports: [RouterLink, RefPicker], changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
1922
|
+
args: [{ selector: 'o-code-file', imports: [RouterLink, RefPicker, FormsModule, CommitBox], changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
1706
1923
|
<div class="o-page o-wide">
|
|
1707
1924
|
@if (repo(); as r) {
|
|
1708
1925
|
<div class="o-flex o-wrap">
|
|
@@ -1716,6 +1933,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImpo
|
|
|
1716
1933
|
</nav>
|
|
1717
1934
|
</div>
|
|
1718
1935
|
@if (error()) {<div class="o-note o-red" role="alert">{{ error() }}</div>}
|
|
1936
|
+
@if (note()) {<div class="o-note" role="status">{{ note() }}</div>}
|
|
1719
1937
|
@if (blob(); as b) {
|
|
1720
1938
|
<div class="o-card">
|
|
1721
1939
|
@if (b.lastCommit; as lc) {
|
|
@@ -1735,8 +1953,26 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImpo
|
|
|
1735
1953
|
@if (b.rawUrl) {<a class="o-btn o-xs" [href]="b.rawUrl" target="_blank" rel="noopener">Raw</a>}
|
|
1736
1954
|
@if (!b.binary) {<button type="button" class="o-btn o-xs" (click)="copyText()">{{ copied() ? 'Copied' : 'Copy' }}</button>}
|
|
1737
1955
|
@if (b.downloadUrl) {<a class="o-btn o-xs" [href]="b.downloadUrl">Download</a>}
|
|
1956
|
+
@if (editable()) {
|
|
1957
|
+
@if (!b.binary && !b.truncated) {<button type="button" class="o-btn o-xs" (click)="startEdit()" [disabled]="mode() !== ''" data-testid="file-edit">Edit</button>}
|
|
1958
|
+
<button type="button" class="o-btn o-xs" (click)="mode.set('delete')" [disabled]="mode() !== ''" data-testid="file-delete">Delete</button>
|
|
1959
|
+
}
|
|
1738
1960
|
</span>
|
|
1739
1961
|
</div>
|
|
1962
|
+
@if (mode() === 'edit') {
|
|
1963
|
+
<div class="o-cb o-col" style="gap:10px;background:var(--o-well);border-bottom:1px solid var(--o-line-soft)">
|
|
1964
|
+
@if (text() === null) {<span class="o-faint o-sm">Loading the file…</span>} @else {
|
|
1965
|
+
<textarea class="o-in o-mono" rows="16" style="font-size:12px;height:auto;padding:8px 10px" aria-label="File contents" [ngModel]="text()" (ngModelChange)="text.set($event)" data-testid="file-text"></textarea>
|
|
1966
|
+
<o-commit-box [repo]="r.name" [branch]="branch()" [defaultMessage]="'Update ' + b.name" [busy]="busy()" (commit)="save($event)" (cancel)="mode.set('')" />
|
|
1967
|
+
}
|
|
1968
|
+
</div>
|
|
1969
|
+
}
|
|
1970
|
+
@if (mode() === 'delete') {
|
|
1971
|
+
<div class="o-cb o-col" style="gap:10px;background:var(--o-well);border-bottom:1px solid var(--o-line-soft)">
|
|
1972
|
+
<span class="o-sm">Delete <b class="o-mono">{{ b.path }}</b> from <b class="o-mono">{{ branch() }}</b>?</span>
|
|
1973
|
+
<o-commit-box [repo]="r.name" [branch]="branch()" [defaultMessage]="'Delete ' + b.name" [busy]="busy()" (commit)="save($event, true)" (cancel)="mode.set('')" />
|
|
1974
|
+
</div>
|
|
1975
|
+
}
|
|
1740
1976
|
@if (b.image && b.rawUrl) {
|
|
1741
1977
|
<div class="o-cb" style="text-align:center"><img [src]="b.rawUrl" [alt]="b.name" style="max-width:100%" /></div>
|
|
1742
1978
|
} @else if (b.binary || b.truncated) {
|
|
@@ -2779,7 +3015,7 @@ class PullPage extends RepoPage {
|
|
|
2779
3015
|
<span class="o-lbl">Reviewers</span>
|
|
2780
3016
|
@for (r of reviewers(); track r.id) {<div class="o-flex"><span class="o-av" aria-hidden="true">{{ people().initials(r.id) }}</span><span>{{ people().name(r.id) }}</span><span class="o-r o-sm" [style.color]="r.colour">{{ r.label }}</span></div>}
|
|
2781
3017
|
@if (!reviewers().length) {<span class="o-faint o-sm">No reviewers yet.</span>}
|
|
2782
|
-
<span class="o-faint o-xs">
|
|
3018
|
+
<span class="o-faint o-xs">Code owners of changed paths (CODEOWNERS) are asked automatically.</span>
|
|
2783
3019
|
</div>
|
|
2784
3020
|
<div class="o-col"><span class="o-lbl">Labels</span><div class="o-flex o-wrap">@for (l of p.labels; track l) {<span class="o-chip o-out">{{ l }}</span>}@if (!p.labels.length) {<span class="o-faint o-sm">None</span>}</div></div>
|
|
2785
3021
|
<div class="o-col">
|
|
@@ -2949,7 +3185,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImpo
|
|
|
2949
3185
|
<span class="o-lbl">Reviewers</span>
|
|
2950
3186
|
@for (r of reviewers(); track r.id) {<div class="o-flex"><span class="o-av" aria-hidden="true">{{ people().initials(r.id) }}</span><span>{{ people().name(r.id) }}</span><span class="o-r o-sm" [style.color]="r.colour">{{ r.label }}</span></div>}
|
|
2951
3187
|
@if (!reviewers().length) {<span class="o-faint o-sm">No reviewers yet.</span>}
|
|
2952
|
-
<span class="o-faint o-xs">
|
|
3188
|
+
<span class="o-faint o-xs">Code owners of changed paths (CODEOWNERS) are asked automatically.</span>
|
|
2953
3189
|
</div>
|
|
2954
3190
|
<div class="o-col"><span class="o-lbl">Labels</span><div class="o-flex o-wrap">@for (l of p.labels; track l) {<span class="o-chip o-out">{{ l }}</span>}@if (!p.labels.length) {<span class="o-faint o-sm">None</span>}</div></div>
|
|
2955
3191
|
<div class="o-col">
|
|
@@ -2988,7 +3224,7 @@ function writePref(key, value) {
|
|
|
2988
3224
|
}
|
|
2989
3225
|
}
|
|
2990
3226
|
|
|
2991
|
-
const BLANK = { id: '', pattern: '', blockDirectPush: true, blockForcePush: true, blockDeletion: true, requiredApprovals: 1, dismissStaleApprovals: true, requireUpToDate: false, requiredChecks: [] };
|
|
3227
|
+
const BLANK = { id: '', pattern: '', blockDirectPush: true, blockForcePush: true, blockDeletion: true, requiredApprovals: 1, dismissStaleApprovals: true, requireUpToDate: false, requireCodeOwnerReview: false, requiredChecks: [] };
|
|
2992
3228
|
// /{org}/{project}/code/{repo}/settings?section= (prototype "code_settings"): general settings and
|
|
2993
3229
|
// merge methods, branch protection rules, and the danger zone (archive, delete).
|
|
2994
3230
|
class RepoSettingsPage extends RepoPage {
|
|
@@ -3010,6 +3246,7 @@ class RepoSettingsPage extends RepoPage {
|
|
|
3010
3246
|
{ key: 'blockDeletion', label: 'Block deletion', hint: 'Matching branches cannot be deleted.' },
|
|
3011
3247
|
{ key: 'dismissStaleApprovals', label: 'Dismiss stale approvals', hint: 'A new commit dismisses approvals given on an older one.' },
|
|
3012
3248
|
{ key: 'requireUpToDate', label: 'Require branches to be up to date', hint: 'The branch must contain the target’s latest commit before it merges.' },
|
|
3249
|
+
{ key: 'requireCodeOwnerReview', label: 'Require review from CODEOWNERS', hint: 'Owners of changed paths are requested automatically; one of them approves each owned file.' },
|
|
3013
3250
|
];
|
|
3014
3251
|
this.collabs = signal([], ...(ngDevMode ? [{ debugName: "collabs" }] : /* istanbul ignore next */ []));
|
|
3015
3252
|
this.perms = [{ id: 'read', label: 'Read' }, { id: 'write', label: 'Write' }, { id: 'maintain', label: 'Maintain' }];
|
|
@@ -3075,6 +3312,8 @@ class RepoSettingsPage extends RepoPage {
|
|
|
3075
3312
|
parts.push('no direct push');
|
|
3076
3313
|
if (p.requireUpToDate)
|
|
3077
3314
|
parts.push('up to date');
|
|
3315
|
+
if (p.requireCodeOwnerReview)
|
|
3316
|
+
parts.push('code owners');
|
|
3078
3317
|
if (p.blockDeletion)
|
|
3079
3318
|
parts.push('no deletion');
|
|
3080
3319
|
return parts.join(' · ') || 'no restrictions';
|
|
@@ -3790,6 +4029,169 @@ function sortTags(tags) {
|
|
|
3790
4029
|
return [...tags].sort((a, b) => when(b) - when(a) || b.name.localeCompare(a.name, undefined, { numeric: true }));
|
|
3791
4030
|
}
|
|
3792
4031
|
|
|
4032
|
+
/** Uploads in one commit stay under the server's 25 MB request limit (base64 grows them by a third). */
|
|
4033
|
+
const MAX_UPLOAD = 18 * 1024 * 1024;
|
|
4034
|
+
// /{org}/{project}/code/{repo}/new?ref=&path= and /upload (DEV-13): a new file written in the
|
|
4035
|
+
// browser, or files uploaded from the computer, committed to the branch or to a new branch with a
|
|
4036
|
+
// pull request (protection applies).
|
|
4037
|
+
class NewFilePage extends RepoPage {
|
|
4038
|
+
constructor() {
|
|
4039
|
+
super();
|
|
4040
|
+
this.router = inject(Router);
|
|
4041
|
+
this.upload = signal(false, ...(ngDevMode ? [{ debugName: "upload" }] : /* istanbul ignore next */ []));
|
|
4042
|
+
this.ref = signal('', ...(ngDevMode ? [{ debugName: "ref" }] : /* istanbul ignore next */ []));
|
|
4043
|
+
this.uploads = signal([], ...(ngDevMode ? [{ debugName: "uploads" }] : /* istanbul ignore next */ []));
|
|
4044
|
+
this.busy = signal(false, ...(ngDevMode ? [{ debugName: "busy" }] : /* istanbul ignore next */ []));
|
|
4045
|
+
this.filePath = '';
|
|
4046
|
+
this.content = '';
|
|
4047
|
+
this.dir = '';
|
|
4048
|
+
this.branch = computed(() => this.ref() || this.repo()?.defaultBranch || 'main', ...(ngDevMode ? [{ debugName: "branch" }] : /* istanbul ignore next */ []));
|
|
4049
|
+
this.defaultMessage = computed(() => (this.upload() ? 'Add files via upload' : 'Create a file'), ...(ngDevMode ? [{ debugName: "defaultMessage" }] : /* istanbul ignore next */ []));
|
|
4050
|
+
this.fmt = size;
|
|
4051
|
+
this.upload.set(this.route.snapshot.url.at(-1)?.path === 'upload');
|
|
4052
|
+
this.route.queryParamMap.subscribe((q) => {
|
|
4053
|
+
this.ref.set(q.get('ref') ?? '');
|
|
4054
|
+
const p = q.get('path') ?? '';
|
|
4055
|
+
this.dir = p;
|
|
4056
|
+
this.filePath = p ? p.replace(/\/+$/, '') + '/' : '';
|
|
4057
|
+
});
|
|
4058
|
+
}
|
|
4059
|
+
onRepo(_) { }
|
|
4060
|
+
async pick(ev) {
|
|
4061
|
+
const files = Array.from(ev.target.files ?? []);
|
|
4062
|
+
const out = [...this.uploads()];
|
|
4063
|
+
for (const f of files) {
|
|
4064
|
+
const buf = new Uint8Array(await f.arrayBuffer());
|
|
4065
|
+
let bin = '';
|
|
4066
|
+
for (let i = 0; i < buf.length; i += 0x8000)
|
|
4067
|
+
bin += String.fromCharCode(...buf.subarray(i, i + 0x8000));
|
|
4068
|
+
out.push({ name: f.name, size: f.size, b64: btoa(bin) });
|
|
4069
|
+
}
|
|
4070
|
+
if (out.reduce((a, u) => a + u.size, 0) > MAX_UPLOAD) {
|
|
4071
|
+
this.error.set('One upload is at most 18 MB; commit large files with Git.');
|
|
4072
|
+
return;
|
|
4073
|
+
}
|
|
4074
|
+
this.error.set('');
|
|
4075
|
+
this.uploads.set(out);
|
|
4076
|
+
}
|
|
4077
|
+
shown(name) {
|
|
4078
|
+
const prefix = this.dir.trim().replace(/^\/+|\/+$/g, '');
|
|
4079
|
+
return (prefix ? prefix + '/' : '') + name;
|
|
4080
|
+
}
|
|
4081
|
+
drop(name) {
|
|
4082
|
+
this.uploads.set(this.uploads().filter((u) => u.name !== name));
|
|
4083
|
+
}
|
|
4084
|
+
back() {
|
|
4085
|
+
void this.router.navigate(this.link(), { queryParams: { ref: this.ref() || null, path: this.dir || null } });
|
|
4086
|
+
}
|
|
4087
|
+
save(c) {
|
|
4088
|
+
const r = this.repo();
|
|
4089
|
+
if (!r)
|
|
4090
|
+
return;
|
|
4091
|
+
const body = c.newBranch ? { branch: c.newBranch, from: this.branch(), message: c.message, pull: {} } : { branch: this.branch(), message: c.message };
|
|
4092
|
+
if (this.upload()) {
|
|
4093
|
+
if (!this.uploads().length) {
|
|
4094
|
+
this.error.set('Choose files to upload.');
|
|
4095
|
+
return;
|
|
4096
|
+
}
|
|
4097
|
+
const prefix = this.dir.trim().replace(/^\/+|\/+$/g, '');
|
|
4098
|
+
body.uploads = Object.fromEntries(this.uploads().map((u) => [(prefix ? prefix + '/' : '') + u.name, u.b64]));
|
|
4099
|
+
}
|
|
4100
|
+
else {
|
|
4101
|
+
const p = this.filePath.trim().replace(/^\/+/, '');
|
|
4102
|
+
if (!p || p.endsWith('/')) {
|
|
4103
|
+
this.error.set('Name the file.');
|
|
4104
|
+
return;
|
|
4105
|
+
}
|
|
4106
|
+
body.files = { [p]: this.content };
|
|
4107
|
+
}
|
|
4108
|
+
this.busy.set(true);
|
|
4109
|
+
this.error.set('');
|
|
4110
|
+
this.api.commitFiles(this.projectId(), r.name, body).subscribe({
|
|
4111
|
+
next: (res) => {
|
|
4112
|
+
this.busy.set(false);
|
|
4113
|
+
if (res.pull) {
|
|
4114
|
+
void this.router.navigate(this.link('pull/' + res.pull));
|
|
4115
|
+
return;
|
|
4116
|
+
}
|
|
4117
|
+
if (!this.upload()) {
|
|
4118
|
+
void this.router.navigate(this.link('blob'), { queryParams: { ref: res.branch, path: this.filePath.trim().replace(/^\/+/, '') } });
|
|
4119
|
+
return;
|
|
4120
|
+
}
|
|
4121
|
+
void this.router.navigate(this.link(), { queryParams: { ref: res.branch, path: this.dir.trim() || null } });
|
|
4122
|
+
},
|
|
4123
|
+
error: (e) => { this.busy.set(false); this.error.set(apiMessage(e, 'Could not commit.')); },
|
|
4124
|
+
});
|
|
4125
|
+
}
|
|
4126
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: NewFilePage, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
4127
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.12", type: NewFilePage, isStandalone: true, selector: "o-code-new-file", usesInheritance: true, ngImport: i0, template: `
|
|
4128
|
+
<div class="o-page o-wide">
|
|
4129
|
+
@if (repo(); as r) {
|
|
4130
|
+
<div class="o-ph"><div class="o-col"><span class="o-eyebrow">{{ r.name }} · {{ branch() }}</span><h1 class="o-t">{{ upload() ? 'Upload files' : 'Create a file' }}</h1></div></div>
|
|
4131
|
+
@if (error()) {<div class="o-note o-red" role="alert">{{ error() }}</div>}
|
|
4132
|
+
@if (!canWrite()) {
|
|
4133
|
+
<div class="o-card"><div class="o-empty">Your role cannot change this repository.</div></div>
|
|
4134
|
+
} @else {
|
|
4135
|
+
<div class="o-card">
|
|
4136
|
+
<div class="o-cb o-col" style="gap:12px">
|
|
4137
|
+
@if (upload()) {
|
|
4138
|
+
<div class="o-fld"><label for="nf-dir">Into folder</label><input id="nf-dir" class="o-in o-mono" [(ngModel)]="dir" placeholder="(the repository root)" /></div>
|
|
4139
|
+
<div class="o-fld"><label for="nf-files">Files</label><input id="nf-files" type="file" multiple class="o-in" (change)="pick($event)" data-testid="upload-files" /></div>
|
|
4140
|
+
@for (u of uploads(); track u.name) {<div class="o-row o-sm"><span class="o-mono">{{ shown(u.name) }}</span><span class="o-r o-faint">{{ fmt(u.size) }}</span><button type="button" class="o-lnk" (click)="drop(u.name)" [attr.aria-label]="'Remove ' + u.name">×</button></div>}
|
|
4141
|
+
} @else {
|
|
4142
|
+
<div class="o-fld"><label for="nf-path">File path</label><input id="nf-path" class="o-in o-mono" [(ngModel)]="filePath" placeholder="docs/notes.md" data-testid="new-file-path" /></div>
|
|
4143
|
+
<textarea class="o-in o-mono" rows="16" style="font-size:12px;height:auto;padding:8px 10px" aria-label="File contents" [(ngModel)]="content" data-testid="new-file-text"></textarea>
|
|
4144
|
+
}
|
|
4145
|
+
</div>
|
|
4146
|
+
<div class="o-cb" style="border-top:1px solid var(--o-line-soft)">
|
|
4147
|
+
<o-commit-box [repo]="r.name" [branch]="branch()" [defaultMessage]="defaultMessage()" [busy]="busy()" (commit)="save($event)" (cancel)="back()" />
|
|
4148
|
+
</div>
|
|
4149
|
+
</div>
|
|
4150
|
+
}
|
|
4151
|
+
} @else if (error()) {
|
|
4152
|
+
<div class="o-card o-empty"><p>{{ error() }}</p><a class="o-btn" [routerLink]="host.link('code')">All repositories</a></div>
|
|
4153
|
+
}
|
|
4154
|
+
</div>
|
|
4155
|
+
`, isInline: true, 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: CommitBox, selector: "o-commit-box", inputs: ["repo", "branch", "defaultMessage", "busy"], outputs: ["commit", "cancel"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
4156
|
+
}
|
|
4157
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: NewFilePage, decorators: [{
|
|
4158
|
+
type: Component,
|
|
4159
|
+
args: [{
|
|
4160
|
+
selector: 'o-code-new-file',
|
|
4161
|
+
imports: [FormsModule, RouterLink, CommitBox],
|
|
4162
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
4163
|
+
template: `
|
|
4164
|
+
<div class="o-page o-wide">
|
|
4165
|
+
@if (repo(); as r) {
|
|
4166
|
+
<div class="o-ph"><div class="o-col"><span class="o-eyebrow">{{ r.name }} · {{ branch() }}</span><h1 class="o-t">{{ upload() ? 'Upload files' : 'Create a file' }}</h1></div></div>
|
|
4167
|
+
@if (error()) {<div class="o-note o-red" role="alert">{{ error() }}</div>}
|
|
4168
|
+
@if (!canWrite()) {
|
|
4169
|
+
<div class="o-card"><div class="o-empty">Your role cannot change this repository.</div></div>
|
|
4170
|
+
} @else {
|
|
4171
|
+
<div class="o-card">
|
|
4172
|
+
<div class="o-cb o-col" style="gap:12px">
|
|
4173
|
+
@if (upload()) {
|
|
4174
|
+
<div class="o-fld"><label for="nf-dir">Into folder</label><input id="nf-dir" class="o-in o-mono" [(ngModel)]="dir" placeholder="(the repository root)" /></div>
|
|
4175
|
+
<div class="o-fld"><label for="nf-files">Files</label><input id="nf-files" type="file" multiple class="o-in" (change)="pick($event)" data-testid="upload-files" /></div>
|
|
4176
|
+
@for (u of uploads(); track u.name) {<div class="o-row o-sm"><span class="o-mono">{{ shown(u.name) }}</span><span class="o-r o-faint">{{ fmt(u.size) }}</span><button type="button" class="o-lnk" (click)="drop(u.name)" [attr.aria-label]="'Remove ' + u.name">×</button></div>}
|
|
4177
|
+
} @else {
|
|
4178
|
+
<div class="o-fld"><label for="nf-path">File path</label><input id="nf-path" class="o-in o-mono" [(ngModel)]="filePath" placeholder="docs/notes.md" data-testid="new-file-path" /></div>
|
|
4179
|
+
<textarea class="o-in o-mono" rows="16" style="font-size:12px;height:auto;padding:8px 10px" aria-label="File contents" [(ngModel)]="content" data-testid="new-file-text"></textarea>
|
|
4180
|
+
}
|
|
4181
|
+
</div>
|
|
4182
|
+
<div class="o-cb" style="border-top:1px solid var(--o-line-soft)">
|
|
4183
|
+
<o-commit-box [repo]="r.name" [branch]="branch()" [defaultMessage]="defaultMessage()" [busy]="busy()" (commit)="save($event)" (cancel)="back()" />
|
|
4184
|
+
</div>
|
|
4185
|
+
</div>
|
|
4186
|
+
}
|
|
4187
|
+
} @else if (error()) {
|
|
4188
|
+
<div class="o-card o-empty"><p>{{ error() }}</p><a class="o-btn" [routerLink]="host.link('code')">All repositories</a></div>
|
|
4189
|
+
}
|
|
4190
|
+
</div>
|
|
4191
|
+
`,
|
|
4192
|
+
}]
|
|
4193
|
+
}], ctorParameters: () => [] });
|
|
4194
|
+
|
|
3793
4195
|
/*
|
|
3794
4196
|
* @chisquare-tech/origine-ngx-apps-code: the Code module's screens (TRD §6.10). The host app provides
|
|
3795
4197
|
* CodeHost and mounts the pages at /{org}/{project}/code, /code/new, /code/pulls and
|
|
@@ -3800,5 +4202,5 @@ function sortTags(tags) {
|
|
|
3800
4202
|
* Generated bundle index. Do not edit.
|
|
3801
4203
|
*/
|
|
3802
4204
|
|
|
3803
|
-
export { BranchesPage, CodeApi, CodeHost, CommitPage, CommitsPage, CurrentRepo, CurrentRepoPage, DiffView, FilePage, NewRepoPage, People, PullPage, PullsPage, RefPicker, ReleasesPage, RepoHomePage, RepoPage, RepoSettingsPage, ReposPage, TOKEN_CSS, ago, apiMessage, checkDot, copy, dayLabel, initials, langColour, pair, shortDate, size };
|
|
4205
|
+
export { BranchesPage, CodeApi, CodeHost, CommitBox, CommitPage, CommitsPage, CurrentRepo, CurrentRepoPage, DiffView, FilePage, NewFilePage, NewRepoPage, People, PullPage, PullsPage, RefPicker, ReleasesPage, RepoHomePage, RepoPage, RepoSettingsPage, ReposPage, TOKEN_CSS, ago, apiMessage, checkDot, copy, dayLabel, initials, langColour, matchesPattern, pair, shortDate, size };
|
|
3804
4206
|
//# sourceMappingURL=chisquare-tech-origine-ngx-apps-code.mjs.map
|