Skip to content

Commit db8d647

Browse files
authored
Add banner to the ModesView encouraging organization creation (#3388)
* refactor(storybook): refactor marketplace view stories to use shared mock utilities Remove inline mock data and state manager from MarketplaceView stories, replacing with imports from createMockMarketplaceStateManager and createExtensionStateMock for better maintainability and reusability. * refactor(storybook): use shared sidebar container decorator in stories Replace inline decorator definitions in MarketplaceView and ModesView stories with a reusable withSidebarContainer decorator to reduce code duplication and improve maintainability. * feat(modes): add banner for sharing modes by creating organization Add a new promotional banner in the ModesView component that links to organization creation, enabling users to share modes. Includes telemetry tracking for link clicks and internationalization support across all supported locales. Updated Storybook story to accommodate the wider content layout. * refactor(modes): replace inline organization banner with KiloShareModesBanner component - Extract banner JSX into reusable KiloShareModesBanner component - Remove unused imports (getAppUrl, TelemetryEventName, telemetryClient) - Clean up comments in mock data file for consistency
1 parent 3c0b3f9 commit db8d647

28 files changed

+270
-80
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import type { Decorator } from "@storybook/react-vite"
2+
3+
// Mimics the VSCode sidebar appearance: background, width,
4+
// and uses scale-100 to "contain" position:fixed elements
5+
export const withSidebarContainer = (width: number = 400): Decorator => {
6+
return (Story) => (
7+
<div className={`w-[${width}px] min-h-[600px] bg-vscode-sideBar-background scale-100`}>
8+
<Story />
9+
</div>
10+
)
11+
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
// kilocode_change - new file
2+
export * from "./clineMessages"
3+
4+
export const mockMarketplaceItems = [
5+
{
6+
id: "filesystem-mcp",
7+
name: "File System MCP",
8+
description: "Provides tools for reading, writing, and managing files and directories on the local filesystem.",
9+
author: "Anthropic",
10+
tags: ["files", "filesystem", "core"],
11+
type: "mcp" as const,
12+
url: "https://github.com/anthropics/mcp-filesystem",
13+
content: "npm install @anthropic-ai/mcp-filesystem",
14+
},
15+
{
16+
id: "database-mcp",
17+
name: "Database MCP",
18+
description: "Connect to and query various databases including PostgreSQL, MySQL, and SQLite.",
19+
author: "Community",
20+
tags: ["database", "sql", "data"],
21+
type: "mcp" as const,
22+
url: "https://github.com/community/mcp-database",
23+
content: "npm install mcp-database",
24+
},
25+
{
26+
id: "architect-mode",
27+
name: "Architect Mode",
28+
description: "Plan and design system architecture before implementation. Perfect for complex projects.",
29+
author: "Kilocode",
30+
tags: ["planning", "design", "architecture"],
31+
type: "mode" as const,
32+
content:
33+
"slug: architect\nname: Architect\nmodel: anthropic/claude-sonnet-4\nprompt: |\n You are an experienced software architect.",
34+
},
35+
{
36+
id: "debug-mode",
37+
name: "Debug Mode",
38+
description: "Advanced debugging capabilities with step-by-step analysis and error tracking.",
39+
author: "Kilocode",
40+
tags: ["debugging", "analysis", "troubleshooting"],
41+
type: "mode" as const,
42+
content:
43+
"slug: debug\nname: Debug\nmodel: anthropic/claude-sonnet-4\nprompt: |\n You are a debugging specialist.",
44+
},
45+
]
46+
47+
export const mockModes = [
48+
{
49+
slug: "code",
50+
name: "Code",
51+
description:
52+
"Write, modify, or refactor code. Ideal for implementing features, fixing bugs, creating new files, or making code improvements across any programming language or framework.",
53+
roleDefinition:
54+
"You are Kilo Code, a highly skilled software engineer with extensive knowledge in many programming languages, frameworks, design patterns, and best practices.",
55+
whenToUse: "Use this mode when you need to write, modify, or refactor code.",
56+
groups: ["edit", "read", "command"],
57+
source: "builtin" as const,
58+
},
59+
{
60+
slug: "architect",
61+
name: "Architect",
62+
description:
63+
"Plan, design, or strategize before implementation. Perfect for breaking down complex problems, creating technical specifications, designing system architecture.",
64+
roleDefinition: "You are an experienced software architect specializing in system design and planning.",
65+
whenToUse: "Use this mode when you need to plan, design, or strategize before implementation.",
66+
groups: ["edit", "read"],
67+
source: "builtin" as const,
68+
},
69+
]
70+
71+
export const createMockMarketplaceStateManager = (activeTab: "mcp" | "mode" = "mcp") => ({
72+
getState: () => ({
73+
allItems: mockMarketplaceItems,
74+
organizationMcps: [],
75+
displayItems: mockMarketplaceItems,
76+
displayOrganizationMcps: [],
77+
isFetching: false,
78+
activeTab,
79+
filters: {
80+
type: "",
81+
search: "",
82+
tags: [],
83+
installed: "all" as const,
84+
},
85+
installedMetadata: { global: {}, project: {} },
86+
}),
87+
transition: () => Promise.resolve(),
88+
onStateChange: () => () => {},
89+
cleanup: () => {},
90+
handleMessage: () => Promise.resolve(),
91+
})
Lines changed: 27 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -1,78 +1,12 @@
11
// kilocode_change - new file
2-
import React from "react"
32
import type { Meta, StoryObj } from "@storybook/react-vite"
4-
import { fn } from "storybook/test"
5-
63
import { MarketplaceView } from "../../../webview-ui/src/components/marketplace/MarketplaceView"
7-
8-
const mockItems = [
9-
{
10-
id: "filesystem-mcp",
11-
name: "File System MCP",
12-
description: "Provides tools for reading, writing, and managing files and directories on the local filesystem.",
13-
author: "Anthropic",
14-
tags: ["files", "filesystem", "core"],
15-
type: "mcp",
16-
url: "https://github.com/anthropics/mcp-filesystem",
17-
content: "npm install @anthropic-ai/mcp-filesystem",
18-
},
19-
{
20-
id: "database-mcp",
21-
name: "Database MCP",
22-
description: "Connect to and query various databases including PostgreSQL, MySQL, and SQLite.",
23-
author: "Community",
24-
tags: ["database", "sql", "data"],
25-
type: "mcp",
26-
url: "https://github.com/community/mcp-database",
27-
content: "npm install mcp-database",
28-
},
29-
{
30-
id: "architect-mode",
31-
name: "Architect Mode",
32-
description: "Plan and design system architecture before implementation. Perfect for complex projects.",
33-
author: "Kilocode",
34-
tags: ["planning", "design", "architecture"],
35-
type: "mode",
36-
content:
37-
"slug: architect\nname: Architect\nmodel: anthropic/claude-sonnet-4\nprompt: |\n You are an experienced software architect.",
38-
},
39-
{
40-
id: "debug-mode",
41-
name: "Debug Mode",
42-
description: "Advanced debugging capabilities with step-by-step analysis and error tracking.",
43-
author: "Kilocode",
44-
tags: ["debugging", "analysis", "troubleshooting"],
45-
type: "mode",
46-
content:
47-
"slug: debug\nname: Debug\nmodel: anthropic/claude-sonnet-4\nprompt: |\n You are a debugging specialist.",
48-
},
49-
]
50-
51-
// Simple mock state manager - using no-op functions to avoid performance issues with action logging
52-
const createMockStateManager = (activeTab: "mcp" | "mode" = "mcp") => ({
53-
getState: () => ({
54-
allItems: mockItems,
55-
organizationMcps: [],
56-
displayItems: mockItems,
57-
displayOrganizationMcps: [],
58-
isFetching: false,
59-
activeTab,
60-
filters: {
61-
type: "",
62-
search: "",
63-
tags: [],
64-
installed: "all",
65-
},
66-
installedMetadata: { global: {}, project: {} },
67-
}),
68-
transition: () => Promise.resolve(), // No-op async function
69-
onStateChange: () => () => {}, // Returns unsubscribe function
70-
cleanup: () => {}, // No-op function
71-
handleMessage: () => Promise.resolve(), // No-op async function
72-
})
4+
import { createMockMarketplaceStateManager } from "../src/mockData"
5+
import { createExtensionStateMock } from "../src/utils/createExtensionStateMock"
6+
import { withSidebarContainer } from "../src/decorators/withSidebarContainer"
737

748
const meta = {
75-
title: "Marketplace/MarketplaceView",
9+
title: "Views/MarketplaceView",
7610
component: MarketplaceView,
7711
argTypes: {
7812
targetTab: {
@@ -93,28 +27,42 @@ const meta = {
9327
hideHeader: false,
9428
onDone: () => {},
9529
},
96-
decorators: [
97-
(Story) => (
98-
<div className="w-[300px] min-h-[600px] bg-vscode-sideBar-background">
99-
<Story />
100-
</div>
101-
),
102-
],
30+
decorators: [withSidebarContainer()],
31+
parameters: {
32+
extensionState: createExtensionStateMock({
33+
organizationAllowList: {
34+
allowAll: true,
35+
providers: {},
36+
},
37+
apiConfiguration: {
38+
apiProvider: "anthropic",
39+
apiModelId: "claude-3-5-sonnet-20241022",
40+
apiKey: "mock-key",
41+
},
42+
marketplaceInstalledMetadata: {
43+
global: {},
44+
project: {},
45+
},
46+
mcpServers: [],
47+
mode: "code",
48+
customModes: [],
49+
}),
50+
},
10351
} satisfies Meta<typeof MarketplaceView>
10452

10553
export default meta
10654
type Story = StoryObj<typeof meta>
10755

10856
export const MCPTab: Story = {
10957
args: {
110-
stateManager: createMockStateManager("mcp") as any,
58+
stateManager: createMockMarketplaceStateManager("mcp") as any,
11159
targetTab: "mcp",
11260
},
11361
}
11462

11563
export const ModeTab: Story = {
11664
args: {
117-
stateManager: createMockStateManager("mode") as any,
65+
stateManager: createMockMarketplaceStateManager("mode") as any,
11866
targetTab: "mode",
11967
},
12068
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// kilocode_change - new file
2+
import type { Meta, StoryObj } from "@storybook/react-vite"
3+
4+
import ModesView from "../../../webview-ui/src/components/modes/ModesView"
5+
import { mockModes } from "../src/mockData"
6+
import { createExtensionStateMock } from "../src/utils/createExtensionStateMock"
7+
import { withSidebarContainer } from "../src/decorators/withSidebarContainer"
8+
9+
const meta = {
10+
title: "Views/ModesView",
11+
component: ModesView,
12+
argTypes: {
13+
onDone: {
14+
action: "onDone",
15+
description: "Callback when done button is clicked",
16+
},
17+
},
18+
decorators: [withSidebarContainer(400)], // ModesView needs more width for its content
19+
parameters: {
20+
extensionState: createExtensionStateMock({
21+
customModePrompts: {},
22+
listApiConfigMeta: [],
23+
currentApiConfigName: "anthropic",
24+
mode: "code",
25+
customInstructions: "",
26+
setCustomInstructions: () => {},
27+
customModes: mockModes as any, // Cast to bypass complex type requirements for Storybook
28+
}),
29+
},
30+
} satisfies Meta<typeof ModesView>
31+
32+
export default meta
33+
type Story = StoryObj<typeof meta>
34+
35+
export const Default: Story = {
36+
args: {
37+
onDone: () => {},
38+
},
39+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// kilocode_change - new file
2+
import React from "react"
3+
import { useAppTranslation } from "@src/i18n/TranslationContext"
4+
import { getAppUrl, TelemetryEventName } from "@roo-code/types"
5+
import { telemetryClient } from "@/utils/TelemetryClient"
6+
7+
interface KiloShareModesBannerProps {
8+
className?: string
9+
}
10+
11+
export const KiloShareModesBanner: React.FC<KiloShareModesBannerProps> = ({ className = "" }) => {
12+
const { t } = useAppTranslation()
13+
14+
return (
15+
<a
16+
className={`flex items-center gap-1 bg-vscode-editor-background border border-vscode-panel-border rounded-md p-1 my-1 mb-2 text-[var(--vscode-activityWarningBadge-background)] cursor-pointer hover:[filter:brightness(1.1)] ${className}`}
17+
href={getAppUrl("/organizations/new")}
18+
onClick={() => {
19+
telemetryClient.capture(TelemetryEventName.CREATE_ORGANIZATION_LINK_CLICKED, {
20+
origin: "modes-view",
21+
})
22+
}}>
23+
<div className="flex items-center px-2 py-0.5 rounded-full text-xs font-bold">
24+
<span className="codicon codicon-sparkle text-xs" />
25+
</div>
26+
<span className="text-sm">
27+
<span className="font-bold">NEW:</span> {t("kilocode:modes.shareModesNewBanner").replace("New: ", "")}
28+
</span>
29+
</a>
30+
)
31+
}

webview-ui/src/components/modes/ModesView.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import React, { useState, useEffect, useMemo, useCallback, useRef } from "react"
22
import BottomControls from "../kilocode/BottomControls" // kilocode_change
3+
import { KiloShareModesBanner } from "../kilocode/KiloShareModesBanner" // kilocode_change
34
import {
45
VSCodeCheckbox,
56
VSCodeRadioGroup,
@@ -605,7 +606,10 @@ const ModesView = ({ onDone }: ModesViewProps) => {
605606
</div>
606607
</div>
607608

608-
<div className="text-sm text-vscode-descriptionForeground mb-3">
609+
<div className="text-sm text-vscode-descriptionForeground mb-6">
610+
{/* kilocode_change - add KiloShareModesBanner */}
611+
<KiloShareModesBanner />
612+
609613
<Trans i18nKey="prompts:modes.createModeHelpText">
610614
<VSCodeLink
611615
href={buildDocLink("basic-usage/using-modes", "prompts_view_modes")}

webview-ui/src/i18n/locales/ar/kilocode.json

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

webview-ui/src/i18n/locales/ca/kilocode.json

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

webview-ui/src/i18n/locales/cs/kilocode.json

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

webview-ui/src/i18n/locales/de/kilocode.json

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)