File size: 4,627 Bytes
2beb552
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
import { Zap, Square } from "lucide-react";

interface ControlsProps {
  quality: number;
  setQuality: (value: number) => void;
  speed: number;
  setSpeed: (value: number) => void;
  voice: string;
  setVoice: (value: string) => void;
  onGenerate: () => void;
  onStop: () => void;
  isGenerating: boolean;
  canGenerate: boolean;
  pipelineReady: boolean;
  progress?: number;
  loadingProgress: number;
}

export const Controls = ({
  quality,
  setQuality,
  speed,
  setSpeed,
  voice,
  setVoice,
  onGenerate,
  onStop,
  isGenerating,
  canGenerate,
  pipelineReady,
  progress,
  loadingProgress,
}: ControlsProps) => {
  return (
    <div className="w-full md:w-1/2 p-8 bg-[#F9FAFB] flex flex-col gap-8 border-t md:border-t-0">
      <div className="flex items-center gap-6">
        <span className="font-semibold text-gray-900">Voice:</span>
        <div className="flex gap-4 text-sm">
          {["Female", "Male"].map((v) => (
            <button
              key={v}
              onClick={() => setVoice(v)}
              className={`pb-1 transition-all font-medium border-b-2 ${
                voice === v ? "text-blue-600 border-blue-600" : "text-gray-400 hover:text-gray-600 border-transparent"
              }`}
            >
              {v}
            </button>
          ))}
        </div>
      </div>

      <div>
        <div className="flex justify-between mb-3 items-end">
          <span className="font-semibold text-gray-900 text-sm">
            Quality (Steps): <span className="text-base">{quality}</span>
          </span>
          <span className="text-gray-400 text-xs italic">Higher = Better quality but slower</span>
        </div>
        <input
          type="range"
          min="1"
          max="50"
          value={quality}
          onChange={(e) => setQuality(parseInt(e.target.value))}
          className="w-full h-1.5 bg-gray-300 rounded-lg appearance-none cursor-pointer accent-gray-900 hover:accent-blue-600"
        />
        <div className="w-full flex justify-center mt-1">
          <div className="w-0.5 h-1 bg-gray-300"></div>
        </div>
      </div>

      <div>
        <div className="flex justify-between mb-3 items-end">
          <span className="font-semibold text-gray-900 text-sm">
            Speed: <span className="text-base">{speed.toFixed(2)}x</span>
          </span>
          <span className="text-gray-400 text-xs italic">Higher = faster speech</span>
        </div>
        <input
          type="range"
          min="0.8"
          max="1.2"
          step="0.01"
          value={speed}
          onChange={(e) => setSpeed(parseFloat(e.target.value))}
          className="w-full h-1.5 bg-gray-300 rounded-lg appearance-none cursor-pointer accent-gray-900 hover:accent-blue-600"
        />
        <div className="w-full flex justify-center mt-1">
          <div className="w-0.5 h-1 bg-gray-300"></div>
        </div>
      </div>

      <div className="mt-auto pt-4 flex gap-2">
        <button
          onClick={onGenerate}
          disabled={isGenerating || !canGenerate}
          className={`
            flex-1 py-4 rounded-lg font-bold text-lg flex items-center justify-center gap-3 shadow-sm transition-all
            ${
              isGenerating || !canGenerate
                ? "bg-gray-200 text-gray-400 cursor-not-allowed"
                : "bg-yellow-400 text-gray-900 hover:bg-yellow-300 active:scale-[0.99]"
            }
          `}
        >
          {isGenerating ? (
            <>
              <div className="animate-spin rounded-full h-5 w-5 border-b-2 border-gray-400"></div>
              <span>
                Generating... {progress !== undefined && <span className="font-mono">({Math.round(progress)}%)</span>}
              </span>
            </>
          ) : (
            <>
              <Zap size={20} className={!canGenerate ? "fill-gray-400" : "fill-black"} />
              {pipelineReady ? (
                "Generate Speech"
              ) : (
                <span>
                  Loading Model...
                  {loadingProgress > 0 && <span className="font-mono"> ({Math.round(loadingProgress)}%)</span>}
                </span>
              )}
            </>
          )}
        </button>
        {isGenerating && (
          <button
            onClick={onStop}
            className="px-6 rounded-lg font-bold text-lg flex items-center justify-center shadow-sm transition-all bg-red-100 text-red-600 hover:bg-red-200 active:scale-[0.99]"
          >
            <Square size={20} fill="currentColor" />
          </button>
        )}
      </div>
    </div>
  );
};