mirror of
https://github.com/HKUDS/nanobot.git
synced 2026-08-07 09:58:34 +00:00
fix(webui): scope preset editor to one row
This commit is contained in:
parent
3fc69b2922
commit
3836c32874
@ -3312,6 +3312,7 @@ function ModelsSettings({
|
|||||||
const tx = (key: string, fallback: string, values?: Record<string, unknown>) =>
|
const tx = (key: string, fallback: string, values?: Record<string, unknown>) =>
|
||||||
t(key, { defaultValue: fallback, ...(values ?? {}) });
|
t(key, { defaultValue: fallback, ...(values ?? {}) });
|
||||||
const [editorOpen, setEditorOpen] = useState(false);
|
const [editorOpen, setEditorOpen] = useState(false);
|
||||||
|
const [editorRowKey, setEditorRowKey] = useState<string | null>(null);
|
||||||
const [advancedOpen, setAdvancedOpen] = useState(false);
|
const [advancedOpen, setAdvancedOpen] = useState(false);
|
||||||
const [draggedCallOrderIndex, setDraggedCallOrderIndex] = useState<number | null>(null);
|
const [draggedCallOrderIndex, setDraggedCallOrderIndex] = useState<number | null>(null);
|
||||||
const [dragOverCallOrderIndex, setDragOverCallOrderIndex] = useState<number | null>(null);
|
const [dragOverCallOrderIndex, setDragOverCallOrderIndex] = useState<number | null>(null);
|
||||||
@ -3338,6 +3339,10 @@ function ModelsSettings({
|
|||||||
})),
|
})),
|
||||||
];
|
];
|
||||||
const selectedPreset = namedPresetsByName.get(form.modelPreset) ?? null;
|
const selectedPreset = namedPresetsByName.get(form.modelPreset) ?? null;
|
||||||
|
const activeEditorRowKey =
|
||||||
|
editorRowKey ??
|
||||||
|
presetRows.find((row) => row.name === selectedPreset?.name)?.key ??
|
||||||
|
null;
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setAdvancedOpen(false);
|
setAdvancedOpen(false);
|
||||||
}, [editorOpen, selectedPreset?.name]);
|
}, [editorOpen, selectedPreset?.name]);
|
||||||
@ -3374,8 +3379,12 @@ function ModelsSettings({
|
|||||||
selectedPreset && callOrder.includes(selectedPreset.name),
|
selectedPreset && callOrder.includes(selectedPreset.name),
|
||||||
);
|
);
|
||||||
const callOrderBusy = orderSaving || saving;
|
const callOrderBusy = orderSaving || saving;
|
||||||
const selectPreset = (preset: SettingsPayload["model_presets"][number]) => {
|
const selectPreset = (
|
||||||
const toggleCurrentPreset = !creating && selectedPreset?.name === preset.name;
|
preset: SettingsPayload["model_presets"][number],
|
||||||
|
rowKey: string,
|
||||||
|
) => {
|
||||||
|
const toggleCurrentPreset =
|
||||||
|
!creating && selectedPreset?.name === preset.name && activeEditorRowKey === rowKey;
|
||||||
onSelectConfiguration();
|
onSelectConfiguration();
|
||||||
if (toggleCurrentPreset) {
|
if (toggleCurrentPreset) {
|
||||||
setEditorOpen((open) => !open);
|
setEditorOpen((open) => !open);
|
||||||
@ -3392,6 +3401,7 @@ function ModelsSettings({
|
|||||||
temperature: preset.temperature,
|
temperature: preset.temperature,
|
||||||
reasoningEffort: preset.reasoning_effort ?? "",
|
reasoningEffort: preset.reasoning_effort ?? "",
|
||||||
}));
|
}));
|
||||||
|
setEditorRowKey(rowKey);
|
||||||
setEditorOpen(true);
|
setEditorOpen(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -3432,9 +3442,8 @@ function ModelsSettings({
|
|||||||
onChangeCallOrder(next);
|
onChangeCallOrder(next);
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderPresetEditor = (key: string) => (
|
const renderPresetEditor = () => (
|
||||||
<div
|
<div
|
||||||
key={key}
|
|
||||||
id="model-preset-editor"
|
id="model-preset-editor"
|
||||||
data-testid="model-preset-editor"
|
data-testid="model-preset-editor"
|
||||||
className="mx-3 mb-3 divide-y divide-border/45 overflow-hidden rounded-[18px] border border-border/45 bg-background/80 shadow-sm motion-reduce:animate-none animate-in fade-in-0 slide-in-from-top-1 duration-200 sm:mx-5 lg:mx-auto lg:w-[calc(100%-2.5rem)] lg:max-w-6xl"
|
className="mx-3 mb-3 divide-y divide-border/45 overflow-hidden rounded-[18px] border border-border/45 bg-background/80 shadow-sm motion-reduce:animate-none animate-in fade-in-0 slide-in-from-top-1 duration-200 sm:mx-5 lg:mx-auto lg:w-[calc(100%-2.5rem)] lg:max-w-6xl"
|
||||||
@ -3675,11 +3684,12 @@ function ModelsSettings({
|
|||||||
draggedCallOrderIndex !== null &&
|
draggedCallOrderIndex !== null &&
|
||||||
draggedCallOrderIndex < orderIndex;
|
draggedCallOrderIndex < orderIndex;
|
||||||
const isSelected =
|
const isSelected =
|
||||||
editorOpen && !creating && selectedPreset?.name === name;
|
editorOpen &&
|
||||||
return [
|
!creating &&
|
||||||
|
activeEditorRowKey === key &&
|
||||||
|
selectedPreset?.name === name;
|
||||||
|
const presetRow = (
|
||||||
<div
|
<div
|
||||||
key={key}
|
|
||||||
role="listitem"
|
|
||||||
tabIndex={ordered ? 0 : -1}
|
tabIndex={ordered ? 0 : -1}
|
||||||
draggable={ordered && !callOrderBusy}
|
draggable={ordered && !callOrderBusy}
|
||||||
aria-label={
|
aria-label={
|
||||||
@ -3731,7 +3741,7 @@ function ModelsSettings({
|
|||||||
moveCallOrderItem(orderIndex, 1);
|
moveCallOrderItem(orderIndex, 1);
|
||||||
} else if ((event.key === "Enter" || event.key === " ") && preset) {
|
} else if ((event.key === "Enter" || event.key === " ") && preset) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
selectPreset(preset);
|
selectPreset(preset, key);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
className={cn(
|
className={cn(
|
||||||
@ -3766,7 +3776,7 @@ function ModelsSettings({
|
|||||||
aria-expanded={isSelected}
|
aria-expanded={isSelected}
|
||||||
aria-controls={isSelected ? "model-preset-editor" : undefined}
|
aria-controls={isSelected ? "model-preset-editor" : undefined}
|
||||||
disabled={!preset}
|
disabled={!preset}
|
||||||
onClick={() => preset && selectPreset(preset)}
|
onClick={() => preset && selectPreset(preset, key)}
|
||||||
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"
|
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"
|
||||||
>
|
>
|
||||||
{ordered ? (
|
{ordered ? (
|
||||||
@ -3846,9 +3856,14 @@ function ModelsSettings({
|
|||||||
aria-hidden
|
aria-hidden
|
||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
</div>,
|
</div>
|
||||||
isSelected ? renderPresetEditor(`editor:${key}`) : null,
|
);
|
||||||
];
|
return (
|
||||||
|
<div key={key} role="listitem">
|
||||||
|
{presetRow}
|
||||||
|
{isSelected ? renderPresetEditor() : null}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex min-h-[58px] flex-col gap-3 px-4 py-3 sm:flex-row sm:items-center sm:justify-between sm:px-5">
|
<div className="flex min-h-[58px] flex-col gap-3 px-4 py-3 sm:flex-row sm:items-center sm:justify-between sm:px-5">
|
||||||
@ -3859,6 +3874,7 @@ function ModelsSettings({
|
|||||||
className="rounded-full"
|
className="rounded-full"
|
||||||
disabled={callOrderBusy}
|
disabled={callOrderBusy}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
setEditorRowKey(null);
|
||||||
setEditorOpen(true);
|
setEditorOpen(true);
|
||||||
onBeginCreate();
|
onBeginCreate();
|
||||||
}}
|
}}
|
||||||
@ -3878,7 +3894,7 @@ function ModelsSettings({
|
|||||||
</SettingsStatusMessage>
|
</SettingsStatusMessage>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
{creating && editorOpen ? renderPresetEditor("editor:new") : null}
|
{creating && editorOpen ? renderPresetEditor() : null}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</SettingsGroup>
|
</SettingsGroup>
|
||||||
|
|||||||
@ -2166,6 +2166,8 @@ describe("SettingsView Apps catalog", () => {
|
|||||||
expect(trigger).toHaveAttribute("aria-pressed", "true");
|
expect(trigger).toHaveAttribute("aria-pressed", "true");
|
||||||
expect(trigger).toHaveAttribute("aria-expanded", "true");
|
expect(trigger).toHaveAttribute("aria-expanded", "true");
|
||||||
expect(trigger).toHaveAttribute("aria-controls", "model-preset-editor");
|
expect(trigger).toHaveAttribute("aria-controls", "model-preset-editor");
|
||||||
|
expect(row.parentElement).toHaveAttribute("role", "listitem");
|
||||||
|
expect(row.parentElement?.parentElement).toHaveAttribute("role", "list");
|
||||||
expect(row.nextElementSibling).toBe(editor);
|
expect(row.nextElementSibling).toBe(editor);
|
||||||
expect(editor).toHaveClass(
|
expect(editor).toHaveClass(
|
||||||
"slide-in-from-top-1",
|
"slide-in-from-top-1",
|
||||||
@ -2317,7 +2319,17 @@ describe("SettingsView Apps catalog", () => {
|
|||||||
renderSettingsView({ initialSection: "models", initialSettings: initialPayload });
|
renderSettingsView({ initialSection: "models", initialSettings: initialPayload });
|
||||||
|
|
||||||
const primaryRow = screen.getByTestId("model-call-order-row-primary");
|
const primaryRow = screen.getByTestId("model-call-order-row-primary");
|
||||||
const firstBackupRow = screen.getAllByTestId("model-call-order-row-backup")[0];
|
const backupRows = screen.getAllByTestId("model-call-order-row-backup");
|
||||||
|
const firstBackupRow = backupRows[0];
|
||||||
|
const secondBackupRow = backupRows[1];
|
||||||
|
const secondBackupTrigger = within(secondBackupRow).getAllByRole("button")[0];
|
||||||
|
fireEvent.click(secondBackupTrigger);
|
||||||
|
expect(screen.getAllByTestId("model-preset-editor")).toHaveLength(1);
|
||||||
|
expect(secondBackupRow.nextElementSibling).toBe(
|
||||||
|
screen.getByTestId("model-preset-editor"),
|
||||||
|
);
|
||||||
|
fireEvent.click(secondBackupTrigger);
|
||||||
|
|
||||||
const dataTransfer = {
|
const dataTransfer = {
|
||||||
dropEffect: "move",
|
dropEffect: "move",
|
||||||
effectAllowed: "move",
|
effectAllowed: "move",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user