Skip to content
This repository has been archived by the owner on Sep 25, 2024. It is now read-only.

Commit

Permalink
Merge pull request #70 from MichaelYuhe/feat/beta-feature-flags
Browse files Browse the repository at this point in the history
refactor: options page
  • Loading branch information
MichaelYuhe authored Dec 12, 2023
2 parents 465fca4 + 0d7d841 commit 41b5929
Show file tree
Hide file tree
Showing 2 changed files with 170 additions and 67 deletions.
27 changes: 27 additions & 0 deletions src/components/Switch.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
export default function Switch({
isChecked,
onChange,
text,
}: {
isChecked: boolean;
onChange: () => void;
text: string;
}) {
return (
<div className="flex items-center mt-2">
<label className="relative inline-flex cursor-pointer items-center">
<input
id="switch"
type="checkbox"
checked={isChecked}
className="peer sr-only"
onClick={onChange}
/>
<label htmlFor="switch" className="hidden"></label>
<div className="peer h-6 w-11 rounded-full border bg-slate-200 after:absolute after:left-[2px] after:top-0.5 after:h-5 after:w-5 after:rounded-full after:border after:border-gray-300 after:bg-white after:transition-all after:content-[''] peer-checked:bg-primary peer-checked:after:translate-x-full peer-checked:after:border-white peer-focus:ring-green-300"></div>
</label>

<span className="ml-3 text-gray-900 text-sm">{text}</span>
</div>
);
}
210 changes: 143 additions & 67 deletions src/options.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,24 @@ import React, { ChangeEvent, useCallback, useEffect, useState } from "react";
import { createRoot } from "react-dom/client";
import "./options.css";
import { DEFAULT_PROMPT, getStorage, setStorage } from "./utils";
import Switch from "./components/Switch";

const Options = () => {
const TABS = [
"Basic Settings",
"Prompt Settings",
"Style Settings",
"Feature Flags",
];

function BasicSettings() {
const [model, setModel] = useState<string | undefined>("gpt-3.5-turbo");
const [apiURL, setApiURL] = useState<string | undefined>(
"https://api.openai.com/v1/chat/completions"
);
const [prompt, setPrompt] = useState<string | undefined>(DEFAULT_PROMPT);
const [isPromptValid, setIsPromptValid] = useState<boolean>(true);
const promptFormatWarning: string = `{{tabURL}}, {{tabTitle}} and {{types}} must be in the prompt`;

useEffect(() => {
getStorage<string>("model").then(setModel);
getStorage<string>("apiURL").then(setApiURL);
getStorage<string>("prompt").then(setPrompt);
}, []);

const updateModel = useCallback((e: ChangeEvent<HTMLSelectElement>) => {
Expand All @@ -28,6 +32,55 @@ const Options = () => {
setStorage("apiURL", e.target.value);
}, []);

return (
<div className="flex flex-col gap-y-8 p-4">
<div className="flex flex-col gap-y-2">
<label htmlFor="models" className="text-xl font-medium">
Choose an model
</label>

<select
value={model}
onChange={updateModel}
id="models"
className="bg-gray-50 border w-64 border-gray-300 text-gray-900 text-sm rounded-lg
focus:ring-blue-500 focus:border-blue-500 block"
>
<option selected>Choose a model</option>
<option value="gpt-4">GPT 4</option>
<option value="gpt-4-32k">GPT 4 32k</option>
<option value="gpt-3.5-turbo-1106">GPT 3.5 turbo 1106</option>
<option value="gpt-3.5-turbo">GPT 3.5 turbo</option>
</select>
</div>

<div className="flex flex-col gap-y-2">
<label htmlFor="api_url" className="text-xl font-medium">
API URL
</label>

<input
className="bg-gray-50 border w-64 border-gray-300 text-gray-900 text-sm rounded-lg
focus:ring-blue-500 focus:border-blue-500 block"
value={apiURL}
onChange={updateApiURL}
id="api_url"
/>
</div>
</div>
);
}

function PromptSettings() {
const [prompt, setPrompt] = useState<string | undefined>(DEFAULT_PROMPT);
const [isPromptValid, setIsPromptValid] = useState<boolean>(true);

const promptFormatWarning: string = `{{tabURL}}, {{tabTitle}} and {{types}} must be in the prompt`;

useEffect(() => {
getStorage<string>("prompt").then(setPrompt);
}, []);

const updatePrompt = useCallback((e: ChangeEvent<HTMLTextAreaElement>) => {
const newPrompt: string = e.target.value;
setIsPromptValid(
Expand All @@ -42,76 +95,99 @@ const Options = () => {
}, []);

return (
<div className="w-screen h-screen flex justify-center p-12">
<div className="w-full max-w-4xl flex flex-col gap-y-8">
<h1 className="text-4xl font-semibold border-b border-black pb-2">
Options Page
</h1>

<div className="flex flex-col gap-y-2">
<label htmlFor="models" className="text-xl font-medium">
Choose an model
<div className="flex flex-col gap-y-8 p-4">
<div className="flex flex-col gap-y-2">
<label htmlFor="prompt" className="text-xl font-medium">
Prompt
</label>
{isPromptValid && (
<label
htmlFor="prompt"
className="text-sm font-normal w-64 text-blue-500"
>
{promptFormatWarning}
</label>
)}

<select
value={model}
onChange={updateModel}
id="models"
className="bg-gray-50 border w-64 border-gray-300 text-gray-900 text-sm rounded-lg
focus:ring-blue-500 focus:border-blue-500 block"
{!isPromptValid && (
<div
className=" w-64 bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative"
role="alert"
>
<option selected>Choose a model</option>
<option value="gpt-4">GPT 4</option>
<option value="gpt-4-32k">GPT 4 32k</option>
<option value="gpt-3.5-turbo-1106">GPT 3.5 turbo 1106</option>
<option value="gpt-3.5-turbo">GPT 3.5 turbo</option>
</select>
</div>
<span className="block sm:inline">{promptFormatWarning}</span>
</div>
)}

<div className="flex flex-col gap-y-2">
<label htmlFor="api_url" className="text-xl font-medium">
API URL
</label>

<input
className="bg-gray-50 border w-64 border-gray-300 text-gray-900 text-sm rounded-lg
<textarea
className="bg-gray-50 border w-64 h-64 border-gray-300 text-gray-900 text-sm rounded-lg
focus:ring-blue-500 focus:border-blue-500 block"
value={apiURL}
onChange={updateApiURL}
id="api_url"
/>
</div>
value={prompt}
onChange={updatePrompt}
id="prompt"
/>
</div>
</div>
);
}

<div className="flex flex-col gap-y-2">
<label htmlFor="prompt" className="text-xl font-medium">
Prompt
</label>
{isPromptValid && (
<label
htmlFor="prompt"
className="text-sm font-normal w-64 text-blue-500"
>
{promptFormatWarning}
</label>
)}

{!isPromptValid && (
<div
className=" w-64 bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative"
role="alert"
>
<span className="block sm:inline">{promptFormatWarning}</span>
</div>
)}
function StyleSettings() {
return <div className="flex flex-col p-4"></div>;
}

<textarea
className="bg-gray-50 border w-64 h-64 border-gray-300 text-gray-900 text-sm rounded-lg
focus:ring-blue-500 focus:border-blue-500 block"
value={prompt}
onChange={updatePrompt}
id="prompt"
/>
function FeatureFlags() {
const [isColorsEnabled, setIsColorsEnabled] = useState<boolean | undefined>(
false
);

useEffect(() => {
getStorage<boolean>("colorsEnabled").then(setIsColorsEnabled);
}, []);

const updateColorsEnabled = useCallback(() => {
setIsColorsEnabled(!isColorsEnabled);
setStorage("colorsEnabled", !isColorsEnabled);
}, [isColorsEnabled]);

return (
<div className="flex flex-col p-4">
<Switch
isChecked={isColorsEnabled !== undefined ? isColorsEnabled : false}
onChange={() => {
updateColorsEnabled();
}}
text="Enable Colors Customization"
/>
</div>
);
}

const Options = () => {
const [activeTab, setActiveTab] = useState<string>(TABS[0]);

return (
<div className="w-screen h-screen bg-slate-100 flex justify-center p-12">
<div className="w-full max-w-4xl flex flex-col">
<div className="flex w-full p-2 bg-slate-200 rounded-2xl">
{TABS.map((tab) => (
<button
className={`${
activeTab === tab ? "bg-white" : ""
} flex-1 text-center py-4 font-medium rounded-lg`}
onClick={() => setActiveTab(tab)}
>
{tab}
</button>
))}
</div>

{/* Basic Settings */}
{activeTab === TABS[0] && <BasicSettings />}

{activeTab === TABS[1] && <PromptSettings />}

{activeTab === TABS[2] && <StyleSettings />}

{activeTab === TABS[3] && <FeatureFlags />}
</div>
</div>
);
Expand Down

0 comments on commit 41b5929

Please sign in to comment.