Skip to content

Commit 2c790db

Browse files
Improves commit composer UX (#4759)
* Add maximize command for Composer webview * Reorganize editor title menu items * Add workbench panel visibility configuration type * Refactor commit message from string to structured object * Fix timing issue with state initialization * Fix snapshot state to use local properties instead of state object * Enhance commit message UI with read-only view and editing improvements * Improve typography in commits panel * Add default cursor style to composer items * Add sticky positioning support for commit messages in details panel * Style composition summary view and add click handler * Removes unused state and adds composition summary selection to snapshot
1 parent 89d48c1 commit 2c790db

19 files changed

+331
-91
lines changed

contributions.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -612,6 +612,19 @@
612612
]
613613
}
614614
},
615+
"gitlens.composer.maximize": {
616+
"label": "Maximize",
617+
"icon": "$(screen-full)",
618+
"menus": {
619+
"editor/title": [
620+
{
621+
"when": "activeWebviewPanelId === gitlens.composer",
622+
"group": "navigation",
623+
"order": -98
624+
}
625+
]
626+
}
627+
},
615628
"gitlens.composer.refresh": {
616629
"label": "Refresh",
617630
"icon": "$(refresh)",

package.json

Lines changed: 40 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -6511,6 +6511,11 @@
65116511
"title": "Compose Commits (Preview)...",
65126512
"icon": "$(sparkle)"
65136513
},
6514+
{
6515+
"command": "gitlens.composer.maximize",
6516+
"title": "Maximize",
6517+
"icon": "$(screen-full)"
6518+
},
65146519
{
65156520
"command": "gitlens.composer.refresh",
65166521
"title": "Refresh",
@@ -11767,6 +11772,10 @@
1176711772
"command": "gitlens.composeCommits:views",
1176811773
"when": "false"
1176911774
},
11775+
{
11776+
"command": "gitlens.composer.maximize",
11777+
"when": "false"
11778+
},
1177011779
{
1177111780
"command": "gitlens.composer.refresh",
1177211781
"when": "false"
@@ -15531,25 +15540,20 @@
1553115540
"when": "activeWebviewPanelId === gitlens.composer",
1553215541
"group": "navigation@-99"
1553315542
},
15534-
{
15535-
"command": "gitlens.graph.refresh",
15536-
"when": "activeWebviewPanelId === gitlens.graph",
15537-
"group": "navigation@-99"
15538-
},
1553915543
{
1554015544
"command": "gitlens.timeline.refresh",
1554115545
"when": "activeWebviewPanelId === gitlens.timeline",
1554215546
"group": "navigation@-99"
1554315547
},
1554415548
{
15545-
"submenu": "gitlens/graph/configuration",
15546-
"when": "activeWebviewPanelId === gitlens.graph",
15549+
"command": "gitlens.composer.maximize",
15550+
"when": "activeWebviewPanelId === gitlens.composer",
1554715551
"group": "navigation@-98"
1554815552
},
1554915553
{
15550-
"command": "gitlens.graph.split",
15551-
"when": "activeWebviewPanelId == gitlens.graph && resourceScheme == webview-panel && config.gitlens.graph.allowMultiple",
15552-
"group": "navigation@-97"
15554+
"submenu": "gitlens/graph/configuration",
15555+
"when": "activeWebviewPanelId === gitlens.graph",
15556+
"group": "navigation@-98"
1555315557
},
1555415558
{
1555515559
"command": "gitlens.timeline.split",
@@ -15578,6 +15582,32 @@
1557815582
"group": "navigation@100",
1557915583
"alt": "gitlens.toggleFileBlame:editor/title"
1558015584
},
15585+
{
15586+
"command": "gitlens.diffWithPrevious:editor/title",
15587+
"when": "resource in gitlens:tabs:tracked && config.gitlens.menus.editorGroup.compare",
15588+
"group": "navigation@97",
15589+
"alt": "gitlens.diffWithRevision"
15590+
},
15591+
{
15592+
"command": "gitlens.diffWithNext:editor/title",
15593+
"when": "resource in gitlens:tabs:tracked && config.gitlens.menus.editorGroup.compare",
15594+
"group": "navigation@99"
15595+
},
15596+
{
15597+
"command": "gitlens.diffWithWorking:editor/title",
15598+
"when": "resourceScheme =~ /^(gitlens|pr)$/ && gitlens:enabled",
15599+
"group": "navigation@-99"
15600+
},
15601+
{
15602+
"command": "gitlens.graph.refresh",
15603+
"when": "activeWebviewPanelId === gitlens.graph",
15604+
"group": "navigation@-99"
15605+
},
15606+
{
15607+
"command": "gitlens.graph.split",
15608+
"when": "activeWebviewPanelId == gitlens.graph && resourceScheme == webview-panel && config.gitlens.graph.allowMultiple",
15609+
"group": "navigation@-97"
15610+
},
1558115611
{
1558215612
"command": "gitlens.toggleFileHeatmap:editor/title",
1558315613
"when": "resource in gitlens:tabs:blameable && resource not in gitlens:tabs:annotated && config.gitlens.menus.editorGroup.blame && config.gitlens.fileAnnotations.command == heatmap",
@@ -15589,32 +15619,16 @@
1558915619
"when": "resource in gitlens:tabs:blameable && resource not in gitlens:tabs:annotated && !gitlens:window:annotated && config.gitlens.menus.editorGroup.blame && !config.gitlens.fileAnnotations.command",
1559015620
"group": "navigation@100"
1559115621
},
15592-
{
15593-
"command": "gitlens.diffWithPrevious:editor/title",
15594-
"when": "resource in gitlens:tabs:tracked && config.gitlens.menus.editorGroup.compare",
15595-
"group": "navigation@97",
15596-
"alt": "gitlens.diffWithRevision"
15597-
},
1559815622
{
1559915623
"command": "gitlens.showQuickRevisionDetails:editor/title",
1560015624
"when": "resource in gitlens:tabs:tracked && config.gitlens.menus.editorGroup.compare",
1560115625
"group": "navigation@98"
1560215626
},
15603-
{
15604-
"command": "gitlens.diffWithNext:editor/title",
15605-
"when": "resource in gitlens:tabs:tracked && config.gitlens.menus.editorGroup.compare",
15606-
"group": "navigation@99"
15607-
},
1560815627
{
1560915628
"command": "gitlens.openWorkingFile:editor/title",
1561015629
"when": "resourceScheme == git && gitlens:enabled && !isInDiffEditor",
1561115630
"group": "navigation@-98"
1561215631
},
15613-
{
15614-
"command": "gitlens.diffWithWorking:editor/title",
15615-
"when": "resourceScheme =~ /^(gitlens|pr)$/ && gitlens:enabled",
15616-
"group": "navigation@-99"
15617-
},
1561815632
{
1561915633
"command": "gitlens.openWorkingFile:editor/title",
1562015634
"when": "resourceScheme =~ /^(gitlens|pr)$/ && gitlens:enabled",

src/config.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1121,6 +1121,9 @@ export type CoreConfig = {
11211121
};
11221122
readonly workbench: {
11231123
readonly editorAssociations: Record<string, string> | { viewType: string; filenamePattern: string }[];
1124+
readonly panel: {
1125+
readonly visible: boolean;
1126+
};
11241127
readonly tree: {
11251128
readonly renderIndentGuides: 'always' | 'none' | 'onHover';
11261129
readonly indent: number;

src/constants.commands.generated.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ export type ContributedCommands =
3737
| 'gitlens.composeCommits:graph'
3838
| 'gitlens.composeCommits:scm'
3939
| 'gitlens.composeCommits:views'
40+
| 'gitlens.composer.maximize'
4041
| 'gitlens.composer.refresh'
4142
| 'gitlens.computingFileAnnotations'
4243
| 'gitlens.connectRemoteProvider'

src/constants.commands.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,8 @@ export type CoreCommands =
206206
| 'workbench.action.openSettings'
207207
| 'workbench.action.openWalkthrough'
208208
| 'workbench.action.toggleMaximizedPanel'
209+
| 'workbench.action.focusPanel'
210+
| 'workbench.action.togglePanel'
209211
| 'workbench.extensions.action.switchToRelease'
210212
| 'workbench.extensions.installExtension'
211213
| 'workbench.extensions.uninstallExtension'

src/webviews/apps/plus/composer/components/app.ts

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,11 @@ interface ComposerDataSnapshot {
5454
commits: ComposerCommit[];
5555
selectedCommitId: string | null;
5656
selectedCommitIds: Set<string>;
57-
selectedUnassignedSection: string | null;
57+
selectedUnassignedSection: 'staged' | 'unstaged' | 'unassigned' | null;
5858
selectedHunkIds: Set<string>;
5959
hasUsedAutoCompose: boolean;
6060
recompose: { enabled: boolean; branchName?: string; locked: boolean; commitShas?: string[] } | null;
61+
compositionSummarySelected: boolean;
6162
}
6263

6364
interface ComposerHistory {
@@ -410,13 +411,13 @@ export class ComposerApp extends LitElement {
410411
private lastMouseEvent?: MouseEvent;
411412

412413
override firstUpdated() {
413-
this.initializeResetStateIfNeeded();
414414
// Delay initialization to ensure DOM is ready
415415
setTimeout(() => this.initializeSortable(), 200);
416416
this.initializeDragTracking();
417417
if (this.state.commits.length > 0) {
418418
this.selectCommit(this.state.commits[0].id);
419419
}
420+
this.initializeResetStateIfNeeded();
420421
if (!this.state.onboardingDismissed) {
421422
this.openOnboarding();
422423
}
@@ -614,12 +615,13 @@ export class ComposerApp extends LitElement {
614615
return {
615616
hunks: JSON.parse(JSON.stringify(this.state?.hunks ?? [])),
616617
commits: JSON.parse(JSON.stringify(this.state?.commits ?? [])),
617-
selectedCommitId: this.state?.selectedCommitId ?? null,
618+
selectedCommitId: this.selectedCommitId,
618619
selectedCommitIds: new Set([...this.selectedCommitIds]),
619-
selectedUnassignedSection: this.state?.selectedUnassignedSection ?? null,
620+
selectedUnassignedSection: this.selectedUnassignedSection,
620621
selectedHunkIds: new Set([...this.selectedHunkIds]),
621622
hasUsedAutoCompose: this.state?.hasUsedAutoCompose ?? false,
622623
recompose: this.state?.recompose ? JSON.parse(JSON.stringify(this.state.recompose)) : null,
624+
compositionSummarySelected: this.compositionSummarySelected,
623625
};
624626
}
625627

@@ -628,16 +630,17 @@ export class ComposerApp extends LitElement {
628630
...this.state,
629631
hunks: snapshot.hunks,
630632
commits: snapshot.commits,
631-
selectedCommitId: snapshot.selectedCommitId,
632-
selectedUnassignedSection: snapshot.selectedUnassignedSection,
633633
hasUsedAutoCompose: snapshot.hasUsedAutoCompose,
634634
recompose: snapshot.recompose,
635635
timestamp: Date.now(),
636636
};
637637

638638
(this as any).state = updatedState;
639+
this.selectedCommitId = snapshot.selectedCommitId;
639640
this.selectedCommitIds = snapshot.selectedCommitIds;
641+
this.selectedUnassignedSection = snapshot.selectedUnassignedSection;
640642
this.selectedHunkIds = snapshot.selectedHunkIds;
643+
this.compositionSummarySelected = snapshot.compositionSummarySelected;
641644
this.requestUpdate();
642645
}
643646

@@ -834,7 +837,7 @@ export class ComposerApp extends LitElement {
834837
// Create new commit
835838
const newCommit: ComposerCommit = {
836839
id: `commit-${Date.now()}`,
837-
message: '', // Empty message - user will add their own
840+
message: { content: '', isGenerated: false },
838841
hunkIndices: hunkIndices,
839842
};
840843

@@ -1070,7 +1073,7 @@ export class ComposerApp extends LitElement {
10701073
this.commitMessageBeingEdited = null;
10711074
}, 1000);
10721075

1073-
commit.message = message;
1076+
commit.message = { content: message, isGenerated: false };
10741077
this.requestUpdate();
10751078
}
10761079
}
@@ -1241,7 +1244,10 @@ export class ComposerApp extends LitElement {
12411244
}
12421245

12431246
private get isReadyToFinishAndCommit(): boolean {
1244-
return this.state.commits.length > 0 && this.state.commits.every(commit => commit.message.trim().length > 0);
1247+
return (
1248+
this.state.commits.length > 0 &&
1249+
this.state.commits.every(commit => commit.message.content.trim().length > 0)
1250+
);
12451251
}
12461252

12471253
private get canGenerateCommitsWithAI(): boolean {
@@ -1334,7 +1340,7 @@ export class ComposerApp extends LitElement {
13341340

13351341
// Create Commits loading dialog
13361342
if (this.state.committing) {
1337-
const commitCount = this.state.commits.filter(c => c.message.trim() !== '').length;
1343+
const commitCount = this.state.commits.filter(c => c.message.content.trim() !== '').length;
13381344
return this.renderLoadingDialog(
13391345
'Creating Commits',
13401346
`Committing ${commitCount} commit${commitCount === 1 ? '' : 's'}.`,
@@ -1538,7 +1544,7 @@ export class ComposerApp extends LitElement {
15381544
this._ipc.sendCommand(GenerateCommitMessageCommand, {
15391545
commitId: commitId,
15401546
commitHunkIndices: commit.hunkIndices,
1541-
overwriteExistingMessage: commit.message.trim() !== '',
1547+
overwriteExistingMessage: commit.message.content.trim() !== '',
15421548
});
15431549
}
15441550

@@ -1557,7 +1563,7 @@ export class ComposerApp extends LitElement {
15571563

15581564
// Combine commit messages from selected commits
15591565
const combinedMessage = selectedCommits
1560-
.map(commit => commit.message)
1566+
.map(commit => commit.message.content)
15611567
.filter(message => message && message.trim() !== '')
15621568
.join('\n\n');
15631569

@@ -1567,10 +1573,13 @@ export class ComposerApp extends LitElement {
15671573
.filter(explanation => explanation && explanation.trim() !== '')
15681574
.join('\n\n');
15691575

1576+
// Determine if any of the combined commits were AI-generated
1577+
const isGenerated = selectedCommits.some(commit => commit.message.isGenerated);
1578+
15701579
// Create new combined commit
15711580
const combinedCommit: ComposerCommit = {
15721581
id: `commit-${Date.now()}`,
1573-
message: combinedMessage || 'Combined commit',
1582+
message: { content: combinedMessage || 'Combined commit', isGenerated: isGenerated },
15741583
hunkIndices: combinedHunkIndices,
15751584
aiExplanation: combinedExplanation || undefined,
15761585
};

0 commit comments

Comments
 (0)