File size: 9,622 Bytes
c2c7576
ea6c2a8
a306cb1
c2c7576
 
 
ea6c2a8
c2c7576
 
 
 
 
 
a306cb1
 
c2c7576
 
 
 
 
 
a306cb1
 
c2c7576
a306cb1
 
 
 
 
 
 
 
 
 
ea6c2a8
c2c7576
ea6c2a8
c2c7576
 
 
 
ea6c2a8
c2c7576
ea6c2a8
 
 
c9c8965
ea6c2a8
 
 
 
c2c7576
ea6c2a8
 
 
c9c8965
ea6c2a8
 
 
 
 
c2c7576
 
 
 
 
 
070f5ee
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f3119fd
070f5ee
 
 
 
 
 
 
 
 
 
c2c7576
 
 
 
 
 
070f5ee
c2c7576
 
 
070f5ee
c2c7576
070f5ee
c2c7576
 
 
 
070f5ee
 
c2c7576
 
 
070f5ee
c2c7576
 
 
070f5ee
 
c2c7576
 
070f5ee
c2c7576
 
 
a306cb1
c2c7576
a306cb1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c2c7576
ea6c2a8
 
 
 
a306cb1
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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
/* eslint-disable @typescript-eslint/no-explicit-any */
import classNames from "classnames";
import { useEffect } from "react";
import { PiGearSixFill } from "react-icons/pi";
// @ts-expect-error not needed
import { PROVIDERS } from "./../../../utils/providers";

function Settings({
  open,
  onClose,
  provider,
  error,
  onChange,
  localSettings,
  setLocalSettings,
}: {
  open: boolean;
  provider: string;
  error?: string;
  onClose: React.Dispatch<React.SetStateAction<boolean>>;
  onChange: (provider: string) => void;
  localSettings: any;
  setLocalSettings: React.Dispatch<React.SetStateAction<any>>;
}) {

  // persist the local settings to local storage
  const persistLocalSettings = () => {
    localStorage.setItem('localSettings', JSON.stringify(localSettings));
  };

  useEffect(() => {
    persistLocalSettings();
  }, [localSettings]);

  return (
    <div className="">
      <button
        className="relative overflow-hidden cursor-pointer flex-none flex items-center justify-center rounded-full text-base font-semibold size-8 text-center bg-gray-800 hover:bg-gray-700 text-gray-100 shadow-sm dark:shadow-highlight/20"
        onClick={() => {
          onClose((prev) => !prev);
        }}
      >
        <PiGearSixFill />
      </button>
      <div
        className={classNames(
          "h-screen w-screen bg-black/20 fixed left-0 top-0 z-40",
          {
            "opacity-0 pointer-events-none": !open,
          }
        )}
        onClick={() => onClose(false)}
      ></div>
      <div
        className={classNames(
          "absolute top-0 -translate-y-[calc(100%+16px)] right-0 z-40 w-96 bg-white border border-gray-200 rounded-lg shadow-lg transition-all duration-75 overflow-hidden",
          {
            "opacity-0 pointer-events-none": !open,
          }
        )}
      >
        <header className="flex items-center text-sm px-4 py-2 border-b border-gray-200 gap-2 bg-gray-100 font-semibold text-gray-700">
          <span className="text-xs bg-blue-500/10 text-blue-500 rounded-full pl-1.5 pr-2.5 py-0.5 flex items-center justify-start gap-1.5">
            Provider
          </span>
          Customize Settings
        </header>
        <main className="px-4 pt-3 pb-4 space-y-4">
          {/* toggle using tailwind css */}
          <div>
            <div className="flex items-center justify-between">
              <p className="text-gray-800 text-sm font-medium flex items-center justify-between">
                Use auto-provider
              </p>
              <div
                className={classNames(
                  "bg-gray-200 rounded-full w-10 h-6 flex items-center justify-between p-1 cursor-pointer transition-all duration-200",
                  {
                    "!bg-blue-500": provider === "auto",
                  }
                )}
                onClick={() => {
                  onChange(provider === "auto" ? "fireworks-ai" : "auto");
                }}
              >
                <div
                  className={classNames(
                    "w-4 h-4 rounded-full shadow-md transition-all duration-200 bg-white",
                    {
                      "translate-x-4": provider === "auto",
                    }
                  )}
                />
              </div>
            </div>
            <p className="text-xs text-gray-500 mt-2">
              We'll automatically select the best provider for you based on your
              prompt.
            </p>
          </div>
          {error !== "" && (
            <p className="text-red-500 text-sm font-medium mb-2 flex items-center justify-between bg-red-500/10 p-2 rounded-md">
              {error}
            </p>
          )}
          <label className="block">
            <p className="text-gray-800 text-sm font-medium mb-2 flex items-center justify-between">
              Inference Provider
            </p>
            <div className="grid grid-cols-2 gap-1.5">
              {Object.keys(PROVIDERS).map((id: string) => (
                <div
                  key={id}
                  className={classNames(
                    "text-gray-600 text-sm font-medium cursor-pointer border p-2 rounded-md flex items-center justify-start gap-2",
                    {
                      "bg-blue-500/10 border-blue-500/15 text-blue-500":
                        id === provider,
                      "hover:bg-gray-100 border-gray-100": id !== provider,
                    }
                  )}
                  onClick={() => {
                    onChange(id);
                  }}
                >
                  <img
                    src={`/providers/${id}.svg`}
                    alt={PROVIDERS[id].name}
                    className="size-5"
                  />
                  {PROVIDERS[id].name}
                </div>
              ))}
            </div>
            <hr className="text-gray-800 text-sm font-medium mb-2" />
          </label>
          {provider === "local" && (
            <div className="space-y-2">
              <p className="text-gray-800 text-sm font-medium mb-2">
                Make sure to run the local server first
              </p>
              <hr className="text-gray-800 text-sm font-medium mb-2" />
              <label className="block">
                <p className="text-gray-800 text-sm font-medium mb-1">
                  API Key
                </p>
                <input
                  type="text"
                  value={localSettings.apiKey}
                  onChange={(e) => {
                    setLocalSettings({ ...localSettings, apiKey: e.target.value });
                  }}
                  className="w-full border border-gray-200 rounded-md px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
                />
              </label>
              <label className="block">
                <p className="text-gray-800 text-sm font-medium mb-1">
                  API URL
                </p>
                <input
                  type="text"
                  value={localSettings.apiUrl || "http://localhost:11434/v1"}
                  onChange={(e) => {
                    setLocalSettings({ ...localSettings, apiUrl: e.target.value });
                  }}
                  className="w-full border border-gray-200 rounded-md px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
                />
              </label>
              <label className="block">
                <p className="text-gray-800 text-sm font-medium mb-1">
                  Model
                </p>
                <input
                  type="text"
                  value={localSettings.model || "gemma3:1b"}
                  onChange={(e) => {
                    setLocalSettings({ ...localSettings, model: e.target.value });
                  }}
                  className="w-full border border-gray-200 rounded-md px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
                />
              </label>
            </div>
          )}
          {provider === "openrouter" && (
            <div className="space-y-2">
              <p className="text-gray-800 text-sm font-medium mb-2">
                Get your OpenRouter API key from
                <a
                  href="https://openrouter.ai/
                  "
                  target="_blank"
                  rel="noopener noreferrer"
                  className="text-blue-500"
                >
                  here
                </a>
              </p>
              <hr className="text-gray-800 text-sm font-medium mb-2" />
              <label className="block">
                <p className="text-gray-800 text-sm font-medium mb-1">
                  API Key
                </p>
                <input
                  type="text"
                  value={localSettings.openRouterApiKey}
                  onChange={(e) => {
                    setLocalSettings({ ...localSettings, openRouterApiKey: e.target.value });
                  }}
                  className="w-full border border-gray-200 rounded-md px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
                />
              </label>
              <label className="block">
                <p className="text-gray-800 text-sm font-medium mb-1">
                  Base URL
                </p>
                <input
                  type="text"
                  value={localSettings.openRouterApiUrl || "https://openrouter.ai/api/v1"}
                  onChange={(e) => {
                    setLocalSettings({ ...localSettings, openRouterApiUrl: e.target.value });
                  }}
                  className="w-full border border-gray-200 rounded-md px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
                />
              </label>
              <label className="block">
                <p className="text-gray-800 text-sm font-medium mb-1">
                  Model
                </p>
                <input
                  type="text"
                  value={localSettings.openRouterModel || "deepseek/deepseek-chat-v3-0324:free"}
                  onChange={(e) => {
                    setLocalSettings({ ...localSettings, openRouterModel: e.target.value });
                  }}
                  className="w-full border border-gray-200 rounded-md px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
                />
              </label>
            </div>
          )}
        </main>
      </div>
    </div>
  );
}
export default Settings;