({
maxHeight,
})
| 78 | } |
| 79 | |
| 80 | export const FreebuffModelSelector: React.FC<FreebuffModelSelectorProps> = ({ |
| 81 | maxHeight, |
| 82 | }) => { |
| 83 | const theme = useTheme() |
| 84 | // contentMaxWidth (not terminalWidth) is the real budget — the parent |
| 85 | // waiting-room screen wraps this picker in a `maxWidth: contentMaxWidth` |
| 86 | // box (capped at 80 cols), so a wide terminal doesn't actually let us |
| 87 | // sprawl the buttons across it. |
| 88 | const { contentMaxWidth } = useTerminalDimensions() |
| 89 | const selectedModel = useFreebuffModelStore((s) => s.selectedModel) |
| 90 | const setSelectedModel = useFreebuffModelStore((s) => s.setSelectedModel) |
| 91 | const session = useFreebuffSessionStore((s) => s.session) |
| 92 | const accessTier = |
| 93 | session && 'accessTier' in session ? session.accessTier : 'full' |
| 94 | const now = useNow(60_000) |
| 95 | const deploymentAvailabilityLabel = useMemo( |
| 96 | () => getFreebuffDeploymentAvailabilityLabel(new Date(now)), |
| 97 | [now], |
| 98 | ) |
| 99 | const [pending, setPending] = useState<string | null>(null) |
| 100 | const [hoveredId, setHoveredId] = useState<string | null>(null) |
| 101 | // Keyboard cursor — separate from the actually-selected model so that |
| 102 | // Tab/arrow navigation can preview without committing. Re-syncs to the |
| 103 | // selected model whenever the selection changes (after a successful switch |
| 104 | // or an external selectedModel update). |
| 105 | const [focusedId, setFocusedId] = useState<string>(selectedModel) |
| 106 | const availableModels = useMemo( |
| 107 | () => getFreebuffModelsForAccessTier(accessTier), |
| 108 | [accessTier], |
| 109 | ) |
| 110 | // Limited tier only ever surfaces one model, so a comparative tagline |
| 111 | // ("Most efficient") reads as filler. Hide it; the warning (data-collection) |
| 112 | // is the row's real content. |
| 113 | const showTagline = accessTier !== 'limited' |
| 114 | const availableModelIds = useMemo( |
| 115 | () => availableModels.map((m) => m.id), |
| 116 | [availableModels], |
| 117 | ) |
| 118 | const sections = useMemo(() => { |
| 119 | if (accessTier === 'limited') { |
| 120 | return [ |
| 121 | { |
| 122 | key: 'limited', |
| 123 | label: '', |
| 124 | models: availableModels, |
| 125 | }, |
| 126 | ] satisfies readonly Section[] |
| 127 | } |
| 128 | return ( |
| 129 | [ |
| 130 | { |
| 131 | key: 'premium', |
| 132 | label: 'PREMIUM', |
| 133 | models: availableModels.filter((m) => isFreebuffPremiumModelId(m.id)), |
| 134 | }, |
| 135 | { |
| 136 | key: 'unlimited', |
| 137 | label: 'UNLIMITED', |
nothing calls this directly
no test coverage detected