Skip to content

Commit bf51a06

Browse files
Michael Borckclaude
andcommitted
feat: warn on the landing page when the AI provider is unreachable
Probes the provider via /api/models on mount and on settings changes; shows a calm banner with a Settings link before the user invests in a 30-second session prep. Hidden while the first-run banner already covers setup. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent a383666 commit bf51a06

1 file changed

Lines changed: 64 additions & 0 deletions

File tree

components/Hero.tsx

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,17 @@ import { FC, useState, useEffect } from "react";
44
import InitialInputArea from "./InitialInputArea";
55
import { suggestions } from "@/utils/utils";
66
import { strategies } from "@/utils/strategies";
7+
import { loadClientSettings } from "@/utils/settings";
8+
9+
const PROVIDER_LABELS: Record<string, string> = {
10+
ollama: "Ollama",
11+
openai: "OpenAI",
12+
anthropic: "Anthropic",
13+
google: "Google",
14+
groq: "Groq",
15+
openrouter: "OpenRouter",
16+
together: "Together AI",
17+
};
718

819
type THeroProps = {
920
promptValue: string;
@@ -40,6 +51,7 @@ const Hero: FC<THeroProps> = ({
4051
}) => {
4152
const [showNotes, setShowNotes] = useState(false);
4253
const [showFirstRun, setShowFirstRun] = useState(false);
54+
const [providerWarning, setProviderWarning] = useState<string | null>(null);
4355

4456
useEffect(() => {
4557
const dismissed = localStorage.getItem("studybuddy-first-run-dismissed");
@@ -48,6 +60,43 @@ const Hero: FC<THeroProps> = ({
4860
}
4961
}, []);
5062

63+
// Check the AI provider is reachable before the user invests in a session.
64+
useEffect(() => {
65+
let cancelled = false;
66+
67+
const check = async () => {
68+
const settings = loadClientSettings();
69+
const label = PROVIDER_LABELS[settings.llmProvider] || settings.llmProvider;
70+
const warning =
71+
settings.llmProvider === "ollama"
72+
? "I can't reach Ollama right now. Make sure it's running on your computer, or pick another provider in Settings."
73+
: `I can't reach ${label} right now. Check your secret key and connection in Settings.`;
74+
try {
75+
const res = await fetch("/api/models", {
76+
method: "POST",
77+
headers: { "Content-Type": "application/json" },
78+
body: JSON.stringify({
79+
provider: settings.llmProvider,
80+
baseUrl: settings.llmBaseUrl || undefined,
81+
apiKey: settings.llmApiKey || undefined,
82+
}),
83+
});
84+
if (!cancelled) setProviderWarning(res.ok ? null : warning);
85+
} catch {
86+
if (!cancelled) setProviderWarning(warning);
87+
}
88+
};
89+
90+
check();
91+
window.addEventListener("settingsChanged", check);
92+
window.addEventListener("settingsLoaded", check);
93+
return () => {
94+
cancelled = true;
95+
window.removeEventListener("settingsChanged", check);
96+
window.removeEventListener("settingsLoaded", check);
97+
};
98+
}, []);
99+
51100
const dismissFirstRun = () => {
52101
setShowFirstRun(false);
53102
localStorage.setItem("studybuddy-first-run-dismissed", "true");
@@ -99,6 +148,21 @@ const Hero: FC<THeroProps> = ({
99148
</div>
100149
)}
101150

151+
{/* Provider connectivity warning (hidden while the first-run banner covers setup) */}
152+
{!showFirstRun && providerWarning && (
153+
<div className="mb-4 w-full rounded-soft border border-hairline-strong bg-paper-warm px-4 py-3">
154+
<p className="text-sm text-ink-muted" style={{ lineHeight: 1.6 }}>
155+
{providerWarning}{" "}
156+
<Link
157+
href="/settings"
158+
className="font-medium text-ink underline transition-colors duration-normal hover:text-accent"
159+
>
160+
Open Settings
161+
</Link>
162+
</p>
163+
</div>
164+
)}
165+
102166
<h2 className="mt-2 text-center text-4xl font-semibold tracking-tight text-ink sm:text-6xl" style={{ lineHeight: 1.1 }}>
103167
Your Personal{" "}
104168
<span className="text-accent">Tutor</span>

0 commit comments

Comments
 (0)