Compare commits

...

37 Commits

Author SHA1 Message Date
Zhongying Qiao bee6e31dd6 Merge branch 'add-fe-banner' of github.com:cline/cline into add-fe-banner 2025-12-03 14:55:55 -08:00
Zhongying Qiao b549f8d813 use light blue banner background 2025-12-03 14:54:55 -08:00
Zhongying Qiao 3dd81227fe Merge branch 'main' into add-fe-banner 2025-12-02 16:14:31 -08:00
Zhongying Qiao a1f05631f3 fix imports 2025-12-02 15:19:54 -08:00
Zhongying Qiao 2866182e6a Update webview-ui/src/components/chat/chat-view/components/layout/WelcomeSection.tsx
Co-authored-by: Bee <68532117+abeatrix@users.noreply.github.com>
2025-12-02 14:44:52 -08:00
Zhongying Qiao 9ecb0e2fa7 Update webview-ui/src/components/common/ApiBanner.tsx
Co-authored-by: Bee <68532117+abeatrix@users.noreply.github.com>
2025-12-02 14:44:43 -08:00
Zhongying Qiao 98f7012761 Merge branch 'add-fe-banner' of github.com:cline/cline into add-fe-banner 2025-12-01 15:00:39 -08:00
Zhongying Qiao 3f27e331fb fix e2e tests 2025-12-01 15:00:17 -08:00
Zhongying Qiao 92be7bf9ef Merge branch 'main' into add-fe-banner 2025-12-01 14:46:23 -08:00
Zhongying Qiao 606dc61c2f Merge branch 'add-fe-banner' of github.com:cline/cline into add-fe-banner 2025-12-01 12:29:57 -08:00
Zhongying Qiao d45e1b4935 fix jetbrain errors 2025-12-01 12:29:25 -08:00
Zhongying Qiao 29445add4c Merge branch 'banner-ui-dismiss' of github.com:cline/cline into add-fe-banner 2025-12-01 12:13:19 -08:00
Zhongying Qiao 92910eff60 do not hash instance id 2025-12-01 11:49:33 -08:00
Zhongying Qiao 08bc26f7f2 do not log error when banner array is empty, only when missing or not defined 2025-12-01 11:39:53 -08:00
Zhongying Qiao 5ceab2adb8 use BannerService.isInitialized() instead 2025-12-01 11:34:15 -08:00
Zhongying Qiao 404840f9c2 Merge branch 'banner-ui-dismiss' into add-fe-banner 2025-12-01 11:02:50 -08:00
Zhongying Qiao 3e68a6dda6 Merge branch 'main' into banner-ui-dismiss 2025-12-01 10:57:38 -08:00
Zhongying Qiao 5231c8a535 tweak UI 2025-11-26 15:52:43 -08:00
Zhongying Qiao a8ebd05995 fix nativeToolCallSetting type 2025-11-26 14:30:30 -08:00
Zhongying Qiao f0b259a2b0 pull from source branch, handle merge conflict 2025-11-26 14:24:12 -08:00
Zhongying Qiao f7ca3608a3 fix quality check 2025-11-26 13:54:32 -08:00
Zhongying Qiao 6fe35e187e Merge branch 'banner-ui-dismiss' of github.com:cline/cline into banner-ui-dismiss 2025-11-26 13:49:41 -08:00
Zhongying Qiao 65c5011abc fix ci 2025-11-26 13:48:13 -08:00
Zhongying Qiao 3918c66cdc Merge branch 'main' into banner-ui-dismiss 2025-11-26 13:27:02 -08:00
Zhongying Qiao 32630f913a fix: build error 2025-11-26 13:19:31 -08:00
Zhongying Qiao b0c956f923 feat: use json polling approach for active banners 2025-11-26 13:00:22 -08:00
Zhongying Qiao 7d2bf6577c add frontend to display banner via webview 2025-11-24 17:04:37 -08:00
Zhongying Qiao 6894f6708b fix banner service tests 2025-11-24 16:47:32 -08:00
Zhongying Qiao 7e96edd55a fix quality check errors 2025-11-24 16:31:33 -08:00
Zhongying Qiao f64dc439a8 fix proto file 2025-11-24 16:26:22 -08:00
Zhongying Qiao dd0ebafbb7 clean up 2025-11-24 16:12:00 -08:00
Zhongying Qiao 18b6a28cda seperate out frontend code 2025-11-24 16:07:34 -08:00
Zhongying Qiao b241517a5e audit every rule check to ensure it is doing correct filtering and working locally 2025-11-24 15:51:29 -08:00
Zhongying Qiao 571f833eae feat: wire up controller and UI for banners 2025-11-24 10:07:27 -08:00
Zhongying Qiao 0eb757ba72 feat: add cli as ide type, clean up some code 2025-11-24 09:42:42 -08:00
Zhongying Qiao c9f2427836 Merge branch 'main' of github.com:cline/cline into banner-ui-dismiss 2025-11-21 17:05:20 -08:00
Zhongying Qiao 5c75f4d04a feat: add banners ui, dismissal state handling and event log 2025-11-21 17:04:00 -08:00
8 changed files with 99 additions and 2 deletions
+2
View File
@@ -898,6 +898,7 @@ export class Controller {
const lastDismissedModelBannerVersion = this.stateManager.getGlobalStateKey("lastDismissedModelBannerVersion") || 0
const lastDismissedCliBannerVersion = this.stateManager.getGlobalStateKey("lastDismissedCliBannerVersion") || 0
const subagentsEnabled = this.stateManager.getGlobalSettingsKey("subagentsEnabled")
const activeBanners = this.stateManager.getGlobalStateKey("activeBanners") || []
const localClineRulesToggles = this.stateManager.getWorkspaceStateKey("localClineRulesToggles")
const localWindsurfRulesToggles = this.stateManager.getWorkspaceStateKey("localWindsurfRulesToggles")
@@ -997,6 +998,7 @@ export class Controller {
lastDismissedModelBannerVersion,
remoteConfigSettings: this.stateManager.getRemoteConfigSettings(),
lastDismissedCliBannerVersion,
activeBanners,
subagentsEnabled,
nativeToolCallSetting: this.stateManager.getGlobalStateKey("nativeToolCallEnabled"),
}
@@ -216,6 +216,18 @@ export async function initializeWebview(controller: Controller, _request: EmptyR
// Silently refresh MCP marketplace catalog
controller.refreshMcpMarketplace(true /* sendCatalogEvent */)
controller
.fetchBannersForDisplay()
.then((banners) => {
controller.stateManager.setGlobalState("activeBanners", banners)
if (banners.length > 0) {
controller.postStateToWebview()
}
})
.catch((error) => {
console.error("Failed to fetch banners:", error)
})
// Initialize telemetry service with user's current setting
controller.getStateToPostToWebview().then((state) => {
const { telemetrySetting } = state
+1
View File
@@ -674,6 +674,7 @@ export async function readGlobalStateFromDisk(context: ExtensionContext): Promis
lastDismissedModelBannerVersion: lastDismissedModelBannerVersion ?? 0,
lastDismissedCliBannerVersion: lastDismissedCliBannerVersion ?? 0,
dismissedBanners: dismissedBanners || [],
activeBanners: [],
nativeToolCallEnabled: nativeToolCallEnabled ?? true,
// Multi-root workspace support
workspaceRoots,
+2
View File
@@ -6,6 +6,7 @@ import type { Environment } from "../config"
import { AutoApprovalSettings } from "./AutoApprovalSettings"
import { ApiConfiguration } from "./api"
import { BrowserSettings } from "./BrowserSettings"
import type { Banner } from "./ClineBanner"
import { ClineFeatureSetting } from "./ClineFeatureSetting"
import { ClineRulesToggles } from "./cline-rules"
import { DictationSettings } from "./DictationSettings"
@@ -104,6 +105,7 @@ export interface ExtensionState {
remoteConfigSettings?: Partial<RemoteConfigFields>
subagentsEnabled?: boolean
nativeToolCallSetting?: boolean
activeBanners?: Banner[]
}
export interface ClineMessage {
+2
View File
@@ -1,6 +1,7 @@
import { AutoApprovalSettings } from "@shared/AutoApprovalSettings"
import { ApiProvider, ModelInfo, type OcaModelInfo } from "@shared/api"
import { BrowserSettings } from "@shared/BrowserSettings"
import type { Banner } from "@shared/ClineBanner"
import { ClineRulesToggles } from "@shared/cline-rules"
import { DictationSettings } from "@shared/DictationSettings"
import { FocusChainSettings } from "@shared/FocusChainSettings"
@@ -57,6 +58,7 @@ export interface GlobalState {
remoteRulesToggles: ClineRulesToggles
remoteWorkflowToggles: ClineRulesToggles
dismissedBanners: Array<{ bannerId: string; dismissedAt: number }>
activeBanners: Banner[]
}
export interface Settings {
@@ -1,5 +1,6 @@
import React from "react"
import React, { useMemo } from "react"
import Announcement from "@/components/chat/Announcement"
import ApiBanner from "@/components/common/ApiBanner"
import CliInstallBanner, { CURRENT_CLI_BANNER_VERSION } from "@/components/common/CliInstallBanner"
import InfoBanner, { CURRENT_INFO_BANNER_VERSION } from "@/components/common/InfoBanner"
import NewModelBanner, { CURRENT_MODEL_BANNER_VERSION } from "@/components/common/NewModelBanner"
@@ -23,10 +24,12 @@ export const WelcomeSection: React.FC<WelcomeSectionProps> = ({
taskHistory,
shouldShowQuickWins,
}) => {
const { lastDismissedInfoBannerVersion, lastDismissedCliBannerVersion, lastDismissedModelBannerVersion } = useExtensionState()
const { lastDismissedInfoBannerVersion, lastDismissedCliBannerVersion, lastDismissedModelBannerVersion, activeBanners } =
useExtensionState()
const shouldShowInfoBanner = lastDismissedInfoBannerVersion < CURRENT_INFO_BANNER_VERSION
const shouldShowNewModelBanner = lastDismissedModelBannerVersion < CURRENT_MODEL_BANNER_VERSION
const topBanners = useMemo(() => activeBanners?.filter((banner) => banner.placement === "top") || [], [activeBanners])
// Show CLI banner if not dismissed and platform is VSCode (not JetBrains/standalone)
const shouldShowCliBanner =
@@ -41,6 +44,9 @@ export const WelcomeSection: React.FC<WelcomeSectionProps> = ({
{showAnnouncement && <Announcement hideAnnouncement={hideAnnouncement} version={version} />}
{shouldShowNewModelBanner && <NewModelBanner />}
{shouldShowCliBanner && <CliInstallBanner />}
{topBanners?.map((banner) => (
<ApiBanner banner={banner} key={banner.id} />
))}
<HomeHeader shouldShowQuickWins={shouldShowQuickWins} />
{!shouldShowQuickWins && taskHistory.length > 0 && <HistoryPreview showHistoryView={showHistoryView} />}
</div>
@@ -0,0 +1,70 @@
import type { Banner } from "@shared/ClineBanner"
import { XIcon } from "lucide-react"
import { useCallback, useEffect } from "react"
import { useRemark } from "react-remark"
import { Button } from "@/components/ui/button"
import { StateServiceClient } from "@/services/grpc-client"
import { getAsVar, VSC_INACTIVE_SELECTION_BACKGROUND } from "@/utils/vscStyles"
interface ApiBannerProps {
banner: Banner
}
export const ApiBanner: React.FC<ApiBannerProps> = ({ banner }) => {
const [titleContent, setTitleMarkdown] = useRemark()
const [bodyContent, setBodyMarkdown] = useRemark()
useEffect(() => {
setTitleMarkdown(banner.titleMd || "")
}, [banner.titleMd, setTitleMarkdown])
useEffect(() => {
setBodyMarkdown(banner.bodyMd || "")
}, [banner.bodyMd, setBodyMarkdown])
const handleDismiss = useCallback(
(e: React.MouseEvent) => {
e.preventDefault()
e.stopPropagation()
StateServiceClient.dismissBanner({ value: banner.id }).catch(console.error)
},
[banner.id],
)
const getAlertVariant = () => {
switch (banner.severity) {
case "warning":
return "warning"
case "success":
case "info":
default:
return "default"
}
}
return (
<div
className="px-4 py-3 flex flex-col shrink-0 mb-1 relative text-sm mt-1.5 m-4 transition-colors border-0 text-left w-auto rounded-sm"
style={{
backgroundColor: getAsVar(VSC_INACTIVE_SELECTION_BACKGROUND),
}}>
{/* Title */}
<div className="font-semibold mb-2 [&_p]:m-0 [&_h4]:m-0">{titleContent}</div>
{/* Body */}
<div className="[&_p]:m-0">{bodyContent}</div>
{/* Close button */}
<Button
className="absolute top-2.5 right-2"
data-testid={`api-banner-close-${banner.id}`}
onClick={handleDismiss}
size="icon"
variant="icon">
<XIcon />
</Button>
</div>
)
}
export default ApiBanner
@@ -242,6 +242,7 @@ export const ExtensionStateContextProvider: React.FC<{
backgroundCommandTaskId: undefined,
lastDismissedCliBannerVersion: 0,
subagentsEnabled: false,
activeBanners: [],
// NEW: Add workspace information with defaults
workspaceRoots: [],
@@ -730,6 +731,7 @@ export const ExtensionStateContextProvider: React.FC<{
remoteWorkflowToggles: state.remoteWorkflowToggles || {},
enableCheckpointsSetting: state.enableCheckpointsSetting,
currentFocusChainChecklist: state.currentFocusChainChecklist,
activeBanners: state.activeBanners || [],
// Navigation functions
navigateToMcp,