From 6e15e1ef9638c7e695e3d2f2e6b82cda83bd6331 Mon Sep 17 00:00:00 2001 From: Ammar Date: Sun, 30 Nov 2025 10:23:01 -0600 Subject: [PATCH 01/13] =?UTF-8?q?=F0=9F=A4=96=20feat:=20unify=20model=20se?= =?UTF-8?q?lector=20and=20settings=20page=20UX?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Show built-in models (from KNOWN_MODELS) in Settings → Models section - Add favoriting UX (star icon) to set default model from settings - Display model aliases for built-in models (e.g., /sonnet, /haiku) - Maintain edit/delete actions only for custom models - Unified styling between built-in and custom model rows --- .../Settings/sections/ModelsSection.tsx | 362 +++++++++++------- 1 file changed, 222 insertions(+), 140 deletions(-) diff --git a/src/browser/components/Settings/sections/ModelsSection.tsx b/src/browser/components/Settings/sections/ModelsSection.tsx index 3c634e772..053f58bc2 100644 --- a/src/browser/components/Settings/sections/ModelsSection.tsx +++ b/src/browser/components/Settings/sections/ModelsSection.tsx @@ -1,7 +1,11 @@ import React, { useState, useEffect, useCallback } from "react"; -import { Plus, Trash2, Pencil, Check, X, Loader2 } from "lucide-react"; +import { Plus, Trash2, Pencil, Check, X, Loader2, Star } from "lucide-react"; import type { ProvidersConfigMap } from "../types"; import { SUPPORTED_PROVIDERS, PROVIDER_DISPLAY_NAMES } from "@/common/constants/providers"; +import { KNOWN_MODELS } from "@/common/constants/knownModels"; +import { cn } from "@/common/lib/utils"; +import { useModelLRU } from "@/browser/hooks/useModelLRU"; +import { TooltipWrapper, Tooltip } from "@/browser/components/Tooltip"; interface NewModelForm { provider: string; @@ -20,6 +24,7 @@ export function ModelsSection() { const [saving, setSaving] = useState(false); const [editing, setEditing] = useState(null); const [error, setError] = useState(null); + const { defaultModel, setDefaultModel } = useModelLRU(); // Load config on mount useEffect(() => { @@ -153,164 +158,241 @@ export function ModelsSection() { } // Get all custom models across providers - const getAllModels = (): Array<{ provider: string; modelId: string }> => { - const models: Array<{ provider: string; modelId: string }> = []; + const getCustomModels = (): Array<{ provider: string; modelId: string; fullId: string }> => { + const models: Array<{ provider: string; modelId: string; fullId: string }> = []; for (const [provider, providerConfig] of Object.entries(config)) { if (providerConfig.models) { for (const modelId of providerConfig.models) { - models.push({ provider, modelId }); + models.push({ provider, modelId, fullId: `${provider}:${modelId}` }); } } } return models; }; - const allModels = getAllModels(); + // Get built-in models from KNOWN_MODELS + const builtInModels = Object.values(KNOWN_MODELS).map((model) => ({ + provider: model.provider, + modelId: model.providerModelId, + fullId: model.id, + aliases: model.aliases, + })); + + const customModels = getCustomModels(); + + // Model row component for consistent styling + const ModelRow = ({ + provider, + modelId, + fullId, + aliases, + isCustom, + }: { + provider: string; + modelId: string; + fullId: string; + aliases?: string[]; + isCustom: boolean; + }) => { + const isEditing = + editing?.provider === provider && editing?.originalModelId === modelId && isCustom; + const isDefault = defaultModel === fullId; + + return ( +
+
+ + {PROVIDER_DISPLAY_NAMES[provider as keyof typeof PROVIDER_DISPLAY_NAMES] ?? provider} + + {isEditing ? ( +
+ + setEditing((prev) => (prev ? { ...prev, newModelId: e.target.value } : null)) + } + onKeyDown={(e) => { + if (e.key === "Enter") void handleSaveEdit(); + if (e.key === "Escape") handleCancelEdit(); + }} + className="bg-modal-bg border-border-medium focus:border-accent min-w-0 flex-1 rounded border px-2 py-1 font-mono text-xs focus:outline-none" + autoFocus + /> + {error &&
{error}
} +
+ ) : ( +
+ {modelId} + {aliases && aliases.length > 0 && ( + + aliases: {aliases.map((a) => `/${a}`).join(", ")} + + )} +
+ )} +
+
+ {isEditing ? ( + <> + + + + ) : ( + <> + {/* Favorite/default button */} + + + + {isDefault ? "Default model" : "Set as default"} + + + {/* Edit/delete buttons only for custom models */} + {isCustom && ( + <> + + + + )} + + )} +
+
+ ); + }; return ( -
+

- Add custom models to use with your providers. These will appear in the model selector. + Manage your models. Click the star to set a default model for new workspaces.

- {/* Add new model form */} -
-
Add Custom Model
-
- - setNewModel((prev) => ({ ...prev, modelId: e.target.value }))} - placeholder="model-id (e.g., gpt-4-turbo)" - className="bg-modal-bg border-border-medium focus:border-accent flex-1 rounded border px-2 py-1.5 font-mono text-xs focus:outline-none" - onKeyDown={(e) => { - if (e.key === "Enter") void handleAddModel(); - }} - /> - + {/* Built-in Models */} +
+
+ Built-in Models
- {error && !editing &&
{error}
} + {builtInModels.map((model) => ( + + ))}
- {/* List of custom models */} - {allModels.length > 0 ? ( -
-
- Custom Models + {/* Custom Models */} +
+
Custom Models
+ + {/* Add new model form */} +
+
+ + setNewModel((prev) => ({ ...prev, modelId: e.target.value }))} + placeholder="model-id (e.g., gpt-4-turbo)" + className="bg-modal-bg border-border-medium focus:border-accent flex-1 rounded border px-2 py-1.5 font-mono text-xs focus:outline-none" + onKeyDown={(e) => { + if (e.key === "Enter") void handleAddModel(); + }} + /> +
- {allModels.map(({ provider, modelId }) => { - const isEditing = - editing?.provider === provider && editing?.originalModelId === modelId; - - return ( -
-
- - {PROVIDER_DISPLAY_NAMES[provider as keyof typeof PROVIDER_DISPLAY_NAMES] ?? - provider} - - {isEditing ? ( -
- - setEditing((prev) => - prev ? { ...prev, newModelId: e.target.value } : null - ) - } - onKeyDown={(e) => { - if (e.key === "Enter") void handleSaveEdit(); - if (e.key === "Escape") handleCancelEdit(); - }} - className="bg-modal-bg border-border-medium focus:border-accent min-w-0 flex-1 rounded border px-2 py-1 font-mono text-xs focus:outline-none" - autoFocus - /> - {error &&
{error}
} -
- ) : ( - - {modelId} - - )} -
-
- {isEditing ? ( - <> - - - - ) : ( - <> - - - - )} -
-
- ); - })} + {error && !editing &&
{error}
}
- ) : ( -
- No custom models configured. Add one above to get started. -
- )} + + {/* List custom models */} + {customModels.length > 0 ? ( + customModels.map((model) => ( + + )) + ) : ( +
+ No custom models. Add one above to use models not listed in built-in. +
+ )} +
); } From 4a5d6996ca824a6a2c2de125543b17dcc79672d2 Mon Sep 17 00:00:00 2001 From: Ammar Date: Sun, 30 Nov 2025 10:25:39 -0600 Subject: [PATCH 02/13] refactor: extract ModelRow into separate component --- .../components/Settings/sections/ModelRow.tsx | 137 ++++++++++++++ .../Settings/sections/ModelsSection.tsx | 175 ++++-------------- 2 files changed, 169 insertions(+), 143 deletions(-) create mode 100644 src/browser/components/Settings/sections/ModelRow.tsx diff --git a/src/browser/components/Settings/sections/ModelRow.tsx b/src/browser/components/Settings/sections/ModelRow.tsx new file mode 100644 index 000000000..4eaba10b3 --- /dev/null +++ b/src/browser/components/Settings/sections/ModelRow.tsx @@ -0,0 +1,137 @@ +import React from "react"; +import { Check, Pencil, Star, Trash2, X } from "lucide-react"; +import { cn } from "@/common/lib/utils"; +import { PROVIDER_DISPLAY_NAMES } from "@/common/constants/providers"; +import { TooltipWrapper, Tooltip } from "@/browser/components/Tooltip"; + +export interface ModelRowProps { + provider: string; + modelId: string; + fullId: string; + aliases?: string[]; + isCustom: boolean; + isDefault: boolean; + isEditing: boolean; + editValue?: string; + editError?: string | null; + saving?: boolean; + hasActiveEdit?: boolean; + onSetDefault: () => void; + onStartEdit?: () => void; + onSaveEdit?: () => void; + onCancelEdit?: () => void; + onEditChange?: (value: string) => void; + onRemove?: () => void; +} + +export function ModelRow(props: ModelRowProps) { + return ( +
+
+ + {PROVIDER_DISPLAY_NAMES[props.provider as keyof typeof PROVIDER_DISPLAY_NAMES] ?? + props.provider} + + {props.isEditing ? ( +
+ props.onEditChange?.(e.target.value)} + onKeyDown={(e) => { + if (e.key === "Enter") props.onSaveEdit?.(); + if (e.key === "Escape") props.onCancelEdit?.(); + }} + className="bg-modal-bg border-border-medium focus:border-accent min-w-0 flex-1 rounded border px-2 py-1 font-mono text-xs focus:outline-none" + autoFocus + /> + {props.editError &&
{props.editError}
} +
+ ) : ( +
+ + {props.modelId} + + {props.aliases && props.aliases.length > 0 && ( + + aliases: {props.aliases.map((a) => `/${a}`).join(", ")} + + )} +
+ )} +
+
+ {props.isEditing ? ( + <> + + + + ) : ( + <> + {/* Favorite/default button */} + + + + {props.isDefault ? "Default model" : "Set as default"} + + + {/* Edit/delete buttons only for custom models */} + {props.isCustom && ( + <> + + + + )} + + )} +
+
+ ); +} diff --git a/src/browser/components/Settings/sections/ModelsSection.tsx b/src/browser/components/Settings/sections/ModelsSection.tsx index 053f58bc2..8883f3d64 100644 --- a/src/browser/components/Settings/sections/ModelsSection.tsx +++ b/src/browser/components/Settings/sections/ModelsSection.tsx @@ -1,11 +1,10 @@ import React, { useState, useEffect, useCallback } from "react"; -import { Plus, Trash2, Pencil, Check, X, Loader2, Star } from "lucide-react"; +import { Plus, Loader2 } from "lucide-react"; import type { ProvidersConfigMap } from "../types"; import { SUPPORTED_PROVIDERS, PROVIDER_DISPLAY_NAMES } from "@/common/constants/providers"; import { KNOWN_MODELS } from "@/common/constants/knownModels"; -import { cn } from "@/common/lib/utils"; import { useModelLRU } from "@/browser/hooks/useModelLRU"; -import { TooltipWrapper, Tooltip } from "@/browser/components/Tooltip"; +import { ModelRow } from "./ModelRow"; interface NewModelForm { provider: string; @@ -180,137 +179,6 @@ export function ModelsSection() { const customModels = getCustomModels(); - // Model row component for consistent styling - const ModelRow = ({ - provider, - modelId, - fullId, - aliases, - isCustom, - }: { - provider: string; - modelId: string; - fullId: string; - aliases?: string[]; - isCustom: boolean; - }) => { - const isEditing = - editing?.provider === provider && editing?.originalModelId === modelId && isCustom; - const isDefault = defaultModel === fullId; - - return ( -
-
- - {PROVIDER_DISPLAY_NAMES[provider as keyof typeof PROVIDER_DISPLAY_NAMES] ?? provider} - - {isEditing ? ( -
- - setEditing((prev) => (prev ? { ...prev, newModelId: e.target.value } : null)) - } - onKeyDown={(e) => { - if (e.key === "Enter") void handleSaveEdit(); - if (e.key === "Escape") handleCancelEdit(); - }} - className="bg-modal-bg border-border-medium focus:border-accent min-w-0 flex-1 rounded border px-2 py-1 font-mono text-xs focus:outline-none" - autoFocus - /> - {error &&
{error}
} -
- ) : ( -
- {modelId} - {aliases && aliases.length > 0 && ( - - aliases: {aliases.map((a) => `/${a}`).join(", ")} - - )} -
- )} -
-
- {isEditing ? ( - <> - - - - ) : ( - <> - {/* Favorite/default button */} - - - - {isDefault ? "Default model" : "Set as default"} - - - {/* Edit/delete buttons only for custom models */} - {isCustom && ( - <> - - - - )} - - )} -
-
- ); - }; - return (

@@ -330,6 +198,9 @@ export function ModelsSection() { fullId={model.fullId} aliases={model.aliases} isCustom={false} + isDefault={defaultModel === model.fullId} + isEditing={false} + onSetDefault={() => setDefaultModel(model.fullId)} /> ))}

@@ -378,15 +249,33 @@ export function ModelsSection() { {/* List custom models */} {customModels.length > 0 ? ( - customModels.map((model) => ( - - )) + customModels.map((model) => { + const isModelEditing = + editing?.provider === model.provider && editing?.originalModelId === model.modelId; + return ( + setDefaultModel(model.fullId)} + onStartEdit={() => handleStartEdit(model.provider, model.modelId)} + onSaveEdit={() => void handleSaveEdit()} + onCancelEdit={handleCancelEdit} + onEditChange={(value) => + setEditing((prev) => (prev ? { ...prev, newModelId: value } : null)) + } + onRemove={() => void handleRemoveModel(model.provider, model.modelId)} + /> + ); + }) ) : (
No custom models. Add one above to use models not listed in built-in. From 31bf8fc1d064559c5f8a94b09970818661eaf34b Mon Sep 17 00:00:00 2001 From: Ammar Date: Sun, 30 Nov 2025 10:30:49 -0600 Subject: [PATCH 03/13] refactor: improve UX - aliases tooltip, custom models first, increased density - Aliases now shown without '/' prefix with tooltip explaining '/m ' usage - Custom Models section moved to top for easier access - Increased UI density: smaller padding, font sizes, and gaps - Simplified empty state (no message when no custom models) --- .../components/Settings/sections/ModelRow.tsx | 47 ++++--- .../Settings/sections/ModelsSection.tsx | 124 +++++++++--------- 2 files changed, 85 insertions(+), 86 deletions(-) diff --git a/src/browser/components/Settings/sections/ModelRow.tsx b/src/browser/components/Settings/sections/ModelRow.tsx index 4eaba10b3..40d57468b 100644 --- a/src/browser/components/Settings/sections/ModelRow.tsx +++ b/src/browser/components/Settings/sections/ModelRow.tsx @@ -26,14 +26,14 @@ export interface ModelRowProps { export function ModelRow(props: ModelRowProps) { return ( -
-
- +
+
+ {PROVIDER_DISPLAY_NAMES[props.provider as keyof typeof PROVIDER_DISPLAY_NAMES] ?? props.provider} {props.isEditing ? ( -
+
{props.editError &&
{props.editError}
}
) : ( -
- +
+ {props.modelId} {props.aliases && props.aliases.length > 0 && ( - - aliases: {props.aliases.map((a) => `/${a}`).join(", ")} - + + + ({props.aliases.join(", ")}) + + + Use with /m {props.aliases[0]} + + )}
)}
-
+
{props.isEditing ? ( <> ) : ( @@ -92,7 +97,7 @@ export function ModelRow(props: ModelRowProps) { if (!props.isDefault) props.onSetDefault(); }} className={cn( - "p-1 transition-colors", + "p-0.5 transition-colors", props.isDefault ? "cursor-default text-yellow-400" : "text-muted hover:text-yellow-400" @@ -100,7 +105,7 @@ export function ModelRow(props: ModelRowProps) { disabled={props.isDefault} aria-label={props.isDefault ? "Current default model" : "Set as default model"} > - + {props.isDefault ? "Default model" : "Set as default"} @@ -113,19 +118,19 @@ export function ModelRow(props: ModelRowProps) { type="button" onClick={props.onStartEdit} disabled={Boolean(props.saving) || Boolean(props.hasActiveEdit)} - className="text-muted hover:text-foreground p-1 transition-colors disabled:opacity-50" + className="text-muted hover:text-foreground p-0.5 transition-colors disabled:opacity-50" title="Edit model" > - + )} diff --git a/src/browser/components/Settings/sections/ModelsSection.tsx b/src/browser/components/Settings/sections/ModelsSection.tsx index 8883f3d64..8aac16926 100644 --- a/src/browser/components/Settings/sections/ModelsSection.tsx +++ b/src/browser/components/Settings/sections/ModelsSection.tsx @@ -180,44 +180,24 @@ export function ModelsSection() { const customModels = getCustomModels(); return ( -
+

Manage your models. Click the star to set a default model for new workspaces.

- {/* Built-in Models */} -
-
- Built-in Models -
- {builtInModels.map((model) => ( - setDefaultModel(model.fullId)} - /> - ))} -
- - {/* Custom Models */} -
+ {/* Custom Models - shown first */} +
Custom Models
{/* Add new model form */} -
-
+
+
Date: Sun, 30 Nov 2025 10:41:30 -0600 Subject: [PATCH 05/13] docs: add README for provider icons with instructions for adding new ones --- src/browser/assets/icons/README.md | 36 ++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 src/browser/assets/icons/README.md diff --git a/src/browser/assets/icons/README.md b/src/browser/assets/icons/README.md new file mode 100644 index 000000000..b21406ab1 --- /dev/null +++ b/src/browser/assets/icons/README.md @@ -0,0 +1,36 @@ +# Provider Icons + +This directory contains SVG icons for AI providers displayed in the UI. + +## Current icons + +| File | Provider | Source | +|------|----------|--------| +| `anthropic.svg` | Anthropic | [Brand assets](https://www.anthropic.com/brand) | +| `openai.svg` | OpenAI | [Brand guidelines](https://openai.com/brand) | +| `aws.svg` | Amazon Bedrock | [AWS Architecture Icons](https://aws.amazon.com/architecture/icons/) | +| `mux.svg` | Mux Gateway | Internal | + +## Adding a new icon + +1. **Get the official SVG** from the provider's brand/press kit +2. **Optimize the SVG** - remove unnecessary metadata, comments, and attributes +3. **Ensure single color** - icons should use `fill="currentColor"` or a class like `.st0` that can be styled via CSS +4. **Name the file** `{provider}.svg` matching the provider key in `src/common/constants/providers.ts` +5. **Register in ProviderIcon** - add to `PROVIDER_ICONS` map in `src/browser/components/ProviderIcon.tsx`: + +```tsx +import NewProviderIcon from "@/browser/assets/icons/newprovider.svg?react"; + +const PROVIDER_ICONS: Partial> = { + // ...existing icons + newprovider: NewProviderIcon, +}; +``` + +## SVG requirements + +- Monochrome (single fill color) +- Use classes (`.st0`) or `currentColor` for fills so the icon inherits text color +- Reasonable viewBox (icons are rendered at 1em × 1em) +- No embedded raster images From 875e2712fff1bec575e5a8b4839309ac013dd7ea Mon Sep 17 00:00:00 2001 From: Ammar Date: Sun, 30 Nov 2025 10:43:00 -0600 Subject: [PATCH 06/13] feat: add provider icons to Providers settings section --- .../components/Settings/sections/ProvidersSection.tsx | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/browser/components/Settings/sections/ProvidersSection.tsx b/src/browser/components/Settings/sections/ProvidersSection.tsx index bd97027e5..ba9e863c1 100644 --- a/src/browser/components/Settings/sections/ProvidersSection.tsx +++ b/src/browser/components/Settings/sections/ProvidersSection.tsx @@ -1,8 +1,9 @@ import React, { useState, useEffect, useCallback } from "react"; import { ChevronDown, ChevronRight, Check, X } from "lucide-react"; import type { ProvidersConfigMap } from "../types"; -import { SUPPORTED_PROVIDERS, PROVIDER_DISPLAY_NAMES } from "@/common/constants/providers"; +import { SUPPORTED_PROVIDERS } from "@/common/constants/providers"; import type { ProviderName } from "@/common/constants/providers"; +import { ProviderWithIcon } from "@/browser/components/ProviderIcon"; interface FieldConfig { key: string; @@ -204,9 +205,11 @@ export function ProvidersSection() { ) : ( )} - - {PROVIDER_DISPLAY_NAMES[provider]} - +
Date: Sun, 30 Nov 2025 10:44:57 -0600 Subject: [PATCH 07/13] feat: add Google Gemini icon for google provider --- src/browser/assets/icons/README.md | 1 + src/browser/assets/icons/google.svg | 3 +++ src/browser/components/ProviderIcon.tsx | 2 ++ 3 files changed, 6 insertions(+) create mode 100644 src/browser/assets/icons/google.svg diff --git a/src/browser/assets/icons/README.md b/src/browser/assets/icons/README.md index b21406ab1..f14fef80a 100644 --- a/src/browser/assets/icons/README.md +++ b/src/browser/assets/icons/README.md @@ -8,6 +8,7 @@ This directory contains SVG icons for AI providers displayed in the UI. |------|----------|--------| | `anthropic.svg` | Anthropic | [Brand assets](https://www.anthropic.com/brand) | | `openai.svg` | OpenAI | [Brand guidelines](https://openai.com/brand) | +| `google.svg` | Google (Gemini) | [Wikimedia Commons](https://commons.wikimedia.org/wiki/File:Google_Gemini_icon_2025.svg) | | `aws.svg` | Amazon Bedrock | [AWS Architecture Icons](https://aws.amazon.com/architecture/icons/) | | `mux.svg` | Mux Gateway | Internal | diff --git a/src/browser/assets/icons/google.svg b/src/browser/assets/icons/google.svg new file mode 100644 index 000000000..8b51002ef --- /dev/null +++ b/src/browser/assets/icons/google.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/browser/components/ProviderIcon.tsx b/src/browser/components/ProviderIcon.tsx index cd82a3204..0afad68fa 100644 --- a/src/browser/components/ProviderIcon.tsx +++ b/src/browser/components/ProviderIcon.tsx @@ -1,6 +1,7 @@ import React from "react"; import AnthropicIcon from "@/browser/assets/icons/anthropic.svg?react"; import OpenAIIcon from "@/browser/assets/icons/openai.svg?react"; +import GoogleIcon from "@/browser/assets/icons/google.svg?react"; import AWSIcon from "@/browser/assets/icons/aws.svg?react"; import MuxIcon from "@/browser/assets/icons/mux.svg?react"; import { PROVIDER_DISPLAY_NAMES, type ProviderName } from "@/common/constants/providers"; @@ -9,6 +10,7 @@ import { cn } from "@/common/lib/utils"; const PROVIDER_ICONS: Partial> = { anthropic: AnthropicIcon, openai: OpenAIIcon, + google: GoogleIcon, bedrock: AWSIcon, "mux-gateway": MuxIcon, }; From f77471d2548d9b3dac315fb45594ed7c327586df Mon Sep 17 00:00:00 2001 From: Ammar Date: Sun, 30 Nov 2025 10:46:40 -0600 Subject: [PATCH 08/13] feat: add Google (Gemini) and xAI provider icons --- src/browser/assets/icons/README.md | 1 + src/browser/assets/icons/google.svg | 2 +- src/browser/assets/icons/xai.svg | 8 ++++++++ src/browser/components/ProviderIcon.tsx | 2 ++ 4 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 src/browser/assets/icons/xai.svg diff --git a/src/browser/assets/icons/README.md b/src/browser/assets/icons/README.md index f14fef80a..df5977c60 100644 --- a/src/browser/assets/icons/README.md +++ b/src/browser/assets/icons/README.md @@ -9,6 +9,7 @@ This directory contains SVG icons for AI providers displayed in the UI. | `anthropic.svg` | Anthropic | [Brand assets](https://www.anthropic.com/brand) | | `openai.svg` | OpenAI | [Brand guidelines](https://openai.com/brand) | | `google.svg` | Google (Gemini) | [Wikimedia Commons](https://commons.wikimedia.org/wiki/File:Google_Gemini_icon_2025.svg) | +| `xai.svg` | xAI | [Wikimedia Commons](https://commons.wikimedia.org/wiki/File:XAI_Logo.svg) | | `aws.svg` | Amazon Bedrock | [AWS Architecture Icons](https://aws.amazon.com/architecture/icons/) | | `mux.svg` | Mux Gateway | Internal | diff --git a/src/browser/assets/icons/google.svg b/src/browser/assets/icons/google.svg index 8b51002ef..06c79f5e4 100644 --- a/src/browser/assets/icons/google.svg +++ b/src/browser/assets/icons/google.svg @@ -1,3 +1,3 @@ - + diff --git a/src/browser/assets/icons/xai.svg b/src/browser/assets/icons/xai.svg new file mode 100644 index 000000000..ae7c56c2e --- /dev/null +++ b/src/browser/assets/icons/xai.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/browser/components/ProviderIcon.tsx b/src/browser/components/ProviderIcon.tsx index 0afad68fa..e13e05141 100644 --- a/src/browser/components/ProviderIcon.tsx +++ b/src/browser/components/ProviderIcon.tsx @@ -2,6 +2,7 @@ import React from "react"; import AnthropicIcon from "@/browser/assets/icons/anthropic.svg?react"; import OpenAIIcon from "@/browser/assets/icons/openai.svg?react"; import GoogleIcon from "@/browser/assets/icons/google.svg?react"; +import XAIIcon from "@/browser/assets/icons/xai.svg?react"; import AWSIcon from "@/browser/assets/icons/aws.svg?react"; import MuxIcon from "@/browser/assets/icons/mux.svg?react"; import { PROVIDER_DISPLAY_NAMES, type ProviderName } from "@/common/constants/providers"; @@ -11,6 +12,7 @@ const PROVIDER_ICONS: Partial> = anthropic: AnthropicIcon, openai: OpenAIIcon, google: GoogleIcon, + xai: XAIIcon, bedrock: AWSIcon, "mux-gateway": MuxIcon, }; From bb5ebb85547553d6f1a1f25952715bfe83d245fb Mon Sep 17 00:00:00 2001 From: Ammar Date: Sun, 30 Nov 2025 10:51:42 -0600 Subject: [PATCH 09/13] fix: simplify SVG fill styling for provider icons --- src/browser/components/ProviderIcon.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/browser/components/ProviderIcon.tsx b/src/browser/components/ProviderIcon.tsx index e13e05141..115b30ab0 100644 --- a/src/browser/components/ProviderIcon.tsx +++ b/src/browser/components/ProviderIcon.tsx @@ -33,7 +33,7 @@ export function ProviderIcon(props: ProviderIconProps) { return ( From 98bf41bc36843fab07f734f5e85d5fa7b00f02d8 Mon Sep 17 00:00:00 2001 From: Ammar Date: Sun, 30 Nov 2025 10:52:47 -0600 Subject: [PATCH 10/13] fix: crop xAI viewBox for proper sizing, add source comments to SVGs - xAI SVG: cropped viewBox to content bounds (was full page size) - Google SVG: simplified to monochrome, kept original path data - Both now include source URL comments for attribution --- src/browser/assets/icons/google.svg | 4 +++- src/browser/assets/icons/xai.svg | 16 +++++++++------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/browser/assets/icons/google.svg b/src/browser/assets/icons/google.svg index 06c79f5e4..2f272fd63 100644 --- a/src/browser/assets/icons/google.svg +++ b/src/browser/assets/icons/google.svg @@ -1,3 +1,5 @@ + + - + diff --git a/src/browser/assets/icons/xai.svg b/src/browser/assets/icons/xai.svg index ae7c56c2e..faffef116 100644 --- a/src/browser/assets/icons/xai.svg +++ b/src/browser/assets/icons/xai.svg @@ -1,8 +1,10 @@ - - - - - - - + + + + + + + + + From 27c447b5dafba1e8bd05a4972a243b159b22cd5c Mon Sep 17 00:00:00 2001 From: Ammar Date: Sun, 30 Nov 2025 10:54:09 -0600 Subject: [PATCH 11/13] fix: remove hardcoded fill color from Anthropic icon --- src/browser/assets/icons/anthropic.svg | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/browser/assets/icons/anthropic.svg b/src/browser/assets/icons/anthropic.svg index 4b8ac74d8..2308cd010 100644 --- a/src/browser/assets/icons/anthropic.svg +++ b/src/browser/assets/icons/anthropic.svg @@ -1,9 +1,4 @@ - - - - - - \ No newline at end of file + + + + From d4191ea6eeb00dd964e985ad95a472f77a3ed773 Mon Sep 17 00:00:00 2001 From: Ammar Date: Sun, 30 Nov 2025 10:57:44 -0600 Subject: [PATCH 12/13] fix: update Settings story to match new Models section text --- src/browser/components/Settings/Settings.stories.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/browser/components/Settings/Settings.stories.tsx b/src/browser/components/Settings/Settings.stories.tsx index 34bc95b4a..481582ef8 100644 --- a/src/browser/components/Settings/Settings.stories.tsx +++ b/src/browser/components/Settings/Settings.stories.tsx @@ -262,7 +262,7 @@ export const SidebarNavigation: Story = { // Content should update to show Models section text await waitFor(async () => { - const modelsText = canvas.getByText(/Add custom models/i); + const modelsText = canvas.getByText(/Manage your models/i); await expect(modelsText).toBeVisible(); }); }, From d8923b9aa95290b2943baf300bc0e9a1f1818533 Mon Sep 17 00:00:00 2001 From: Ammar Date: Sun, 30 Nov 2025 11:01:40 -0600 Subject: [PATCH 13/13] fix: update e2e test for new Models section layout --- tests/e2e/scenarios/settings.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/e2e/scenarios/settings.spec.ts b/tests/e2e/scenarios/settings.spec.ts index e2cb9bd1d..a6205d36e 100644 --- a/tests/e2e/scenarios/settings.spec.ts +++ b/tests/e2e/scenarios/settings.spec.ts @@ -109,8 +109,8 @@ test.describe("Settings Modal", () => { await ui.settings.open(); await ui.settings.selectSection("Models"); - // Verify add model form elements - use exact match for title - await expect(page.getByText("Add Custom Model", { exact: true })).toBeVisible(); + // Verify add model form elements + await expect(page.getByText("Custom Models")).toBeVisible(); await expect(page.getByRole("combobox")).toBeVisible(); // Provider dropdown await expect(page.getByPlaceholder(/model-id/i)).toBeVisible(); await expect(page.getByRole("button", { name: /^Add$/i })).toBeVisible();