mirror of
https://github.com/stablyai/orca.git
synced 2026-11-15 10:27:33 +00:00
Show a Fast-only speed as the Fast mode switch on mobile (#40523)
On the phone, Codex's Standard/Fast speed was a Speed row opening a list while Claude's Fast mode was a switch. A Fast-only speed now uses the same switch row; more speeds, or a lone Flex, keep the list. The lone-Fast rule moves to shared code so desktop and mobile follow one rule.
This commit is contained in:
@@ -160,6 +160,31 @@ describe('MobileNativeChatSessionOptionPickers', () => {
|
||||
renderer = null
|
||||
})
|
||||
|
||||
it('toggles a Fast-only Codex speed on the same Fast mode switch as Claude', async () => {
|
||||
const speed: SessionOptionDescriptor = {
|
||||
id: 'serviceTier',
|
||||
label: 'Speed',
|
||||
kind: {
|
||||
type: 'select',
|
||||
currentValue: 'default',
|
||||
choices: [
|
||||
{ value: 'default', label: 'Standard' },
|
||||
{ value: 'priority', label: 'Fast' }
|
||||
]
|
||||
},
|
||||
valueSource: 'reported',
|
||||
transport: 'catalog',
|
||||
settable: true
|
||||
}
|
||||
mount([MODEL_DESCRIPTOR, speed], false, 'model')
|
||||
const toggle = findAllByHostType(renderer!.root, 'Switch').find(
|
||||
(node) => node.props.accessibilityLabel === 'Fast mode'
|
||||
)
|
||||
expect(toggle?.props.value).toBe(false)
|
||||
await act(async () => toggle!.props.onValueChange(true))
|
||||
expect(setOption).toHaveBeenCalledWith('serviceTier', 'priority')
|
||||
})
|
||||
|
||||
it('consumes a refused page without replay, then permits a fresh open and permission pick', async () => {
|
||||
const placeholder: SessionOptionDescriptor = {
|
||||
...MODEL_DESCRIPTOR,
|
||||
|
||||
@@ -36,7 +36,8 @@ import {
|
||||
descriptorRowElements,
|
||||
ModelPill,
|
||||
SessionOptionCaption,
|
||||
SessionOptionSummaryRow
|
||||
SessionOptionSummaryRow,
|
||||
showsSessionOptionAsSwitch
|
||||
} from './MobileNativeChatSessionOptionRows'
|
||||
import {
|
||||
SessionOptionSegments,
|
||||
@@ -383,7 +384,7 @@ function OptionRows({
|
||||
{descriptors.flatMap((descriptor) => {
|
||||
const isPermission = descriptor === permission
|
||||
const disabled = locked(descriptor) || !descriptor.settable
|
||||
if (descriptor.kind.type === 'boolean' && !descriptor.action) {
|
||||
if (showsSessionOptionAsSwitch(descriptor)) {
|
||||
return descriptorRowElements(
|
||||
{
|
||||
descriptor,
|
||||
|
||||
@@ -17,6 +17,10 @@ import {
|
||||
nativeChatSessionOptionActionLabel,
|
||||
nativeChatSessionOptionLabel
|
||||
} from '../../../../../src/shared/native-chat/input/native-chat-session-option-labels'
|
||||
import {
|
||||
loneFastTier,
|
||||
speedButtonTiers
|
||||
} from '../../../../../src/shared/native-chat/input/native-chat-session-speed-tiers'
|
||||
import { sayMobileNativeChatSessionOption as say } from './native-chat-session-option-copy'
|
||||
import { MobileSwitch } from '../../../components/MobileSwitch'
|
||||
|
||||
@@ -131,6 +135,20 @@ type DescriptorRowsProps = {
|
||||
moreChoices?: { shown: boolean; onToggle: () => void }
|
||||
}
|
||||
|
||||
/** A Fast-only speed, which toggles on one row like Claude's Fast mode. */
|
||||
function fastSpeedSwitch(descriptor: SessionOptionDescriptor) {
|
||||
const speed = speedButtonTiers(descriptor)
|
||||
const fast = speed ? loneFastTier(speed.tiers) : undefined
|
||||
return speed && fast ? { standard: speed.standard, fast } : null
|
||||
}
|
||||
|
||||
export function showsSessionOptionAsSwitch(descriptor: SessionOptionDescriptor): boolean {
|
||||
return (
|
||||
!descriptor.action &&
|
||||
(descriptor.kind.type === 'boolean' || fastSpeedSwitch(descriptor) !== null)
|
||||
)
|
||||
}
|
||||
|
||||
/** One descriptor's rows as an array, so a ListGroup can draw the separators between them. */
|
||||
export function descriptorRowElements(
|
||||
{
|
||||
@@ -156,8 +174,10 @@ export function descriptorRowElements(
|
||||
]
|
||||
}
|
||||
// An unknown baseline still renders a switch; the source marker prevents implying the value was confirmed.
|
||||
if (descriptor.kind.type === 'boolean') {
|
||||
const label = nativeChatSessionOptionLabel(descriptor, say)
|
||||
const fastSpeed = fastSpeedSwitch(descriptor)
|
||||
if (descriptor.kind.type === 'boolean' || fastSpeed) {
|
||||
const { kind } = descriptor
|
||||
const label = fastSpeed ? say('fastMode') : nativeChatSessionOptionLabel(descriptor, say)
|
||||
const marker = sessionOptionValueMarker(descriptor)
|
||||
return [
|
||||
<ListRow
|
||||
@@ -173,8 +193,16 @@ export function descriptorRowElements(
|
||||
accessory={
|
||||
<MobileSwitch
|
||||
accessibilityLabel={label}
|
||||
value={descriptor.kind.currentValue}
|
||||
onValueChange={(next) => onSetOption(next)}
|
||||
value={
|
||||
kind.type === 'boolean'
|
||||
? kind.currentValue
|
||||
: kind.currentValue === fastSpeed?.fast.value
|
||||
}
|
||||
onValueChange={(next) =>
|
||||
onSetOption(
|
||||
fastSpeed ? (next ? fastSpeed.fast.value : fastSpeed.standard.value) : next
|
||||
)
|
||||
}
|
||||
disabled={locked}
|
||||
/>
|
||||
}
|
||||
|
||||
+2
-1
@@ -22,7 +22,8 @@ import {
|
||||
} from '../../../../../../../shared/native-chat/input/native-chat-session-option-labels'
|
||||
import { sayNativeChatSessionOptionTranslated as say } from './native-chat-session-option-labels-text'
|
||||
import { NativeChatSessionOptionWriteGroup } from './NativeChatSessionOptionWriteGroup'
|
||||
import { NativeChatSessionSpeedButton, speedButtonTiers } from './NativeChatSessionSpeedButton'
|
||||
import { NativeChatSessionSpeedButton } from './NativeChatSessionSpeedButton'
|
||||
import { speedButtonTiers } from '../../../../../../../shared/native-chat/input/native-chat-session-speed-tiers'
|
||||
|
||||
type NativeChatSessionOptionRowsProps = {
|
||||
/** The current model's options, in display order. */
|
||||
|
||||
+3
-17
@@ -8,28 +8,14 @@ import {
|
||||
DropdownMenuTrigger
|
||||
} from '@/components/ui/dropdown-menu'
|
||||
import type {
|
||||
SessionOptionDescriptor,
|
||||
SessionOptionSelectChoice,
|
||||
SessionOptionValue
|
||||
} from '../../../../../../../shared/native-chat/input/native-chat-session-options'
|
||||
import { nativeChatSessionChoiceLabel } from '../../../../../../../shared/native-chat/input/native-chat-session-option-labels'
|
||||
import { isFastServiceTier } from '../../../../../../../shared/agent-session/options/structured-agent-session-discovered-model'
|
||||
import { loneFastTier } from '../../../../../../../shared/native-chat/input/native-chat-session-speed-tiers'
|
||||
import { sayNativeChatSessionOptionTranslated as say } from './native-chat-session-option-labels-text'
|
||||
|
||||
const STANDARD_TIER = 'default'
|
||||
|
||||
/** Standard plus the other tiers, or null when the select is not a speed choice with any. */
|
||||
export function speedButtonTiers(
|
||||
descriptor: SessionOptionDescriptor
|
||||
): { standard: SessionOptionSelectChoice; tiers: SessionOptionSelectChoice[] } | null {
|
||||
if (descriptor.id !== 'serviceTier' || descriptor.kind.type !== 'select') {
|
||||
return null
|
||||
}
|
||||
const standard = descriptor.kind.choices.find((choice) => choice.value === STANDARD_TIER)
|
||||
const tiers = descriptor.kind.choices.filter((choice) => choice.value !== STANDARD_TIER)
|
||||
return standard && tiers.length > 0 ? { standard, tiers } : null
|
||||
}
|
||||
|
||||
/** A lone Fast tier toggles like Claude's Fast; anything else opens a list instead of a segmented switcher. */
|
||||
export function NativeChatSessionSpeedButton(props: {
|
||||
label: string
|
||||
@@ -46,8 +32,8 @@ export function NativeChatSessionSpeedButton(props: {
|
||||
const nameOf = (choice: SessionOptionSelectChoice) =>
|
||||
nativeChatSessionChoiceLabel(choice, say, 'serviceTier')
|
||||
// Only Fast takes the toggle and bolt; Flex and other tiers are picked from the list.
|
||||
if (tiers.length === 1 && isFastServiceTier(tiers[0])) {
|
||||
const only = tiers[0]
|
||||
const only = loneFastTier(tiers)
|
||||
if (only) {
|
||||
return (
|
||||
<Toggle
|
||||
variant="chip"
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import type {
|
||||
SessionOptionDescriptor,
|
||||
SessionOptionSelectChoice
|
||||
} from './native-chat-session-options'
|
||||
import { isFastServiceTier } from '../../agent-session/options/structured-agent-session-discovered-model'
|
||||
|
||||
const STANDARD_TIER = 'default'
|
||||
|
||||
export type SessionSpeedTiers = {
|
||||
standard: SessionOptionSelectChoice
|
||||
tiers: SessionOptionSelectChoice[]
|
||||
}
|
||||
|
||||
/** Standard plus the other tiers, or null when the select is not a speed choice with any. */
|
||||
export function speedButtonTiers(descriptor: SessionOptionDescriptor): SessionSpeedTiers | null {
|
||||
if (descriptor.id !== 'serviceTier' || descriptor.kind.type !== 'select') {
|
||||
return null
|
||||
}
|
||||
const standard = descriptor.kind.choices.find((choice) => choice.value === STANDARD_TIER)
|
||||
const tiers = descriptor.kind.choices.filter((choice) => choice.value !== STANDARD_TIER)
|
||||
return standard && tiers.length > 0 ? { standard, tiers } : null
|
||||
}
|
||||
|
||||
/** A lone Fast tier toggles like Claude's Fast; Flex and several tiers are picked from a list. */
|
||||
export function loneFastTier(
|
||||
tiers: readonly SessionOptionSelectChoice[]
|
||||
): SessionOptionSelectChoice | undefined {
|
||||
return tiers.length === 1 && isFastServiceTier(tiers[0]) ? tiers[0] : undefined
|
||||
}
|
||||
Reference in New Issue
Block a user