feat(app): provider system overhaul with model selector, diagnostics, and UX polish

- Add CombinedModelSelector with two-level drill-down (providers → models),
  favorites section, search, and single-provider auto-drill mode
- Add provider snapshot system (ProviderSnapshotManager) for cached provider/model state
- Add provider diagnostics with resolved binary paths and versions across all providers
- Add StatusBadge shared component for consistent status visual language
- Add ProviderDiagnosticSheet for detailed provider health inspection
- Add desktop auto-focus on input after any status bar interaction (model, mode,
  thinking, features) using focusWithRetries
- Fix Combobox flicker on height change by switching to bottom-based CSS positioning
  once initial floating-ui position resolves
- Add dynamic dropdown height: Level 1 fits content, Level 2 calculates from model count
- Remove "Other providers" section from model selector (only show available providers)
This commit is contained in:
Mohamed Boudra
2026-04-05 15:32:13 +07:00
parent 6907f6e71d
commit 0d88ce2270
25 changed files with 2225 additions and 160 deletions
@@ -75,6 +75,7 @@ export function WorkspaceDraftAgentTab({
modeOptions,
availableModels,
allProviderModels,
allProviderEntries,
isAllModelsLoading,
availableThinkingOptions,
isModelLoading,
@@ -250,6 +251,60 @@ export function WorkspaceDraftAgentTab({
addImagesRef.current = addImages;
}, []);
const focusInputRef = useRef<(() => void) | null>(null);
const handleFocusInputCallback = useCallback((focus: () => void) => {
focusInputRef.current = focus;
}, []);
const handleProviderSelectWithFocus = useCallback(
(provider: string) => {
setProviderFromUser(provider);
focusInputRef.current?.();
},
[setProviderFromUser],
);
const handleModeSelectWithFocus = useCallback(
(modeId: string) => {
setModeFromUser(modeId);
focusInputRef.current?.();
},
[setModeFromUser],
);
const handleModelSelectWithFocus = useCallback(
(modelId: string) => {
setModelFromUser(modelId);
focusInputRef.current?.();
},
[setModelFromUser],
);
const handleProviderAndModelSelectWithFocus = useCallback(
(provider: string, modelId: string) => {
setProviderAndModelFromUser(provider, modelId);
focusInputRef.current?.();
},
[setProviderAndModelFromUser],
);
const handleThinkingOptionSelectWithFocus = useCallback(
(optionId: string) => {
setThinkingOptionFromUser(optionId);
focusInputRef.current?.();
},
[setThinkingOptionFromUser],
);
const handleSetFeatureWithFocus = useCallback(
(featureId: string, value: unknown) => {
setDraftFeatureValue(featureId, value);
focusInputRef.current?.();
},
[setDraftFeatureValue],
);
return (
<FileDropZone onFilesDropped={handleFilesDropped}>
<View style={styles.container}>
@@ -296,26 +351,28 @@ export function WorkspaceDraftAgentTab({
clearDraft={draftInput.clear}
autoFocus={shouldAutoFocusWorkspaceDraftComposer({ isPaneFocused, isSubmitting })}
onAddImages={handleAddImagesCallback}
onFocusInput={handleFocusInputCallback}
commandDraftConfig={draftCommandConfig}
statusControls={{
providerDefinitions,
selectedProvider,
onSelectProvider: setProviderFromUser,
onSelectProvider: handleProviderSelectWithFocus,
modeOptions,
selectedMode,
onSelectMode: setModeFromUser,
onSelectMode: handleModeSelectWithFocus,
models: availableModels,
selectedModel,
onSelectModel: setModelFromUser,
onSelectModel: handleModelSelectWithFocus,
isModelLoading,
allProviderModels,
isAllModelsLoading,
onSelectProviderAndModel: setProviderAndModelFromUser,
onSelectProviderAndModel: handleProviderAndModelSelectWithFocus,
thinkingOptions: availableThinkingOptions,
selectedThinkingOptionId,
onSelectThinkingOption: setThinkingOptionFromUser,
onSelectThinkingOption: handleThinkingOptionSelectWithFocus,
features: draftFeatures,
onSetFeature: setDraftFeatureValue,
onSetFeature: handleSetFeatureWithFocus,
onDropdownClose: () => focusInputRef.current?.(),
disabled: isSubmitting,
}}
/>