diff --git a/webui/src/components/settings/SettingsView.tsx b/webui/src/components/settings/SettingsView.tsx index c95cc9b2d..237e310b9 100644 --- a/webui/src/components/settings/SettingsView.tsx +++ b/webui/src/components/settings/SettingsView.tsx @@ -3375,9 +3375,10 @@ function ModelsSettings({ ); const callOrderBusy = orderSaving || saving; const selectPreset = (preset: SettingsPayload["model_presets"][number]) => { + const toggleCurrentPreset = !creating && selectedPreset?.name === preset.name; onSelectConfiguration(); - if (!creating && selectedPreset?.name === preset.name) { - setEditorOpen(true); + if (toggleCurrentPreset) { + setEditorOpen((open) => !open); return; } setForm((prev) => ({ @@ -3393,11 +3394,6 @@ function ModelsSettings({ })); setEditorOpen(true); }; - const closeEditor = () => { - if (creatingSaving) return; - if (creating) onCancelCreate(); - setEditorOpen(false); - }; const moveCallOrderItem = (index: number, offset: -1 | 1) => { if (callOrderBusy) return; @@ -3436,6 +3432,177 @@ function ModelsSettings({ onChangeCallOrder(next); }; + const renderPresetEditor = (key: string) => ( +
+ {creating ? ( +
+ + {tx("settings.models.newPreset", "New model preset")} + +
+ ) : null} + + + setForm((prev) => ({ ...prev, presetLabel: event.target.value })) + } + className="h-8 w-[min(280px,70vw)] rounded-full text-[13px]" + /> + + + + setForm((prev) => ({ + ...prev, + provider, + model: provider === prev.provider ? prev.model : "", + })) + } + /> + + {selectedProviderNeedsSignIn ? ( + + + + ) : null} + + setForm((prev) => ({ ...prev, model }))} + /> + + + {advancedOpen ? ( +
+ setForm((prev) => ({ ...prev, ...value }))} + /> +
+ ) : null} +
+ {creating ? ( + + ) : selectedPreset ? ( + + ) : null} +
+ +
+
+
+ ); + return (
@@ -3477,13 +3644,7 @@ function ModelsSettings({
) : ( -
-
+ <>
{presetRows.map(({ key, name, orderIndex, preset }) => { const ordered = orderIndex >= 0; @@ -3507,7 +3668,7 @@ function ModelsSettings({ draggedCallOrderIndex < orderIndex; const isSelected = editorOpen && !creating && selectedPreset?.name === name; - return ( + return [
preset && selectPreset(preset)} className="flex min-w-0 flex-1 items-center gap-3 rounded-[12px] text-left outline-none focus-visible:ring-2 focus-visible:ring-ring" @@ -3640,8 +3802,8 @@ function ModelsSettings({ @@ -3676,8 +3838,9 @@ function ModelsSettings({ aria-hidden /> -
- ); +
, + isSelected ? renderPresetEditor(`editor:${key}`) : null, + ]; })}
@@ -3707,217 +3870,8 @@ function ModelsSettings({ ) : null}
-
-
- {editorOpen && (selectedPreset || creating) ? ( - <> -
- - - - {creating - ? tx("settings.models.newPreset", "New model preset") - : selectedPreset?.label} - - - {creating - ? tx( - "settings.models.newPresetHelp", - "Save a reusable model and its generation settings.", - ) - : tx("settings.models.editPreset", "Edit preset")} - - -
- - - setForm((prev) => ({ ...prev, presetLabel: event.target.value })) - } - className="h-8 w-[min(280px,70vw)] rounded-full text-[13px]" - /> - - - - setForm((prev) => ({ - ...prev, - provider, - model: provider === prev.provider ? prev.model : "", - })) - } - /> - - {selectedProviderNeedsSignIn ? ( - - - - ) : null} - - setForm((prev) => ({ ...prev, model }))} - /> - - - {advancedOpen ? ( -
- setForm((prev) => ({ ...prev, ...value }))} - /> -
- ) : null} -
- {creating ? ( - - ) : selectedPreset ? ( - - ) : null} -
- -
-
- - ) : ( -
-
- - - -

- {tx("settings.models.selectPreset", "Select a preset")} -

-

- {tx( - "settings.models.selectPresetHelp", - "Select a preset from the list to edit its model and generation settings.", - )} -

-
-
- )} -
- + {creating && editorOpen ? renderPresetEditor("editor:new") : null} + )} diff --git a/webui/src/i18n/locales/en/common.json b/webui/src/i18n/locales/en/common.json index 817117543..9d47eb82b 100644 --- a/webui/src/i18n/locales/en/common.json +++ b/webui/src/i18n/locales/en/common.json @@ -132,9 +132,6 @@ "newPresetHelp": "Save a reusable model and its generation settings.", "presets": "Model presets", "editPreset": "Edit preset", - "backToPresets": "Back to model presets", - "selectPreset": "Select a preset", - "selectPresetHelp": "Select a preset from the list to edit its model and generation settings.", "presetName": "Preset name", "presetNameHelp": "A short name used throughout model settings.", "presetNamePlaceholder": "Fast writing", diff --git a/webui/src/i18n/locales/es/common.json b/webui/src/i18n/locales/es/common.json index bed369d16..b0a15b096 100644 --- a/webui/src/i18n/locales/es/common.json +++ b/webui/src/i18n/locales/es/common.json @@ -408,9 +408,6 @@ "newPresetHelp": "Guarda un modelo reutilizable y sus ajustes de generación.", "presets": "Preajustes de modelo", "editPreset": "Editar preajuste", - "backToPresets": "Volver a los preajustes de modelo", - "selectPreset": "Seleccionar un preajuste", - "selectPresetHelp": "Selecciona un preajuste de la lista para editar su modelo y los ajustes de generación.", "presetName": "Nombre del preajuste", "presetNameHelp": "Un nombre corto para usar en los ajustes de modelos.", "presetNamePlaceholder": "Escritura rápida", diff --git a/webui/src/i18n/locales/fr/common.json b/webui/src/i18n/locales/fr/common.json index c470e4561..45efeb992 100644 --- a/webui/src/i18n/locales/fr/common.json +++ b/webui/src/i18n/locales/fr/common.json @@ -408,9 +408,6 @@ "newPresetHelp": "Enregistrez un modèle réutilisable et ses paramètres de génération.", "presets": "Préréglages de modèle", "editPreset": "Modifier le préréglage", - "backToPresets": "Retour aux préréglages de modèle", - "selectPreset": "Sélectionner un préréglage", - "selectPresetHelp": "Sélectionnez un préréglage dans la liste pour modifier son modèle et ses paramètres de génération.", "presetName": "Nom du préréglage", "presetNameHelp": "Un nom court utilisé dans les paramètres des modèles.", "presetNamePlaceholder": "Rédaction rapide", diff --git a/webui/src/i18n/locales/id/common.json b/webui/src/i18n/locales/id/common.json index 09d20cb2d..b420bae1f 100644 --- a/webui/src/i18n/locales/id/common.json +++ b/webui/src/i18n/locales/id/common.json @@ -408,9 +408,6 @@ "newPresetHelp": "Simpan model yang dapat digunakan kembali beserta pengaturan generasinya.", "presets": "Prasetel model", "editPreset": "Ubah prasetel", - "backToPresets": "Kembali ke prasetel model", - "selectPreset": "Pilih prasetel", - "selectPresetHelp": "Pilih prasetel dari daftar untuk mengubah model dan pengaturan generasinya.", "presetName": "Nama prasetel", "presetNameHelp": "Nama singkat yang digunakan di pengaturan model.", "presetNamePlaceholder": "Menulis cepat", diff --git a/webui/src/i18n/locales/ja/common.json b/webui/src/i18n/locales/ja/common.json index 6e13d2261..332a16e0b 100644 --- a/webui/src/i18n/locales/ja/common.json +++ b/webui/src/i18n/locales/ja/common.json @@ -408,9 +408,6 @@ "newPresetHelp": "再利用できるモデルと生成設定を保存します。", "presets": "モデルプリセット", "editPreset": "プリセットを編集", - "backToPresets": "モデルプリセットに戻る", - "selectPreset": "プリセットを選択", - "selectPresetHelp": "リストからプリセットを選択して、モデルと生成設定を編集します。", "presetName": "プリセット名", "presetNameHelp": "モデル設定で使用する短い名前です。", "presetNamePlaceholder": "高速執筆", diff --git a/webui/src/i18n/locales/ko/common.json b/webui/src/i18n/locales/ko/common.json index 068810d7e..361449be6 100644 --- a/webui/src/i18n/locales/ko/common.json +++ b/webui/src/i18n/locales/ko/common.json @@ -408,9 +408,6 @@ "newPresetHelp": "재사용할 모델과 생성 설정을 저장합니다.", "presets": "모델 프리셋", "editPreset": "프리셋 편집", - "backToPresets": "모델 프리셋으로 돌아가기", - "selectPreset": "프리셋 선택", - "selectPresetHelp": "목록에서 프리셋을 선택하여 모델과 생성 설정을 편집하세요.", "presetName": "프리셋 이름", "presetNameHelp": "모델 설정에서 사용할 짧은 이름입니다.", "presetNamePlaceholder": "빠른 작성", diff --git a/webui/src/i18n/locales/pt-BR/common.json b/webui/src/i18n/locales/pt-BR/common.json index 05c8495dd..7f499f650 100644 --- a/webui/src/i18n/locales/pt-BR/common.json +++ b/webui/src/i18n/locales/pt-BR/common.json @@ -132,9 +132,6 @@ "newPresetHelp": "Salve um modelo reutilizável e suas configurações de geração.", "presets": "Predefinições de modelo", "editPreset": "Editar predefinição", - "backToPresets": "Voltar às predefinições de modelo", - "selectPreset": "Selecionar uma predefinição", - "selectPresetHelp": "Selecione uma predefinição na lista para editar o modelo e as configurações de geração.", "presetName": "Nome da predefinição", "presetNameHelp": "Um nome curto usado nas configurações de modelo.", "presetNamePlaceholder": "Escrita rápida", diff --git a/webui/src/i18n/locales/vi/common.json b/webui/src/i18n/locales/vi/common.json index 9cada0a46..7586d6aab 100644 --- a/webui/src/i18n/locales/vi/common.json +++ b/webui/src/i18n/locales/vi/common.json @@ -408,9 +408,6 @@ "newPresetHelp": "Lưu mô hình có thể tái sử dụng cùng các thiết lập sinh nội dung.", "presets": "Cấu hình mô hình đặt trước", "editPreset": "Sửa cấu hình đặt trước", - "backToPresets": "Quay lại cấu hình mô hình đặt trước", - "selectPreset": "Chọn một cấu hình đặt trước", - "selectPresetHelp": "Chọn một cấu hình đặt trước trong danh sách để chỉnh sửa mô hình và cài đặt sinh nội dung.", "presetName": "Tên cấu hình đặt trước", "presetNameHelp": "Tên ngắn dùng trong phần thiết lập mô hình.", "presetNamePlaceholder": "Viết nhanh", diff --git a/webui/src/i18n/locales/zh-CN/common.json b/webui/src/i18n/locales/zh-CN/common.json index 9e6200a4e..4290de08a 100644 --- a/webui/src/i18n/locales/zh-CN/common.json +++ b/webui/src/i18n/locales/zh-CN/common.json @@ -132,9 +132,6 @@ "newPresetHelp": "保存可复用的模型及其生成参数。", "presets": "模型预设", "editPreset": "编辑预设", - "backToPresets": "返回模型预设", - "selectPreset": "选择一个预设", - "selectPresetHelp": "从列表中选择一个预设,编辑它的模型和生成参数。", "presetName": "预设名称", "presetNameHelp": "在模型设置中使用的简短名称。", "presetNamePlaceholder": "快速写作", diff --git a/webui/src/i18n/locales/zh-TW/common.json b/webui/src/i18n/locales/zh-TW/common.json index 6a7e163eb..593c79c9f 100644 --- a/webui/src/i18n/locales/zh-TW/common.json +++ b/webui/src/i18n/locales/zh-TW/common.json @@ -408,9 +408,6 @@ "newPresetHelp": "儲存可重複使用的模型與生成參數。", "presets": "模型預設", "editPreset": "編輯預設", - "backToPresets": "返回模型預設", - "selectPreset": "選擇一個預設", - "selectPresetHelp": "從列表中選擇一個預設,編輯它的模型與生成參數。", "presetName": "預設名稱", "presetNameHelp": "模型設定中使用的簡短名稱。", "presetNamePlaceholder": "快速寫作", diff --git a/webui/src/tests/settings-view.test.tsx b/webui/src/tests/settings-view.test.tsx index 67908d160..aa2156265 100644 --- a/webui/src/tests/settings-view.test.tsx +++ b/webui/src/tests/settings-view.test.tsx @@ -2137,7 +2137,7 @@ describe("SettingsView Apps catalog", () => { expect(reasoningEffort).toHaveValue("provider-native-mode"); }); - it("associates preset selection with the responsive detail pane", async () => { + it("expands the model preset editor directly below the selected row", async () => { vi.stubGlobal( "fetch", vi.fn(async (input: RequestInfo | URL) => { @@ -2157,22 +2157,23 @@ describe("SettingsView Apps catalog", () => { const row = await screen.findByTestId("model-call-order-row-primary"); const trigger = within(row).getAllByRole("button")[0]; - const detail = document.getElementById("model-preset-detail"); - expect(detail).toHaveClass("hidden", "xl:block"); - expect(trigger).toHaveAttribute("aria-controls", "model-preset-detail"); + expect(screen.queryByTestId("model-preset-editor")).not.toBeInTheDocument(); + expect(trigger).toHaveAttribute("aria-expanded", "false"); fireEvent.click(trigger); + const editor = screen.getByTestId("model-preset-editor"); expect(trigger).toHaveAttribute("aria-pressed", "true"); - expect(detail).not.toHaveClass("hidden"); - expect(within(detail as HTMLElement).getByDisplayValue("Primary")).toBeInTheDocument(); + expect(trigger).toHaveAttribute("aria-expanded", "true"); + expect(trigger).toHaveAttribute("aria-controls", "model-preset-editor"); + expect(row.nextElementSibling).toBe(editor); + expect(editor).toHaveClass("slide-in-from-top-1"); + expect(within(editor).getByDisplayValue("Primary")).toBeInTheDocument(); - const backButton = screen.getByRole("button", { name: "Back to model presets" }); - expect(backButton).toHaveClass("xl:hidden"); - fireEvent.click(backButton); + fireEvent.click(trigger); - expect(trigger).toHaveAttribute("aria-pressed", "false"); - expect(detail).toHaveClass("hidden", "xl:block"); + expect(trigger).toHaveAttribute("aria-expanded", "false"); + expect(screen.queryByTestId("model-preset-editor")).not.toBeInTheDocument(); }); it("drags model presets to reorder and saves the model call order immediately", async () => { @@ -4048,10 +4049,6 @@ describe("SettingsView Apps catalog", () => { expect(screen.queryByRole("dialog", { name: "New model preset" })).not.toBeInTheDocument(); expect(screen.getByPlaceholderText("Fast writing")).toHaveValue(""); expect(screen.queryByText("Temperature")).not.toBeInTheDocument(); - fireEvent.click(screen.getByRole("button", { name: "Back to model presets" })); - expect(screen.queryByPlaceholderText("Fast writing")).not.toBeInTheDocument(); - - fireEvent.click(screen.getByRole("button", { name: "New model preset" })); fireEvent.click(screen.getByRole("button", { name: /Advanced options/ })); expect(screen.getByText("Temperature")).toBeInTheDocument(); fireEvent.click(screen.getByRole("button", { name: "Cancel" }));