fix(ui): show disabled codex model tiles

This commit is contained in:
777genius 2026-05-17 01:13:51 +03:00
parent c1a2ccc0a7
commit e185f1f686
2 changed files with 96 additions and 2 deletions

View file

@ -306,6 +306,32 @@ function getVisibleRuntimeModels(
);
}
function withSupplementalDisabledRuntimeModels(
providerId: SupportedProviderId,
models: readonly string[],
providerStatus?: TeamModelRuntimeProviderStatus | null
): string[] {
if (providerId !== 'codex') {
return [...models];
}
const modelSet = new Set(models);
const supplementalDisabledModels = getTeamProviderModelOptions(providerId)
.map((option) => option.value.trim())
.filter(
(model) =>
model.length > 0 &&
!modelSet.has(model) &&
getTeamModelUiDisabledReason(providerId, model, providerStatus) !== null
);
return sortTeamProviderModels(
providerId,
[...models, ...supplementalDisabledModels],
providerStatus
);
}
function getModelAvailabilityMap(
providerStatus?: TeamModelRuntimeProviderStatus | null
): Map<string, CliProviderModelAvailability> {
@ -416,7 +442,11 @@ export function getAvailableTeamProviderModelOptions(
return getFallbackTeamProviderModelOptions(providerId, providerStatus);
}
const visibleModels = getRuntimeSelectorModels(providerId, providerStatus);
const visibleModels = withSupplementalDisabledRuntimeModels(
providerId,
getRuntimeSelectorModels(providerId, providerStatus),
providerStatus
);
return [
{ value: '', label: 'Default', badgeLabel: 'Default' },
...visibleModels.map((model) => {

View file

@ -218,7 +218,11 @@ describe('TeamModelSelector disabled Codex models', () => {
expect(onValueChange).toHaveBeenCalledWith('');
expect(host.textContent).toContain('5.4');
expect(host.textContent).toContain('5.3 Codex');
expect(host.textContent).not.toContain('5.2 Codex');
const disabledCodexButton = Array.from(host.querySelectorAll('button')).find((button) =>
button.textContent?.includes('5.2 Codex')
);
expect(disabledCodexButton).not.toBeNull();
expect(disabledCodexButton?.getAttribute('aria-disabled')).toBe('true');
await act(async () => {
root.unmount();
@ -703,6 +707,66 @@ describe('TeamModelSelector disabled Codex models', () => {
});
});
it('keeps known disabled Codex tiles visible when the runtime omits them', async () => {
vi.stubGlobal('IS_REACT_ACT_ENVIRONMENT', true);
storeState.cliStatus = {
providers: [
{
providerId: 'codex',
models: ['gpt-5.5', 'gpt-5.4', 'gpt-5.4-mini', 'gpt-5.3-codex', 'gpt-5.2'],
modelVerificationState: 'idle',
modelAvailability: [],
},
],
};
const host = document.createElement('div');
document.body.appendChild(host);
const root = createRoot(host);
const onValueChange = vi.fn();
await act(async () => {
root.render(
React.createElement(TeamModelSelector, {
providerId: 'codex',
onProviderChange: () => undefined,
value: '',
onValueChange,
})
);
await Promise.resolve();
});
const disabledButtons = ['5.3 Codex Spark', '5.2 Codex', '5.1 Codex Mini'].map((label) => {
const button = Array.from(host.querySelectorAll('button')).find((candidate) =>
candidate.textContent?.includes(label)
);
expect(button, `${label} should stay visible as a disabled option`).not.toBeNull();
expect(button?.getAttribute('aria-disabled')).toBe('true');
expect(button?.textContent).toContain('Disabled');
expect(button?.getAttribute('title')).toContain('Temporarily disabled for team agents');
return button;
});
const activeButton = Array.from(host.querySelectorAll('button')).find((button) =>
button.textContent?.includes('5.2')
);
expect(activeButton?.textContent).toContain('Recommended');
expect(activeButton?.getAttribute('aria-disabled')).toBe('false');
await act(async () => {
disabledButtons[0]?.dispatchEvent(new MouseEvent('click', { bubbles: true }));
await Promise.resolve();
});
expect(onValueChange).not.toHaveBeenCalled();
await act(async () => {
root.unmount();
await Promise.resolve();
});
});
it('keeps 5.1 Codex Max selectable on the native Codex path', async () => {
vi.stubGlobal('IS_REACT_ACT_ENVIRONMENT', true);
storeState.cliStatus = {