Skip to content

Commit 6c09d23

Browse files
axosoft-raminteamodio
authored andcommitted
Updates telemetry/context and adds warning for workdir/index changes
1 parent f84abe1 commit 6c09d23

File tree

9 files changed

+1378
-788
lines changed

9 files changed

+1378
-788
lines changed

docs/telemetry-events.md

Lines changed: 687 additions & 443 deletions
Large diffs are not rendered by default.

src/constants.storage.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ export type GlobalStorage = {
8282
'confirm:draft:storage': boolean;
8383
// Value based on `currentOnboardingVersion` in composer's protocol
8484
'composer:onboarding:dismissed': string;
85+
'composer:onboarding:stepReached': number;
8586
'home:sections:collapsed': string[];
8687
'home:walkthrough:dismissed': boolean;
8788
'launchpad:groups:collapsed': StoredLaunchpadGroup[];

src/constants.telemetry.ts

Lines changed: 81 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -134,25 +134,33 @@ export interface TelemetryEvents extends WebviewShowAbortedEvents, WebviewShownE
134134
/** Sent when the Commit Composer is reloaded */
135135
'composer/reloaded': ComposerEvent;
136136
/** Sent when the user adds unstaged changes to draft commits in the Commit Composer */
137-
'composer/includedUnstagedChanges': ComposerEvent;
137+
'composer/action/includedUnstagedChanges': ComposerEvent;
138138
/** Sent when the user uses auto-compose in the Commit Composer */
139-
'composer/generateCommits': ComposerEvent;
140-
/** Sent when the user cancels an auto-compose operation in the Commit Composer */
141-
'composer/generateCommits/cancelled': ComposerEvent;
139+
'composer/action/compose': ComposerEvent;
140+
/** Sent when the user fails an auto-compose operation in the Commit Composer */
141+
'composer/action/compose/failed': ComposerEvent;
142+
/** Sent when the user uses recompose in the Commit Composer */
143+
'composer/action/recompose': ComposerEvent;
144+
/** Sent when the user fails a recompose operation in the Commit Composer */
145+
'composer/action/recompose/failed': ComposerEvent;
142146
/** Sent when the user uses generate commit message in the Commit Composer */
143-
'composer/generateCommitMessage': ComposerEvent;
144-
/** Sent when the user cancels a generate commit message operation in the Commit Composer */
145-
'composer/generateCommitMessage/cancelled': ComposerEvent;
146-
/** Sent when the user updates custom instructions in the Commit Composer */
147-
'composer/customInstructions/updated': ComposerEvent;
147+
'composer/action/generateCommitMessage': ComposerEvent;
148+
/** Sent when the user fails a generate commit message operation in the Commit Composer */
149+
'composer/action/generateCommitMessage/failed': ComposerEvent;
148150
/** Sent when the user changes the AI model in the Commit Composer */
149-
'composer/aiModel/changed': ComposerEvent;
151+
'composer/action/changeAiModel': ComposerEvent;
150152
/** Sent when the user finishes and commits in the Commit Composer */
151-
'composer/finishAndCommit': ComposerEvent;
153+
'composer/action/finishAndCommit': ComposerEvent;
154+
/** Sent when the user fails to finish and commit in the Commit Composer */
155+
'composer/action/finishAndCommit/failed': ComposerEvent;
152156
/** Sent when the user uses the undo button in the Commit Composer */
153-
'composer/undo': ComposerEvent;
157+
'composer/action/undo': ComposerEvent;
154158
/** Sent when the user uses the reset button in the Commit Composer */
155-
'composer/reset': ComposerEvent;
159+
'composer/action/reset': ComposerEvent;
160+
/** Sent when the user is warned that the working directory has changed in the Commit Composer */
161+
'composer/warning/workingDirectoryChanged': ComposerEvent;
162+
/** Sent when the user is warned that the index has changed in the Commit Composer */
163+
'composer/warning/indexChanged': ComposerEvent;
156164

157165
/** Sent when the Commit Graph is shown */
158166
'graph/shown': GraphShownEvent;
@@ -703,53 +711,73 @@ export type InspectShownTelemetryContext = InspectShownEventData;
703711

704712
export type ComposerTelemetryContext = ComposerContextEventData;
705713
type ComposerContextEventData = WebviewTelemetryContext & ComposerSessionContextEventData;
706-
type ComposerContextModelData = {
707-
'context.model.id': string | undefined;
708-
'context.model.name': string | undefined;
709-
'context.model.provider.id': AIProviders | undefined;
710-
'context.model.temperature': number | undefined;
711-
'context.model.maxTokens.input': number | undefined;
712-
'context.model.maxTokens.output': number | undefined;
713-
'context.model.default': boolean | undefined;
714-
'context.model.hidden': boolean | undefined;
714+
type ComposerContextSessionData = {
715+
'context.session.start': string;
716+
'context.session.duration': number | undefined;
715717
};
716-
type ComposerContextAIOperationData = {
718+
type ComposerContextDiffData = {
719+
'context.diff.files.count': number;
720+
'context.diff.hunks.count': number;
721+
'context.diff.lines.count': number;
722+
'context.diff.staged.exists': boolean;
723+
'context.diff.unstaged.exists': boolean;
724+
'context.diff.unstaged.included': boolean;
725+
};
726+
type ComposerContextCommitsData = {
727+
'context.commits.initialCount': number;
728+
'context.commits.autoComposedCount': number | undefined;
729+
'context.commits.composedCount': number | undefined;
730+
'context.commits.finalCount': number | undefined;
731+
};
732+
type ComposerContextOnboardingData = {
733+
'context.onboarding.dismissed': boolean;
734+
'context.onboarding.stepReached': number | undefined;
735+
};
736+
type ComposerContextAIData = {
717737
'context.ai.enabled.org': boolean;
718738
'context.ai.enabled.config': boolean;
719-
'context.ai.operations.generateCommits.count': number;
720-
'context.ai.operations.generateCommits.cancelled.count': number;
721-
'context.ai.operations.generateCommits.error.count': number;
722-
'context.ai.operations.generateCommits.customInstructions.used': boolean;
723-
'context.ai.operations.generateCommits.customInstructions.length': number;
724-
'context.ai.operations.generateCommits.customInstructions.hash': string;
725-
'context.ai.operations.generateCommits.customInstructions.setting.used': boolean;
726-
'context.ai.operations.generateCommits.customInstructions.setting.length': number;
727-
'context.ai.operations.generateCommits.feedback.upvote.count': number;
728-
'context.ai.operations.generateCommits.feedback.downvote.count': number;
729-
'context.ai.operations.generateCommitMessage.count': number;
730-
'context.ai.operations.generateCommitMessage.cancelled.count': number;
731-
'context.ai.operations.generateCommitMessage.error.count': number;
732-
'context.ai.operations.generateCommitMessage.customInstructions.setting.used': boolean;
733-
'context.ai.operations.generateCommitMessage.customInstructions.setting.length': number;
739+
'context.ai.model.id': string | undefined;
740+
'context.ai.model.name': string | undefined;
741+
'context.ai.model.provider.id': AIProviders | undefined;
742+
'context.ai.model.temperature': number | undefined;
743+
'context.ai.model.maxTokens.input': number | undefined;
744+
'context.ai.model.maxTokens.output': number | undefined;
745+
'context.ai.model.default': boolean | undefined;
746+
'context.ai.model.hidden': boolean | undefined;
747+
};
748+
type ComposerContextOperationData = {
749+
'context.operations.generateCommits.count': number;
750+
'context.operations.generateCommits.cancelled.count': number;
751+
'context.operations.generateCommits.error.count': number;
752+
'context.operations.generateCommits.feedback.upvote.count': number;
753+
'context.operations.generateCommits.feedback.downvote.count': number;
754+
'context.operations.generateCommitMessage.count': number;
755+
'context.operations.generateCommitMessage.cancelled.count': number;
756+
'context.operations.generateCommitMessage.error.count': number;
757+
'context.operations.finishAndCommit.error.count': number;
758+
'context.operations.undo.count': number;
759+
'context.operations.redo.count': number;
760+
'context.operations.reset.count': number;
761+
};
762+
type ComposerContextWarningsData = {
763+
'context.warnings.workingDirectoryChanged': boolean;
764+
'context.warnings.indexChanged': boolean;
765+
};
766+
type ComposerContextErrorsData = {
767+
'context.errors.safety.count': number;
768+
'context.errors.operation.count': number;
734769
};
735770

736-
type ComposerSessionContextEventData = ComposerContextModelData &
737-
ComposerContextAIOperationData & {
771+
type ComposerSessionContextEventData = ComposerContextSessionData &
772+
ComposerContextDiffData &
773+
ComposerContextCommitsData &
774+
ComposerContextOnboardingData &
775+
ComposerContextAIData &
776+
ComposerContextOperationData &
777+
ComposerContextWarningsData &
778+
ComposerContextErrorsData & {
738779
'context.source': Sources | undefined;
739780
'context.mode': 'experimental' | 'preview';
740-
'context.sessionId': string;
741-
'context.files.count': number;
742-
'context.hunks.count': number;
743-
'context.lines.count': number;
744-
'context.draftCommits.initialCount': number;
745-
'context.draftCommits.finalCount': number | undefined;
746-
'context.diffSources.staged': boolean;
747-
'context.diffSources.unstaged': boolean;
748-
'context.diffSources.unstaged.included': boolean;
749-
'context.onboarding.dismissed': boolean;
750-
'context.history.undo.count': number;
751-
'context.history.redo.count': number;
752-
'context.history.reset.count': number;
753781
};
754782

755783
type ComposerEvent = ComposerContextEventData;

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

Lines changed: 79 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { when } from 'lit/directives/when.js';
66
import Sortable from 'sortablejs';
77
import type { ComposerCommit, ComposerHunk, State } from '../../../../plus/composer/protocol';
88
import {
9+
AdvanceOnboardingCommand,
910
AIFeedbackHelpfulCommand,
1011
AIFeedbackUnhelpfulCommand,
1112
CancelGenerateCommitMessageCommand,
@@ -21,7 +22,7 @@ import {
2122
OnResetCommand,
2223
OnSelectAIModelCommand,
2324
OnUndoCommand,
24-
OnUpdateCustomInstructionsCommand,
25+
OpenOnboardingCommand,
2526
ReloadComposerCommand,
2627
} from '../../../../plus/composer/protocol';
2728
import { createCombinedDiffForCommit, updateHunkAssignments } from '../../../../plus/composer/utils';
@@ -132,6 +133,31 @@ export class ComposerApp extends LitElement {
132133
justify-content: flex-end;
133134
}
134135
136+
.working-directory-warning {
137+
display: flex;
138+
align-items: center;
139+
gap: 0.8rem;
140+
padding: 0.8rem 1.2rem;
141+
background-color: var(--vscode-inputValidation-warningBackground);
142+
border: 1px solid var(--vscode-inputValidation-warningBorder);
143+
border-radius: 0.3rem;
144+
margin-inline-start: 1.6rem;
145+
}
146+
147+
.working-directory-warning--error {
148+
background-color: var(--vscode-inputValidation-errorBackground);
149+
border-color: var(--vscode-inputValidation-errorBorder);
150+
}
151+
152+
.working-directory-warning__text {
153+
color: var(--vscode-inputValidation-warningForeground);
154+
font-size: 1.3rem;
155+
}
156+
157+
.working-directory-warning--error .working-directory-warning__text {
158+
color: var(--vscode-inputValidation-errorForeground);
159+
}
160+
135161
.main-content {
136162
display: flex;
137163
flex: 1;
@@ -361,6 +387,9 @@ export class ComposerApp extends LitElement {
361387
@state()
362388
private showCommitsGeneratedModal: boolean = false;
363389

390+
@state()
391+
private onboardingStepNumber: number = 0;
392+
364393
private commitsSortable?: Sortable;
365394
private hunksSortable?: Sortable;
366395
private isDragging = false;
@@ -1364,12 +1393,6 @@ export class ComposerApp extends LitElement {
13641393
this.customInstructions = e.detail?.customInstructions ?? '';
13651394
}
13661395

1367-
private handleCustomInstructionsUpdate(e: CustomEvent) {
1368-
this._ipc.sendCommand(OnUpdateCustomInstructionsCommand, {
1369-
customInstructions: e.detail?.customInstructions ?? '',
1370-
});
1371-
}
1372-
13731396
@query('gl-details-panel')
13741397
private detailsPanel!: DetailsPanel;
13751398

@@ -1434,6 +1457,7 @@ export class ComposerApp extends LitElement {
14341457
this._ipc.sendCommand(GenerateCommitMessageCommand, {
14351458
commitId: commitId,
14361459
diff: patch,
1460+
overwriteExistingMessage: commit.message.trim() !== '',
14371461
});
14381462
}
14391463

@@ -1521,7 +1545,7 @@ export class ComposerApp extends LitElement {
15211545
><code-icon icon="feedback"></code-icon
15221546
></gl-button>
15231547
</h1>
1524-
${this.renderActions()}
1548+
${this.renderWorkingDirectoryWarning()} ${this.renderActions()}
15251549
</header>
15261550
15271551
<main class="main-content">
@@ -1556,7 +1580,6 @@ export class ComposerApp extends LitElement {
15561580
@finish-and-commit=${this.finishAndCommit}
15571581
@generate-commits-with-ai=${this.handleGenerateCommitsWithAI}
15581582
@custom-instructions-change=${this.handleCustomInstructionsChange}
1559-
@custom-instructions-update=${this.handleCustomInstructionsUpdate}
15601583
@focus-commit-message=${this.handleFocusCommitMessage}
15611584
@commit-reorder=${(e: CustomEvent) => this.reorderCommits(e.detail.oldIndex, e.detail.newIndex)}
15621585
@create-new-commit=${(e: CustomEvent) => this.createNewCommitWithHunks(e.detail.hunkIds)}
@@ -1670,6 +1693,41 @@ export class ComposerApp extends LitElement {
16701693
`;
16711694
}
16721695

1696+
private renderWorkingDirectoryWarning() {
1697+
const { indexHasChanged, workingDirectoryHasChanged } = this.state || {};
1698+
1699+
// No warnings if neither flag is set
1700+
if (!indexHasChanged && !workingDirectoryHasChanged) return nothing;
1701+
1702+
// Check if there are any assigned unstaged hunks
1703+
const hasAssignedUnstagedHunks = this.hunksWithAssignments.some(
1704+
hunk => hunk.source === 'unstaged' && hunk.assigned,
1705+
);
1706+
1707+
let warningText: string;
1708+
let isError: boolean;
1709+
1710+
if (indexHasChanged) {
1711+
warningText = 'Index has changed. You must reload to commit.';
1712+
isError = true;
1713+
} else if (workingDirectoryHasChanged && hasAssignedUnstagedHunks) {
1714+
warningText = 'Working directory has changed. You must reload to commit.';
1715+
isError = true;
1716+
} else if (workingDirectoryHasChanged) {
1717+
warningText = 'Working directory has changed';
1718+
isError = false;
1719+
} else {
1720+
return nothing;
1721+
}
1722+
1723+
return html`
1724+
<div class="working-directory-warning ${isError ? 'working-directory-warning--error' : ''}">
1725+
<span class="working-directory-warning__text">${warningText}</span>
1726+
<gl-button appearance="primary" @click=${this.handleReloadComposer}>Reload</gl-button>
1727+
</div>
1728+
`;
1729+
}
1730+
16731731
private renderActions() {
16741732
if (!this.showHistoryButtons) return nothing;
16751733

@@ -1746,11 +1804,18 @@ export class ComposerApp extends LitElement {
17461804
onDestroyStarted: (_el, _step) => {
17471805
this.dismissOnboarding();
17481806
},
1807+
onNextClick: (_el, _step) => {
1808+
this.advanceOnboardingStep();
1809+
},
17491810
});
17501811

1812+
this.onboardingStepNumber = 1;
1813+
17511814
setTimeout(() => {
17521815
this.onboarding?.drive();
17531816
}, 1500);
1817+
1818+
this._ipc.sendCommand(OpenOnboardingCommand);
17541819
}
17551820

17561821
dismissOnboarding() {
@@ -1762,6 +1827,11 @@ export class ComposerApp extends LitElement {
17621827
this.state.onboardingDismissed = true;
17631828
this.requestUpdate();
17641829
}
1830+
1831+
advanceOnboardingStep() {
1832+
this.onboardingStepNumber++;
1833+
this._ipc.sendCommand(AdvanceOnboardingCommand, { stepNumber: this.onboardingStepNumber });
1834+
}
17651835
}
17661836

17671837
function replaceLineBreaks(text?: string | null, replaceWith: string = '<br>'): string | undefined {

src/webviews/apps/plus/composer/components/commits-panel.ts

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -356,9 +356,6 @@ export class CommitsPanel extends LitElement {
356356
@property({ type: String })
357357
customInstructions: string = '';
358358

359-
// Track the original value for telemetry purposes
360-
private _originalCustomInstructions = '';
361-
362359
@property({ type: Boolean })
363360
hasUsedAutoCompose: boolean = false;
364361

@@ -860,27 +857,6 @@ export class CommitsPanel extends LitElement {
860857
);
861858
}
862859

863-
private handleCustomInstructionsFocus(e: Event) {
864-
const input = e.target as HTMLInputElement;
865-
// Store the original value when focus is gained
866-
this._originalCustomInstructions = input.value;
867-
}
868-
869-
private handleCustomInstructionsBlur(e: Event) {
870-
const input = e.target as HTMLInputElement;
871-
const currentValue = input.value;
872-
873-
// Only dispatch telemetry event if the value actually changed
874-
if (currentValue !== this._originalCustomInstructions) {
875-
this.dispatchEvent(
876-
new CustomEvent('custom-instructions-update', {
877-
detail: { customInstructions: currentValue },
878-
bubbles: true,
879-
}),
880-
);
881-
}
882-
}
883-
884860
private getIncludeButtonText(sectionKey: string): string {
885861
switch (sectionKey) {
886862
case 'unstaged':
@@ -1058,8 +1034,6 @@ export class CommitsPanel extends LitElement {
10581034
placeholder="Include additional instructions"
10591035
.value=${this.customInstructions}
10601036
@input=${this.handleCustomInstructionsChange}
1061-
@focus=${this.handleCustomInstructionsFocus}
1062-
@blur=${this.handleCustomInstructionsBlur}
10631037
/>
10641038
<gl-button appearance="toolbar" class="auto-compose__instructions-info">
10651039
<code-icon icon="info"></code-icon>

0 commit comments

Comments
 (0)