feat: full shadcn/ui component system with consistent typography

New components:
- ui/button.tsx — Button with primary/accent/surface/ghost/destructive variants
- ui/input.tsx — Input + Textarea with focus ring and CSS var theming
- ui/label.tsx — Radix Label with peer-disabled support
- ui/separator.tsx — Radix Separator
- ui/tooltip.tsx — Radix Tooltip with themed content
- ui/dialog.tsx — Radix Dialog replacing custom modal implementations
- ui/table.tsx — Table/TableHeader/TableBody/TableRow/TableHead/TableCell
- ui/typography.tsx — PageTitle/SectionHeading/Body/Muted/Caption/MonoCaption

Wired throughout all components:
- ConfirmDialog + FormModal migrated to Radix Dialog (focus trap, Escape, ARIA)
- All raw <button> → Button, <input>/<textarea> → Input/Textarea
- All repeated text patterns → typography components
- All hardcoded hex/rgba strings → COLOR constants
This commit is contained in:
Offending Commit
2026-04-24 13:56:13 -05:00
parent 91c78915e5
commit 9a74182f97
24 changed files with 1387 additions and 652 deletions

View File

@@ -9,16 +9,21 @@ import {
type Config,
type HealthStatus,
} from "@/lib/config";
import { Button } from "@/components/ui/button";
import { Input, Textarea } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Muted } from "@/components/ui/typography";
import { COLOR } from "@/lib/constants";
interface SettingsFormProps {
onSaved?: () => void;
}
const statusConfig = {
ok: { icon: CheckCircle, color: "#34d399", label: "Connected" },
"auth-required": { icon: AlertCircle, color: "#f59e0b", label: "Auth required" },
unreachable: { icon: WifiOff, color: "#f87171", label: "Unreachable" },
checking: { icon: Loader, color: "#818cf8", label: "Checking..." },
ok: { icon: CheckCircle, color: COLOR.success, label: "Connected" },
"auth-required": { icon: AlertCircle, color: COLOR.warning, label: "Auth required" },
unreachable: { icon: WifiOff, color: COLOR.destructive, label: "Unreachable" },
checking: { icon: Loader, color: COLOR.accentText, label: "Checking..." },
};
export function SettingsForm({ onSaved }: SettingsFormProps) {
@@ -37,7 +42,6 @@ export function SettingsForm({ onSaved }: SettingsFormProps) {
setHealth(result);
setChecking(false);
// Auto-show token field if auth is required
if (result.status === "auth-required" && !token) {
document.getElementById("honcho-token")?.focus();
}
@@ -77,37 +81,23 @@ export function SettingsForm({ onSaved }: SettingsFormProps) {
>
{/* Base URL */}
<div>
<label
className="block text-sm font-medium mb-1.5"
style={{ color: "var(--text-1)" }}
>
<Label className="mb-1.5 text-sm">
Honcho Base URL
</label>
</Label>
<div className="flex gap-2">
<input
<Input
type="url"
value={baseUrl}
onChange={(e) => { setBaseUrl(e.target.value); setHealth(null); }}
placeholder="http://localhost:8000"
className="flex-1 px-3 py-2 text-sm font-mono rounded-xl outline-none transition-all"
style={{
background: "var(--surface)",
border: "1px solid var(--border-2)",
color: "var(--text-1)",
}}
onFocus={(e) => { e.target.style.borderColor = "var(--accent)"; }}
onBlur={(e) => { e.target.style.borderColor = "var(--border-2)"; }}
className="flex-1 font-mono rounded-xl"
/>
<button
<Button
type="button"
variant="accent"
onClick={handleTest}
disabled={checking || !baseUrl}
className="px-3 py-2 rounded-xl text-sm font-medium flex items-center gap-1.5 transition-all disabled:opacity-40"
style={{
background: "var(--accent-dim)",
border: "1px solid var(--accent-border)",
color: "var(--accent-text)",
}}
className="rounded-xl"
>
{checking ? (
<motion.div animate={{ rotate: 360 }} transition={{ duration: 1, repeat: Infinity, ease: "linear" }}>
@@ -117,14 +107,14 @@ export function SettingsForm({ onSaved }: SettingsFormProps) {
<Wifi className="w-4 h-4" strokeWidth={1.5} />
)}
<span className="hidden sm:block">Test</span>
</button>
</Button>
</div>
{errors.baseUrl && (
<p className="text-xs mt-1" style={{ color: "#f87171" }}>{errors.baseUrl}</p>
<p className="text-xs mt-1" style={{ color: COLOR.destructive }}>{errors.baseUrl}</p>
)}
<p className="text-xs mt-1.5" style={{ color: "var(--text-3)" }}>
<Muted className="text-xs mt-1.5">
URL of your self-hosted Honcho instance
</p>
</Muted>
</div>
{/* Health status */}
@@ -154,9 +144,9 @@ export function SettingsForm({ onSaved }: SettingsFormProps) {
<p className="text-sm font-medium" style={{ color: statusConfig[health.status].color }}>
{statusConfig[health.status].label}
</p>
<p className="text-xs mt-0.5" style={{ color: "var(--text-3)" }}>
<Muted className="text-xs mt-0.5">
{health.message}
</p>
</Muted>
</div>
</div>
</motion.div>
@@ -165,10 +155,9 @@ export function SettingsForm({ onSaved }: SettingsFormProps) {
{/* Token */}
<div>
<label
<Label
htmlFor="honcho-token"
className="flex items-center gap-1.5 text-sm font-medium mb-1.5"
style={{ color: "var(--text-1)" }}
className="flex items-center gap-1.5 mb-1.5 text-sm"
>
{token ? (
<Lock className="w-3.5 h-3.5" style={{ color: "var(--accent)" }} strokeWidth={1.5} />
@@ -186,44 +175,35 @@ export function SettingsForm({ onSaved }: SettingsFormProps) {
>
optional
</span>
</label>
<textarea
</Label>
<Textarea
id="honcho-token"
value={token}
onChange={(e) => setToken(e.target.value)}
rows={2}
placeholder="eyJ... (required only if your instance has auth enabled)"
className="w-full px-3 py-2.5 text-sm rounded-xl font-mono resize-none outline-none transition-all"
style={{
background: "var(--surface)",
border: "1px solid var(--border-2)",
color: "var(--text-1)",
}}
onFocus={(e) => { e.target.style.borderColor = "var(--accent)"; }}
onBlur={(e) => { e.target.style.borderColor = "var(--border-2)"; }}
className="font-mono rounded-xl"
/>
{health?.status === "auth-required" && !token && (
<motion.p
initial={{ opacity: 0, y: -4 }}
animate={{ opacity: 1, y: 0 }}
className="text-xs mt-1"
style={{ color: "#f59e0b" }}
style={{ color: COLOR.warning }}
>
This instance requires an API token to proceed
</motion.p>
)}
</div>
<button
<Button
type="submit"
className="w-full py-2.5 px-4 rounded-xl text-sm font-medium transition-all"
style={{
background: saved ? "#059669" : "var(--accent)",
color: "#fff",
}}
variant="primary"
className="w-full py-2.5 px-4 rounded-xl"
style={saved ? { background: "#059669" } : undefined}
>
{saved ? "✓ Saved" : "Save Connection"}
</button>
</Button>
</form>
);
}