Skip to content
Merged
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
29 changes: 7 additions & 22 deletions cli/src/chat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,8 @@ import {
} from 'react'
import { useShallow } from 'zustand/react/shallow'

import { getAdsEnabled, handleAdsDisable } from './commands/ads'
import { getAdsEnabled } from './commands/ads'
import { routeUserPrompt, addBashMessageToHistory } from './commands/router'
import { AdBanner } from './components/ad-banner'
import { ChoiceAdBanner } from './components/choice-ad-banner'
import { ChatInputBar } from './components/chat-input-bar'
import { LoadPreviousButton } from './components/load-previous-button'
Expand Down Expand Up @@ -175,13 +174,7 @@ export const Chat = ({
})
const hasSubscription = subscriptionData?.hasSubscription ?? false

const { ad, adData, recordImpression } = useGravityAd({ enabled: IS_FREEBUFF || !hasSubscription })
const [adsManuallyDisabled, setAdsManuallyDisabled] = useState(false)

const handleDisableAds = useCallback(() => {
handleAdsDisable()
setAdsManuallyDisabled(true)
}, [])
const { adData, recordImpression } = useGravityAd({ enabled: IS_FREEBUFF || !hasSubscription })

// Set initial mode from CLI flag on mount
useEffect(() => {
Expand Down Expand Up @@ -1466,19 +1459,11 @@ export const Chat = ({
/>
)}

{ad && (IS_FREEBUFF || (!adsManuallyDisabled && getAdsEnabled())) && (
adData?.variant === 'choice' ? (
<ChoiceAdBanner
ads={adData.ads}
onImpression={recordImpression}
/>
) : (
<AdBanner
ad={ad}
onDisableAds={handleDisableAds}
isFreeMode={IS_FREEBUFF}
/>
)
{adData && (IS_FREEBUFF || getAdsEnabled()) && (
<ChoiceAdBanner
ads={adData.variant === 'choice' ? adData.ads : [adData.ad]}
onImpression={recordImpression}
/>
)}

{reviewMode ? (
Expand Down
236 changes: 0 additions & 236 deletions cli/src/components/ad-banner.tsx

This file was deleted.

21 changes: 9 additions & 12 deletions cli/src/components/waiting-room-screen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { TextAttributes } from '@opentui/core'
import { useRenderer } from '@opentui/react'
import React, { useMemo, useState } from 'react'

import { AdBanner } from './ad-banner'
import { Button } from './button'
import { ChoiceAdBanner } from './choice-ad-banner'
import { FreebuffModelSelector } from './freebuff-model-selector'
Expand Down Expand Up @@ -73,9 +72,11 @@ export const WaitingRoomScreen: React.FC<WaitingRoomScreenProps> = ({
// Always enable ads in the waiting room — this is where monetization lives.
// forceStart bypasses the "wait for first user message" gate inside the hook,
// which would otherwise block ads here since no conversation exists yet.
const { ad, adData, recordImpression } = useGravityAd({
// Uses Carbon (BuySellAds); in-chat ads still use the Gravity default.
const { adData, recordImpression } = useGravityAd({
enabled: true,
forceStart: true,
provider: 'carbon',
})

useFreebuffCtrlCExit()
Expand Down Expand Up @@ -261,21 +262,17 @@ export const WaitingRoomScreen: React.FC<WaitingRoomScreenProps> = ({
</box>

{/* Ad banner pinned to the bottom, same look-and-feel as in chat. */}
{ad && (
{adData && (
<box style={{ flexShrink: 0 }}>
{adData?.variant === 'choice' ? (
<ChoiceAdBanner
ads={adData.ads}
onImpression={recordImpression}
/>
) : (
<AdBanner ad={ad} onDisableAds={() => {}} isFreeMode />
)}
<ChoiceAdBanner
ads={adData.variant === 'choice' ? adData.ads : [adData.ad]}
onImpression={recordImpression}
/>
</box>
)}

{/* Horizontal separator (mirrors chat input divider style) */}
{!ad && (
{!adData && (
<text style={{ fg: theme.muted, flexShrink: 0 }}>
{'─'.repeat(terminalWidth)}
</text>
Expand Down
13 changes: 13 additions & 0 deletions cli/src/hooks/helpers/send-message.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
import { getErrorObject } from '@codebuff/common/util/error'

import {
markFreebuffSessionCountryBlocked,
markFreebuffSessionEnded,
markFreebuffSessionSuperseded,
refreshFreebuffSession,
} from '../use-freebuff-session'
import { getProjectRoot } from '../../project-files'
import { useChatStore } from '../../state/chat-store'
import { IS_FREEBUFF } from '../../utils/constants'
import { processBashContext } from '../../utils/bash-context-processor'
import { markRunningAgentsAsCancelled } from '../../utils/block-operations'
import {
getCountryCodeFromFreeModeError,
getFreebuffGateErrorKind,
isOutOfCreditsError,
isFreeModeUnavailableError,
Expand Down Expand Up @@ -389,6 +392,11 @@ export const handleRunCompletion = (params: {

if (isFreeModeUnavailableError(output)) {
updater.setError(FREE_MODE_UNAVAILABLE_MESSAGE)
if (IS_FREEBUFF) {
markFreebuffSessionCountryBlocked(
getCountryCodeFromFreeModeError(output) ?? 'UNKNOWN',
)
}
finalizeAfterError()
return
}
Expand Down Expand Up @@ -484,6 +492,11 @@ export const handleRunError = (params: {

if (isFreeModeUnavailableError(error)) {
updater.setError(FREE_MODE_UNAVAILABLE_MESSAGE)
if (IS_FREEBUFF) {
markFreebuffSessionCountryBlocked(
getCountryCodeFromFreeModeError(error) ?? 'UNKNOWN',
)
}
return
}

Expand Down
Loading
Loading