diff --git a/surfsense_web/app/desktop/suggestion/page.tsx b/surfsense_web/app/desktop/suggestion/page.tsx index 0815ba622..46e388568 100644 --- a/surfsense_web/app/desktop/suggestion/page.tsx +++ b/surfsense_web/app/desktop/suggestion/page.tsx @@ -27,24 +27,32 @@ interface AgentStep { items: string[]; } -function friendlyError(raw: string | number): string { +type FriendlyError = { message: string; isSetup?: boolean }; + +function friendlyError(raw: string | number): FriendlyError { if (typeof raw === "number") { - if (raw === 401) return "Please sign in to use suggestions."; - if (raw === 403) return "You don\u2019t have permission for this."; - if (raw === 404) return "Suggestion service not found. Is the backend running?"; - if (raw >= 500) return "Something went wrong on the server. Try again."; - return "Something went wrong. Try again."; + if (raw === 401) return { message: "Please sign in to use suggestions." }; + if (raw === 403) return { message: "You don\u2019t have permission for this." }; + if (raw === 404) return { message: "Suggestion service not found. Is the backend running?" }; + if (raw >= 500) return { message: "Something went wrong on the server. Try again." }; + return { message: "Something went wrong. Try again." }; } const lower = raw.toLowerCase(); if (lower.includes("not authenticated") || lower.includes("unauthorized")) - return "Please sign in to use suggestions."; + return { message: "Please sign in to use suggestions." }; if (lower.includes("no vision llm configured") || lower.includes("no llm configured")) - return "No Vision LLM configured. Set one in search space settings."; + return { + message: "Configure a vision-capable model (e.g. GPT-4o, Gemini) to enable autocomplete.", + isSetup: true, + }; if (lower.includes("does not support vision")) - return "Selected model doesn\u2019t support vision. Set a vision-capable model in settings."; + return { + message: "The selected model doesn\u2019t support vision. Choose a vision-capable model.", + isSetup: true, + }; if (lower.includes("fetch") || lower.includes("network") || lower.includes("econnrefused")) - return "Can\u2019t reach the server. Check your connection."; - return "Something went wrong. Try again."; + return { message: "Can\u2019t reach the server. Check your connection." }; + return { message: "Something went wrong. Try again." }; } const AUTO_DISMISS_MS = 3000; @@ -76,7 +84,7 @@ export default function SuggestionPage() { const api = useElectronAPI(); const [options, setOptions] = useState([]); const [isLoading, setIsLoading] = useState(true); - const [error, setError] = useState(null); + const [error, setError] = useState(null); const [steps, setSteps] = useState([]); const [expandedOption, setExpandedOption] = useState(null); const abortRef = useRef(null); @@ -90,7 +98,7 @@ export default function SuggestionPage() { }, [api]); useEffect(() => { - if (!error) return; + if (!error || error.isSetup) return; const timer = setTimeout(() => { api?.dismissSuggestion?.(); }, AUTO_DISMISS_MS); @@ -233,9 +241,47 @@ export default function SuggestionPage() { } if (error) { + if (error.isSetup) { + return ( +
+
+ +
+
+ Vision Model Required + {error.message} + Settings → Vision Models +
+ +
+ ); + } return (
- {error} + {error.message}
); } diff --git a/surfsense_web/app/desktop/suggestion/suggestion.css b/surfsense_web/app/desktop/suggestion/suggestion.css index f5471cf37..fd5ec5d3b 100644 --- a/surfsense_web/app/desktop/suggestion/suggestion.css +++ b/surfsense_web/app/desktop/suggestion/suggestion.css @@ -117,6 +117,66 @@ body:has(.suggestion-body) { font-size: 12px; } +/* --- Setup prompt (vision model not configured) --- */ + +.suggestion-setup { + display: flex; + flex-direction: row; + align-items: flex-start; + gap: 10px; + border-color: #3b2d6b; + padding: 10px 14px; +} + +.setup-icon { + flex-shrink: 0; + margin-top: 1px; +} + +.setup-content { + display: flex; + flex-direction: column; + gap: 3px; + min-width: 0; +} + +.setup-title { + font-size: 13px; + font-weight: 600; + color: #c4b5fd; +} + +.setup-message { + font-size: 11.5px; + color: #a1a1aa; + line-height: 1.4; +} + +.setup-hint { + font-size: 10.5px; + color: #7c6dac; + margin-top: 2px; +} + +.setup-dismiss { + flex-shrink: 0; + align-self: flex-start; + background: none; + border: none; + color: #6b6b7b; + font-size: 14px; + cursor: pointer; + padding: 2px 4px; + line-height: 1; + border-radius: 4px; + transition: color 0.15s, background 0.15s; +} + +.setup-dismiss:hover { + color: #c4b5fd; + background: rgba(124, 109, 172, 0.15); +} + /* --- Agent activity indicator --- */ .agent-activity {