feat(AI): thinking toggle as NOMAD Switch + info tooltip

Address review feedback on the per-model thinking control:
- Use the shared Switch component (matches AI Assistant settings) instead
  of a raw checkbox.
- Label "Thinking:" with a colon to match the adjacent "Model:" label.
- Add an InfoTooltip explaining what thinking does and where the default
  lives. Extend InfoTooltip with optional `position` ('top'|'bottom') and
  `align` ('center'|'right') so it opens downward and expands leftward from
  the right-edge header slot instead of being clipped/crushed against the
  viewport edge. Defaults preserve existing (benchmark page) behavior.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Chris Sherwood 2026-07-10 23:36:15 -07:00
parent d64cadafc2
commit 25fcf55179
2 changed files with 44 additions and 15 deletions

View File

@ -4,9 +4,21 @@ import { useState } from 'react'
interface InfoTooltipProps {
text: string
className?: string
// Which side of the icon the tooltip pops toward. Defaults to 'top' (existing behavior);
// use 'bottom' when the icon sits near the top of the viewport so it isn't clipped.
position?: 'top' | 'bottom'
// Horizontal anchoring. 'center' (default) centers the bubble on the icon. Use 'right' when
// the icon sits near the right edge so the bubble expands leftward into open space instead of
// being squeezed against the viewport edge (which forces one-word-per-line wrapping).
align?: 'center' | 'right'
}
export default function InfoTooltip({ text, className = '' }: InfoTooltipProps) {
export default function InfoTooltip({
text,
className = '',
position = 'top',
align = 'center',
}: InfoTooltipProps) {
const [isVisible, setIsVisible] = useState(false)
return (
@ -23,10 +35,24 @@ export default function InfoTooltip({ text, className = '' }: InfoTooltipProps)
<IconInfoCircle className="w-4 h-4" />
</button>
{isVisible && (
<div className="absolute bottom-full left-1/2 -translate-x-1/2 mb-2 z-50">
<div className="bg-desert-stone-dark text-white text-xs rounded-lg px-3 py-2 max-w-xs whitespace-normal shadow-lg">
<div
className={`absolute z-50 ${position === 'bottom' ? 'top-full mt-2' : 'bottom-full mb-2'} ${
align === 'right' ? 'right-0' : 'left-1/2 -translate-x-1/2'
}`}
>
<div
className={`bg-desert-stone-dark text-white text-xs rounded-lg px-3 py-2 whitespace-normal shadow-lg ${
align === 'right' ? 'w-64' : 'max-w-xs'
}`}
>
{text}
<div className="absolute top-full left-1/2 -translate-x-1/2 border-4 border-transparent border-t-desert-stone-dark" />
<div
className={`absolute border-4 border-transparent ${
position === 'bottom'
? 'bottom-full border-b-desert-stone-dark'
: 'top-full border-t-desert-stone-dark'
} ${align === 'right' ? 'right-3' : 'left-1/2 -translate-x-1/2'}`}
/>
</div>
</div>
)}

View File

@ -12,6 +12,8 @@ import classNames from '~/lib/classNames'
import { IconX } from '@tabler/icons-react'
import { DEFAULT_QUERY_REWRITE_MODEL } from '../../../constants/ollama'
import { useSystemSetting } from '~/hooks/useSystemSetting'
import Switch from '~/components/inputs/Switch'
import InfoTooltip from '~/components/InfoTooltip'
interface ChatProps {
enabled: boolean
@ -534,18 +536,19 @@ export default function Chat({
)}
</div>
{selectedModelSupportsThinking && (
<label
className="flex items-center gap-1.5 text-sm text-text-secondary cursor-pointer select-none"
title="When on, this model reasons before answering. Remembered for this model."
>
<input
type="checkbox"
checked={effectiveThinking(selectedModel)}
onChange={(e) => setModelThinking(selectedModel, e.target.checked)}
className="h-4 w-4 rounded border-border-default text-desert-green focus:ring-desert-green"
<div className="flex items-center">
<span className="text-sm text-text-secondary select-none">Thinking:</span>
<InfoTooltip
position="bottom"
align="right"
text="When on, this model works through its reasoning before answering. Slower, but often better on tricky questions. Your choice is remembered for this model; the default for other models is set in AI Assistant settings."
/>
Thinking
</label>
<Switch
id="chat-thinking-toggle"
checked={effectiveThinking(selectedModel)}
onChange={(v) => setModelThinking(selectedModel, v)}
/>
</div>
)}
{isInModal && (
<button