Rework provider selector and settings UX
Surface all enabled providers in the model selector with inline loading and error states instead of silently hiding non-ready ones. Tap any row to drill in; loading and error states show in the drill-down body, with a Retry button on error. Providers with no models render a synthetic "Default" row so every provider's drill-down has symmetric structure. Redesign the per-provider settings sheet: search lives in the header, discovered and custom models share one scrolling list, and the footer hosts action buttons. Diagnostic and "Add model" each open as their own sub-sheet, so neither expands inline or shifts the surrounding layout. Snapshot refresh now keeps cached data visible: setQueryData replaces the cache atomically and sibling-scope caches are invalidated (not removed), so refreshing a provider no longer blanks its row. Search inputs across the app are uncontrolled — onChangeText drives parent state, and resetKey remounts the field when an explicit reset is needed — to avoid RN's controlled-input flicker. Adds a dev-only mock-slow provider whose discovery never resolves, so the 30s snapshot timeout produces a real error and lets loading and timeout-error UI be exercised end to end.
This commit is contained in:
@@ -93,6 +93,7 @@ interface ControlledAgentControlsProps {
|
||||
onModelSelectorOpen?: () => void;
|
||||
/** Extra elements rendered inline with the agent controls (desktop only). */
|
||||
desktopExtras?: ReactNode;
|
||||
modelSelectorServerId?: string | null;
|
||||
}
|
||||
|
||||
export interface DraftAgentControlsProps {
|
||||
@@ -117,6 +118,7 @@ export interface DraftAgentControlsProps {
|
||||
onDropdownClose?: () => void;
|
||||
onModelSelectorOpen?: () => void;
|
||||
disabled?: boolean;
|
||||
modelSelectorServerId?: string | null;
|
||||
}
|
||||
|
||||
interface AgentControlsProps {
|
||||
@@ -410,6 +412,7 @@ function ControlledAgentControls({
|
||||
onDropdownClose,
|
||||
onModelSelectorOpen,
|
||||
desktopExtras,
|
||||
modelSelectorServerId = null,
|
||||
}: ControlledAgentControlsProps) {
|
||||
const { theme } = useUnistyles();
|
||||
const isCompact = useIsCompactFormFactor();
|
||||
@@ -613,6 +616,7 @@ function ControlledAgentControls({
|
||||
handleOpenChange={handleOpenChange}
|
||||
renderThinkingOption={renderThinkingOption}
|
||||
extras={desktopExtras}
|
||||
modelSelectorServerId={modelSelectorServerId}
|
||||
/>
|
||||
) : (
|
||||
<SheetAgentControlsContent
|
||||
@@ -641,6 +645,7 @@ function ControlledAgentControls({
|
||||
handleSelectThinkingAndClose={handleSelectThinkingAndClose}
|
||||
handleOpenChange={handleOpenChange}
|
||||
renderThinkingOption={renderThinkingOption}
|
||||
modelSelectorServerId={modelSelectorServerId}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
@@ -693,6 +698,7 @@ interface DesktopAgentControlsContentProps {
|
||||
onPress: () => void;
|
||||
}) => ReactElement;
|
||||
extras?: ReactNode;
|
||||
modelSelectorServerId: string | null;
|
||||
}
|
||||
|
||||
const DESKTOP_SEARCH_THRESHOLD = 6;
|
||||
@@ -739,6 +745,7 @@ function DesktopAgentControlsContent(props: DesktopAgentControlsContentProps) {
|
||||
handleOpenChange,
|
||||
renderThinkingOption,
|
||||
extras,
|
||||
modelSelectorServerId,
|
||||
} = props;
|
||||
|
||||
return (
|
||||
@@ -791,6 +798,7 @@ function DesktopAgentControlsContent(props: DesktopAgentControlsContentProps) {
|
||||
disabled={modelDisabled}
|
||||
onOpen={onModelSelectorOpen}
|
||||
onClose={onDropdownClose}
|
||||
serverId={modelSelectorServerId}
|
||||
/>
|
||||
</View>
|
||||
</TooltipTrigger>
|
||||
@@ -884,6 +892,7 @@ interface SheetAgentControlsContentProps {
|
||||
active: boolean;
|
||||
onPress: () => void;
|
||||
}) => ReactElement;
|
||||
modelSelectorServerId: string | null;
|
||||
}
|
||||
|
||||
function SheetAgentControlsContent(props: SheetAgentControlsContentProps) {
|
||||
@@ -914,6 +923,7 @@ function SheetAgentControlsContent(props: SheetAgentControlsContentProps) {
|
||||
handleSelectThinkingAndClose,
|
||||
handleOpenChange,
|
||||
renderThinkingOption,
|
||||
modelSelectorServerId,
|
||||
} = props;
|
||||
|
||||
const thinkingAnchorRef = useRef<View | null>(null);
|
||||
@@ -983,6 +993,7 @@ function SheetAgentControlsContent(props: SheetAgentControlsContentProps) {
|
||||
onOpen={onModelSelectorOpen}
|
||||
onClose={onDropdownClose}
|
||||
renderTrigger={renderModelTrigger}
|
||||
serverId={modelSelectorServerId}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
@@ -1521,6 +1532,7 @@ export const AgentControls = memo(function AgentControls({
|
||||
onDropdownClose={onDropdownClose}
|
||||
disabled={!client}
|
||||
desktopExtras={modeChip}
|
||||
modelSelectorServerId={serverId}
|
||||
/>
|
||||
);
|
||||
});
|
||||
@@ -1547,6 +1559,7 @@ export function DraftAgentControls({
|
||||
onDropdownClose,
|
||||
onModelSelectorOpen,
|
||||
disabled = false,
|
||||
modelSelectorServerId = null,
|
||||
}: DraftAgentControlsProps) {
|
||||
const { preferences, updatePreferences } = useFormPreferences();
|
||||
const isCompact = useIsCompactFormFactor();
|
||||
@@ -1614,6 +1627,7 @@ export function DraftAgentControls({
|
||||
disabled={disabled}
|
||||
onOpen={onModelSelectorOpen}
|
||||
onClose={onDropdownClose}
|
||||
serverId={modelSelectorServerId}
|
||||
/>
|
||||
{selectedProvider ? (
|
||||
<ControlledAgentControls
|
||||
@@ -1650,6 +1664,7 @@ export function DraftAgentControls({
|
||||
onSetFeature={onSetFeature}
|
||||
onModelSelectorOpen={onModelSelectorOpen}
|
||||
disabled={disabled}
|
||||
modelSelectorServerId={modelSelectorServerId}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -187,13 +187,12 @@ function AgentModeControlView({
|
||||
() => ({
|
||||
title: "Mode",
|
||||
search: {
|
||||
value: searchQuery,
|
||||
onChange: setSearchQuery,
|
||||
placeholder: "Search modes...",
|
||||
testID: "mode-search-input",
|
||||
},
|
||||
}),
|
||||
[searchQuery],
|
||||
[],
|
||||
);
|
||||
|
||||
if (!selectedMode) return null;
|
||||
|
||||
@@ -46,6 +46,7 @@ export function buildDraftAgentControls(input: {
|
||||
onSetFeature,
|
||||
onDropdownClose,
|
||||
onModelSelectorOpen: formState.refetchProviderModelsIfStale,
|
||||
modelSelectorServerId: formState.selectedServerId,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user