Merge remote-tracking branch 'origin/main' into dev
# Conflicts: # nix/package.nix # packages/app/src/components/adaptive-modal-sheet.tsx # packages/app/src/hooks/use-providers-snapshot.ts # packages/app/src/screens/workspace/workspace-screen.tsx # packages/server/src/server/agent/agent-management-mcp.ts # packages/server/src/server/agent/mcp-server.ts # packages/server/src/server/persistence-hooks.ts # packages/server/src/server/schedule/service.ts # packages/server/src/server/session.ts
This commit is contained in:
@@ -70,6 +70,7 @@ import { useProvidersSnapshot } from "@/hooks/use-providers-snapshot";
|
||||
import { useIsCompactFormFactor } from "@/constants/layout";
|
||||
import { getProviderIcon } from "@/components/provider-icons";
|
||||
import { ProviderDiagnosticSheet } from "@/components/provider-diagnostic-sheet";
|
||||
import { SpinningRefreshIcon } from "@/components/spinning-refresh-icon";
|
||||
import { StatusBadge } from "@/components/ui/status-badge";
|
||||
import { buildProviderDefinitions } from "@/utils/provider-definitions";
|
||||
import { isWeb } from "@/constants/platform";
|
||||
@@ -521,9 +522,11 @@ interface ProvidersSectionProps {
|
||||
function ProvidersSection({ routeServerId }: ProvidersSectionProps) {
|
||||
const { theme } = useUnistyles();
|
||||
const isConnected = useHostRuntimeIsConnected(routeServerId);
|
||||
const { entries, isLoading, isFetching, refresh } = useProvidersSnapshot(routeServerId);
|
||||
const { entries, isLoading, isRefreshing, refresh } = useProvidersSnapshot(routeServerId);
|
||||
const [diagnosticProvider, setDiagnosticProvider] = useState<string | null>(null);
|
||||
const providerDefinitions = buildProviderDefinitions(entries);
|
||||
const providerRefreshInFlight =
|
||||
isRefreshing || (entries?.some((entry) => entry.status === "loading") ?? false);
|
||||
|
||||
const hasServer = routeServerId.length > 0;
|
||||
|
||||
@@ -534,18 +537,25 @@ function ProvidersSection({ routeServerId }: ProvidersSectionProps) {
|
||||
<Text style={settingsStyles.sectionHeaderTitle}>Providers</Text>
|
||||
{hasServer && isConnected ? (
|
||||
<Pressable
|
||||
onPress={refresh}
|
||||
disabled={isFetching}
|
||||
style={[settingsStyles.sectionHeaderLink, isFetching ? { opacity: 0.5 } : null]}
|
||||
onPress={() => {
|
||||
void refresh();
|
||||
}}
|
||||
disabled={providerRefreshInFlight}
|
||||
hitSlop={8}
|
||||
style={({ hovered, pressed }) => [
|
||||
settingsStyles.sectionHeaderLink,
|
||||
styles.providerRefreshButton,
|
||||
(hovered || pressed) && styles.providerRefreshButtonHovered,
|
||||
providerRefreshInFlight ? styles.providerRefreshButtonDisabled : null,
|
||||
]}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel="Refresh providers"
|
||||
>
|
||||
<Text
|
||||
style={{
|
||||
color: theme.colors.primary,
|
||||
fontSize: theme.fontSize.xs,
|
||||
}}
|
||||
>
|
||||
Refresh
|
||||
</Text>
|
||||
<SpinningRefreshIcon
|
||||
spinning={providerRefreshInFlight}
|
||||
size={theme.iconSize.sm}
|
||||
color={theme.colors.foregroundMuted}
|
||||
/>
|
||||
</Pressable>
|
||||
) : null}
|
||||
</View>
|
||||
@@ -570,8 +580,15 @@ function ProvidersSection({ routeServerId }: ProvidersSectionProps) {
|
||||
? entry.error.trim()
|
||||
: null;
|
||||
|
||||
const modelCount = entry?.models?.length ?? 0;
|
||||
|
||||
return (
|
||||
<View key={def.id} style={styles.audioRow}>
|
||||
<Pressable
|
||||
key={def.id}
|
||||
style={styles.audioRow}
|
||||
onPress={() => setDiagnosticProvider(def.id)}
|
||||
accessibilityRole="button"
|
||||
>
|
||||
<View style={styles.audioRowContent}>
|
||||
<View
|
||||
style={{ flexDirection: "row", alignItems: "center", gap: theme.spacing[2] }}
|
||||
@@ -584,31 +601,27 @@ function ProvidersSection({ routeServerId }: ProvidersSectionProps) {
|
||||
{providerError}
|
||||
</Text>
|
||||
) : null}
|
||||
{status === "ready" && modelCount > 0 ? (
|
||||
<Text style={styles.audioRowSubtitle}>
|
||||
{modelCount === 1 ? "1 model" : `${modelCount} models`}
|
||||
</Text>
|
||||
) : null}
|
||||
</View>
|
||||
<View style={styles.providerActions}>
|
||||
<StatusBadge
|
||||
label={
|
||||
status === "ready"
|
||||
? "Available"
|
||||
: status === "error"
|
||||
? "Error"
|
||||
: status === "loading"
|
||||
? "Loading..."
|
||||
: "Not installed"
|
||||
}
|
||||
variant={
|
||||
status === "ready" ? "success" : status === "error" ? "error" : "muted"
|
||||
}
|
||||
/>
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
onPress={() => setDiagnosticProvider(def.id)}
|
||||
>
|
||||
Diagnostic
|
||||
</Button>
|
||||
</View>
|
||||
</View>
|
||||
<StatusBadge
|
||||
label={
|
||||
status === "ready"
|
||||
? "Available"
|
||||
: status === "error"
|
||||
? "Error"
|
||||
: status === "loading"
|
||||
? "Loading..."
|
||||
: "Not installed"
|
||||
}
|
||||
variant={
|
||||
status === "ready" ? "success" : status === "error" ? "error" : "muted"
|
||||
}
|
||||
/>
|
||||
</Pressable>
|
||||
);
|
||||
})}
|
||||
</View>
|
||||
@@ -1952,6 +1965,18 @@ const styles = StyleSheet.create((theme) => ({
|
||||
formButtonPrimaryText: {
|
||||
color: theme.colors.palette.white,
|
||||
},
|
||||
providerRefreshButton: {
|
||||
width: 30,
|
||||
height: 30,
|
||||
borderRadius: theme.borderRadius.full,
|
||||
justifyContent: "center",
|
||||
},
|
||||
providerRefreshButtonHovered: {
|
||||
backgroundColor: theme.colors.surface2,
|
||||
},
|
||||
providerRefreshButtonDisabled: {
|
||||
opacity: 0.5,
|
||||
},
|
||||
// Audio settings card
|
||||
audioCard: {
|
||||
overflow: "hidden",
|
||||
@@ -1980,11 +2005,6 @@ const styles = StyleSheet.create((theme) => ({
|
||||
fontSize: theme.fontSize.sm,
|
||||
marginTop: theme.spacing[1],
|
||||
},
|
||||
providerActions: {
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
gap: theme.spacing[2],
|
||||
},
|
||||
aboutValue: {
|
||||
color: theme.colors.foregroundMuted,
|
||||
fontSize: theme.fontSize.sm,
|
||||
|
||||
Reference in New Issue
Block a user