Browse Source

Adds avatar context menu on the graph

Adds graph context menus for virtual GitHub repos
Adds comparison commands to the graph
Fixes `when` clause contitions for some commands
main
Eric Amodio 2 years ago
parent
commit
a9c0288be8
4 changed files with 313 additions and 37 deletions
  1. +98
    -10
      package.json
  2. +21
    -13
      src/env/node/git/localGitProvider.ts
  3. +78
    -4
      src/plus/github/githubGitProvider.ts
  4. +116
    -10
      src/plus/webviews/graph/graphWebview.ts

+ 98
- 10
package.json View File

@ -6443,6 +6443,40 @@
"icon": "$(copy)"
},
{
"command": "gitlens.graph.compareAncestryWithWorking",
"title": "Compare Ancestry with Working Tree",
"category": "GitLens"
},
{
"command": "gitlens.graph.compareWithHead",
"title": "Compare with HEAD",
"category": "GitLens",
"icon": "$(compare-changes)"
},
{
"command": "gitlens.graph.compareWithUpstream",
"title": "Compare with Upstream",
"category": "GitLens"
},
{
"command": "gitlens.graph.compareWithWorking",
"title": "Compare with Working Tree",
"category": "GitLens",
"icon": "$(gitlens-compare-ref-working)"
},
{
"command": "gitlens.graph.addAuthor",
"title": "Add as Co-author",
"category": "GitLens",
"icon": "$(person-add)"
},
{
"command": "gitlens.graph.copy",
"title": "Copy",
"category": "GitLens",
"icon": "$(copy)"
},
{
"command": "gitlens.graph.columnAuthorOn",
"title": "Show Author",
"category": "GitLens"
@ -7120,7 +7154,7 @@
},
{
"command": "gitlens.addAuthors",
"when": "gitlens:enabled"
"when": "gitlens:enabled && !gitlens:readonly && !gitlens:untrusted && !gitlens:hasVirtualFolders"
},
{
"command": "gitlens.connectRemoteProvider",
@ -8395,6 +8429,30 @@
"when": "false"
},
{
"command": "gitlens.graph.compareAncestryWithWorking",
"when": "false"
},
{
"command": "gitlens.graph.compareWithHead",
"when": "false"
},
{
"command": "gitlens.graph.compareWithUpstream",
"when": "false"
},
{
"command": "gitlens.graph.compareWithWorking",
"when": "false"
},
{
"command": "gitlens.graph.addAuthor",
"when": "false"
},
{
"command": "gitlens.graph.copy",
"when": "false"
},
{
"command": "gitlens.graph.columnAuthorOn",
"when": "false"
},
@ -9027,7 +9085,7 @@
},
{
"command": "gitlens.views.addAuthors",
"when": "view =~ /^gitlens\\.views\\.contributors/",
"when": "!gitlens:readonly && !gitlens:untrusted && !gitlens:hasVirtualFolders && view =~ /^gitlens\\.views\\.contributors/",
"group": "navigation@10"
},
{
@ -9739,7 +9797,7 @@
},
{
"command": "gitlens.views.createWorktree",
"when": "!gitlens:readonly && viewItem =~ /gitlens:branch\\b/",
"when": "!gitlens:readonly && !gitlens:untrusted && !gitlens:hasVirtualFolders && viewItem =~ /gitlens:branch\\b/",
"group": "1_gitlens_actions_@9"
},
{
@ -9810,12 +9868,12 @@
},
{
"command": "gitlens.views.addAuthors",
"when": "viewItem =~ /gitlens:contributors\\b/",
"when": "!gitlens:readonly && !gitlens:untrusted && !gitlens:hasVirtualFolders && viewItem =~ /gitlens:contributors\\b/",
"group": "inline@1"
},
{
"command": "gitlens.views.addAuthors",
"when": "viewItem =~ /gitlens:contributors\\b/",
"when": "!gitlens:readonly && !gitlens:untrusted && !gitlens:hasVirtualFolders && viewItem =~ /gitlens:contributors\\b/",
"group": "1_gitlens_actions@1"
},
{
@ -9825,7 +9883,7 @@
},
{
"command": "gitlens.views.addAuthor",
"when": "viewItem =~ /gitlens:contributor\\b(?!.*?\\b\\+current\\b)/",
"when": "!gitlens:readonly && !gitlens:untrusted && !gitlens:hasVirtualFolders && viewItem =~ /gitlens:contributor\\b(?!.*?\\b\\+current\\b)/",
"group": "inline@2"
},
{
@ -9840,7 +9898,7 @@
},
{
"command": "gitlens.views.addAuthor",
"when": "viewItem =~ /gitlens:contributor\\b(?!.*?\\b\\+current\\b)/",
"when": "!gitlens:readonly && !gitlens:untrusted && !gitlens:hasVirtualFolders && viewItem =~ /gitlens:contributor\\b(?!.*?\\b\\+current\\b)/",
"group": "1_gitlens_actions@2"
},
{
@ -10748,12 +10806,12 @@
},
{
"command": "gitlens.views.createWorktree",
"when": "!gitlens:readonly && viewItem =~ /gitlens:worktrees\\b/",
"when": "!gitlens:readonly && !gitlens:untrusted && !gitlens:hasVirtualFolders && viewItem =~ /gitlens:worktrees\\b/",
"group": "inline@1"
},
{
"command": "gitlens.views.createWorktree",
"when": "!gitlens:readonly && viewItem =~ /gitlens:worktrees\\b/",
"when": "!gitlens:readonly && !gitlens:untrusted && !gitlens:hasVirtualFolders && viewItem =~ /gitlens:worktrees\\b/",
"group": "1_gitlens_actions@1"
},
{
@ -10907,7 +10965,7 @@
},
{
"command": "gitlens.graph.createWorktree",
"when": "!gitlens:readonly && webviewItem =~ /gitlens:branch\\b/",
"when": "!gitlens:readonly && !gitlens:untrusted && !gitlens:hasVirtualFolders && webviewItem =~ /gitlens:branch\\b/",
"group": "1_gitlens_actions_@9"
},
{
@ -11018,6 +11076,36 @@
"group": "7_gitlens_cutcopypaste@1"
},
{
"command": "gitlens.graph.compareWithUpstream",
"when": "!gitlens:hasVirtualFolders && webviewItem =~ /gitlens:branch\\b(?=.*?\\b\\+tracking\\b)/",
"group": "4_gitlens_compare@1"
},
{
"command": "gitlens.graph.compareWithHead",
"when": "!gitlens:hasVirtualFolders && webviewItem =~ /gitlens:(branch\\b(?!.*?\\b\\+current\\b)|commit\\b|stash\\b|tag\\b)/",
"group": "4_gitlens_compare@2"
},
{
"command": "gitlens.graph.compareWithWorking",
"when": "!gitlens:hasVirtualFolders && webviewItem =~ /gitlens:(branch|commit|stash|tag)\\b/",
"group": "4_gitlens_compare@3"
},
{
"command": "gitlens.graph.compareAncestryWithWorking",
"when": "!gitlens:hasVirtualFolders && webviewItem =~ /gitlens:branch\\b(?!.*?\\b\\+current\\b)/",
"group": "4_gitlens_compare@4"
},
{
"command": "gitlens.graph.addAuthor",
"when": "!gitlens:readonly && !gitlens:untrusted && !gitlens:hasVirtualFolders && webviewItem =~ /gitlens:contributor\\b(?!.*?\\b\\+current\\b)/",
"group": "1_gitlens_actions@2"
},
{
"command": "gitlens.graph.copy",
"when": "webviewItem =~ /gitlens:contributor\\b/",
"group": "7_gitlens_cutcopypaste@1"
},
{
"command": "gitlens.graph.columnAuthorOn",
"when": "webviewItem =~ /gitlens:graph:columns\\b/ && webviewItemValue =~ /\\bauthor\\b/",
"group": "1_columns@1"

+ 21
- 13
src/env/node/git/localGitProvider.ts View File

@ -1634,16 +1634,19 @@ export class LocalGitProvider implements GitProvider, Disposable {
const defaultPageLimit = configuration.get('graph.pageItemLimit') ?? 1000;
const ordering = configuration.get('graph.commitOrdering', undefined, 'date');
const [refResult, stashResult, branchesResult, remotesResult] = await Promise.allSettled([
const [refResult, stashResult, branchesResult, remotesResult, currentUserResult] = await Promise.allSettled([
this.git.log2(repoPath, undefined, ...refParser.arguments, '-n1', options?.ref ?? 'HEAD'),
this.getStash(repoPath),
this.getBranches(repoPath),
this.getRemotes(repoPath),
this.getCurrentUser(repoPath),
]);
const branches = getSettledValue(branchesResult)?.values;
const branchMap = branches != null ? new Map(branches.map(r => [r.name, r])) : new Map<string, GitBranch>();
const currentUser = getSettledValue(currentUserResult);
const remotes = getSettledValue(remotesResult);
const remoteMap =
remotes != null
@ -1747,8 +1750,8 @@ export class LocalGitProvider implements GitProvider, Disposable {
let branch: GitBranch | undefined;
let branchName: string;
let current = false;
let headCommit = false;
let head = false;
let isCurrentUser = false;
let refHeads: GitGraphRowHead[];
let refRemoteHeads: GitGraphRowRemoteHead[];
let refTags: GitGraphRowTag[];
@ -1776,7 +1779,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
refRemoteHeads = [];
refTags = [];
contexts = undefined;
headCommit = false;
head = false;
if (commit.tips) {
for (let tip of commit.tips.split(', ')) {
@ -1803,9 +1806,8 @@ export class LocalGitProvider implements GitProvider, Disposable {
continue;
}
current = tip.startsWith('HEAD');
if (current) {
headCommit = true;
head = tip.startsWith('HEAD');
if (head) {
reachableFromHEAD.add(commit.sha);
if (tip !== 'HEAD') {
@ -1854,9 +1856,9 @@ export class LocalGitProvider implements GitProvider, Disposable {
refHeads.push({
id: branchId,
name: tip,
isCurrentHead: current,
isCurrentHead: head,
context: serializeWebviewItemContext<GraphItemRefContext>({
webviewItem: `gitlens:branch${current ? '+current' : ''}${
webviewItem: `gitlens:branch${head ? '+current' : ''}${
branch?.upstream != null ? '+tracking' : ''
}`,
webviewItemValue: {
@ -1898,6 +1900,8 @@ export class LocalGitProvider implements GitProvider, Disposable {
}
}
isCurrentUser = isUserMatch(currentUser, commit.author, commit.authorEmail);
contexts = {};
if (stashCommit != null) {
contexts.row = serializeWebviewItemContext<GraphItemRefContext>({
@ -1913,7 +1917,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
});
} else {
contexts.row = serializeWebviewItemContext<GraphItemRefContext>({
webviewItem: `gitlens:commit${headCommit ? '+HEAD' : ''}${
webviewItem: `gitlens:commit${head ? '+HEAD' : ''}${
reachableFromHEAD.has(commit.sha) ? '+current' : ''
}`,
webviewItemValue: {
@ -1924,11 +1928,15 @@ export class LocalGitProvider implements GitProvider, Disposable {
}),
},
});
contexts.avatar = serializeWebviewItemContext<GraphItemContext>({
webviewItem: 'gitlens:avatar',
webviewItem: `gitlens:contributor${isCurrentUser ? '+current' : ''}`,
webviewItemValue: {
type: 'avatar',
type: 'contributor',
repoPath: repoPath,
name: commit.author,
email: commit.authorEmail,
current: isCurrentUser,
},
});
}
@ -1936,7 +1944,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
rows.push({
sha: commit.sha,
parents: parents,
author: commit.author,
author: isCurrentUser ? 'You' : commit.author,
email: commit.authorEmail,
date: Number(ordering === 'author-date' ? commit.authorDate : commit.committerDate) * 1000,
message: emojify(commit.message.trim()),

+ 78
- 4
src/plus/github/githubGitProvider.ts View File

@ -49,6 +49,7 @@ import { GitFileChange, GitFileIndexStatus } from '../../git/models/file';
import type {
GitGraph,
GitGraphRow,
GitGraphRowContexts,
GitGraphRowHead,
GitGraphRowRemoteHead,
GitGraphRowTag,
@ -57,8 +58,8 @@ import { GitGraphRowType } from '../../git/models/graph';
import type { GitLog } from '../../git/models/log';
import type { GitMergeStatus } from '../../git/models/merge';
import type { GitRebaseStatus } from '../../git/models/rebase';
import type { GitBranchReference, GitReference } from '../../git/models/reference';
import { GitRevision } from '../../git/models/reference';
import type { GitBranchReference } from '../../git/models/reference';
import { GitReference, GitRevision } from '../../git/models/reference';
import type { GitReflog } from '../../git/models/reflog';
import { getRemoteIconUri, GitRemote, GitRemoteType } from '../../git/models/remote';
import type { RepositoryChangeEvent } from '../../git/models/repository';
@ -83,11 +84,13 @@ import { debug, getLogScope, log } from '../../system/decorators/log';
import { filterMap, first, last, some } from '../../system/iterable';
import { isAbsolute, isFolderGlob, maybeUri, normalizePath, relative } from '../../system/path';
import { getSettledValue } from '../../system/promise';
import { serializeWebviewItemContext } from '../../system/webview';
import type { CachedBlame, CachedLog } from '../../trackers/gitDocumentTracker';
import { GitDocumentState } from '../../trackers/gitDocumentTracker';
import type { TrackedDocument } from '../../trackers/trackedDocument';
import type { GitHubAuthorityMetadata, Metadata, RemoteHubApi } from '../remotehub';
import { getRemoteHubApi } from '../remotehub';
import type { GraphItemContext, GraphItemRefContext } from '../webviews/graph/graphWebview';
import type { GitHubApi } from './github';
import { fromCommitFileStatus } from './models';
@ -1076,11 +1079,12 @@ export class GitHubGitProvider implements GitProvider, Disposable {
// const defaultPageLimit = configuration.get('graph.pageItemLimit') ?? 1000;
const ordering = configuration.get('graph.commitOrdering', undefined, 'date');
const [logResult, branchResult, remotesResult, tagsResult] = await Promise.allSettled([
const [logResult, branchResult, remotesResult, tagsResult, currentUserResult] = await Promise.allSettled([
this.getLog(repoPath, { all: true, ordering: ordering, limit: defaultLimit }),
this.getBranch(repoPath),
this.getRemotes(repoPath),
this.getTags(repoPath),
this.getCurrentUser(repoPath),
]);
const avatars = new Map<string, string>();
@ -1093,6 +1097,7 @@ export class GitHubGitProvider implements GitProvider, Disposable {
getSettledValue(branchResult),
getSettledValue(remotesResult)?.[0],
getSettledValue(tagsResult)?.values,
getSettledValue(currentUserResult),
avatars,
ids,
options,
@ -1106,6 +1111,7 @@ export class GitHubGitProvider implements GitProvider, Disposable {
branch: GitBranch | undefined,
remote: GitRemote | undefined,
tags: GitTag[] | undefined,
currentUser: GitUser | undefined,
avatars: Map<string, string>,
ids: Set<string>,
options?: {
@ -1136,21 +1142,39 @@ export class GitHubGitProvider implements GitProvider, Disposable {
const rows: GitGraphRow[] = [];
let head = false;
let isCurrentUser = false;
let refHeads: GitGraphRowHead[];
let refRemoteHeads: GitGraphRowRemoteHead[];
let refTags: GitGraphRowTag[];
let contexts: GitGraphRowContexts | undefined;
const hasHeadShaAndRemote = branch?.sha != null && remote != null;
for (const commit of commits) {
ids.add(commit.sha);
if (hasHeadShaAndRemote && commit.sha === branch.sha) {
head = commit.sha === branch?.sha;
if (hasHeadShaAndRemote && head) {
refHeads = [
{
id: getBranchId(repoPath, false, branch.name),
name: branch.name,
isCurrentHead: true,
context: serializeWebviewItemContext<GraphItemRefContext>({
webviewItem: `gitlens:branch${head ? '+current' : ''}${
branch?.upstream != null ? '+tracking' : ''
}`,
webviewItemValue: {
type: 'branch',
ref: GitReference.create(branch.name, repoPath, {
refType: 'branch',
name: branch.name,
remote: false,
upstream: branch.upstream,
}),
},
}),
},
];
refRemoteHeads = [
@ -1162,6 +1186,18 @@ export class GitHubGitProvider implements GitProvider, Disposable {
avatarUrl: (
remote.provider?.avatarUri ?? getRemoteIconUri(this.container, remote, asWebviewUri)
)?.toString(true),
context: serializeWebviewItemContext<GraphItemRefContext>({
webviewItem: 'gitlens:branch+remote',
webviewItemValue: {
type: 'branch',
ref: GitReference.create(branch.name, repoPath, {
refType: 'branch',
name: branch.name,
remote: true,
upstream: { name: remote.name, missing: false },
}),
},
}),
},
];
} else {
@ -1177,6 +1213,16 @@ export class GitHubGitProvider implements GitProvider, Disposable {
return {
name: t.name,
annotated: Boolean(t.message),
context: serializeWebviewItemContext<GraphItemRefContext>({
webviewItem: 'gitlens:tag',
webviewItemValue: {
type: 'tag',
ref: GitReference.create(t.name, repoPath, {
refType: 'tag',
name: t.name,
}),
},
}),
};
}),
];
@ -1191,6 +1237,32 @@ export class GitHubGitProvider implements GitProvider, Disposable {
}
}
isCurrentUser = commit.author.name === 'You';
contexts = {
row: serializeWebviewItemContext<GraphItemRefContext>({
webviewItem: `gitlens:commit${
hasHeadShaAndRemote && commit.sha === branch.sha ? '+HEAD' : ''
}+current`,
webviewItemValue: {
type: 'commit',
ref: GitReference.create(commit.sha, repoPath, {
refType: 'revision',
message: commit.message,
}),
},
}),
avatar: serializeWebviewItemContext<GraphItemContext>({
webviewItem: `gitlens:contributor${isCurrentUser ? '+current' : ''}`,
webviewItemValue: {
type: 'contributor',
repoPath: repoPath,
name: isCurrentUser && currentUser?.name != null ? currentUser.name : commit.author.name,
email: commit.author.email,
current: isCurrentUser,
},
}),
};
rows.push({
sha: commit.sha,
parents: commit.parents,
@ -1203,6 +1275,7 @@ export class GitHubGitProvider implements GitProvider, Disposable {
heads: refHeads,
remotes: refRemoteHeads,
tags: refTags,
contexts: contexts,
});
}
@ -1233,6 +1306,7 @@ export class GitHubGitProvider implements GitProvider, Disposable {
branch,
remote,
tags,
currentUser,
avatars,
ids,
options,

+ 116
- 10
src/plus/webviews/graph/graphWebview.ts View File

@ -9,6 +9,7 @@ import type {
import {
CancellationTokenSource,
Disposable,
env,
EventEmitter,
MarkdownString,
StatusBarAlignment,
@ -34,6 +35,7 @@ import { getContext, onDidChangeContext, setContext } from '../../../context';
import { PlusFeatures } from '../../../features';
import { GitSearchError } from '../../../git/errors';
import type { GitCommit } from '../../../git/models/commit';
import { GitContributor } from '../../../git/models/contributor';
import { GitGraphRowType } from '../../../git/models/graph';
import type { GitGraph } from '../../../git/models/graph';
import type {
@ -336,9 +338,17 @@ export class GraphWebview extends WebviewBase {
this,
),
registerCommand('gitlens.graph.compareWithUpstream', this.compareWithUpstream, this),
registerCommand('gitlens.graph.compareWithHead', this.compareHeadWith, this),
registerCommand('gitlens.graph.compareWithWorking', this.compareWorkingWith, this),
registerCommand('gitlens.graph.compareAncestryWithWorking', this.compareAncestryWithWorking, this),
registerCommand('gitlens.graph.copy', this.copy, this),
registerCommand('gitlens.graph.copyMessage', this.copyMessage, this),
registerCommand('gitlens.graph.copySha', this.copySha, this),
registerCommand('gitlens.graph.addAuthor', this.addAuthor, this),
registerCommand('gitlens.graph.columnAuthorOn', () => this.toggleColumn('author', true)),
registerCommand('gitlens.graph.columnAuthorOff', () => this.toggleColumn('author', false)),
registerCommand('gitlens.graph.columnDateTimeOn', () => this.toggleColumn('datetime', true)),
@ -1392,6 +1402,16 @@ export class GraphWebview extends WebviewBase {
}
@debug()
private async copy(item: GraphItemContext) {
if (isGraphItemTypedContext(item, 'contributor')) {
const { name, email } = item.webviewItemValue;
await env.clipboard.writeText(`${name}${email ? ` <${email}>` : ''}`);
}
return Promise.resolve();
}
@debug()
private copyMessage(item: GraphItemContext) {
if (isGraphItemRefContext(item)) {
const { ref } = item.webviewItemValue;
@ -1635,6 +1655,72 @@ export class GraphWebview extends WebviewBase {
}
@debug()
private async compareAncestryWithWorking(item: GraphItemContext) {
if (isGraphItemRefContext(item)) {
const { ref } = item.webviewItemValue;
const branch = await this.container.git.getBranch(ref.repoPath);
if (branch == null) return undefined;
const commonAncestor = await this.container.git.getMergeBase(ref.repoPath, branch.ref, ref.ref);
if (commonAncestor == null) return undefined;
return this.container.searchAndCompareView.compare(
ref.repoPath,
{ ref: commonAncestor, label: `ancestry with ${ref.ref} (${GitRevision.shorten(commonAncestor)})` },
'',
);
}
return Promise.resolve();
}
@debug()
private compareHeadWith(item: GraphItemContext) {
if (isGraphItemRefContext(item)) {
const { ref } = item.webviewItemValue;
return this.container.searchAndCompareView.compare(ref.repoPath, 'HEAD', ref.ref);
}
return Promise.resolve();
}
@debug()
private compareWithUpstream(item: GraphItemContext) {
if (isGraphItemRefContext(item, 'branch')) {
const { ref } = item.webviewItemValue;
if (ref.upstream != null) {
return this.container.searchAndCompareView.compare(ref.repoPath, ref.ref, ref.upstream.name);
}
}
return Promise.resolve();
}
@debug()
private compareWorkingWith(item: GraphItemContext) {
if (isGraphItemRefContext(item)) {
const { ref } = item.webviewItemValue;
return this.container.searchAndCompareView.compare(ref.repoPath, '', ref.ref);
}
return Promise.resolve();
}
@debug()
private addAuthor(item: GraphItemContext) {
if (isGraphItemTypedContext(item, 'contributor')) {
const { repoPath, name, email, current } = item.webviewItemValue;
return GitActions.Contributor.addAuthors(
repoPath,
new GitContributor(repoPath, name, email, 0, undefined, current),
);
}
return Promise.resolve();
}
@debug()
private async toggleColumn(name: GraphColumnName, visible: boolean) {
let columns = this.container.storage.getWorkspace('graph:columns');
let column = columns?.[name];
@ -1669,19 +1755,22 @@ export type GraphItemRefContextValue =
| GraphCommitContextValue
| GraphStashContextValue
| GraphTagContextValue;
export type GraphItemContextValue =
| GraphAvatarContextValue
| GraphColumnsContextValue
| GraphPullRequestContextValue
| GraphItemRefContextValue;
export interface GraphAvatarContextValue {
type: 'avatar';
email: string;
}
export type GraphItemTypedContext<T = GraphItemTypedContextValue> = WebviewItemContext<T>;
export type GraphItemTypedContextValue = GraphContributorContextValue | GraphPullRequestContextValue;
export type GraphItemContextValue = GraphColumnsContextValue | GraphItemTypedContextValue | GraphItemRefContextValue;
export type GraphColumnsContextValue = string;
export interface GraphContributorContextValue {
type: 'contributor';
repoPath: string;
name: string;
email: string | undefined;
current?: boolean;
}
export interface GraphPullRequestContextValue {
type: 'pullrequest';
id: string;
@ -1714,6 +1803,23 @@ function isGraphItemContext(item: unknown): item is GraphItemContext {
return isWebviewItemContext(item) && item.webview === 'gitlens.graph';
}
function isGraphItemTypedContext(
item: unknown,
type: 'contributor',
): item is GraphItemTypedContext<GraphContributorContextValue>;
function isGraphItemTypedContext(
item: unknown,
type: 'pullrequest',
): item is GraphItemTypedContext<GraphPullRequestContextValue>;
function isGraphItemTypedContext(
item: unknown,
type: GraphItemTypedContextValue['type'],
): item is GraphItemTypedContext {
if (item == null) return false;
return isGraphItemContext(item) && typeof item.webviewItemValue === 'object' && item.webviewItemValue.type === type;
}
function isGraphItemRefContext(item: unknown): item is GraphItemRefContext;
function isGraphItemRefContext(item: unknown, refType: 'branch'): item is GraphItemRefContext<GraphBranchContextValue>;
function isGraphItemRefContext(

Loading…
Cancel
Save