Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/types/src/global-settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ export const globalSettingsSchema = z.object({
historyPreviewCollapsed: z.boolean().optional(),
profileThresholds: z.record(z.string(), z.number()).optional(),
hasOpenedModeSelector: z.boolean().optional(),
systemPromptWarningDismissed: z.boolean().optional(),
})

export type GlobalSettings = z.infer<typeof globalSettingsSchema>
Expand Down
1 change: 1 addition & 0 deletions src/shared/ExtensionMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@ export type ExtensionState = Pick<
| "codebaseIndexConfig"
| "codebaseIndexModels"
| "profileThresholds"
| "systemPromptWarningDismissed"
> & {
version: string
clineMessages: ClineMessage[]
Expand Down
3 changes: 2 additions & 1 deletion webview-ui/src/components/chat/ChatView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
customModes,
telemetrySetting,
hasSystemPromptOverride,
systemPromptWarningDismissed,
historyPreviewCollapsed, // Added historyPreviewCollapsed
soundEnabled,
soundVolume,
Expand Down Expand Up @@ -1381,7 +1382,7 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
onClose={handleTaskCloseButtonClick}
/>

{hasSystemPromptOverride && (
{hasSystemPromptOverride && !systemPromptWarningDismissed && (
<div className="px-3">
<SystemPromptWarning />
</div>
Expand Down
14 changes: 13 additions & 1 deletion webview-ui/src/components/chat/SystemPromptWarning.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,27 @@
import React from "react"
import { useAppTranslation } from "@/i18n/TranslationContext"
import { useExtensionState } from "@/context/ExtensionStateContext"

export const SystemPromptWarning: React.FC = () => {
const { t } = useAppTranslation()
const { setSystemPromptWarningDismissed } = useExtensionState()

const handleDismiss = () => {
setSystemPromptWarningDismissed(true)
}

return (
<div className="flex items-center px-4 py-2 mb-2 text-sm rounded bg-vscode-editorWarning-foreground text-vscode-editor-background">
<div className="flex items-center justify-center w-5 h-5 mr-2">
<span className="codicon codicon-warning" />
</div>
<span>{t("chat:systemPromptWarning")}</span>
<span className="flex-1">{t("chat:systemPromptWarning")}</span>
<button
onClick={handleDismiss}
className="flex items-center justify-center w-5 h-5 ml-2 hover:bg-vscode-editor-background hover:bg-opacity-20 rounded"
title="Dismiss warning">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider using a translatable label for the dismiss button title (e.g., t('chat:dismissWarning')) instead of the hardcoded string.

Suggested change
title="Dismiss warning">
title={t("chat:dismissWarning")}>

This comment was generated because it violated a code review rule: irule_C0ez7Rji6ANcGkkX.

<span className="codicon codicon-close" />
</button>
</div>
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// npx vitest run src/components/chat/__tests__/SystemPromptWarning.spec.tsx

import { describe, it, expect, vi } from "vitest"
import { render, screen, fireEvent } from "@testing-library/react"
import { SystemPromptWarning } from "../SystemPromptWarning"
import { useExtensionState } from "@/context/ExtensionStateContext"
import { useAppTranslation } from "@/i18n/TranslationContext"

// Mock the hooks
vi.mock("@/context/ExtensionStateContext")
vi.mock("@/i18n/TranslationContext")

const mockSetSystemPromptWarningDismissed = vi.fn()
const mockT = vi.fn((key: string) => {
if (key === "chat:systemPromptWarning") {
return "WARNING: Custom system prompt override active. This can severely break functionality and cause unpredictable behavior."
}
return key
})

describe("SystemPromptWarning", () => {
beforeEach(() => {
vi.clearAllMocks()
;(useExtensionState as any).mockReturnValue({
setSystemPromptWarningDismissed: mockSetSystemPromptWarningDismissed,
})
;(useAppTranslation as any).mockReturnValue({
t: mockT,
})
})

it("renders the warning message", () => {
render(<SystemPromptWarning />)

expect(screen.getByText(/WARNING: Custom system prompt override active/)).toBeInTheDocument()
})

it("renders the warning icon", () => {
render(<SystemPromptWarning />)

const warningIcon = document.querySelector(".codicon-warning")
expect(warningIcon).toBeInTheDocument()
})

it("renders the dismiss button", () => {
render(<SystemPromptWarning />)

const dismissButton = screen.getByRole("button")
expect(dismissButton).toBeInTheDocument()
expect(dismissButton).toHaveAttribute("title", "Dismiss warning")
})

it("renders the close icon in dismiss button", () => {
render(<SystemPromptWarning />)

const closeIcon = document.querySelector(".codicon-close")
expect(closeIcon).toBeInTheDocument()
})

it("calls setSystemPromptWarningDismissed when dismiss button is clicked", () => {
render(<SystemPromptWarning />)

const dismissButton = screen.getByRole("button")
fireEvent.click(dismissButton)

expect(mockSetSystemPromptWarningDismissed).toHaveBeenCalledWith(true)
expect(mockSetSystemPromptWarningDismissed).toHaveBeenCalledTimes(1)
})

it("has correct CSS classes for styling", () => {
render(<SystemPromptWarning />)

const container = document.querySelector(".flex.items-center.px-4.py-2.mb-2")
expect(container).toBeInTheDocument()
expect(container).toHaveClass("bg-vscode-editorWarning-foreground", "text-vscode-editor-background")
})

it("uses translation for the warning text", () => {
render(<SystemPromptWarning />)

expect(mockT).toHaveBeenCalledWith("chat:systemPromptWarning")
})
})
5 changes: 5 additions & 0 deletions webview-ui/src/context/ExtensionStateContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ export interface ExtensionStateContextType extends ExtensionState {
mdmCompliant?: boolean
hasOpenedModeSelector: boolean // New property to track if user has opened mode selector
setHasOpenedModeSelector: (value: boolean) => void // Setter for the new property
systemPromptWarningDismissed?: boolean // New property to track if system prompt warning is dismissed
setSystemPromptWarningDismissed: (value: boolean) => void // Setter for the new property
condensingApiConfigId?: string
setCondensingApiConfigId: (value: string) => void
customCondensingPrompt?: string
Expand Down Expand Up @@ -183,6 +185,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
condensingApiConfigId: "", // Default empty string for condensing API config ID
customCondensingPrompt: "", // Default empty string for custom condensing prompt
hasOpenedModeSelector: false, // Default to false (not opened yet)
systemPromptWarningDismissed: false, // Default to false (not dismissed yet)
autoApprovalEnabled: false,
customModes: [],
maxOpenTabsContext: 20,
Expand Down Expand Up @@ -431,6 +434,8 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
setHistoryPreviewCollapsed: (value) =>
setState((prevState) => ({ ...prevState, historyPreviewCollapsed: value })),
setHasOpenedModeSelector: (value) => setState((prevState) => ({ ...prevState, hasOpenedModeSelector: value })),
setSystemPromptWarningDismissed: (value) =>
setState((prevState) => ({ ...prevState, systemPromptWarningDismissed: value })),
setAutoCondenseContext: (value) => setState((prevState) => ({ ...prevState, autoCondenseContext: value })),
setAutoCondenseContextPercent: (value) =>
setState((prevState) => ({ ...prevState, autoCondenseContextPercent: value })),
Expand Down
Loading