diff --git a/src-tauri/src/managers/model.rs b/src-tauri/src/managers/model.rs
index ba0a188..dc60dda 100644
--- a/src-tauri/src/managers/model.rs
+++ b/src-tauri/src/managers/model.rs
@@ -70,7 +70,7 @@ impl ModelManager {
description: "Fast and fairly accurate.".to_string(),
filename: "ggml-small.bin".to_string(),
url: Some("https://blob.handy.computer/ggml-small.bin".to_string()),
- size_mb: 244,
+ size_mb: 487,
is_downloaded: false,
is_downloading: false,
partial_size: 0,
@@ -88,7 +88,7 @@ impl ModelManager {
description: "Good accuracy, medium speed".to_string(),
filename: "whisper-medium-q4_1.bin".to_string(),
url: Some("https://blob.handy.computer/whisper-medium-q4_1.bin".to_string()),
- size_mb: 491, // Approximate size
+ size_mb: 492, // Approximate size
is_downloaded: false,
is_downloading: false,
partial_size: 0,
@@ -122,7 +122,7 @@ impl ModelManager {
description: "Good accuracy, but slow.".to_string(),
filename: "ggml-large-v3-q5_0.bin".to_string(),
url: Some("https://blob.handy.computer/ggml-large-v3-q5_0.bin".to_string()),
- size_mb: 1080, // Approximate size
+ size_mb: 1100, // Approximate size
is_downloaded: false,
is_downloading: false,
partial_size: 0,
diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json
index 30a45f8..6ea31d8 100644
--- a/src-tauri/tauri.conf.json
+++ b/src-tauri/tauri.conf.json
@@ -16,9 +16,9 @@
"label": "main",
"title": "Handy",
"width": 680,
- "height": 510,
+ "height": 520,
"minWidth": 680,
- "minHeight": 510,
+ "minHeight": 520,
"resizable": true,
"maximizable": false,
"visible": false
diff --git a/src/App.tsx b/src/App.tsx
index 546440a..1a6754f 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -69,7 +69,7 @@ function App() {
if (showOnboarding) {
return (
-
+
diff --git a/src/components/model-selector/ModelDropdown.tsx b/src/components/model-selector/ModelDropdown.tsx
index e2752d4..d825fd6 100644
--- a/src/components/model-selector/ModelDropdown.tsx
+++ b/src/components/model-selector/ModelDropdown.tsx
@@ -1,6 +1,7 @@
import React from "react";
import { ModelInfo } from "../../lib/types";
-import { ProgressBar, ProgressData } from "../shared";
+import { formatModelSize } from "../../lib/utils/format";
+import { ProgressBar } from "../shared";
interface DownloadProgress {
model_id: string;
@@ -178,6 +179,9 @@ const ModelDropdown: React.FC
= ({
{model.description}
+
+ Download size ยท {formatModelSize(model.size_mb)}
+
{isDownloading && progress ? (
diff --git a/src/components/onboarding/ModelCard.tsx b/src/components/onboarding/ModelCard.tsx
new file mode 100644
index 0000000..4dafd9a
--- /dev/null
+++ b/src/components/onboarding/ModelCard.tsx
@@ -0,0 +1,80 @@
+import React from "react";
+import { Download } from "lucide-react";
+import { ModelInfo } from "../../lib/types";
+import { formatModelSize } from "../../lib/utils/format";
+
+interface ModelCardProps {
+ model: ModelInfo;
+ variant?: "default" | "featured";
+ disabled?: boolean;
+ badgeText?: string;
+ className?: string;
+ onSelect: (modelId: string) => void;
+}
+
+const ModelCard: React.FC
= ({
+ model,
+ variant = "default",
+ disabled = false,
+ badgeText,
+ className = "",
+ onSelect,
+}) => {
+ const isFeatured = variant === "featured";
+
+ const baseButtonClasses =
+ "relative rounded-xl p-3 p-4 text-left transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed focus:outline-none focus:ring-2 focus:ring-logo-primary/25 active:scale-[0.98] cursor-pointer group";
+
+ const variantClasses = isFeatured
+ ? "border-2 border-logo-primary/40 bg-logo-primary/5 hover:border-logo-primary/60 hover:bg-logo-primary/10 hover:shadow-lg hover:scale-[1.02] disabled:hover:border-logo-primary/40 disabled:hover:bg-logo-primary/5 disabled:hover:shadow-none disabled:hover:scale-100"
+ : "border-2 border-mid-gray/20 hover:border-logo-primary/50 hover:bg-logo-primary/5 hover:shadow-lg hover:scale-[1.02] disabled:hover:border-mid-gray/20 disabled:hover:bg-transparent disabled:hover:shadow-none disabled:hover:scale-100";
+
+ const titleClasses =
+ "text-lg font-semibold text-text group-hover:text-logo-primary transition-colors";
+
+ const descriptionClasses = "text-text/60 text-sm leading-relaxed";
+
+ const sizeRowClasses =
+ "mt-1 flex items-center gap-2 text-xs text-text/60 tabular-nums";
+
+ const containerSpacing = "space-y-3";
+
+ const sizeIconClasses = "h-3.5 w-3.5 text-text/45";
+
+ return (
+
+ );
+};
+
+export default ModelCard;
diff --git a/src/components/onboarding/Onboarding.tsx b/src/components/onboarding/Onboarding.tsx
index c2f7988..92b3b32 100644
--- a/src/components/onboarding/Onboarding.tsx
+++ b/src/components/onboarding/Onboarding.tsx
@@ -1,6 +1,7 @@
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;
@@ -58,57 +59,32 @@ const Onboarding: React.FC = ({ onModelSelected }) => {
)}
-
- {/* Recommended model - full width */}
- {availableModels
- .filter((model) => getRecommendedBadge(model.id))
- .map((model) => (
-