Skip to content

Commit b66174d

Browse files
committed
Stack model buttons vertically if screen is not wide
1 parent b336001 commit b66174d

1 file changed

Lines changed: 33 additions & 9 deletions

File tree

cli/src/components/freebuff-model-selector.tsx

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { FREEBUFF_MODELS } from '@codebuff/common/constants/freebuff-models'
88
import { switchFreebuffModel } from '../hooks/use-freebuff-session'
99
import { useFreebuffModelStore } from '../state/freebuff-model-store'
1010
import { useFreebuffSessionStore } from '../state/freebuff-session-store'
11+
import { useTerminalDimensions } from '../hooks/use-terminal-dimensions'
1112
import { useTheme } from '../hooks/use-theme'
1213

1314
import type { KeyEvent } from '@opentui/core'
@@ -27,6 +28,7 @@ import type { KeyEvent } from '@opentui/core'
2728
*/
2829
export const FreebuffModelSelector: React.FC = () => {
2930
const theme = useTheme()
31+
const { terminalWidth } = useTerminalDimensions()
3032
const selectedModel = useFreebuffModelStore((s) => s.selectedModel)
3133
const session = useFreebuffSessionStore((s) => s.session)
3234
const [pending, setPending] = useState<string | null>(null)
@@ -64,6 +66,27 @@ export const FreebuffModelSelector: React.FC = () => {
6466
[],
6567
)
6668

69+
// Decide row vs column layout based on whether both buttons actually fit
70+
// side-by-side. Each button's inner text is "● {displayName} · {tagline} {hint}",
71+
// plus 2 cols of border and 2 cols of padding. Buttons are separated by a
72+
// gap of 2. If the total exceeds the terminal width, stack vertically.
73+
const stackVertically = useMemo(() => {
74+
const BUTTON_CHROME = 4 // 2 border + 2 padding
75+
const GAP = 2
76+
const total = FREEBUFF_MODELS.reduce((sum, model, idx) => {
77+
const inner =
78+
2 /* indicator + space */ +
79+
model.displayName.length +
80+
3 /* " · " */ +
81+
model.tagline.length +
82+
2 /* " " */ +
83+
hintWidth
84+
return sum + inner + BUTTON_CHROME + (idx > 0 ? GAP : 0)
85+
}, 0)
86+
// Leave a small margin for the surrounding padding on the waiting-room screen.
87+
return total > terminalWidth - 4
88+
}, [hintWidth, terminalWidth])
89+
6790
const pick = useCallback(
6891
(modelId: string) => {
6992
if (pending) return
@@ -74,18 +97,18 @@ export const FreebuffModelSelector: React.FC = () => {
7497
[pending, selectedModel],
7598
)
7699

77-
// Tab / Shift+Tab and Left/Right arrow keys move the focus highlight only;
78-
// Enter or Space commits the switch. Two-step navigation prevents the user
79-
// from accidentally giving up their place in line by tabbing past their
80-
// queue. Up/Down intentionally do nothing so they don't fight other
81-
// vertical UI.
100+
// Tab / Shift+Tab and arrow keys move the focus highlight only; Enter or
101+
// Space commits the switch. Two-step navigation prevents the user from
102+
// accidentally giving up their place in line by tabbing past their queue.
82103
useKeyboard(
83104
useCallback(
84105
(key: KeyEvent) => {
85106
if (pending) return
86107
const name = key.name ?? ''
87-
const isForward = name === 'right' || (name === 'tab' && !key.shift)
88-
const isBackward = name === 'left' || (name === 'tab' && key.shift)
108+
const isForward =
109+
name === 'right' || name === 'down' || (name === 'tab' && !key.shift)
110+
const isBackward =
111+
name === 'left' || name === 'up' || (name === 'tab' && key.shift)
89112
const isCommit = name === 'return' || name === 'enter' || name === 'space'
90113
if (!isForward && !isBackward && !isCommit) return
91114
if (isCommit) {
@@ -121,8 +144,9 @@ export const FreebuffModelSelector: React.FC = () => {
121144
>
122145
<box
123146
style={{
124-
flexDirection: 'row',
125-
gap: 2,
147+
flexDirection: stackVertically ? 'column' : 'row',
148+
gap: stackVertically ? 0 : 2,
149+
alignItems: 'flex-start',
126150
}}
127151
>
128152
{FREEBUFF_MODELS.map((model) => {

0 commit comments

Comments
 (0)