FN-7942: index Project Models chat-default settings in Settings search
Extends the shared Settings search index so queries like "chat" surface the Project Models section's Direct-chat default fields, which previously had no matching searchable terms. - Add chat-related searchableText keywords (chat, new chat, chat default, chat model, chat agent, etc.) to the project-models SETTINGS_SECTIONS entry - Add searchableKeys covering the i18n chat-default labels (chatHeading, chatDescription, chatNewSessionMode*, chatDefaultKind/Model/Agent) so translated labels are also indexed - Export SettingsSection type plus normalizeSettingsSearchText/sectionMatchesSettingsSearch/filterSettingsSectionsForSearch/SETTINGS_SECTIONS from SettingsModal.tsx for direct unit testing - Add SettingsModal.search.test.ts covering chat-query matches and a negative case (unrelated Remote Access term does not match) - Add changeset fn-7942-settings-search-chat.md (patch, fix category) Files changed: .changeset/fn-7942-settings-search-chat.md | 7 +++ .../dashboard/app/components/SettingsModal.tsx | 50 +++++++++++++++++++--- .../__tests__/SettingsModal.search.test.ts | 41 ++++++++++++++++++ 3 files changed, 92 insertions(+), 6 deletions(-) Fusion-Task-Id: FN-7942 Fusion-Task-Lineage: fa044908-7ada-4334-a789-6d2bf08afdb8 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
This commit is contained in:
7
.changeset/fn-7942-settings-search-chat.md
Normal file
7
.changeset/fn-7942-settings-search-chat.md
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
"@runfusion/fusion": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
summary: Settings search now surfaces Project Models Chat default settings when searching for chat.
|
||||||
|
category: fix
|
||||||
|
dev: Adds chat-default searchableText/searchableKeys to the project-models entry in SETTINGS_SECTIONS (SettingsModal.tsx); fixes FN-7907 search-index drift.
|
||||||
@@ -225,7 +225,7 @@ function useGitHubStarCount(): number | null {
|
|||||||
* 2. Add a corresponding case in renderSectionFields()
|
* 2. Add a corresponding case in renderSectionFields()
|
||||||
*/
|
*/
|
||||||
/** Section entry type with optional icon */
|
/** Section entry type with optional icon */
|
||||||
type SettingsSection = {
|
export type SettingsSection = {
|
||||||
id: string;
|
id: string;
|
||||||
label: string;
|
label: string;
|
||||||
labelKey: string;
|
labelKey: string;
|
||||||
@@ -303,11 +303,11 @@ function removeEmptySettingsGroups(sections: SettingsSection[]): SettingsSection
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizeSettingsSearchText(value: string): string {
|
export function normalizeSettingsSearchText(value: string): string {
|
||||||
return value.trim().toLocaleLowerCase();
|
return value.trim().toLocaleLowerCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
function sectionMatchesSettingsSearch(
|
export function sectionMatchesSettingsSearch(
|
||||||
section: SettingsSection,
|
section: SettingsSection,
|
||||||
query: string,
|
query: string,
|
||||||
label: string,
|
label: string,
|
||||||
@@ -326,7 +326,7 @@ function sectionMatchesSettingsSearch(
|
|||||||
.some((candidate) => candidate.includes(query));
|
.some((candidate) => candidate.includes(query));
|
||||||
}
|
}
|
||||||
|
|
||||||
function filterSettingsSectionsForSearch(
|
export function filterSettingsSectionsForSearch(
|
||||||
sections: SettingsSection[],
|
sections: SettingsSection[],
|
||||||
query: string,
|
query: string,
|
||||||
translateLabel: (section: SettingsSection) => string,
|
translateLabel: (section: SettingsSection) => string,
|
||||||
@@ -413,7 +413,7 @@ function resolveMaxAutoMergeRetriesForSettingsForm(settings?: { maxAutoMergeRetr
|
|||||||
return Number.isFinite(configured) && configured > 0 ? Math.floor(configured) : 3;
|
return Number.isFinite(configured) && configured > 0 ? Math.floor(configured) : 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
const SETTINGS_SECTIONS: SettingsSection[] = [
|
export const SETTINGS_SECTIONS: SettingsSection[] = [
|
||||||
// Global group (shared across all Fusion projects)
|
// Global group (shared across all Fusion projects)
|
||||||
{ id: "__global_header", label: "Global", labelKey: "settings.nav.globalHeader", scope: undefined, isGroupHeader: true },
|
{ id: "__global_header", label: "Global", labelKey: "settings.nav.globalHeader", scope: undefined, isGroupHeader: true },
|
||||||
{ id: "global-general", label: "General", labelKey: "settings.nav.globalGeneral", scope: "global", searchableText: ["global defaults", "modal outside dismiss", "agent logs", "persist tool output", "thinking logs", "GitLab instance URL", "global tracking repo"] },
|
{ id: "global-general", label: "General", labelKey: "settings.nav.globalGeneral", scope: "global", searchableText: ["global defaults", "modal outside dismiss", "agent logs", "persist tool output", "thinking logs", "GitLab instance URL", "global tracking repo"] },
|
||||||
@@ -484,7 +484,45 @@ const SETTINGS_SECTIONS: SettingsSection[] = [
|
|||||||
{ id: "memory", label: "Memory", labelKey: "settings.nav.memory", scope: "project", searchableText: ["memory backend", "Dreams", "long-term memory", "qmd", "memory file", "retrieval"] },
|
{ id: "memory", label: "Memory", labelKey: "settings.nav.memory", scope: "project", searchableText: ["memory backend", "Dreams", "long-term memory", "qmd", "memory file", "retrieval"] },
|
||||||
{ id: "backups", label: "Backups", labelKey: "settings.nav.backups", scope: "project", searchableText: ["backup", "restore", "settings export", "settings import"] },
|
{ id: "backups", label: "Backups", labelKey: "settings.nav.backups", scope: "project", searchableText: ["backup", "restore", "settings export", "settings import"] },
|
||||||
{ id: "research-project", label: "Research", labelKey: "settings.nav.researchProject", scope: "project", searchableText: ["project research", "research runs", "citations", "search limits", "fetch synthesis"] },
|
{ id: "research-project", label: "Research", labelKey: "settings.nav.researchProject", scope: "project", searchableText: ["project research", "research runs", "citations", "search limits", "fetch synthesis"] },
|
||||||
{ id: "project-models", label: "Project Models", labelKey: "settings.nav.projectModels", scope: "project", searchableText: ["default provider", "default model", "workflow model lanes", "Plan/Triage", "Executor", "Reviewer", "summarization model"] },
|
/**
|
||||||
|
* FNXC:SettingsNavigation 2026-07-13-00:00:
|
||||||
|
* Project Models owns the FN-7907 Direct-chat default settings. Its shared Settings search index must advertise chat-default terms and i18n labels so desktop nav, the mobile section picker, and filtered search all surface this section when operators search for Chat defaults.
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
id: "project-models",
|
||||||
|
label: "Project Models",
|
||||||
|
labelKey: "settings.nav.projectModels",
|
||||||
|
scope: "project",
|
||||||
|
searchableText: [
|
||||||
|
"default provider",
|
||||||
|
"default model",
|
||||||
|
"workflow model lanes",
|
||||||
|
"Plan/Triage",
|
||||||
|
"Executor",
|
||||||
|
"Reviewer",
|
||||||
|
"summarization model",
|
||||||
|
"chat",
|
||||||
|
"new chat",
|
||||||
|
"new chat behavior",
|
||||||
|
"chat default",
|
||||||
|
"chat default model",
|
||||||
|
"chat default agent",
|
||||||
|
"chat model",
|
||||||
|
"chat agent",
|
||||||
|
"prompt for model",
|
||||||
|
"always use default",
|
||||||
|
],
|
||||||
|
searchableKeys: [
|
||||||
|
"settings.projectModels.chatHeading",
|
||||||
|
"settings.projectModels.chatDescription",
|
||||||
|
"settings.projectModels.chatNewSessionMode",
|
||||||
|
"settings.projectModels.chatNewSessionModePrompt",
|
||||||
|
"settings.projectModels.chatNewSessionModeAlwaysDefault",
|
||||||
|
"settings.projectModels.chatDefaultKind",
|
||||||
|
"settings.projectModels.chatDefaultModel",
|
||||||
|
"settings.projectModels.chatDefaultAgent",
|
||||||
|
],
|
||||||
|
},
|
||||||
{ id: "secrets", label: "Secrets", labelKey: "settings.nav.secrets", scope: "project", searchableText: ["secrets", "secret storage", "environment", "credentials"] },
|
{ id: "secrets", label: "Secrets", labelKey: "settings.nav.secrets", scope: "project", searchableText: ["secrets", "secret storage", "environment", "credentials"] },
|
||||||
{ id: "mcp", label: "MCP Servers", labelKey: "settings.nav.mcp", scope: "project", searchableText: ["project MCP servers", "workspace MCP", "project tool servers", "mcp config"] },
|
{ id: "mcp", label: "MCP Servers", labelKey: "settings.nav.mcp", scope: "project", searchableText: ["project MCP servers", "workspace MCP", "project tool servers", "mcp config"] },
|
||||||
{ id: "prompts", label: "Prompts", labelKey: "settings.nav.prompts", scope: "project", searchableText: ["prompt instructions", "PR title prompt", "PR description prompt", "custom prompts"] },
|
{ id: "prompts", label: "Prompts", labelKey: "settings.nav.prompts", scope: "project", searchableText: ["prompt instructions", "PR title prompt", "PR description prompt", "custom prompts"] },
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
|
||||||
|
import {
|
||||||
|
filterSettingsSectionsForSearch,
|
||||||
|
normalizeSettingsSearchText,
|
||||||
|
SETTINGS_SECTIONS,
|
||||||
|
type SettingsSection,
|
||||||
|
} from "../SettingsModal";
|
||||||
|
|
||||||
|
const searchTranslations: Record<string, string> = {
|
||||||
|
"settings.projectModels.chatHeading": "Chat",
|
||||||
|
"settings.projectModels.chatDescription": "Choose the default target for new Direct chats and whether New Chat should prompt or immediately use that default.",
|
||||||
|
"settings.projectModels.chatNewSessionMode": "New Chat behavior",
|
||||||
|
"settings.projectModels.chatNewSessionModePrompt": "Prompt for model each time",
|
||||||
|
"settings.projectModels.chatNewSessionModeAlwaysDefault": "Always use configured default",
|
||||||
|
"settings.projectModels.chatDefaultKind": "Chat default target",
|
||||||
|
"settings.projectModels.chatDefaultModel": "Chat Default Model",
|
||||||
|
"settings.projectModels.chatDefaultAgent": "Chat Default Agent",
|
||||||
|
};
|
||||||
|
|
||||||
|
function searchSettingsSectionIds(query: string): string[] {
|
||||||
|
return filterSettingsSectionsForSearch(
|
||||||
|
SETTINGS_SECTIONS,
|
||||||
|
normalizeSettingsSearchText(query),
|
||||||
|
(section: SettingsSection) => section.label,
|
||||||
|
(key: string) => searchTranslations[key] ?? key,
|
||||||
|
).map((section) => section.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("SettingsModal Settings search index", () => {
|
||||||
|
it.each(["chat", "new chat", "chat model", "chat default agent"])(
|
||||||
|
"surfaces Project Models for the chat-default query %s",
|
||||||
|
(query) => {
|
||||||
|
expect(searchSettingsSectionIds(query)).toContain("project-models");
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
it("does not surface Project Models for unrelated Remote Access terms", () => {
|
||||||
|
expect(searchSettingsSectionIds("cloudflared")).not.toContain("project-models");
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user