diff --git a/.changeset/fn-7942-settings-search-chat.md b/.changeset/fn-7942-settings-search-chat.md new file mode 100644 index 0000000000..3e083c9505 --- /dev/null +++ b/.changeset/fn-7942-settings-search-chat.md @@ -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. diff --git a/packages/dashboard/app/components/SettingsModal.tsx b/packages/dashboard/app/components/SettingsModal.tsx index ef3512c9c5..c0cf59db0c 100644 --- a/packages/dashboard/app/components/SettingsModal.tsx +++ b/packages/dashboard/app/components/SettingsModal.tsx @@ -225,7 +225,7 @@ function useGitHubStarCount(): number | null { * 2. Add a corresponding case in renderSectionFields() */ /** Section entry type with optional icon */ -type SettingsSection = { +export type SettingsSection = { id: string; label: 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(); } -function sectionMatchesSettingsSearch( +export function sectionMatchesSettingsSearch( section: SettingsSection, query: string, label: string, @@ -326,7 +326,7 @@ function sectionMatchesSettingsSearch( .some((candidate) => candidate.includes(query)); } -function filterSettingsSectionsForSearch( +export function filterSettingsSectionsForSearch( sections: SettingsSection[], query: string, translateLabel: (section: SettingsSection) => string, @@ -413,7 +413,7 @@ function resolveMaxAutoMergeRetriesForSettingsForm(settings?: { maxAutoMergeRetr 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) { 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"] }, @@ -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: "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: "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: "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"] }, diff --git a/packages/dashboard/app/components/__tests__/SettingsModal.search.test.ts b/packages/dashboard/app/components/__tests__/SettingsModal.search.test.ts new file mode 100644 index 0000000000..702170c9ae --- /dev/null +++ b/packages/dashboard/app/components/__tests__/SettingsModal.search.test.ts @@ -0,0 +1,41 @@ +import { describe, expect, it } from "vitest"; + +import { + filterSettingsSectionsForSearch, + normalizeSettingsSearchText, + SETTINGS_SECTIONS, + type SettingsSection, +} from "../SettingsModal"; + +const searchTranslations: Record = { + "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"); + }); +});