feat: add health indicator and localhost auto-detect
Surfaces live connection health for the active instance in the sidebar and probes localhost:8000 on the first-run choose-type screen so users running Honcho locally can connect in one tap. - useHealthStatus hook polls checkConnection every 30s via TanStack Query - HealthDot component renders a colored status dot with tooltip - choose-type screen silently probes http://localhost:8000 once; on success it surfaces a "Detected Honcho at localhost:8000 — tap to connect" banner that opens the self-hosted form
This commit is contained in:
@@ -1,13 +1,15 @@
|
||||
import { motion } from "framer-motion";
|
||||
import { Check, ChevronRight, Cloud, Pencil, Plus, Server, Trash2 } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
import { AnimatePresence, motion } from "framer-motion";
|
||||
import { Check, ChevronRight, Cloud, Pencil, Plus, Server, Sparkles, Trash2 } from "lucide-react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { type ConnectionPreset, SettingsForm } from "@/components/settings/SettingsForm";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Muted } from "@/components/ui/typography";
|
||||
import { useInstances } from "@/hooks/useInstances";
|
||||
import { HONCHO_CLOUD_URL, type Instance, isCloudInstance } from "@/lib/config";
|
||||
import { checkConnection, HONCHO_CLOUD_URL, type Instance, isCloudInstance } from "@/lib/config";
|
||||
import { COLOR } from "@/lib/constants";
|
||||
|
||||
const LOCALHOST_PROBE_URL = "http://localhost:8000";
|
||||
|
||||
type Mode =
|
||||
| { kind: "list" }
|
||||
| { kind: "choose-type" }
|
||||
@@ -99,6 +101,21 @@ interface ConnectionTypeChooserProps {
|
||||
}
|
||||
|
||||
function ConnectionTypeChooser({ onPick, onCancel }: ConnectionTypeChooserProps) {
|
||||
const [localhostDetected, setLocalhostDetected] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
void checkConnection(LOCALHOST_PROBE_URL).then((result) => {
|
||||
if (cancelled) return;
|
||||
if (result.status === "ok" || result.status === "auth-required") {
|
||||
setLocalhostDetected(true);
|
||||
}
|
||||
});
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div
|
||||
className="rounded-2xl p-6 space-y-3"
|
||||
@@ -116,6 +133,35 @@ function ConnectionTypeChooser({ onPick, onCancel }: ConnectionTypeChooserProps)
|
||||
</Muted>
|
||||
</div>
|
||||
|
||||
<AnimatePresence>
|
||||
{localhostDetected && (
|
||||
<motion.button
|
||||
type="button"
|
||||
initial={{ opacity: 0, height: 0 }}
|
||||
animate={{ opacity: 1, height: "auto" }}
|
||||
exit={{ opacity: 0, height: 0 }}
|
||||
onClick={() => onPick("self-hosted")}
|
||||
className="w-full overflow-hidden rounded-xl p-3 flex items-center gap-2.5 text-left"
|
||||
style={{
|
||||
background: COLOR.successDim,
|
||||
border: `1px solid ${COLOR.successBorder}`,
|
||||
}}
|
||||
>
|
||||
<Sparkles
|
||||
className="w-4 h-4 shrink-0"
|
||||
style={{ color: COLOR.success }}
|
||||
strokeWidth={1.5}
|
||||
/>
|
||||
<div className="min-w-0 flex-1">
|
||||
<p className="text-xs font-medium" style={{ color: COLOR.success }}>
|
||||
Detected Honcho at {LOCALHOST_PROBE_URL.replace(/^https?:\/\//, "")}
|
||||
</p>
|
||||
<Muted className="text-xs mt-0.5">Tap to connect to it</Muted>
|
||||
</div>
|
||||
</motion.button>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
|
||||
<ConnectionTypeButton
|
||||
icon={Cloud}
|
||||
title="Honcho Cloud"
|
||||
|
||||
Reference in New Issue
Block a user