mirror of
https://github.com/decolua/9router.git
synced 2026-05-08 12:01:28 +00:00
feat: add model deselection functionality in ComboFormModal and ComboDetailPage (#889)
- Implemented handleDeselectModel function to allow users to deselect models in both ComboFormModal and ComboDetailPage. - Updated ModelSelectModal to handle deselection and visually indicate selected models. - Enhanced user experience by allowing models to be removed from the selection without closing the modal.
This commit is contained in:
@@ -390,6 +390,10 @@ function ComboFormModal({ isOpen, combo, onClose, onSave, activeProviders, kindF
|
||||
}
|
||||
};
|
||||
|
||||
const handleDeselectModel = (model) => {
|
||||
setModels(models.filter((m) => m !== model.value));
|
||||
};
|
||||
|
||||
const handleRemoveModel = (index) => {
|
||||
setModels(models.filter((_, i) => i !== index));
|
||||
};
|
||||
@@ -502,10 +506,13 @@ function ComboFormModal({ isOpen, combo, onClose, onSave, activeProviders, kindF
|
||||
isOpen={showModelSelect}
|
||||
onClose={() => setShowModelSelect(false)}
|
||||
onSelect={handleAddModel}
|
||||
onDeselect={handleDeselectModel}
|
||||
activeProviders={activeProviders}
|
||||
modelAliases={modelAliases}
|
||||
title="Add Model to Combo"
|
||||
kindFilter={kindFilter}
|
||||
addedModelValues={models}
|
||||
closeOnSelect={false}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -126,6 +126,14 @@ export default function ComboDetailPage() {
|
||||
await saveCombo({ models: next });
|
||||
};
|
||||
|
||||
const handleDeselectModel = async (model) => {
|
||||
const value = model?.value || model;
|
||||
if (!value || !providers.includes(value)) return;
|
||||
const next = providers.filter((p) => p !== value);
|
||||
setProviders(next);
|
||||
await saveCombo({ models: next });
|
||||
};
|
||||
|
||||
const handleRemoveProvider = async (idx) => {
|
||||
const next = providers.filter((_, i) => i !== idx);
|
||||
setProviders(next);
|
||||
@@ -389,10 +397,13 @@ export default function ComboDetailPage() {
|
||||
isOpen={showPicker}
|
||||
onClose={() => setShowPicker(false)}
|
||||
onSelect={handleAddModel}
|
||||
onDeselect={handleDeselectModel}
|
||||
activeProviders={connections}
|
||||
modelAliases={modelAliases}
|
||||
title={`Add ${kindLabel} Model`}
|
||||
kindFilter={combo.kind}
|
||||
addedModelValues={providers}
|
||||
closeOnSelect={false}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -21,11 +21,14 @@ export default function ModelSelectModal({
|
||||
isOpen,
|
||||
onClose,
|
||||
onSelect,
|
||||
onDeselect,
|
||||
selectedModel,
|
||||
activeProviders = [],
|
||||
title = "Select Model",
|
||||
modelAliases = {},
|
||||
kindFilter = null,
|
||||
addedModelValues = [],
|
||||
closeOnSelect = true,
|
||||
}) {
|
||||
// Filter activeProviders by serviceKinds when kindFilter set (e.g. "webSearch", "webFetch")
|
||||
const filteredActiveProviders = useMemo(() => {
|
||||
@@ -342,9 +345,19 @@ export default function ModelSelectModal({
|
||||
}, [groupedModels, searchQuery]);
|
||||
|
||||
const handleSelect = (model) => {
|
||||
onSelect(model);
|
||||
onClose();
|
||||
setSearchQuery("");
|
||||
const value = model?.value || model?.name || model;
|
||||
const isAdded = addedModelValues.includes(value);
|
||||
|
||||
if (isAdded && onDeselect) {
|
||||
onDeselect(model);
|
||||
} else {
|
||||
onSelect(model);
|
||||
}
|
||||
|
||||
if (closeOnSelect) {
|
||||
onClose();
|
||||
setSearchQuery("");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -392,13 +405,18 @@ export default function ModelSelectModal({
|
||||
key={combo.id}
|
||||
onClick={() => handleSelect({ id: combo.name, name: combo.name, value: combo.name })}
|
||||
className={`
|
||||
px-2 py-1 rounded-xl text-xs font-medium transition-all border hover:cursor-pointer
|
||||
px-2 py-1 rounded-xl text-xs font-medium transition-all border hover:cursor-pointer flex items-center gap-1
|
||||
${isSelected
|
||||
? "bg-primary text-white border-primary"
|
||||
: "bg-surface border-border text-text-main hover:border-primary/50 hover:bg-primary/5"
|
||||
: addedModelValues.includes(combo.name)
|
||||
? "bg-green-500/10 border-green-500/30 text-green-700 dark:text-green-400 hover:border-green-500/50"
|
||||
: "bg-surface border-border text-text-main hover:border-primary/50 hover:bg-primary/5"
|
||||
}
|
||||
`}
|
||||
>
|
||||
{addedModelValues.includes(combo.name) && (
|
||||
<span className="material-symbols-outlined text-[12px]">check_circle</span>
|
||||
)}
|
||||
{combo.name}
|
||||
</button>
|
||||
);
|
||||
@@ -439,21 +457,30 @@ export default function ModelSelectModal({
|
||||
? "border-dashed border-border text-text-muted hover:border-primary/50 hover:text-primary bg-surface italic"
|
||||
: isSelected
|
||||
? "bg-primary text-white border-primary"
|
||||
: "bg-surface border-border text-text-main hover:border-primary/50 hover:bg-primary/5"
|
||||
: addedModelValues.includes(model.value)
|
||||
? "bg-green-500/10 border-green-500/30 text-green-700 dark:text-green-400 hover:border-green-500/50"
|
||||
: "bg-surface border-border text-text-main hover:border-primary/50 hover:bg-primary/5"
|
||||
}
|
||||
`}
|
||||
>
|
||||
{isPlaceholder ? (
|
||||
<span className="flex items-center gap-1">
|
||||
<span className="material-symbols-outlined text-[11px]">edit</span>
|
||||
{model.name}
|
||||
</span>
|
||||
) : model.isCustom ? (
|
||||
<span className="flex items-center gap-1">
|
||||
{model.name}
|
||||
<span className="text-[9px] opacity-60 font-normal">custom</span>
|
||||
</span>
|
||||
) : model.name}
|
||||
<span className="flex items-center gap-1">
|
||||
{addedModelValues.includes(model.value) && !isPlaceholder && (
|
||||
<span className="material-symbols-outlined text-[12px]">check_circle</span>
|
||||
)}
|
||||
{isPlaceholder ? (
|
||||
<>
|
||||
<span className="material-symbols-outlined text-[11px]">edit</span>
|
||||
{model.name}
|
||||
</>
|
||||
) : model.isCustom ? (
|
||||
<>
|
||||
{model.name}
|
||||
<span className="text-[9px] opacity-60 font-normal">custom</span>
|
||||
</>
|
||||
) : (
|
||||
model.name
|
||||
)}
|
||||
</span>
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
@@ -478,6 +505,7 @@ ModelSelectModal.propTypes = {
|
||||
isOpen: PropTypes.bool.isRequired,
|
||||
onClose: PropTypes.func.isRequired,
|
||||
onSelect: PropTypes.func.isRequired,
|
||||
onDeselect: PropTypes.func,
|
||||
selectedModel: PropTypes.string,
|
||||
activeProviders: PropTypes.arrayOf(
|
||||
PropTypes.shape({
|
||||
@@ -487,5 +515,7 @@ ModelSelectModal.propTypes = {
|
||||
title: PropTypes.string,
|
||||
modelAliases: PropTypes.object,
|
||||
kindFilter: PropTypes.string,
|
||||
addedModelValues: PropTypes.arrayOf(PropTypes.string),
|
||||
closeOnSelect: PropTypes.bool,
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user