From ce04be6b89aac89ac11011014b4da5465afafdfa Mon Sep 17 00:00:00 2001 From: Menci Date: Fri, 13 Nov 2026 17:16:16 +0800 Subject: [PATCH] fix(web): keep disabled-model editing equally available in both editor surfaces The provider-owned-catalog kinds (Copilot, Codex, Claude Code) could not toggle a listed model from the model-list switch: the switch folded in `readOnly`, which for those kinds is always true because the upstream owns the catalog and the rows cannot be added, removed or restructured. The disabled-models combobox in the sidebar moved the same field for the same editor, so the two disagreed. The mirror-image case was broken too: the combobox was gated on Full connection editability, so a models-level editor could flip the switch but not the combobox. Both controls now read one predicate, `modelsEditable`, which expresses the actual requirement -- an editor who may change the catalog may set which listed models are enabled. Structural controls keep the shape-based restriction, so catalog-owned rows stay unaddable and uneditable. The regression test mounts both surfaces in one form and asserts they agree for Full, models-level and audit-only editors. It replaces the two workspace-interactions cases, whose assertions only covered `disabled` and missed the `readOnly` state this defect lived in. --- .../disabled-model-parity_test.tsx | 137 ++++++++++++++++++ .../workspace-interactions_test.tsx | 22 --- .../upstream-editor/config-sidebar.tsx | 10 +- .../src/components/upstream-editor/data.ts | 13 +- .../components/upstream-editor/workspace.tsx | 34 +++-- 5 files changed, 172 insertions(+), 44 deletions(-) create mode 100644 apps/web/__tests__/components/upstream-editor/disabled-model-parity_test.tsx diff --git a/apps/web/__tests__/components/upstream-editor/disabled-model-parity_test.tsx b/apps/web/__tests__/components/upstream-editor/disabled-model-parity_test.tsx new file mode 100644 index 0000000000..febdbd894c --- /dev/null +++ b/apps/web/__tests__/components/upstream-editor/disabled-model-parity_test.tsx @@ -0,0 +1,137 @@ +import { fireEvent, screen } from '@testing-library/react'; +import { forwardRef, useState } from 'react'; +import type { PropsWithChildren } from 'react'; +import { FormProvider, useForm } from 'react-hook-form'; +import { MemoryRouter } from 'react-router'; +import { describe, expect, it, vi } from 'vitest'; + +import type { UpstreamRecord } from '../../../src/api/types'; +import { UpstreamConfigSidebar } from '../../../src/components/upstream-editor/config-sidebar'; +import type { UpstreamEditLevel, UpstreamEditorValues } from '../../../src/components/upstream-editor/data'; +import { valuesFromRecord } from '../../../src/components/upstream-editor/data'; +import { UpstreamWorkspace, type ModelsYamlDraft } from '../../../src/components/upstream-editor/workspace'; +import { i18n } from '../../../src/i18n'; +import { upstreamRecord } from '../../api/upstream-fixture'; +import { renderInApp } from '../../render'; +import type { UpstreamModelConfig } from '@floway-dev/provider/browser'; + +vi.mock('../../../src/components/upstream-editor/models-yaml-editor', () => ({ + default: ({ onChange, value }: { onChange: (value: string) => void; value: string }) => ( +