diff --git a/src/browser/components/ContextUsageIndicatorButton.tsx b/src/browser/components/ContextUsageIndicatorButton.tsx index 828d4e194..ec509f1e1 100644 --- a/src/browser/components/ContextUsageIndicatorButton.tsx +++ b/src/browser/components/ContextUsageIndicatorButton.tsx @@ -45,7 +45,7 @@ export const ContextUsageIndicatorButton: React.FC - + diff --git a/src/browser/components/RightSidebar.tsx b/src/browser/components/RightSidebar.tsx index 522082e9d..fe6471456 100644 --- a/src/browser/components/RightSidebar.tsx +++ b/src/browser/components/RightSidebar.tsx @@ -2,13 +2,9 @@ import React from "react"; import { RIGHT_SIDEBAR_TAB_KEY, RIGHT_SIDEBAR_COLLAPSED_KEY } from "@/common/constants/storage"; import { usePersistedState } from "@/browser/hooks/usePersistedState"; import { useWorkspaceUsage } from "@/browser/stores/WorkspaceStore"; -import { useProviderOptions } from "@/browser/hooks/useProviderOptions"; import { useResizeObserver } from "@/browser/hooks/useResizeObserver"; -import { useAutoCompactionSettings } from "@/browser/hooks/useAutoCompactionSettings"; import { CostsTab } from "./RightSidebar/CostsTab"; -import { VerticalTokenMeter } from "./RightSidebar/VerticalTokenMeter"; import { ReviewPanel } from "./RightSidebar/CodeReview/ReviewPanel"; -import { calculateTokenMeterData } from "@/common/utils/tokens/tokenMeterUtils"; import { sumUsageHistory, type ChatUsageDisplay } from "@/common/utils/tokens/usageAggregator"; import { matchesKeybind, KEYBINDS, formatKeybind } from "@/browser/utils/ui/keybinds"; import { Tooltip, TooltipTrigger, TooltipContent } from "./ui/tooltip"; @@ -128,8 +124,6 @@ const RightSidebarComponent: React.FC = ({ }, [setSelectedTab]); const usage = useWorkspaceUsage(workspaceId); - const { options } = useProviderOptions(); - const use1M = options.anthropic?.use1MContext ?? false; const chatAreaSize = useResizeObserver(chatAreaRef); const baseId = `right-sidebar-${workspaceId}`; @@ -138,10 +132,6 @@ const RightSidebarComponent: React.FC = ({ const costsPanelId = `${baseId}-panel-costs`; const reviewPanelId = `${baseId}-panel-review`; - // Use lastContextUsage for context window display (last step = actual context size) - const lastUsage = usage?.liveUsage ?? usage?.lastContextUsage; - const model = lastUsage?.model ?? null; - // Calculate session cost for tab display const sessionCost = React.useMemo(() => { const parts: ChatUsageDisplay[] = []; @@ -162,17 +152,6 @@ const RightSidebarComponent: React.FC = ({ return total > 0 ? total : null; }, [usage.sessionTotal, usage.liveCostUsage]); - // Auto-compaction settings: threshold per-model - const { threshold: autoCompactThreshold, setThreshold: setAutoCompactThreshold } = - useAutoCompactionSettings(workspaceId, model); - - // Memoize vertical meter data calculation to prevent unnecessary re-renders - const verticalMeterData = React.useMemo(() => { - return lastUsage - ? calculateTokenMeterData(lastUsage, model ?? "unknown", use1M, true) - : { segments: [], totalTokens: 0, totalPercentage: 0 }; - }, [lastUsage, model, use1M]); - // Auto-hide sidebar on small screens using hysteresis to prevent oscillation // - Observe ChatArea width directly (independent of sidebar width) // - ChatArea has min-width and flex: 1 @@ -213,19 +192,6 @@ const RightSidebarComponent: React.FC = ({ // Between thresholds: maintain current state (no change) }, [chatAreaWidth, selectedTab, isHidden, setIsHidden, width]); - // Vertical meter only shows on Review tab (context usage indicator is now in ChatInput) - const autoCompactionProps = React.useMemo( - () => ({ - threshold: autoCompactThreshold, - setThreshold: setAutoCompactThreshold, - }), - [autoCompactThreshold, setAutoCompactThreshold] - ); - const verticalMeter = - selectedTab === "review" ? ( - - ) : null; - // Fully hide sidebar on small screens (context usage now shown in ChatInput) if (isHidden) { return null; @@ -251,11 +217,6 @@ const RightSidebarComponent: React.FC = ({ /> )} - {/* Render meter when Review tab is active */} - {selectedTab === "review" && ( -
{verticalMeter}
- )} -
= ({ data, autoCompaction, + autoCompactionThreshold, showTitle = true, testId, }) => { @@ -25,6 +32,10 @@ const ContextUsageBarComponent: React.FC = ({ const showWarning = !data.maxTokens; + // Show read-only indicator when threshold provided but no interactive config + const showReadOnlyIndicator = + autoCompactionThreshold !== undefined && !autoCompaction && data.maxTokens; + return (
@@ -43,6 +54,9 @@ const ContextUsageBarComponent: React.FC = ({
{autoCompaction && data.maxTokens && } + {showReadOnlyIndicator && ( + + )}
{showWarning && ( diff --git a/src/browser/components/RightSidebar/ThresholdSlider.tsx b/src/browser/components/RightSidebar/ThresholdSlider.tsx index ed466bde7..03df7d583 100644 --- a/src/browser/components/RightSidebar/ThresholdSlider.tsx +++ b/src/browser/components/RightSidebar/ThresholdSlider.tsx @@ -279,3 +279,56 @@ export const HorizontalThresholdSlider: React.FC<{ config: AutoCompactionConfig export const VerticalThresholdSlider: React.FC<{ config: AutoCompactionConfig }> = ({ config }) => ( ); + +// ----- Read-only indicator (for tooltips) ----- + +interface ThresholdIndicatorProps { + /** Threshold percentage (0-100). 100 means disabled. */ + threshold: number; +} + +/** + * A read-only visual indicator showing the auto-compaction threshold position. + * Shows the same notch markers as the interactive slider, but without drag functionality. + * Designed for use in tooltips where interaction isn't possible. + */ +export const HorizontalThresholdIndicator: React.FC = ({ threshold }) => { + const isEnabled = threshold < DISABLE_THRESHOLD; + const color = isEnabled ? "var(--color-plan-mode)" : "var(--color-muted)"; + + // Container covers the full bar area + const containerStyle: React.CSSProperties = { + position: "absolute", + top: 0, + bottom: 0, + left: 0, + right: 0, + zIndex: 50, + pointerEvents: "none", + }; + + // Indicator positioning + const indicatorStyle: React.CSSProperties = { + position: "absolute", + pointerEvents: "none", + display: "flex", + alignItems: "center", + left: `${threshold}%`, + top: "50%", + transform: "translate(-50%, -50%)", + flexDirection: "column", + }; + + // Line between triangles + const lineStyle: React.CSSProperties = { width: 1, height: 6, background: color }; + + return ( +
+
+ +
+ +
+
+ ); +};