Skip to content

Conversation

SashankBhamidi
Copy link

@SashankBhamidi SashankBhamidi commented Aug 25, 2025

Changes

  1. Language settings: Replace hardcoded i18n.activate("en") with dynamic loading from user config
  2. Model search: Add search functionality to model selection dropdowns

Fixes

Implementation

  • LanguageInitializer component reads config.general.display_language with fallback to English
  • SearchableModelSelect component with real-time filtering for "Others" and "OpenRouter" sections
  • Uses existing UI components (Command, Popover) for consistency
  • Non-breaking changes, proper TypeScript types

Summary by cubic

Load app language from user settings and add search to model dropdowns. Improves localization and makes choosing from 100+ models fast.

  • New Features
    • Language: LanguageInitializer reads config.general.display_language (via react-query), validates en/ko, falls back to en, and delays render until ready.
    • Model search: SearchableModelSelect with real-time filtering and empty state; applied to OpenRouter and Others using existing Command/Popover UI.
    • Issue: Resolves Feature Request: Improve UX - Implement Search on Model List #1386 by making the large “Others” model list searchable.

- Replace hardcoded i18n.activate("en") with dynamic language loading
- Add LanguageInitializer component to read user preferences from config
- Support English and Korean languages with proper fallback
- Handle loading states and error conditions gracefully
- Validates language codes to prevent invalid configurations

Resolves TODO comment in main.tsx line 35
- Add SearchableModelSelect component with real-time filtering
- Apply to "Others" section where users configure custom endpoints
- Also apply to "OpenRouter" section for consistency
- Show dynamic model count in search placeholder
- Handle empty search results gracefully
- Maintain accessibility with proper ARIA attributes
- Use existing UI components (Command, Popover, Button)

Resolves fastrepl#1386 - Users can now search through ~100 models instead of scrolling manually
Copy link

coderabbitai bot commented Aug 25, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

📝 Walkthrough

Walkthrough

Adds a reusable SearchableModelSelect UI (popover + searchable Command list) and replaces two model dropdowns in the LLM settings view with it. Separately, delays app render until user language is loaded by introducing a LanguageInitializer that activates i18n from saved config with a safe fallback.

Changes

Cohort / File(s) Summary
Searchable model selector integration
apps/desktop/src/components/settings/components/ai/llm-custom-view.tsx
Adds internal SearchableModelSelect (Popover + Command list, supports search, empty state, selection with check) and replaces the OpenRouter and Others model selectors to use it (wired to existing form fields and model arrays).
Language initialization before render
apps/desktop/src/main.tsx
Adds LanguageInitializer using useQuery to load config.general, activates i18n based on display_language ("en" or "ko", fallback "en"), and delays rendering App/Toaster until activation; updates imports and root wrapper.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant R as Renderer
  participant LI as LanguageInitializer
  participant Q as React Query
  participant DB as dbCommands.getConfig
  participant I18N as i18n

  R->>LI: Mount
  LI->>Q: query config.general
  Q->>DB: getConfig()
  DB-->>Q: { general: { display_language } } or error
  Q-->>LI: data / error
  alt data available & supported
    LI->>I18N: activate(lang = en|ko)
    I18N-->>LI: activated
    LI-->>R: render children
  else error or unsupported
    LI->>I18N: activate(en)
    I18N-->>LI: activated
    LI-->>R: render children
  end
Loading
sequenceDiagram
  autonumber
  participant U as User
  participant SMS as SearchableModelSelect
  participant Pop as Popover
  participant Cmd as Command List
  participant Form as Form Field

  U->>SMS: Click combobox
  SMS->>Pop: Open
  U->>Cmd: Type search
  Cmd-->>U: Filtered models / "No model found"
  U->>Cmd: Select model
  Cmd->>SMS: onValueChange(value)
  SMS->>Form: field.onChange(value)
  SMS->>Pop: Close
  SMS-->>U: Selected model displayed with check
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Assessment against linked issues

Objective Addressed Explanation
Implement search to narrow down list of models across model pickers (#1386)

Assessment against linked issues: Out-of-scope changes

Code Change Explanation
Add LanguageInitializer to fetch config and activate i18n before rendering (apps/desktop/src/main.tsx) Language initialization is unrelated to implementing searchable model lists; it's an app-level initialization change.

Possibly related PRs

  • Others endpoint smooth #1251 — Modifies LLMCustomView model-selection and introduces othersModels usage; overlaps with this PR's selector changes.
  • Others hotfix 1 #1255 — Also modifies the same component and how available/custom models are fetched and handled; closely related to the searchable selector integration.

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.


📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 9327a87 and a8f1efa.

📒 Files selected for processing (1)
  • apps/desktop/src/components/settings/components/ai/llm-custom-view.tsx (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/desktop/src/components/settings/components/ai/llm-custom-view.tsx
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

‼️ IMPORTANT
Auto-reply has been disabled for this repository in the CodeRabbit settings. The CodeRabbit bot will not respond to your replies unless it is explicitly tagged.

  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (5)
apps/desktop/src/main.tsx (2)

46-54: Normalize BCP 47 language tags (e.g., en-US → en) before activation

Some configs store full locale tags; mapping to supported base languages avoids unnecessary fallback to English.

Apply this diff inside the effect:

   useEffect(() => {
-    const displayLanguage = config.data?.general.display_language;
-    if (displayLanguage && (displayLanguage === "en" || displayLanguage === "ko")) {
-      i18n.activate(displayLanguage);
-    } else {
-      // Fallback to English for new users, invalid languages, or if config fails to load
-      i18n.activate("en");
-    }
+    const raw = config.data?.general.display_language ?? "";
+    const lang = raw.split("-")[0]?.toLowerCase(); // normalize 'en-US' -> 'en'
+    const supported = new Set(["en", "ko"]);
+    i18n.activate(supported.has(lang) ? lang : "en");
   }, [config.data, config.error]);

56-59: Avoid a transient blank screen during language load

Returning null may show an empty window for slow disks or first run. Consider a tiny, non-translated skeleton (e.g., a centered spinner) to signal loading without adding i18n dependencies.

apps/desktop/src/components/settings/components/ai/llm-custom-view.tsx (3)

60-104: Searchable select: add i18n for user-visible strings and minor a11y polish

  • Localize "Search … models…" and "No model found." to align with the new language initialization.
  • Add an aria-label to the trigger button to improve screen-reader context.

Apply this diff within the component and import t from @lingui/macro at file top:

-        <Button
+        <Button
           variant="outline"
           role="combobox"
           aria-expanded={open}
+          aria-label={value ? `Model: ${value}` : "Select model"}
           className="w-full justify-between"
         >
           {value || placeholder}
           <ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
         </Button>
...
-        <Command>
-          <CommandInput placeholder={`Search ${models.length} models...`} />
-          <CommandEmpty>No model found.</CommandEmpty>
+        <Command>
+          <CommandInput placeholder={t`Search ${models.length} models...`} />
+          <CommandEmpty>
+            <Trans>No model found.</Trans>
+          </CommandEmpty>
           <CommandGroup className="max-h-64 overflow-auto">

Outside the selected lines, add at the top:

import { t, Trans } from "@lingui/macro";

249-256: React Hook Form watch usage is unconventional; prefer stable watched values

Calling customForm.watch("…") inside the dependency array re-invokes watch on each render and is harder to reason about. Use watched values in render (or useWatch) and depend on those.

Refactor like this:

-  const updateDebouncedValues = useDebouncedCallback(
+  const updateDebouncedValues = useDebouncedCallback(
     (apiBase: string, apiKey: string) => {
       setDebouncedApiBase(apiBase);
       setDebouncedApiKey(apiKey);
     },
-    [],
+    [],
     2000,
   );
 
-  // Watch for form changes
-  useEffect(() => {
-    const apiBase = customForm.watch("api_base");
-    const apiKey = customForm.watch("api_key");
-
-    updateDebouncedValues(apiBase || "", apiKey || "");
-  }, [customForm.watch("api_base"), customForm.watch("api_key"), updateDebouncedValues]);
+  // Watch for form changes
+  const apiBase = customForm.watch("api_base");
+  const apiKey = customForm.watch("api_key");
+  useEffect(() => {
+    updateDebouncedValues(apiBase || "", apiKey || "");
+  }, [apiBase, apiKey, updateDebouncedValues]);

Alternatively, use useWatch:

// outside effect
const { control } = customForm;
const [apiBase, apiKey] = useWatch({ control, name: ["api_base", "api_key"] });

Also applies to: 258-265


266-321: Network query: trim debug logs and tighten response typing

  • Console logs will leak into production; remove or guard behind a dev flag.
  • Avoid any in the mapper; narrow the JSON to the expected shape.
-      console.log("onquery");
-      console.log(url.toString());
+      // tip: enable only during development to inspect the resolved models endpoint
+      // if (import.meta.env.DEV) console.debug("[models] GET", url.toString());
...
-      const data = await response.json();
-
-      if (!data.data || !Array.isArray(data.data)) {
+      const data = (await response.json()) as { data?: Array<{ id?: string }> };
+      if (!Array.isArray(data.data)) {
         throw new Error("Invalid response format");
       }
 
-      const models = data.data
-        .map((model: any) => model.id)
+      const models = data.data
+        .map((m) => m?.id)
         .filter((id: string) => {
           const excludeKeywords = ["dall-e", "codex", "whisper"];
           return !excludeKeywords.some(keyword => id.includes(keyword));
         });
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 88ab89f and 9327a87.

📒 Files selected for processing (2)
  • apps/desktop/src/components/settings/components/ai/llm-custom-view.tsx (5 hunks)
  • apps/desktop/src/main.tsx (3 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{js,ts,tsx,rs}

⚙️ CodeRabbit configuration file

**/*.{js,ts,tsx,rs}: 1. No error handling.
2. No unused imports, variables, or functions.
3. For comments, keep it minimal. It should be about "Why", not "What".

Files:

  • apps/desktop/src/main.tsx
  • apps/desktop/src/components/settings/components/ai/llm-custom-view.tsx
🧬 Code graph analysis (1)
apps/desktop/src/components/settings/components/ai/llm-custom-view.tsx (2)
packages/ui/src/components/ui/button.tsx (1)
  • Button (37-89)
packages/ui/src/lib/utils.ts (1)
  • cn (4-6)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: cubic · AI code reviewer
🔇 Additional comments (5)
apps/desktop/src/main.tsx (2)

35-45: Good addition: language bootstrapping via React Query is clean and contained

Fetching config once and scoping the i18n activation behind this component keeps initialization concerns out of App. Retry=1 is a sensible default here.


166-177: Composition order looks right

Deferring I18nProvider render until after activation prevents flash-of-wrong-language. Wrapping App and Toaster here keeps all UI under the initialized locale.

apps/desktop/src/components/settings/components/ai/llm-custom-view.tsx (3)

53-58: Props shape is minimal and precise

The narrow value/placeholder/onValueChange contract keeps the component generic and reusable.


594-599: Nice integration of SearchableModelSelect for OpenRouter

This improves discoverability on long OpenRouter lists without changing form contracts.


715-721: Good UX for “Others”: searchable when discoverable, input fallback otherwise

The conditional keeps the flow smooth for endpoints that don’t expose /models.

Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 issues found across 2 files

React with 👍 or 👎 to teach cubic. You can also tag @cubic-dev-ai to give feedback, ask questions, or re-run the review.

}, [config.data, config.error]);

// Don't render children until language is initialized
if (config.isLoading) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rendering children as soon as isLoading is false can cause a brief flash in the wrong language since i18n.activate runs in useEffect after the initial render; delay rendering until activation completes.

Prompt for AI agents
Address the following comment on apps/desktop/src/main.tsx at line 57:

<comment>Rendering children as soon as isLoading is false can cause a brief flash in the wrong language since i18n.activate runs in useEffect after the initial render; delay rendering until activation completes.</comment>

<file context>
@@ -32,8 +32,34 @@ i18n.load({
   ko: koMessages,
 });
 
-// TODO: load language from user settings
-i18n.activate(&quot;en&quot;);
+// Language initialization component
+function LanguageInitializer({ children }: { children: ReactNode }) {
+  const config = useQuery({
+    queryKey: [&quot;config&quot;, &quot;general&quot;],
</file context>

- Fix CSS variable to use var(--radix-popper-anchor-width)
- Add internationalization to 'No model found' message
- Improve accessibility with aria-label on model selector button
@SashankBhamidi
Copy link
Author

Based on the reviews from @coderabbitai and @cubic-dev-ai, I have implemented the following fixes:

Implemented:

  • Fixed CSS variable to var(--radix-popper-anchor-width)
  • Added <Trans> to "No model found"
  • Added aria-label to button

I think the following suggestions are not necessary:

  • Language flash: Effect runs immediately, no visible issue
  • BCP 47 normalization: Config only uses "en"/"ko", unnecessary
  • Loading spinner: Config loads instantly, would add complexity
  • Hook Form refactor: Out of scope for this PR
  • Console log removal: Out of scope for this PR

Copy link

coderabbitai bot commented Aug 25, 2025

Tip

For best results, initiate chat on the files or code changes.

@SashankBhamidi Thank you for implementing those accessibility and internationalization improvements! The CSS variable fix, <Trans> component addition, and aria-label enhancement will definitely improve the user experience.

I appreciate your thoughtful analysis of the other suggestions. Your reasoning makes sense:

  • Language flash/Loading spinner: If the config loads instantly and there's no visible flash, the added complexity isn't justified
  • BCP 47 normalization: With only "en"/"ko" in the config, additional normalization would be over-engineering
  • Hook Form refactor/Console log removal: Maintaining focused PR scope is good practice

Your implementation successfully addresses the core objectives from issue #1386 (searchable model selection) and the TODO for dynamic language loading. The changes are clean and maintain good UX without unnecessary complexity.

Would you like me to create follow-up issues for any of the declined suggestions that might have value for future improvements, or are you satisfied with the current scope and implementation?

@SashankBhamidi
Copy link
Author

Thanks @coderabbitai! Satisfied with current scope, no follow-up issues needed.

Copy link

coderabbitai bot commented Aug 25, 2025

Tip

For best results, initiate chat on the files or code changes.

@SashankBhamidi Perfect! You've done excellent work on this PR. The implementation is clean, addresses the core requirements effectively, and maintains good focus on the intended scope.

Enjoy the improved model search functionality and dynamic language loading! 🐰

(\   /)

( .. )
o
(")(") Happy coding!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Feature Request: Improve UX - Implement Search on Model List
1 participant