Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 18 additions & 8 deletions src/browser/components/ModelSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -205,17 +205,27 @@ export const ModelSelector = forwardRef<ModelSelectorRef, ModelSelectorProps>(

return (
<div ref={containerRef} className="relative flex items-center gap-1">
{gatewayActive && (
{gateway.canToggleModel(value) && (
<Tooltip>
<TooltipTrigger asChild>
<GatewayIcon
className="text-accent h-3 w-3 shrink-0"
active
role="img"
aria-label="Using Mux Gateway"
/>
<button
type="button"
onClick={() => gateway.toggleModelGateway(value)}
className="flex items-center justify-center"
aria-label={gatewayActive ? "Disable Mux Gateway" : "Enable Mux Gateway"}
>
<GatewayIcon
className={cn(
"h-3 w-3 shrink-0 transition-colors",
gatewayActive ? "text-accent" : "text-muted-light hover:text-accent"
)}
active={gatewayActive}
/>
</button>
</TooltipTrigger>
<TooltipContent align="center">Using Mux Gateway</TooltipContent>
<TooltipContent align="center">
{gatewayActive ? "Using Mux Gateway (click to disable)" : "Enable Mux Gateway"}
</TooltipContent>
</Tooltip>
)}
<Tooltip>
Expand Down
9 changes: 6 additions & 3 deletions src/browser/components/icons/GatewayIcon.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from "react";
import React, { forwardRef } from "react";

interface GatewayIconProps extends React.SVGProps<SVGSVGElement> {
className?: string;
Expand All @@ -10,11 +10,12 @@ interface GatewayIconProps extends React.SVGProps<SVGSVGElement> {
* Gateway icon - represents routing through Mux Gateway.
* Circle with M logo. Active state adds outer ring.
*/
export function GatewayIcon(props: GatewayIconProps) {
export const GatewayIcon = forwardRef<SVGSVGElement, GatewayIconProps>((props, ref) => {
const { active, ...svgProps } = props;

return (
<svg
ref={ref}
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
Expand All @@ -32,4 +33,6 @@ export function GatewayIcon(props: GatewayIconProps) {
<path d="M8 16V8l4 5 4-5v8" />
</svg>
);
}
});

GatewayIcon.displayName = "GatewayIcon";
11 changes: 3 additions & 8 deletions src/browser/hooks/useGatewayModels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,9 +165,8 @@ export function useGateway(): GatewayState {
const isActive = isConfigured && isEnabled;

const toggleEnabled = useCallback(() => {
// setIsEnabled (from usePersistedState) already writes to localStorage synchronously
setIsEnabled((prev) => !prev);
// Also update localStorage synchronously
updatePersistedState<boolean>(GATEWAY_ENABLED_KEY, (prev) => !prev);
}, [setIsEnabled]);

const modelUsesGateway = useCallback(
Expand All @@ -177,15 +176,11 @@ export function useGateway(): GatewayState {

const toggleModelGateway = useCallback(
(modelId: string) => {
// Update React state for UI
// setEnabledModels (from usePersistedState) already writes to localStorage synchronously,
// so we don't need to call updatePersistedState separately.
setEnabledModels((prev) =>
prev.includes(modelId) ? prev.filter((m) => m !== modelId) : [...prev, modelId]
);
// Also update localStorage synchronously so toGatewayModel() sees it immediately
// (usePersistedState batches writes in microtask, which can cause race conditions)
updatePersistedState<string[]>(GATEWAY_MODELS_KEY, (prev) =>
prev.includes(modelId) ? prev.filter((m) => m !== modelId) : [...prev, modelId]
);
},
[setEnabledModels]
);
Expand Down
Loading