Skip to content

Commit b087fda

Browse files
committed
🤖 refactor: simplify ThresholdSlider tooltip and cleanup
- Horizontal slider uses native title attribute (simpler, no clipping) - Vertical slider uses dedicated VerticalSliderTooltip portal component - Removed unused orientation param from SliderTooltip - Fixed VerticalTokenMeter flex layout for proper segment display
1 parent f5afb5c commit b087fda

File tree

2 files changed

+72
-35
lines changed

2 files changed

+72
-35
lines changed

src/browser/components/RightSidebar/ThresholdSlider.tsx

Lines changed: 69 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import React, { useRef } from "react";
1+
import React, { useRef, useState } from "react";
2+
import { createPortal } from "react-dom";
23
import {
34
AUTO_COMPACTION_THRESHOLD_MIN,
45
AUTO_COMPACTION_THRESHOLD_MAX,
56
} from "@/common/constants/ui";
6-
import { TooltipWrapper, Tooltip } from "../Tooltip";
77

88
// ----- Types -----
99

@@ -69,14 +69,54 @@ const applyThreshold = (pct: number, setThreshold: (v: number) => void): void =>
6969
};
7070

7171
/** Get tooltip text based on threshold */
72-
const getTooltip = (threshold: number, orientation: "horizontal" | "vertical"): string => {
72+
const getTooltipText = (threshold: number, orientation: "horizontal" | "vertical"): string => {
7373
const isEnabled = threshold < DISABLE_THRESHOLD;
7474
const direction = orientation === "horizontal" ? "left" : "up";
7575
return isEnabled
7676
? `Auto-compact at ${threshold}% · Drag to adjust (per-model)`
7777
: `Auto-compact disabled · Drag ${direction} to enable (per-model)`;
7878
};
7979

80+
// ----- Portal Tooltip (vertical only) -----
81+
82+
interface VerticalSliderTooltipProps {
83+
text: string;
84+
anchorRect: DOMRect;
85+
threshold: number;
86+
}
87+
88+
/**
89+
* Portal-based tooltip for vertical slider only.
90+
* Renders to document.body to escape the narrow container's clipping.
91+
* Horizontal slider uses native `title` attribute instead (simpler, no clipping issues).
92+
*/
93+
const VerticalSliderTooltip: React.FC<VerticalSliderTooltipProps> = ({
94+
text,
95+
anchorRect,
96+
threshold,
97+
}) => {
98+
// Position to the left of the bar, aligned with threshold position
99+
const indicatorY = anchorRect.top + (anchorRect.height * threshold) / 100;
100+
101+
const style: React.CSSProperties = {
102+
position: "fixed",
103+
zIndex: 9999,
104+
background: "#2d2d30",
105+
color: "#cccccc",
106+
padding: "6px 10px",
107+
borderRadius: 4,
108+
fontSize: 12,
109+
whiteSpace: "nowrap",
110+
pointerEvents: "none",
111+
boxShadow: "0 2px 8px rgba(0,0,0,0.3)",
112+
right: window.innerWidth - anchorRect.left + 8,
113+
top: indicatorY,
114+
transform: "translateY(-50%)",
115+
};
116+
117+
return createPortal(<div style={style}>{text}</div>, document.body);
118+
};
119+
80120
// ----- Main component: ThresholdSlider -----
81121

82122
/**
@@ -99,6 +139,7 @@ const getTooltip = (threshold: number, orientation: "horizontal" | "vertical"):
99139
*/
100140
export const ThresholdSlider: React.FC<ThresholdSliderProps> = ({ config, orientation }) => {
101141
const containerRef = useRef<HTMLDivElement>(null);
142+
const [isHovered, setIsHovered] = useState(false);
102143
const isHorizontal = orientation === "horizontal";
103144

104145
const handleMouseDown = (e: React.MouseEvent) => {
@@ -131,7 +172,7 @@ export const ThresholdSlider: React.FC<ThresholdSliderProps> = ({ config, orient
131172

132173
const isEnabled = config.threshold < DISABLE_THRESHOLD;
133174
const color = isEnabled ? "var(--color-plan-mode)" : "var(--color-muted)";
134-
const title = getTooltip(config.threshold, orientation);
175+
const tooltipText = getTooltipText(config.threshold, orientation);
135176

136177
// Container styles
137178
const containerStyle: React.CSSProperties = {
@@ -170,38 +211,34 @@ export const ThresholdSlider: React.FC<ThresholdSliderProps> = ({ config, orient
170211
? { width: 1, height: 6, background: color }
171212
: { width: 6, height: 1, background: color };
172213

173-
// Indicator content (triangles + line)
174-
const indicatorContent = (
175-
<>
176-
<Triangle direction={isHorizontal ? "down" : "right"} color={color} />
177-
<div style={lineStyle} />
178-
<Triangle direction={isHorizontal ? "up" : "left"} color={color} />
179-
</>
180-
);
214+
// Get container rect for tooltip positioning (vertical only)
215+
const containerRect = containerRef.current?.getBoundingClientRect();
181216

182217
return (
183-
<div ref={containerRef} style={containerStyle} onMouseDown={handleMouseDown}>
218+
<div
219+
ref={containerRef}
220+
style={containerStyle}
221+
onMouseDown={handleMouseDown}
222+
onMouseEnter={() => setIsHovered(true)}
223+
onMouseLeave={() => setIsHovered(false)}
224+
// Horizontal uses native title (simpler, no clipping issues with wide tooltips)
225+
title={isHorizontal ? tooltipText : undefined}
226+
>
227+
{/* Visual indicator - pointer events disabled */}
184228
<div style={indicatorStyle}>
185-
{isHorizontal ? (
186-
// Horizontal: native title tooltip (works well positioned at cursor)
187-
<div
188-
title={title}
189-
style={{ display: "flex", flexDirection: "column", alignItems: "center" }}
190-
>
191-
{indicatorContent}
192-
</div>
193-
) : (
194-
// Vertical: portal-based Tooltip to avoid clipping by overflow:hidden containers
195-
<TooltipWrapper inline>
196-
<div style={{ display: "flex", flexDirection: "row", alignItems: "center" }}>
197-
{indicatorContent}
198-
</div>
199-
<Tooltip position="left">
200-
<div style={{ fontSize: 11 }}>{title}</div>
201-
</Tooltip>
202-
</TooltipWrapper>
203-
)}
229+
<Triangle direction={isHorizontal ? "down" : "right"} color={color} />
230+
<div style={lineStyle} />
231+
<Triangle direction={isHorizontal ? "up" : "left"} color={color} />
204232
</div>
233+
234+
{/* Portal tooltip for vertical only - escapes narrow container clipping */}
235+
{!isHorizontal && isHovered && containerRect && (
236+
<VerticalSliderTooltip
237+
text={tooltipText}
238+
anchorRect={containerRect}
239+
threshold={config.threshold}
240+
/>
241+
)}
205242
</div>
206243
);
207244
};

src/browser/components/RightSidebar/VerticalTokenMeter.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,16 +45,16 @@ const VerticalTokenMeterComponent: React.FC<VerticalTokenMeterProps> = ({
4545
className="flex min-h-[20px] w-full flex-col items-center"
4646
style={{ flex: usagePercentage }}
4747
>
48-
{/* [&>*] selector makes TooltipWrapper fill available space */}
49-
<div className="flex w-full flex-1 flex-col items-center [&>*]:flex [&>*]:flex-1 [&>*]:flex-col">
48+
{/* [&>*] selector makes TooltipWrapper span fill available space */}
49+
<div className="flex h-full w-full flex-col items-center [&>*]:flex [&>*]:h-full [&>*]:flex-col">
5050
<TooltipWrapper>
5151
<TokenMeter
5252
segments={data.segments}
5353
orientation="vertical"
5454
data-meter="token-bar"
5555
data-segment-count={data.segments.length}
5656
/>
57-
<Tooltip>
57+
<Tooltip position="left">
5858
<div className="font-primary flex flex-col gap-2 text-xs">
5959
<div className="text-foreground text-[13px] font-semibold">Last Request</div>
6060
<div className="border-border-light my-1 border-t" />

0 commit comments

Comments
 (0)