mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-09-10 22:41:14 +00:00
feat(webview): persistent NEW badge over version indicator with cross-session acknowledgement
This commit is contained in:
parent
6fd261d3b6
commit
0b8d41e617
8 changed files with 58 additions and 4 deletions
|
|
@ -39,6 +39,8 @@ export const globalSettingsSchema = z.object({
|
|||
pinnedApiConfigs: z.record(z.string(), z.boolean()).optional(),
|
||||
|
||||
lastShownAnnouncementId: z.string().optional(),
|
||||
// Tracks when the user has manually acknowledged the announcement by clicking the version indicator
|
||||
lastAcknowledgedAnnouncementId: z.string().optional(),
|
||||
customInstructions: z.string().optional(),
|
||||
taskHistory: z.array(historyItemSchema).optional(),
|
||||
|
||||
|
|
|
|||
|
|
@ -1670,6 +1670,7 @@ export class ClineProvider
|
|||
const {
|
||||
apiConfiguration,
|
||||
lastShownAnnouncementId,
|
||||
lastAcknowledgedAnnouncementId,
|
||||
customInstructions,
|
||||
alwaysAllowReadOnly,
|
||||
alwaysAllowReadOnlyOutsideWorkspace,
|
||||
|
|
@ -1800,6 +1801,8 @@ export class ClineProvider
|
|||
enableCheckpoints: enableCheckpoints ?? true,
|
||||
shouldShowAnnouncement:
|
||||
telemetrySetting !== "unset" && lastShownAnnouncementId !== this.latestAnnouncementId,
|
||||
// Badge should persist across sessions until manually acknowledged by clicking version indicator
|
||||
shouldShowAnnouncementBadge: lastAcknowledgedAnnouncementId !== this.latestAnnouncementId,
|
||||
allowedCommands: mergedAllowedCommands,
|
||||
deniedCommands: mergedDeniedCommands,
|
||||
soundVolume: soundVolume ?? 0.5,
|
||||
|
|
@ -1963,6 +1966,7 @@ export class ClineProvider
|
|||
return {
|
||||
apiConfiguration: providerSettings,
|
||||
lastShownAnnouncementId: stateValues.lastShownAnnouncementId,
|
||||
lastAcknowledgedAnnouncementId: stateValues.lastAcknowledgedAnnouncementId,
|
||||
customInstructions: stateValues.customInstructions,
|
||||
apiModelId: stateValues.apiModelId,
|
||||
alwaysAllowReadOnly: stateValues.alwaysAllowReadOnly ?? false,
|
||||
|
|
|
|||
|
|
@ -381,6 +381,11 @@ export const webviewMessageHandler = async (
|
|||
await updateGlobalState("lastShownAnnouncementId", provider.latestAnnouncementId)
|
||||
await provider.postStateToWebview()
|
||||
break
|
||||
case "didAcknowledgeAnnouncement":
|
||||
// Persist user's manual acknowledgement so the badge clears across sessions
|
||||
await updateGlobalState("lastAcknowledgedAnnouncementId", provider.latestAnnouncementId)
|
||||
await provider.postStateToWebview()
|
||||
break
|
||||
case "selectImages":
|
||||
const images = await selectImages()
|
||||
await provider.postMessageToWebview({
|
||||
|
|
|
|||
|
|
@ -308,6 +308,8 @@ export type ExtensionState = Pick<
|
|||
renderContext: "sidebar" | "editor"
|
||||
settingsImportedAt?: number
|
||||
historyPreviewCollapsed?: boolean
|
||||
// Whether to show the persistent badge over the version indicator (cross-session)
|
||||
shouldShowAnnouncementBadge?: boolean
|
||||
|
||||
cloudUserInfo: CloudUserInfo | null
|
||||
cloudIsAuthenticated: boolean
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ export interface WebviewMessage {
|
|||
| "terminalOperation"
|
||||
| "clearTask"
|
||||
| "didShowAnnouncement"
|
||||
| "didAcknowledgeAnnouncement"
|
||||
| "selectImages"
|
||||
| "exportCurrentTask"
|
||||
| "shareCurrentTask"
|
||||
|
|
|
|||
|
|
@ -74,12 +74,16 @@ const App = () => {
|
|||
cloudApiUrl,
|
||||
renderContext,
|
||||
mdmCompliant,
|
||||
// New: persisted badge state from extension
|
||||
shouldShowAnnouncementBadge,
|
||||
} = useExtensionState()
|
||||
|
||||
// Create a persistent state manager
|
||||
const marketplaceStateManager = useMemo(() => new MarketplaceViewStateManager(), [])
|
||||
|
||||
const [showAnnouncement, setShowAnnouncement] = useState(false)
|
||||
// Badge indicator over version number that persists until user manually clicks version
|
||||
const [hasNewAnnouncementBadge, setHasNewAnnouncementBadge] = useState(false)
|
||||
const [tab, setTab] = useState<Tab>("chat")
|
||||
|
||||
const [humanRelayDialogState, setHumanRelayDialogState] = useState<HumanRelayDialogState>({
|
||||
|
|
@ -179,11 +183,24 @@ const App = () => {
|
|||
|
||||
useEffect(() => {
|
||||
if (shouldShowAnnouncement) {
|
||||
// Auto-open the announcement modal when a new announcement triggers
|
||||
setShowAnnouncement(true)
|
||||
// Set the badge to persist until the user manually clicks the version indicator
|
||||
setHasNewAnnouncementBadge(true)
|
||||
// Notify extension that the announcement was shown (to prevent re-triggering)
|
||||
vscode.postMessage({ type: "didShowAnnouncement" })
|
||||
}
|
||||
}, [shouldShowAnnouncement])
|
||||
|
||||
// Sync local badge state from extension state for cross-session persistence
|
||||
useEffect(() => {
|
||||
// Only update if the extension indicates we should show the badge
|
||||
// This allows local state to clear immediately after user clicks
|
||||
if (shouldShowAnnouncementBadge) {
|
||||
setHasNewAnnouncementBadge(true)
|
||||
}
|
||||
}, [shouldShowAnnouncementBadge])
|
||||
|
||||
useEffect(() => {
|
||||
if (didHydrateState) {
|
||||
telemetryClient.updateTelemetryState(telemetrySetting, telemetryKey, machineId)
|
||||
|
|
@ -259,6 +276,12 @@ const App = () => {
|
|||
isHidden={tab !== "chat"}
|
||||
showAnnouncement={showAnnouncement}
|
||||
hideAnnouncement={() => setShowAnnouncement(false)}
|
||||
hasNewAnnouncement={hasNewAnnouncementBadge}
|
||||
onVersionIndicatorClick={() => {
|
||||
setHasNewAnnouncementBadge(false)
|
||||
// Persist acknowledgement so badge stays cleared across reloads
|
||||
vscode.postMessage({ type: "didAcknowledgeAnnouncement" })
|
||||
}}
|
||||
/>
|
||||
<MemoizedHumanRelayDialog
|
||||
isOpen={humanRelayDialogState.isOpen}
|
||||
|
|
|
|||
|
|
@ -62,6 +62,10 @@ export interface ChatViewProps {
|
|||
isHidden: boolean
|
||||
showAnnouncement: boolean
|
||||
hideAnnouncement: () => void
|
||||
// When true, shows a "NEW" badge over the version indicator until the user clicks it
|
||||
hasNewAnnouncement?: boolean
|
||||
// Called when the user clicks the version indicator (used to acknowledge the announcement)
|
||||
onVersionIndicatorClick?: () => void
|
||||
}
|
||||
|
||||
export interface ChatViewRef {
|
||||
|
|
@ -73,7 +77,7 @@ export const MAX_IMAGES_PER_MESSAGE = 20 // Anthropic limits to 20 images
|
|||
const isMac = navigator.platform.toUpperCase().indexOf("MAC") >= 0
|
||||
|
||||
const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewProps> = (
|
||||
{ isHidden, showAnnouncement, hideAnnouncement },
|
||||
{ isHidden, showAnnouncement, hideAnnouncement, hasNewAnnouncement = false, onVersionIndicatorClick },
|
||||
ref,
|
||||
) => {
|
||||
const isMountedRef = useRef(true)
|
||||
|
|
@ -1827,7 +1831,11 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
|
|||
className={` w-full flex flex-col gap-4 m-auto ${isExpanded && tasks.length > 0 ? "mt-0" : ""} px-3.5 min-[370px]:px-10 pt-5 transition-all duration-300`}>
|
||||
{/* Version indicator in top-right corner - only on welcome screen */}
|
||||
<VersionIndicator
|
||||
onClick={() => setShowAnnouncementModal(true)}
|
||||
onClick={() => {
|
||||
setShowAnnouncementModal(true)
|
||||
onVersionIndicatorClick?.()
|
||||
}}
|
||||
showBadge={hasNewAnnouncement}
|
||||
className="absolute top-2 right-3 z-10"
|
||||
/>
|
||||
|
||||
|
|
|
|||
|
|
@ -5,17 +5,26 @@ import { Package } from "@roo/package"
|
|||
interface VersionIndicatorProps {
|
||||
onClick: () => void
|
||||
className?: string
|
||||
// When true, renders a small "NEW" badge over the version button
|
||||
showBadge?: boolean
|
||||
}
|
||||
|
||||
const VersionIndicator: React.FC<VersionIndicatorProps> = ({ onClick, className = "" }) => {
|
||||
const VersionIndicator: React.FC<VersionIndicatorProps> = ({ onClick, className = "", showBadge = false }) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<button
|
||||
onClick={onClick}
|
||||
className={`text-xs text-vscode-descriptionForeground hover:text-vscode-foreground transition-colors cursor-pointer px-2 py-1 rounded border ${className}`}
|
||||
className={`relative inline-flex items-center text-xs text-vscode-descriptionForeground hover:text-vscode-foreground transition-colors cursor-pointer px-2 py-1 rounded border ${className}`}
|
||||
aria-label={t("chat:versionIndicator.ariaLabel", { version: Package.version })}>
|
||||
v{Package.version}
|
||||
{showBadge && (
|
||||
<span
|
||||
// The badge uses VS Code theme variables to blend with light/dark themes
|
||||
className="absolute -top-1.5 -right-1.5 rounded-full bg-vscode-button-background text-vscode-button-foreground text-[10px] leading-none px-1.5 py-0.5 shadow ring-2 ring-vscode-editor-background select-none">
|
||||
NEW
|
||||
</span>
|
||||
)}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue