mirror of
https://github.com/awizemann/scarf.git
synced 2026-05-08 02:14:37 +00:00
feat(model-picker): add search filter to Nous overlay model list
Nous returned 402 models in the recent perf capture (~496 KB of JSON). The picker's existing top-bar search field already filters the catalog list (`filteredModels`) but the Nous overlay path showed all 402 unfiltered, making it nearly unusable. Add `filteredNousModels` mirroring the `filteredModels` shape: filters `nousModels` by case-insensitive substring match against both `id` and `owned_by`. Updates the empty-state overlay so "no matches" surfaces a different message from "no models loaded" — the user knows the catalog is fine, the search just didn't match. User feedback: "we need a search in the model picker, some of these lists are large and unorganized." Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -344,7 +344,7 @@ struct ModelPickerSheet: View {
|
||||
}
|
||||
}
|
||||
List(selection: $overlayModelID) {
|
||||
ForEach(nousModels) { model in
|
||||
ForEach(filteredNousModels) { model in
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
Text(model.id)
|
||||
.font(.system(.body, design: .monospaced))
|
||||
@@ -360,12 +360,23 @@ struct ModelPickerSheet: View {
|
||||
.listStyle(.inset)
|
||||
.frame(minHeight: 220)
|
||||
.overlay {
|
||||
if nousModels.isEmpty && !nousIsRefreshing {
|
||||
ContentUnavailableView(
|
||||
"No models loaded",
|
||||
systemImage: "cpu",
|
||||
description: Text("Sign in to Nous Portal to load the catalog, or enter a model ID manually.")
|
||||
)
|
||||
if filteredNousModels.isEmpty && !nousIsRefreshing {
|
||||
if nousModels.isEmpty {
|
||||
ContentUnavailableView(
|
||||
"No models loaded",
|
||||
systemImage: "cpu",
|
||||
description: Text("Sign in to Nous Portal to load the catalog, or enter a model ID manually.")
|
||||
)
|
||||
} else {
|
||||
// Models loaded but the search filtered them all
|
||||
// out. Different message so the user knows the
|
||||
// catalog is fine, just their query didn't match.
|
||||
ContentUnavailableView(
|
||||
"No matches",
|
||||
systemImage: "magnifyingglass",
|
||||
description: Text("No models match \"\(searchText)\".")
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
if nousFetchedAt == nil && !nousModels.isEmpty {
|
||||
@@ -577,6 +588,20 @@ struct ModelPickerSheet: View {
|
||||
}
|
||||
}
|
||||
|
||||
/// Same shape as `filteredModels` but for the Nous overlay path
|
||||
/// (`nousModels` is `[NousModel]`, not `[HermesModelInfo]`).
|
||||
/// Nous returned 402 models in the user's capture; without a
|
||||
/// filter the picker is a flat unsearchable list. Reuses the
|
||||
/// same `searchText` field so the user types once and both
|
||||
/// paths respond.
|
||||
private var filteredNousModels: [NousModel] {
|
||||
guard !searchText.isEmpty else { return nousModels }
|
||||
let q = searchText.lowercased()
|
||||
return nousModels.filter {
|
||||
$0.id.lowercased().contains(q) || ($0.owned_by ?? "").lowercased().contains(q)
|
||||
}
|
||||
}
|
||||
|
||||
private var isSelectedProviderOverlay: Bool {
|
||||
providers.first(where: { $0.providerID == selectedProviderID })?.isOverlay ?? false
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user