diff --git a/package.json b/package.json index 0172fea..8a33894 100644 --- a/package.json +++ b/package.json @@ -64,6 +64,7 @@ "onView:gitlens.views.searchAndCompare", "onView:gitlens.views.worktrees", "onView:gitlens.views.commitDetails", + "onView:gitlens.views.graphDetails", "onWebviewPanel:gitlens.welcome", "onWebviewPanel:gitlens.settings", "onWebviewPanel:gitlens.graph", @@ -2384,16 +2385,16 @@ "scope": "window", "order": 50 }, - "gitlens.graph.experimental.location": { + "gitlens.graph.layout": { "type": "string", - "default": "tab", + "default": "editor", "enum": [ - "tab", - "view" + "editor", + "panel" ], "enumDescriptions": [ "Shows the Commit Graph in an editor tab", - "Shows the Commit Graph in a view (side bar, panel, etc)" + "Shows the Commit Graph in the bottom panel" ], "markdownDescription": "Specifies the location in which the _Commit Graph_ will be shown", "scope": "window", @@ -6874,6 +6875,16 @@ "icon": "$(refresh)" }, { + "command": "gitlens.graph.switchToEditorLayout", + "title": "Switch Commit Graph to Editor Layout", + "category": "GitLens+" + }, + { + "command": "gitlens.graph.switchToPanelLayout", + "title": "Switch Commit Graph to Panel Layout", + "category": "GitLens+" + }, + { "command": "gitlens.graph.push", "title": "Push", "category": "GitLens", @@ -7535,11 +7546,11 @@ }, { "command": "gitlens.showGraphPage", - "when": "gitlens:enabled && config.gitlens.graph.experimental.location == tab" + "when": "gitlens:enabled && config.gitlens.graph.layout == editor" }, { "command": "gitlens.showGraphView", - "when": "gitlens:enabled && config.gitlens.graph.experimental.location == view" + "when": "gitlens:enabled && config.gitlens.graph.layout == panel" }, { "command": "gitlens.showHomeView", @@ -9102,6 +9113,14 @@ "when": "false" }, { + "command": "gitlens.graph.switchToEditorLayout", + "when": "gitlens:enabled && config.gitlens.graph.layout != editor" + }, + { + "command": "gitlens.graph.switchToPanelLayout", + "when": "gitlens:enabled && config.gitlens.graph.layout != panel" + }, + { "command": "gitlens.graph.push", "when": "false" }, @@ -9588,7 +9607,7 @@ "group": "navigation@-99" }, { - "command": "gitlens.showSettingsPage#commit-graph", + "submenu": "gitlens/graph/configuration", "when": "gitlens:webview:graph:active", "group": "navigation@-98" }, @@ -10468,12 +10487,12 @@ }, { "command": "gitlens.graph.refresh", - "when": "view =~ /^gitlens\\.views\\.graph/", + "when": "view =~ /^gitlens\\.views\\.graph\\b/", "group": "navigation@-99" }, { - "command": "gitlens.showSettingsPage#commit-graph", - "when": "view =~ /^gitlens\\.views\\.graph/", + "submenu": "gitlens/graph/configuration", + "when": "view =~ /^gitlens\\.views\\.graph\\b/", "group": "navigation@-98" }, { @@ -12488,6 +12507,22 @@ "group": "2_gitlens@2" } ], + "gitlens/graph/configuration": [ + { + "command": "gitlens.graph.switchToEditorLayout", + "group": "1_gitlens@1", + "when": "config.gitlens.graph.layout != editor" + }, + { + "command": "gitlens.graph.switchToPanelLayout", + "group": "1_gitlens@1", + "when": "config.gitlens.graph.layout != panel" + }, + { + "command": "gitlens.showSettingsPage#commit-graph", + "group": "9_gitlens@1" + } + ], "gitlens/scm/resourceGroup/changes": [ { "command": "gitlens.externalDiffAll", @@ -12707,6 +12742,11 @@ "label": "Commit Changes" }, { + "id": "gitlens/graph/configuration", + "label": "Commit Graph Settings", + "icon": "$(gear)" + }, + { "id": "gitlens/scm/resourceGroup/changes", "label": "Open Changes" }, @@ -13131,15 +13171,6 @@ "initialSize": 5 }, { - "type": "webview", - "id": "gitlens.views.graph", - "name": "Commit Graph", - "when": "!gitlens:disabled && gitlens:plus:enabled && config.gitlens.graph.experimental.location == view", - "contextualTitle": "GitLens", - "icon": "$(gitlens-graph)", - "visibility": "collapsed" - }, - { "id": "gitlens.views.contributors", "name": "Contributors", "when": "!gitlens:disabled", @@ -13152,12 +13183,33 @@ "gitlensPanel": [ { "type": "webview", + "id": "gitlens.views.graph", + "name": "Graph", + "when": "!gitlens:disabled && gitlens:plus:enabled && config.gitlens.graph.layout == panel", + "contextualTitle": "GitLens", + "icon": "$(gitlens-graph)", + "visibility": "visible", + "initialSize": 12 + }, + { + "type": "webview", + "id": "gitlens.views.graphDetails", + "name": "Graph Details", + "when": "!gitlens:disabled && gitlens:plus:enabled && config.gitlens.graph.layout == panel", + "contextualTitle": "GitLens", + "icon": "$(gitlens-commit-view)", + "visibility": "visible", + "initialSize": 4 + }, + { + "type": "webview", "id": "gitlens.views.timeline", "name": "Visual File History", "when": "!gitlens:disabled && gitlens:plus:enabled", "contextualTitle": "GitLens", "icon": "$(gitlens-history-view)", - "visibility": "visible" + "visibility": "collapsed", + "initialSize": 0 } ], "scm": [ diff --git a/src/config.ts b/src/config.ts index be36a5a..cc54ecd 100644 --- a/src/config.ts +++ b/src/config.ts @@ -417,13 +417,13 @@ export interface GraphConfig { defaultItemLimit: number; dimMergeCommits: boolean; experimental: { - location: 'tab' | 'view'; minimap: { enabled: boolean; additionalTypes: GraphMinimapTypes[]; }; }; highlightRowsOnRefHover: boolean; + layout: 'editor' | 'panel'; scrollRowPadding: number; showDetailsView: 'open' | 'selection' | false; showGhostRefsOnRowHover: boolean; diff --git a/src/constants.ts b/src/constants.ts index 8b252b8..4a07ce4 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -302,7 +302,7 @@ export const enum Commands { export type CustomEditorIds = 'rebase'; export type WebviewIds = 'graph' | 'settings' | 'timeline' | 'welcome' | 'focus'; -export type WebviewViewIds = 'commitDetails' | 'graph' | 'home' | 'timeline'; +export type WebviewViewIds = 'commitDetails' | 'graph' | 'graphDetails' | 'home' | 'timeline'; export type ContextKeys = | `${typeof extensionPrefix}:action:${string}` diff --git a/src/container.ts b/src/container.ts index 345eca4..38570fe 100644 --- a/src/container.ts +++ b/src/container.ts @@ -59,7 +59,10 @@ import { ViewCommands } from './views/viewCommands'; import { ViewFileDecorationProvider } from './views/viewDecorationProvider'; import { WorktreesView } from './views/worktreesView'; import { VslsController } from './vsls/vsls'; -import { registerCommitDetailsWebviewView } from './webviews/commitDetails/registration'; +import { + registerCommitDetailsWebviewView, + registerGraphDetailsWebviewView, +} from './webviews/commitDetails/registration'; import { registerHomeWebviewView } from './webviews/home/registration'; import { RebaseEditorProvider } from './webviews/rebase/rebaseEditor'; import { registerSettingsWebviewCommands, registerSettingsWebviewPanel } from './webviews/settings/registration'; @@ -215,7 +218,7 @@ export class Container { context.subscriptions.unshift((this._graphPanel = registerGraphWebviewPanel(this._webviews))); context.subscriptions.unshift(registerGraphWebviewCommands(this, this._graphPanel)); - if (configuration.get('graph.experimental.location') === 'view') { + if (configuration.get('graph.layout') === 'panel') { context.subscriptions.unshift((this._graphView = registerGraphWebviewView(this._webviews))); } context.subscriptions.unshift(new GraphStatusBarController(this)); @@ -231,6 +234,7 @@ export class Container { context.subscriptions.unshift((this._repositoriesView = new RepositoriesView(this))); context.subscriptions.unshift((this._commitDetailsView = registerCommitDetailsWebviewView(this._webviews))); + context.subscriptions.unshift((this._graphDetailsView = registerGraphDetailsWebviewView(this._webviews))); context.subscriptions.unshift((this._commitsView = new CommitsView(this))); context.subscriptions.unshift((this._fileHistoryView = new FileHistoryView(this))); context.subscriptions.unshift((this._lineHistoryView = new LineHistoryView(this))); @@ -306,8 +310,8 @@ export class Container { this.ensureModeApplied(); } - if (configuration.changed(e, 'graph.experimental.location')) { - if (configuration.get('graph.experimental.location') === 'view') { + if (configuration.changed(e, 'graph.layout')) { + if (configuration.get('graph.layout') === 'panel') { this._graphPanel?.close(); this._graphView = registerGraphWebviewView(this._webviews); } else { @@ -447,6 +451,11 @@ export class Container { } } + private readonly _graphDetailsView: WebviewViewProxy; + get graphDetailsView() { + return this._graphDetailsView; + } + private readonly _graphPanel: WebviewPanelProxy; private _graphView: WebviewViewProxy | undefined; get graphView() { diff --git a/src/plus/webviews/focus/registration.ts b/src/plus/webviews/focus/registration.ts index 9363035..44f70f7 100644 --- a/src/plus/webviews/focus/registration.ts +++ b/src/plus/webviews/focus/registration.ts @@ -15,7 +15,7 @@ export function registerFocusWebviewPanel(controller: WebviewsController) { trackingFeature: 'focusWebview', plusFeature: true, column: ViewColumn.Active, - webviewPanelOptions: { + webviewHostOptions: { retainContextWhenHidden: true, enableFindWidget: true, }, diff --git a/src/plus/webviews/graph/graphWebview.ts b/src/plus/webviews/graph/graphWebview.ts index 6b61358..623f302 100644 --- a/src/plus/webviews/graph/graphWebview.ts +++ b/src/plus/webviews/graph/graphWebview.ts @@ -1,5 +1,5 @@ -import type { ColorTheme, ConfigurationChangeEvent, Uri } from 'vscode'; -import { CancellationTokenSource, Disposable, env, ViewColumn, window } from 'vscode'; +import type { ColorTheme, ConfigurationChangeEvent, Uri, ViewColumn } from 'vscode'; +import { CancellationTokenSource, Disposable, env, window } from 'vscode'; import type { CreatePullRequestActionContext } from '../../../api/gitlens'; import { getAvatarUri } from '../../../avatars'; import type { @@ -47,7 +47,12 @@ import { import { getRemoteIconUri } from '../../../git/models/remote'; import { RemoteResourceType } from '../../../git/models/remoteResource'; import type { RepositoryChangeEvent, RepositoryFileSystemChangeEvent } from '../../../git/models/repository'; -import { Repository, RepositoryChange, RepositoryChangeComparisonMode } from '../../../git/models/repository'; +import { + isRepository, + Repository, + RepositoryChange, + RepositoryChangeComparisonMode, +} from '../../../git/models/repository'; import type { GitSearch } from '../../../git/search'; import { getSearchQueryComparisonKey } from '../../../git/search'; import { showRepositoryPicker } from '../../../quickpicks/repositoryPicker'; @@ -237,18 +242,15 @@ export class GraphWebviewProvider implements WebviewProvider { this._disposable.dispose(); } - async canShowWebviewPanel( - firstTime: boolean, - options: { column?: ViewColumn; preserveFocus?: boolean }, + async onShowing( + loading: boolean, + _options: { column?: ViewColumn; preserveFocus?: boolean }, ...args: unknown[] ): Promise { this._firstSelection = true; - if (options.column == null) { - options.column = ViewColumn.Active; - } const context = args[0]; - if (context instanceof Repository) { + if (isRepository(context)) { this.repository = context; } else if (hasGitReference(context)) { this.repository = this.container.git.getRepository(context.ref.repoPath); @@ -280,7 +282,7 @@ export class GraphWebviewProvider implements WebviewProvider { this.repository = context.node.repo; } - if (this.repository != null && !firstTime && this.host.isReady) { + if (this.repository != null && !loading && this.host.isReady) { this.updateState(); } } @@ -696,8 +698,12 @@ export class GraphWebviewProvider implements WebviewProvider { }, ); - if (!this.container.commitDetailsView.loaded) { - void this.container.commitDetailsView.show({ preserveFocus: e.preserveFocus }, { + const details = + configuration.get('graph.layout') === 'panel' + ? this.container.graphDetailsView + : this.container.commitDetailsView; + if (!details.ready) { + void details.show({ preserveFocus: e.preserveFocus }, { commit: commit, interaction: 'active', preserveVisibility: false, diff --git a/src/plus/webviews/graph/registration.ts b/src/plus/webviews/graph/registration.ts index 109637a..da5dd40 100644 --- a/src/plus/webviews/graph/registration.ts +++ b/src/plus/webviews/graph/registration.ts @@ -24,7 +24,7 @@ export function registerGraphWebviewPanel(controller: WebviewsController) { trackingFeature: 'graphWebview', plusFeature: true, column: ViewColumn.Active, - webviewPanelOptions: { + webviewHostOptions: { retainContextWhenHidden: true, enableFindWidget: false, }, @@ -33,7 +33,7 @@ export function registerGraphWebviewPanel(controller: WebviewsController) { const { GraphWebviewProvider } = await import(/* webpackChunkName: "graph" */ './graphWebview'); return new GraphWebviewProvider(container, host); }, - () => configuration.get('graph.experimental.location') === 'tab', + () => configuration.get('graph.layout') === 'editor', ); } @@ -46,7 +46,7 @@ export function registerGraphWebviewView(controller: WebviewsController) { contextKeyPrefix: `gitlens:webviewView:graph`, trackingFeature: 'graphView', plusFeature: true, - webviewViewOptions: { + webviewHostOptions: { retainContextWhenHidden: true, }, }, @@ -54,17 +54,29 @@ export function registerGraphWebviewView(controller: WebviewsController) { const { GraphWebviewProvider } = await import(/* webpackChunkName: "graph" */ './graphWebview'); return new GraphWebviewProvider(container, host); }, - () => configuration.get('graph.experimental.location') === 'view', + () => configuration.get('graph.layout') === 'panel', ); } export function registerGraphWebviewCommands(container: Container, webview: WebviewPanelProxy) { return Disposable.from( registerCommand(Commands.ShowGraph, (...args: any[]) => - configuration.get('graph.experimental.location') === 'view' + configuration.get('graph.layout') === 'panel' ? executeCommand(Commands.ShowGraphView, ...args) : executeCommand(Commands.ShowGraphPage, ...args), ), + registerCommand('gitlens.graph.switchToEditorLayout', async () => { + await configuration.updateEffective('graph.layout', 'editor'); + queueMicrotask(() => void executeCommand(Commands.ShowGraphPage)); + }), + registerCommand('gitlens.graph.switchToPanelLayout', async () => { + await configuration.updateEffective('graph.layout', 'panel'); + queueMicrotask(async () => { + await executeCommand('gitlens.views.graph.resetViewLocation'); + await executeCommand('gitlens.views.graphDetails.resetViewLocation'); + void executeCommand(Commands.ShowGraphView); + }); + }), registerCommand( Commands.ShowInCommitGraph, ( @@ -78,7 +90,7 @@ export function registerGraphWebviewCommands(container: Container, webview: Webv | TagNode, ) => { const preserveFocus = 'preserveFocus' in args ? args.preserveFocus ?? false : false; - if (configuration.get('graph.experimental.location') === 'view') { + if (configuration.get('graph.layout') === 'panel') { void container.graphView.show({ preserveFocus: preserveFocus }, args); } else { void webview.show({ preserveFocus: preserveFocus }, args); diff --git a/src/plus/webviews/timeline/registration.ts b/src/plus/webviews/timeline/registration.ts index d5d1cf2..7b8b14f 100644 --- a/src/plus/webviews/timeline/registration.ts +++ b/src/plus/webviews/timeline/registration.ts @@ -15,7 +15,7 @@ export function registerTimelineWebviewPanel(controller: WebviewsController) { trackingFeature: 'timelineWebview', plusFeature: true, column: ViewColumn.Active, - webviewPanelOptions: { + webviewHostOptions: { retainContextWhenHidden: true, enableFindWidget: false, }, @@ -36,7 +36,7 @@ export function registerTimelineWebviewView(controller: WebviewsController) { contextKeyPrefix: `gitlens:webviewView:timeline`, trackingFeature: 'timelineView', plusFeature: true, - webviewViewOptions: { + webviewHostOptions: { retainContextWhenHidden: false, }, }, diff --git a/src/plus/webviews/timeline/timelineWebview.ts b/src/plus/webviews/timeline/timelineWebview.ts index dbc141a..3a97c80 100644 --- a/src/plus/webviews/timeline/timelineWebview.ts +++ b/src/plus/webviews/timeline/timelineWebview.ts @@ -75,8 +75,8 @@ export class TimelineWebviewProvider implements WebviewProvider { this._disposable.dispose(); } - canShowWebviewPanel( - firstTime: boolean, + onShowing( + loading: boolean, _options: { column?: ViewColumn; preserveFocus?: boolean }, ...args: unknown[] ): boolean { @@ -87,7 +87,7 @@ export class TimelineWebviewProvider implements WebviewProvider { this.updatePendingEditor(window.activeTextEditor); } - if (firstTime) { + if (loading) { this._context = { ...this._context, ...this._pendingContext }; this._pendingContext = undefined; } else { diff --git a/src/storage.ts b/src/storage.ts index fd6091c..13fd1cc 100644 --- a/src/storage.ts +++ b/src/storage.ts @@ -7,6 +7,7 @@ import type { StoredSearchQuery } from './git/search'; import type { Subscription } from './subscription'; import { debug } from './system/decorators/log'; import type { TrackedUsage, TrackedUsageKeys } from './telemetry/usageTracker'; +import type { CommitDetailsDismissed } from './webviews/commitDetails/protocol'; import type { CompletedActions } from './webviews/home/protocol'; export type StorageChangeEvent = @@ -148,7 +149,7 @@ export type GlobalStorage = { preVersion: string; 'views:layout': StoredViewsLayout; 'views:welcome:visible': boolean; - 'views:commitDetails:dismissed': string[]; + 'views:commitDetails:dismissed': CommitDetailsDismissed[]; } & { [key in `provider:authentication:skip:${string}`]: boolean }; export type DeprecatedWorkspaceStorage = { diff --git a/src/webviews/apps/plus/graph/graph.html b/src/webviews/apps/plus/graph/graph.html index 508344f..1a38251 100644 --- a/src/webviews/apps/plus/graph/graph.html +++ b/src/webviews/apps/plus/graph/graph.html @@ -7,7 +7,11 @@ - +
#{endOfBody}