Skip to content

Commit ebb067b

Browse files
brunobergherBruno Berghermrubensdaniel-lxs
authored andcommitted
Modes selector improvements (#4902)
Co-authored-by: Bruno Bergher <[email protected]> Co-authored-by: Matt Rubens <[email protected]> Co-authored-by: Daniel Riccio <[email protected]>
1 parent 41feb8c commit ebb067b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

78 files changed

+891
-116
lines changed

packages/telemetry/src/TelemetryService.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,31 @@ export class TelemetryService {
152152
this.captureEvent(TelemetryEventName.CONSECUTIVE_MISTAKE_ERROR, { taskId })
153153
}
154154

155+
/**
156+
* Captures when a tab is shown due to user action
157+
* @param tab The tab that was shown
158+
*/
159+
public captureTabShown(tab: string): void {
160+
this.captureEvent(TelemetryEventName.TAB_SHOWN, { tab })
161+
}
162+
163+
/**
164+
* Captures when a setting is changed in ModesView
165+
* @param settingName The name of the setting that was changed
166+
*/
167+
public captureModeSettingChanged(settingName: string): void {
168+
this.captureEvent(TelemetryEventName.MODE_SETTINGS_CHANGED, { settingName })
169+
}
170+
171+
/**
172+
* Captures when a user creates a new custom mode
173+
* @param modeSlug The slug of the custom mode
174+
* @param modeName The name of the custom mode
175+
*/
176+
public captureCustomModeCreated(modeSlug: string, modeName: string): void {
177+
this.captureEvent(TelemetryEventName.CUSTOM_MODE_CREATED, { modeSlug, modeName })
178+
}
179+
155180
/**
156181
* Captures a marketplace item installation event
157182
* @param itemId The unique identifier of the marketplace item

packages/types/src/global-settings.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ export const globalSettingsSchema = z.object({
104104
enhancementApiConfigId: z.string().optional(),
105105
historyPreviewCollapsed: z.boolean().optional(),
106106
profileThresholds: z.record(z.string(), z.number()).optional(),
107+
hasOpenedModeSelector: z.boolean().optional(),
107108
})
108109

109110
export type GlobalSettings = z.infer<typeof globalSettingsSchema>

packages/types/src/mode.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ export const modeConfigSchema = z.object({
6666
name: z.string().min(1, "Name is required"),
6767
roleDefinition: z.string().min(1, "Role definition is required"),
6868
whenToUse: z.string().optional(),
69+
description: z.string().optional(),
6970
customInstructions: z.string().optional(),
7071
groups: groupEntryArraySchema,
7172
source: z.enum(["global", "project"]).optional(),
@@ -106,6 +107,7 @@ export type CustomModesSettings = z.infer<typeof customModesSettingsSchema>
106107
export const promptComponentSchema = z.object({
107108
roleDefinition: z.string().optional(),
108109
whenToUse: z.string().optional(),
110+
description: z.string().optional(),
109111
customInstructions: z.string().optional(),
110112
})
111113

packages/types/src/telemetry.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ export enum TelemetryEventName {
3131
CHECKPOINT_RESTORED = "Checkpoint Restored",
3232
CHECKPOINT_DIFFED = "Checkpoint Diffed",
3333

34+
TAB_SHOWN = "Tab Shown",
35+
MODE_SETTINGS_CHANGED = "Mode Setting Changed",
36+
CUSTOM_MODE_CREATED = "Custom Mode Created",
37+
3438
CONTEXT_CONDENSED = "Context Condensed",
3539
SLIDING_WINDOW_TRUNCATION = "Sliding Window Truncation",
3640

@@ -132,6 +136,9 @@ export const rooCodeTelemetryEventSchema = z.discriminatedUnion("type", [
132136
TelemetryEventName.CONSECUTIVE_MISTAKE_ERROR,
133137
TelemetryEventName.CONTEXT_CONDENSED,
134138
TelemetryEventName.SLIDING_WINDOW_TRUNCATION,
139+
TelemetryEventName.TAB_SHOWN,
140+
TelemetryEventName.MODE_SETTINGS_CHANGED,
141+
TelemetryEventName.CUSTOM_MODE_CREATED,
135142
]),
136143
properties: cloudTelemetryPropertiesSchema,
137144
}),

src/core/webview/ClineProvider.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1487,6 +1487,7 @@ export class ClineProvider
14871487
},
14881488
mdmCompliant: this.checkMdmCompliance(),
14891489
profileThresholds: profileThresholds ?? {},
1490+
hasOpenedModeSelector: this.getGlobalState("hasOpenedModeSelector") ?? false,
14901491
}
14911492
}
14921493

src/core/webview/__tests__/ClineProvider.spec.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -523,6 +523,7 @@ describe("ClineProvider", () => {
523523
cloudIsAuthenticated: false,
524524
sharingEnabled: false,
525525
profileThresholds: {},
526+
hasOpenedModeSelector: false,
526527
}
527528

528529
const message: ExtensionMessage = {

src/core/webview/webviewMessageHandler.ts

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -948,8 +948,27 @@ export const webviewMessageHandler = async (
948948
const updatedPrompts = { ...existingPrompts, [message.promptMode]: message.customPrompt }
949949
await updateGlobalState("customModePrompts", updatedPrompts)
950950
const currentState = await provider.getStateToPostToWebview()
951-
const stateWithPrompts = { ...currentState, customModePrompts: updatedPrompts }
951+
const stateWithPrompts = {
952+
...currentState,
953+
customModePrompts: updatedPrompts,
954+
hasOpenedModeSelector: currentState.hasOpenedModeSelector ?? false,
955+
}
952956
provider.postMessageToWebview({ type: "state", state: stateWithPrompts })
957+
958+
if (TelemetryService.hasInstance()) {
959+
// Determine which setting was changed by comparing objects
960+
const oldPrompt = existingPrompts[message.promptMode] || {}
961+
const newPrompt = message.customPrompt
962+
const changedSettings = Object.keys(newPrompt).filter(
963+
(key) =>
964+
JSON.stringify((oldPrompt as Record<string, unknown>)[key]) !==
965+
JSON.stringify((newPrompt as Record<string, unknown>)[key]),
966+
)
967+
968+
if (changedSettings.length > 0) {
969+
TelemetryService.instance.captureModeSettingChanged(changedSettings[0])
970+
}
971+
}
953972
}
954973
break
955974
case "deleteMessage": {
@@ -1085,6 +1104,10 @@ export const webviewMessageHandler = async (
10851104
await updateGlobalState("showRooIgnoredFiles", message.bool ?? true)
10861105
await provider.postStateToWebview()
10871106
break
1107+
case "hasOpenedModeSelector":
1108+
await updateGlobalState("hasOpenedModeSelector", message.bool ?? true)
1109+
await provider.postStateToWebview()
1110+
break
10881111
case "maxReadFileLine":
10891112
await updateGlobalState("maxReadFileLine", message.value)
10901113
await provider.postStateToWebview()
@@ -1414,12 +1437,41 @@ export const webviewMessageHandler = async (
14141437
break
14151438
case "updateCustomMode":
14161439
if (message.modeConfig) {
1440+
// Check if this is a new mode or an update to an existing mode
1441+
const existingModes = await provider.customModesManager.getCustomModes()
1442+
const isNewMode = !existingModes.some((mode) => mode.slug === message.modeConfig?.slug)
1443+
14171444
await provider.customModesManager.updateCustomMode(message.modeConfig.slug, message.modeConfig)
14181445
// Update state after saving the mode
14191446
const customModes = await provider.customModesManager.getCustomModes()
14201447
await updateGlobalState("customModes", customModes)
14211448
await updateGlobalState("mode", message.modeConfig.slug)
14221449
await provider.postStateToWebview()
1450+
1451+
// Track telemetry for custom mode creation or update
1452+
if (TelemetryService.hasInstance()) {
1453+
if (isNewMode) {
1454+
// This is a new custom mode
1455+
TelemetryService.instance.captureCustomModeCreated(
1456+
message.modeConfig.slug,
1457+
message.modeConfig.name,
1458+
)
1459+
} else {
1460+
// Determine which setting was changed by comparing objects
1461+
const existingMode = existingModes.find((mode) => mode.slug === message.modeConfig?.slug)
1462+
const changedSettings = existingMode
1463+
? Object.keys(message.modeConfig).filter(
1464+
(key) =>
1465+
JSON.stringify((existingMode as Record<string, unknown>)[key]) !==
1466+
JSON.stringify((message.modeConfig as Record<string, unknown>)[key]),
1467+
)
1468+
: []
1469+
1470+
if (changedSettings.length > 0) {
1471+
TelemetryService.instance.captureModeSettingChanged(changedSettings[0])
1472+
}
1473+
}
1474+
}
14231475
}
14241476
break
14251477
case "deleteCustomMode":
@@ -1604,6 +1656,7 @@ export const webviewMessageHandler = async (
16041656
)
16051657
await provider.postStateToWebview()
16061658
console.log(`Marketplace item installed and config file opened: ${configFilePath}`)
1659+
16071660
// Send success message to webview
16081661
provider.postMessageToWebview({
16091662
type: "marketplaceInstallResult",
@@ -1656,7 +1709,11 @@ export const webviewMessageHandler = async (
16561709

16571710
case "switchTab": {
16581711
if (message.tab) {
1659-
// Send a message to the webview to switch to the specified tab
1712+
// Capture tab shown event for all switchTab messages (which are user-initiated)
1713+
if (TelemetryService.hasInstance()) {
1714+
TelemetryService.instance.captureTabShown(message.tab)
1715+
}
1716+
16601717
await provider.postMessageToWebview({ type: "action", action: "switchTab", tab: message.tab })
16611718
}
16621719
break

src/package.json

Lines changed: 11 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -80,11 +80,6 @@
8080
"title": "%command.mcpServers.title%",
8181
"icon": "$(server)"
8282
},
83-
{
84-
"command": "roo-cline.promptsButtonClicked",
85-
"title": "%command.prompts.title%",
86-
"icon": "$(organization)"
87-
},
8883
{
8984
"command": "roo-cline.historyButtonClicked",
9085
"title": "%command.history.title%",
@@ -219,39 +214,34 @@
219214
"group": "navigation@1",
220215
"when": "view == roo-cline.SidebarProvider"
221216
},
222-
{
223-
"command": "roo-cline.promptsButtonClicked",
224-
"group": "navigation@2",
225-
"when": "view == roo-cline.SidebarProvider"
226-
},
227217
{
228218
"command": "roo-cline.mcpButtonClicked",
229-
"group": "navigation@3",
219+
"group": "navigation@2",
230220
"when": "view == roo-cline.SidebarProvider"
231221
},
232222
{
233223
"command": "roo-cline.marketplaceButtonClicked",
234-
"group": "navigation@4",
224+
"group": "navigation@3",
235225
"when": "view == roo-cline.SidebarProvider"
236226
},
237227
{
238228
"command": "roo-cline.historyButtonClicked",
239-
"group": "navigation@5",
229+
"group": "navigation@4",
240230
"when": "view == roo-cline.SidebarProvider"
241231
},
242232
{
243233
"command": "roo-cline.popoutButtonClicked",
244-
"group": "navigation@6",
234+
"group": "navigation@5",
245235
"when": "view == roo-cline.SidebarProvider"
246236
},
247237
{
248238
"command": "roo-cline.accountButtonClicked",
249-
"group": "navigation@7",
239+
"group": "navigation@6",
250240
"when": "view == roo-cline.SidebarProvider && config.roo-cline.rooCodeCloudEnabled"
251241
},
252242
{
253243
"command": "roo-cline.settingsButtonClicked",
254-
"group": "navigation@8",
244+
"group": "navigation@7",
255245
"when": "view == roo-cline.SidebarProvider"
256246
}
257247
],
@@ -261,34 +251,29 @@
261251
"group": "navigation@1",
262252
"when": "activeWebviewPanelId == roo-cline.TabPanelProvider"
263253
},
264-
{
265-
"command": "roo-cline.promptsButtonClicked",
266-
"group": "navigation@2",
267-
"when": "activeWebviewPanelId == roo-cline.TabPanelProvider"
268-
},
269254
{
270255
"command": "roo-cline.mcpButtonClicked",
271-
"group": "navigation@3",
256+
"group": "navigation@2",
272257
"when": "activeWebviewPanelId == roo-cline.TabPanelProvider"
273258
},
274259
{
275260
"command": "roo-cline.marketplaceButtonClicked",
276-
"group": "navigation@4",
261+
"group": "navigation@3",
277262
"when": "activeWebviewPanelId == roo-cline.TabPanelProvider"
278263
},
279264
{
280265
"command": "roo-cline.historyButtonClicked",
281-
"group": "navigation@5",
266+
"group": "navigation@4",
282267
"when": "activeWebviewPanelId == roo-cline.TabPanelProvider"
283268
},
284269
{
285270
"command": "roo-cline.accountButtonClicked",
286-
"group": "navigation@6",
271+
"group": "navigation@5",
287272
"when": "activeWebviewPanelId == roo-cline.TabPanelProvider && config.roo-cline.rooCodeCloudEnabled"
288273
},
289274
{
290275
"command": "roo-cline.settingsButtonClicked",
291-
"group": "navigation@7",
276+
"group": "navigation@6",
292277
"when": "activeWebviewPanelId == roo-cline.TabPanelProvider"
293278
}
294279
]

src/shared/ExtensionMessage.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,7 @@ export type ExtensionState = Pick<
261261
marketplaceItems?: MarketplaceItem[]
262262
marketplaceInstalledMetadata?: { project: Record<string, any>; global: Record<string, any> }
263263
profileThresholds: Record<string, number>
264+
hasOpenedModeSelector: boolean
264265
}
265266

266267
export interface ClineSayTool {

src/shared/WebviewMessage.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ export interface WebviewMessage {
148148
| "searchFiles"
149149
| "toggleApiConfigPin"
150150
| "setHistoryPreviewCollapsed"
151+
| "hasOpenedModeSelector"
151152
| "accountButtonClicked"
152153
| "rooCloudSignIn"
153154
| "rooCloudSignOut"

0 commit comments

Comments
 (0)