fix: clean up

fix: typecheck / lint
This commit is contained in:
Dustin Loring 2024-12-07 10:53:33 -05:00
parent 42ebd3d50e
commit a2acc77e89
8 changed files with 70 additions and 67 deletions

View File

@ -21,12 +21,12 @@ jobs:
- name: Update commit file - name: Update commit file
run: | run: |
echo "{ \"commit\": \"$COMMIT_HASH\" }" > commit.json echo "{ \"commit\": \"$COMMIT_HASH\" }" > app/commit.json
- name: Commit and push the update - name: Commit and push the update
run: | run: |
git config --global user.name "github-actions[bot]" git config --global user.name "github-actions[bot]"
git config --global user.email "github-actions[bot]@users.noreply.github.com" git config --global user.email "github-actions[bot]@users.noreply.github.com"
git add commit.json git add app/commit.json
git commit -m "chore: update commit hash to $COMMIT_HASH" git commit -m "chore: update commit hash to $COMMIT_HASH"
git push git push

1
app/commit.json Normal file
View File

@ -0,0 +1 @@
{ "commit": "228cf1f34fd64b6960460f84c9db47bd7ef03150" }

View File

@ -89,6 +89,7 @@ export const BaseChat = React.forwardRef<HTMLDivElement, BaseChatProps>(
const TEXTAREA_MAX_HEIGHT = chatStarted ? 400 : 200; const TEXTAREA_MAX_HEIGHT = chatStarted ? 400 : 200;
const [apiKeys, setApiKeys] = useState<Record<string, string>>(() => { const [apiKeys, setApiKeys] = useState<Record<string, string>>(() => {
const savedKeys = Cookies.get('apiKeys'); const savedKeys = Cookies.get('apiKeys');
if (savedKeys) { if (savedKeys) {
try { try {
return JSON.parse(savedKeys); return JSON.parse(savedKeys);
@ -97,6 +98,7 @@ export const BaseChat = React.forwardRef<HTMLDivElement, BaseChatProps>(
return {}; return {};
} }
} }
return {}; return {};
}); });
const [modelList, setModelList] = useState(MODEL_LIST); const [modelList, setModelList] = useState(MODEL_LIST);
@ -108,15 +110,17 @@ export const BaseChat = React.forwardRef<HTMLDivElement, BaseChatProps>(
// Load enabled providers from cookies // Load enabled providers from cookies
const [enabledProviders, setEnabledProviders] = useState(() => { const [enabledProviders, setEnabledProviders] = useState(() => {
const savedProviders = Cookies.get('providers'); const savedProviders = Cookies.get('providers');
if (savedProviders) { if (savedProviders) {
try { try {
const parsedProviders = JSON.parse(savedProviders); const parsedProviders = JSON.parse(savedProviders);
return PROVIDER_LIST.filter(p => parsedProviders[p.name]); return PROVIDER_LIST.filter((p) => parsedProviders[p.name]);
} catch (error) { } catch (error) {
console.error('Failed to parse providers from cookies:', error); console.error('Failed to parse providers from cookies:', error);
return PROVIDER_LIST; return PROVIDER_LIST;
} }
} }
return PROVIDER_LIST; return PROVIDER_LIST;
}); });
@ -124,10 +128,11 @@ export const BaseChat = React.forwardRef<HTMLDivElement, BaseChatProps>(
useEffect(() => { useEffect(() => {
const updateProvidersFromCookies = () => { const updateProvidersFromCookies = () => {
const savedProviders = Cookies.get('providers'); const savedProviders = Cookies.get('providers');
if (savedProviders) { if (savedProviders) {
try { try {
const parsedProviders = JSON.parse(savedProviders); const parsedProviders = JSON.parse(savedProviders);
setEnabledProviders(PROVIDER_LIST.filter(p => parsedProviders[p.name])); setEnabledProviders(PROVIDER_LIST.filter((p) => parsedProviders[p.name]));
} catch (error) { } catch (error) {
console.error('Failed to parse providers from cookies:', error); console.error('Failed to parse providers from cookies:', error);
} }
@ -135,7 +140,9 @@ export const BaseChat = React.forwardRef<HTMLDivElement, BaseChatProps>(
}; };
updateProvidersFromCookies(); updateProvidersFromCookies();
const interval = setInterval(updateProvidersFromCookies, 1000); const interval = setInterval(updateProvidersFromCookies, 1000);
return () => clearInterval(interval); return () => clearInterval(interval);
}, [PROVIDER_LIST]); }, [PROVIDER_LIST]);
@ -228,23 +235,6 @@ export const BaseChat = React.forwardRef<HTMLDivElement, BaseChatProps>(
} }
}; };
const updateApiKey = (provider: string, key: string) => {
try {
const updatedApiKeys = { ...apiKeys, [provider]: key };
setApiKeys(updatedApiKeys);
// Save updated API keys to cookies with 30 day expiry and secure settings
Cookies.set('apiKeys', JSON.stringify(updatedApiKeys), {
expires: 30, // 30 days
secure: true, // Only send over HTTPS
sameSite: 'strict', // Protect against CSRF
path: '/', // Accessible across the site
});
} catch (error) {
console.error('Error saving API keys to cookies:', error);
}
};
const handleFileUpload = () => { const handleFileUpload = () => {
const input = document.createElement('input'); const input = document.createElement('input');
input.type = 'file'; input.type = 'file';

View File

@ -24,15 +24,17 @@ export const ModelSelector = ({
// Load enabled providers from cookies // Load enabled providers from cookies
const [enabledProviders, setEnabledProviders] = useState(() => { const [enabledProviders, setEnabledProviders] = useState(() => {
const savedProviders = Cookies.get('providers'); const savedProviders = Cookies.get('providers');
if (savedProviders) { if (savedProviders) {
try { try {
const parsedProviders = JSON.parse(savedProviders); const parsedProviders = JSON.parse(savedProviders);
return providerList.filter(p => parsedProviders[p.name]); return providerList.filter((p) => parsedProviders[p.name]);
} catch (error) { } catch (error) {
console.error('Failed to parse providers from cookies:', error); console.error('Failed to parse providers from cookies:', error);
return providerList; return providerList;
} }
} }
return providerList; return providerList;
}); });
@ -41,19 +43,21 @@ export const ModelSelector = ({
// Function to update providers from cookies // Function to update providers from cookies
const updateProvidersFromCookies = () => { const updateProvidersFromCookies = () => {
const savedProviders = Cookies.get('providers'); const savedProviders = Cookies.get('providers');
if (savedProviders) { if (savedProviders) {
try { try {
const parsedProviders = JSON.parse(savedProviders); const parsedProviders = JSON.parse(savedProviders);
const newEnabledProviders = providerList.filter(p => parsedProviders[p.name]); const newEnabledProviders = providerList.filter((p) => parsedProviders[p.name]);
setEnabledProviders(newEnabledProviders); setEnabledProviders(newEnabledProviders);
// If current provider is disabled, switch to first enabled provider // If current provider is disabled, switch to first enabled provider
if (provider && !parsedProviders[provider.name] && newEnabledProviders.length > 0) { if (provider && !parsedProviders[provider.name] && newEnabledProviders.length > 0) {
const firstEnabledProvider = newEnabledProviders[0]; const firstEnabledProvider = newEnabledProviders[0];
setProvider?.(firstEnabledProvider); setProvider?.(firstEnabledProvider);
// Also update the model to the first available one for the new provider // Also update the model to the first available one for the new provider
const firstModel = modelList.find(m => m.provider === firstEnabledProvider.name); const firstModel = modelList.find((m) => m.provider === firstEnabledProvider.name);
if (firstModel) { if (firstModel) {
setModel?.(firstModel.name); setModel?.(firstModel.name);
} }
@ -77,7 +81,8 @@ export const ModelSelector = ({
return ( return (
<div className="mb-2 p-4 rounded-lg border border-bolt-elements-borderColor bg-bolt-elements-prompt-background text-bolt-elements-textPrimary"> <div className="mb-2 p-4 rounded-lg border border-bolt-elements-borderColor bg-bolt-elements-prompt-background text-bolt-elements-textPrimary">
<p className="text-center"> <p className="text-center">
No providers are currently enabled. Please enable at least one provider in the settings to start using the chat. No providers are currently enabled. Please enable at least one provider in the settings to start using the
chat.
</p> </p>
</div> </div>
); );

View File

@ -1,14 +1,14 @@
import * as RadixDialog from '@radix-ui/react-dialog'; import * as RadixDialog from '@radix-ui/react-dialog';
import { motion } from 'framer-motion'; import { motion } from 'framer-motion';
import { useState, useEffect }from 'react'; import { useState } from 'react';
import { classNames } from '~/utils/classNames'; import { classNames } from '~/utils/classNames';
import { Dialog, DialogTitle, dialogVariants, dialogBackdropVariants } from './Dialog'; import { DialogTitle, dialogVariants, dialogBackdropVariants } from './Dialog';
import { IconButton } from './IconButton'; import { IconButton } from './IconButton';
import { providersList } from '~/lib/stores/settings'; import { providersList } from '~/lib/stores/settings';
import { db, getAll, deleteById } from '~/lib/persistence'; import { db, getAll, deleteById } from '~/lib/persistence';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
import { useNavigate } from '@remix-run/react'; import { useNavigate } from '@remix-run/react';
import commit from '../../../commit.json'; import commit from '~/commit.json';
import Cookies from 'js-cookie'; import Cookies from 'js-cookie';
interface SettingsProps { interface SettingsProps {
@ -31,6 +31,7 @@ export const Settings = ({ open, onClose }: SettingsProps) => {
// Load base URLs from cookies // Load base URLs from cookies
const [baseUrls, setBaseUrls] = useState(() => { const [baseUrls, setBaseUrls] = useState(() => {
const savedUrls = Cookies.get('providerBaseUrls'); const savedUrls = Cookies.get('providerBaseUrls');
if (savedUrls) { if (savedUrls) {
try { try {
return JSON.parse(savedUrls); return JSON.parse(savedUrls);
@ -43,6 +44,7 @@ export const Settings = ({ open, onClose }: SettingsProps) => {
}; };
} }
} }
return { return {
Ollama: 'http://localhost:11434', Ollama: 'http://localhost:11434',
LMStudio: 'http://localhost:1234', LMStudio: 'http://localhost:1234',
@ -51,9 +53,10 @@ export const Settings = ({ open, onClose }: SettingsProps) => {
}); });
const handleBaseUrlChange = (provider: string, url: string) => { const handleBaseUrlChange = (provider: string, url: string) => {
setBaseUrls(prev => { setBaseUrls((prev: Record<string, string>) => {
const newUrls = { ...prev, [provider]: url }; const newUrls = { ...prev, [provider]: url };
Cookies.set('providerBaseUrls', JSON.stringify(newUrls)); Cookies.set('providerBaseUrls', JSON.stringify(newUrls));
return newUrls; return newUrls;
}); });
}; };
@ -62,46 +65,52 @@ export const Settings = ({ open, onClose }: SettingsProps) => {
{ id: 'chat-history', label: 'Chat History', icon: 'i-ph:book' }, { id: 'chat-history', label: 'Chat History', icon: 'i-ph:book' },
{ id: 'providers', label: 'Providers', icon: 'i-ph:key' }, { id: 'providers', label: 'Providers', icon: 'i-ph:key' },
{ id: 'features', label: 'Features', icon: 'i-ph:star' }, { id: 'features', label: 'Features', icon: 'i-ph:star' },
...(isDebugEnabled ? [{ id: 'debug', label: 'Debug Tab', icon: 'i-ph:bug' }] : []), ...(isDebugEnabled ? [{ id: 'debug' as TabType, label: 'Debug Tab', icon: 'i-ph:bug' }] : []),
]; ];
// Load providers from cookies on mount // Load providers from cookies on mount
const [providers, setProviders] = useState(() => { const [providers, setProviders] = useState(() => {
const savedProviders = Cookies.get('providers'); const savedProviders = Cookies.get('providers');
if (savedProviders) { if (savedProviders) {
try { try {
const parsedProviders = JSON.parse(savedProviders); const parsedProviders = JSON.parse(savedProviders);
// Merge saved enabled states with the base provider list // Merge saved enabled states with the base provider list
return providersList.map(provider => ({ return providersList.map((provider) => ({
...provider, ...provider,
isEnabled: parsedProviders[provider.name] || false isEnabled: parsedProviders[provider.name] || false,
})); }));
} catch (error) { } catch (error) {
console.error('Failed to parse providers from cookies:', error); console.error('Failed to parse providers from cookies:', error);
} }
} }
return providersList; return providersList;
}); });
const handleToggleProvider = (providerName: string) => { const handleToggleProvider = (providerName: string) => {
setProviders((prevProviders) => { setProviders((prevProviders) => {
const newProviders = prevProviders.map((provider) => const newProviders = prevProviders.map((provider) =>
provider.name === providerName ? { ...provider, isEnabled: !provider.isEnabled } : provider provider.name === providerName ? { ...provider, isEnabled: !provider.isEnabled } : provider,
); );
// Save to cookies // Save to cookies
const enabledStates = newProviders.reduce((acc, provider) => ({ const enabledStates = newProviders.reduce(
...acc, (acc, provider) => ({
[provider.name]: provider.isEnabled ...acc,
}), {}); [provider.name]: provider.isEnabled,
}),
{},
);
Cookies.set('providers', JSON.stringify(enabledStates)); Cookies.set('providers', JSON.stringify(enabledStates));
return newProviders; return newProviders;
}); });
}; };
const filteredProviders = providers const filteredProviders = providers
.filter(provider => provider.name.toLowerCase().includes(searchTerm.toLowerCase())) .filter((provider) => provider.name.toLowerCase().includes(searchTerm.toLowerCase()))
.sort((a, b) => a.name.localeCompare(b.name)); .sort((a, b) => a.name.localeCompare(b.name));
const handleCopyToClipboard = () => { const handleCopyToClipboard = () => {
@ -141,11 +150,12 @@ export const Settings = ({ open, onClose }: SettingsProps) => {
try { try {
setIsDeleting(true); setIsDeleting(true);
const allChats = await getAll(db); const allChats = await getAll(db);
// Delete all chats one by one // Delete all chats one by one
await Promise.all(allChats.map(chat => deleteById(db!, chat.id))); await Promise.all(allChats.map((chat) => deleteById(db!, chat.id)));
toast.success('All chats deleted successfully'); toast.success('All chats deleted successfully');
navigate('/', { replace: true }); navigate('/', { replace: true });
} catch (error) { } catch (error) {
@ -168,7 +178,7 @@ export const Settings = ({ open, onClose }: SettingsProps) => {
chats: allChats, chats: allChats,
exportDate: new Date().toISOString(), exportDate: new Date().toISOString(),
}; };
downloadAsJson(exportData, `all-chats-${new Date().toISOString()}.json`); downloadAsJson(exportData, `all-chats-${new Date().toISOString()}.json`);
toast.success('Chats exported successfully'); toast.success('Chats exported successfully');
} catch (error) { } catch (error) {
@ -207,9 +217,7 @@ export const Settings = ({ open, onClose }: SettingsProps) => {
onClick={() => setActiveTab(tab.id)} onClick={() => setActiveTab(tab.id)}
className={classNames( className={classNames(
'w-full flex items-center gap-2 px-4 py-3 rounded-lg text-left text-sm transition-all mb-2', 'w-full flex items-center gap-2 px-4 py-3 rounded-lg text-left text-sm transition-all mb-2',
activeTab === tab.id activeTab === tab.id ? 'bg-blue-600 text-white' : 'bg-gray-600 text-gray-200 hover:bg-blue-500',
? 'bg-blue-600 text-white'
: 'bg-gray-600 text-gray-200 hover:bg-blue-500'
)} )}
> >
<div className={tab.icon} /> <div className={tab.icon} />
@ -239,7 +247,7 @@ export const Settings = ({ open, onClose }: SettingsProps) => {
<div className="flex-1 flex flex-col p-8"> <div className="flex-1 flex flex-col p-8">
<DialogTitle className="flex-shrink-0 text-lg font-semibold text-white">Settings</DialogTitle> <DialogTitle className="flex-shrink-0 text-lg font-semibold text-white">Settings</DialogTitle>
<div className="flex-1 overflow-y-auto"> <div className="flex-1 overflow-y-auto">
{activeTab === 'chat-history' && ( {activeTab === 'chat-history' && (
<div className="p-4"> <div className="p-4">
<h3 className="text-lg font-medium text-white mb-4">Chat History</h3> <h3 className="text-lg font-medium text-white mb-4">Chat History</h3>
<button <button
@ -248,7 +256,7 @@ export const Settings = ({ open, onClose }: SettingsProps) => {
> >
Export All Chats Export All Chats
</button> </button>
<div className="bg-red-500 text-white rounded-lg p-4 mb-4"> <div className="bg-red-500 text-white rounded-lg p-4 mb-4">
<h4 className="font-semibold">Danger Area</h4> <h4 className="font-semibold">Danger Area</h4>
<p className="mb-2">This action cannot be undone!</p> <p className="mb-2">This action cannot be undone!</p>
@ -256,8 +264,8 @@ export const Settings = ({ open, onClose }: SettingsProps) => {
onClick={handleDeleteAllChats} onClick={handleDeleteAllChats}
disabled={isDeleting} disabled={isDeleting}
className={classNames( className={classNames(
"bg-red-700 text-white rounded-lg px-4 py-2 transition-colors duration-200", 'bg-red-700 text-white rounded-lg px-4 py-2 transition-colors duration-200',
isDeleting ? "opacity-50 cursor-not-allowed" : "hover:bg-red-800" isDeleting ? 'opacity-50 cursor-not-allowed' : 'hover:bg-red-800',
)} )}
> >
{isDeleting ? 'Deleting...' : 'Delete All Chats'} {isDeleting ? 'Deleting...' : 'Delete All Chats'}
@ -297,7 +305,7 @@ export const Settings = ({ open, onClose }: SettingsProps) => {
></div> ></div>
</label> </label>
</div> </div>
{/* Base URL input for configurable providers */} {/* Base URL input for configurable providers */}
{URL_CONFIGURABLE_PROVIDERS.includes(provider.name) && provider.isEnabled && ( {URL_CONFIGURABLE_PROVIDERS.includes(provider.name) && provider.isEnabled && (
<div className="mt-2"> <div className="mt-2">
@ -334,9 +342,7 @@ export const Settings = ({ open, onClose }: SettingsProps) => {
></div> ></div>
</label> </label>
</div> </div>
<div className="feature-row"> <div className="feature-row">{/* Your feature content here */}</div>
{/* Your feature content here */}
</div>
</div> </div>
)} )}
{activeTab === 'debug' && isDebugEnabled && ( {activeTab === 'debug' && isDebugEnabled && (
@ -348,7 +354,7 @@ export const Settings = ({ open, onClose }: SettingsProps) => {
> >
Copy to Clipboard Copy to Clipboard
</button> </button>
<h4 className="text-md font-medium text-white">System Information</h4> <h4 className="text-md font-medium text-white">System Information</h4>
<p className="text-white">OS: {navigator.platform}</p> <p className="text-white">OS: {navigator.platform}</p>
<p className="text-white">Browser: {navigator.userAgent}</p> <p className="text-white">Browser: {navigator.userAgent}</p>
@ -358,7 +364,9 @@ export const Settings = ({ open, onClose }: SettingsProps) => {
{providers {providers
.filter((provider) => provider.isEnabled) .filter((provider) => provider.isEnabled)
.map((provider) => ( .map((provider) => (
<li key={provider.name} className="text-white">{provider.name}</li> <li key={provider.name} className="text-white">
{provider.name}
</li>
))} ))}
</ul> </ul>
@ -384,4 +392,4 @@ export const Settings = ({ open, onClose }: SettingsProps) => {
</RadixDialog.Portal> </RadixDialog.Portal>
</RadixDialog.Root> </RadixDialog.Root>
); );
}; };

View File

@ -15,4 +15,4 @@ export const SettingsButton = memo(({ onClick }: SettingsButtonProps) => {
className="text-[#666] hover:text-bolt-elements-textPrimary hover:bg-bolt-elements-item-backgroundActive/10 transition-colors" className="text-[#666] hover:text-bolt-elements-textPrimary hover:bg-bolt-elements-item-backgroundActive/10 transition-colors"
/> />
); );
}); });

View File

@ -26,24 +26,24 @@ export const SettingsSlider = memo(<T,>({ selected, options, setSelected }: Sett
<motion.div <motion.div
className={classNames( className={classNames(
'absolute h-full bg-green-500 transition-all duration-300 rounded-lg', 'absolute h-full bg-green-500 transition-all duration-300 rounded-lg',
isLeftSelected ? 'left-0 w-1/2' : 'right-0 w-1/2' isLeftSelected ? 'left-0 w-1/2' : 'right-0 w-1/2',
)} )}
initial={false} initial={false}
animate={{ animate={{
x: isLeftSelected ? 0 : '100%', x: isLeftSelected ? 0 : '100%',
opacity: 0.2 opacity: 0.2,
}} }}
transition={{ transition={{
type: 'spring', type: 'spring',
stiffness: 300, stiffness: 300,
damping: 30 damping: 30,
}} }}
/> />
<button <button
onClick={() => setSelected?.(options.left.value)} onClick={() => setSelected?.(options.left.value)}
className={classNames( className={classNames(
'relative z-10 flex-1 p-2 rounded-lg text-sm transition-colors duration-200', 'relative z-10 flex-1 p-2 rounded-lg text-sm transition-colors duration-200',
isLeftSelected ? 'text-white' : 'text-bolt-elements-textSecondary hover:text-bolt-elements-textPrimary' isLeftSelected ? 'text-white' : 'text-bolt-elements-textSecondary hover:text-bolt-elements-textPrimary',
)} )}
> >
{options.left.text} {options.left.text}
@ -52,11 +52,11 @@ export const SettingsSlider = memo(<T,>({ selected, options, setSelected }: Sett
onClick={() => setSelected?.(options.right.value)} onClick={() => setSelected?.(options.right.value)}
className={classNames( className={classNames(
'relative z-10 flex-1 p-2 rounded-lg text-sm transition-colors duration-200', 'relative z-10 flex-1 p-2 rounded-lg text-sm transition-colors duration-200',
!isLeftSelected ? 'text-white' : 'text-bolt-elements-textSecondary hover:text-bolt-elements-textPrimary' !isLeftSelected ? 'text-white' : 'text-bolt-elements-textSecondary hover:text-bolt-elements-textPrimary',
)} )}
> >
{options.right.text} {options.right.text}
</button> </button>
</div> </div>
); );
}); });

View File

@ -1 +0,0 @@
{ "commit": "228cf1f34fd64b6960460f84c9db47bd7ef03150" }