Handy/src/components/onboarding/Onboarding.tsx
2025-10-17 16:57:51 -07:00

95 lines
2.8 KiB
TypeScript

import React, { useState, useEffect } from "react";
import { invoke } from "@tauri-apps/api/core";
import { ModelInfo } from "../../lib/types";
import ModelCard from "./ModelCard";
interface OnboardingProps {
onModelSelected: () => void;
}
const Onboarding: React.FC<OnboardingProps> = ({ onModelSelected }) => {
const [availableModels, setAvailableModels] = useState<ModelInfo[]>([]);
const [downloading, setDownloading] = useState(false);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
loadModels();
}, []);
const loadModels = async () => {
try {
const models: ModelInfo[] = await invoke("get_available_models");
// Only show downloadable models for onboarding
setAvailableModels(models.filter((m) => !m.is_downloaded));
} catch (err) {
console.error("Failed to load models:", err);
setError("Failed to load available models");
}
};
const handleDownloadModel = async (modelId: string) => {
setDownloading(true);
setError(null);
// Immediately transition to main app - download will continue in footer
onModelSelected();
try {
await invoke("download_model", { modelId });
} catch (err) {
console.error("Download failed:", err);
setError(`Failed to download model: ${err}`);
setDownloading(false);
}
};
const getRecommendedBadge = (modelId: string): boolean => {
return modelId === "parakeet-tdt-0.6b-v3";
};
return (
<div className="max-w-4xl mx-auto text-center space-y-6">
<p className="text-text/70 max-w-md font-medium mx-auto">
To get started, choose a transcription model
</p>
{error && (
<div className="bg-red-500/10 border border-red-500/20 rounded-lg p-4">
<p className="text-red-400 text-sm">{error}</p>
</div>
)}
<div className="max-w-2xl mx-auto mt-4">
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
{availableModels
.filter((model) => getRecommendedBadge(model.id))
.map((model) => (
<ModelCard
key={model.id}
model={model}
variant="featured"
badgeText="Recommended"
disabled={downloading}
className="sm:col-span-2"
onSelect={handleDownloadModel}
/>
))}
{availableModels
.filter((model) => !getRecommendedBadge(model.id))
.sort((a, b) => a.size_mb - b.size_mb)
.map((model) => (
<ModelCard
key={model.id}
model={model}
disabled={downloading}
onSelect={handleDownloadModel}
/>
))}
</div>
</div>
</div>
);
};
export default Onboarding;