Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/contrib/editTelemetry/browser/editStats/aiStatsStatusBar.ts
3296 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { n } from '../../../../../base/browser/dom.js';
7
import { ActionBar, IActionBarOptions, IActionOptions } from '../../../../../base/browser/ui/actionbar/actionbar.js';
8
import { IAction } from '../../../../../base/common/actions.js';
9
import { Codicon } from '../../../../../base/common/codicons.js';
10
import { createHotClass } from '../../../../../base/common/hotReloadHelpers.js';
11
import { Disposable, DisposableStore } from '../../../../../base/common/lifecycle.js';
12
import { autorun, derived } from '../../../../../base/common/observable.js';
13
import { ThemeIcon } from '../../../../../base/common/themables.js';
14
import { localize } from '../../../../../nls.js';
15
import { ICommandService } from '../../../../../platform/commands/common/commands.js';
16
import { nativeHoverDelegate } from '../../../../../platform/hover/browser/hover.js';
17
import { ITelemetryService } from '../../../../../platform/telemetry/common/telemetry.js';
18
import { IStatusbarService, StatusbarAlignment } from '../../../../services/statusbar/browser/statusbar.js';
19
import { AI_STATS_SETTING_ID } from '../settingIds.js';
20
import type { AiStatsFeature } from './aiStatsFeature.js';
21
import './media.css';
22
23
export class AiStatsStatusBar extends Disposable {
24
public static readonly hot = createHotClass(AiStatsStatusBar);
25
26
constructor(
27
private readonly _aiStatsFeature: AiStatsFeature,
28
@IStatusbarService private readonly _statusbarService: IStatusbarService,
29
@ICommandService private readonly _commandService: ICommandService,
30
@ITelemetryService private readonly _telemetryService: ITelemetryService,
31
) {
32
super();
33
34
this._register(autorun((reader) => {
35
const statusBarItem = this._createStatusBar().keepUpdated(reader.store);
36
37
const store = this._register(new DisposableStore());
38
39
reader.store.add(this._statusbarService.addEntry({
40
name: localize('inlineSuggestions', "Inline Suggestions"),
41
ariaLabel: localize('inlineSuggestionsStatusBar', "Inline suggestions status bar"),
42
text: '',
43
tooltip: {
44
element: async (_token) => {
45
this._sendHoverTelemetry();
46
store.clear();
47
const elem = this._createStatusBarHover();
48
return elem.keepUpdated(store).element;
49
},
50
markdownNotSupportedFallback: undefined,
51
},
52
content: statusBarItem.element,
53
}, 'aiStatsStatusBar', StatusbarAlignment.RIGHT, 100));
54
}));
55
}
56
57
private _sendHoverTelemetry(): void {
58
this._telemetryService.publicLog2<{
59
aiRate: number;
60
}, {
61
owner: 'hediet';
62
comment: 'Fired when the AI stats status bar hover tooltip is shown';
63
aiRate: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The current AI rate percentage' };
64
}>(
65
'aiStatsStatusBar.hover',
66
{
67
aiRate: this._aiStatsFeature.aiRate.get(),
68
}
69
);
70
}
71
72
73
private _createStatusBar() {
74
return n.div({
75
style: {
76
height: '100%',
77
display: 'flex',
78
alignItems: 'center',
79
justifyContent: 'center',
80
}
81
}, [
82
n.div(
83
{
84
class: 'ai-stats-status-bar',
85
style: {
86
display: 'flex',
87
flexDirection: 'column',
88
89
width: 50,
90
height: 6,
91
92
borderRadius: 6,
93
borderWidth: '1px',
94
borderStyle: 'solid',
95
}
96
},
97
[
98
n.div({
99
style: {
100
flex: 1,
101
102
display: 'flex',
103
overflow: 'hidden',
104
105
borderRadius: 6,
106
border: '1px solid transparent',
107
}
108
}, [
109
n.div({
110
style: {
111
width: this._aiStatsFeature.aiRate.map(v => `${v * 100}%`),
112
backgroundColor: 'currentColor',
113
}
114
})
115
])
116
]
117
)
118
]);
119
}
120
121
private _createStatusBarHover() {
122
const aiRatePercent = this._aiStatsFeature.aiRate.map(r => `${Math.round(r * 100)}%`);
123
124
return n.div({
125
class: 'ai-stats-status-bar',
126
}, [
127
n.div({
128
class: 'header',
129
style: {
130
minWidth: '200px',
131
}
132
},
133
[
134
n.div({ style: { flex: 1 } }, [localize('aiStatsStatusBarHeader', "AI Usage Statistics")]),
135
n.div({ style: { marginLeft: 'auto' } }, actionBar([
136
{
137
action: {
138
id: 'aiStats.statusBar.settings',
139
label: '',
140
enabled: true,
141
run: () => openSettingsCommand({ ids: [AI_STATS_SETTING_ID] }).run(this._commandService),
142
class: ThemeIcon.asClassName(Codicon.gear),
143
tooltip: localize('aiStats.statusBar.configure', "Configure")
144
},
145
options: { icon: true, label: false, hoverDelegate: nativeHoverDelegate }
146
}
147
]))
148
]
149
),
150
151
n.div({ style: { display: 'flex' } }, [
152
n.div({ style: { flex: 1, paddingRight: '4px' } }, [
153
localize('text1', "AI vs Typing Average: {0}", aiRatePercent.get()),
154
]),
155
/*
156
TODO: Write article that explains the ratio and link to it.
157
158
n.div({ style: { marginLeft: 'auto' } }, actionBar([
159
{
160
action: {
161
id: 'aiStatsStatusBar.openSettings',
162
label: '',
163
enabled: true,
164
run: () => { },
165
class: ThemeIcon.asClassName(Codicon.info),
166
tooltip: ''
167
},
168
options: { icon: true, label: true, }
169
}
170
]))*/
171
]),
172
n.div({ style: { flex: 1, paddingRight: '4px' } }, [
173
localize('text2', "Accepted inline suggestions today: {0}", this._aiStatsFeature.acceptedInlineSuggestionsToday.get()),
174
]),
175
]);
176
}
177
}
178
179
function actionBar(actions: { action: IAction; options: IActionOptions }[], options?: IActionBarOptions) {
180
return derived((_reader) => n.div({
181
class: [],
182
style: {
183
},
184
ref: elem => {
185
const actionBar = _reader.store.add(new ActionBar(elem, options));
186
for (const { action, options } of actions) {
187
actionBar.push(action, options);
188
}
189
}
190
}));
191
}
192
193
class CommandWithArgs {
194
constructor(
195
public readonly commandId: string,
196
public readonly args: unknown[] = [],
197
) { }
198
199
public run(commandService: ICommandService): void {
200
commandService.executeCommand(this.commandId, ...this.args);
201
}
202
}
203
204
function openSettingsCommand(options: { ids?: string[] } = {}) {
205
return new CommandWithArgs('workbench.action.openSettings', [{
206
query: options.ids ? options.ids.map(id => `@id:${id}`).join(' ') : undefined,
207
}]);
208
}
209
210