From 42ebd3d50e00e2ac634c9773a3ee7dc77aa2ad49 Mon Sep 17 00:00:00 2001 From: Dustin Loring Date: Sat, 7 Dec 2024 10:27:50 -0500 Subject: [PATCH 1/2] Added a tabbed setting modal --- .github/actions/setup-and-build/commit.yaml | 32 ++ app/components/chat/BaseChat.tsx | 55 ++- app/components/chat/ModelSelector.tsx | 68 +++- app/components/sidebar/Menu.client.tsx | 9 +- app/components/ui/Settings.tsx | 387 ++++++++++++++++++++ app/components/ui/SettingsButton.tsx | 18 + app/components/ui/SettingsSlider.tsx | 62 ++++ app/lib/stores/settings.ts | 24 ++ app/types/model.ts | 1 + commit.json | 1 + 10 files changed, 650 insertions(+), 7 deletions(-) create mode 100644 .github/actions/setup-and-build/commit.yaml create mode 100644 app/components/ui/Settings.tsx create mode 100644 app/components/ui/SettingsButton.tsx create mode 100644 app/components/ui/SettingsSlider.tsx create mode 100644 commit.json diff --git a/.github/actions/setup-and-build/commit.yaml b/.github/actions/setup-and-build/commit.yaml new file mode 100644 index 0000000..e920a88 --- /dev/null +++ b/.github/actions/setup-and-build/commit.yaml @@ -0,0 +1,32 @@ +name: Update Commit Hash File + +on: + push: + branches: + - main + +permissions: + contents: write + +jobs: + update-commit: + runs-on: ubuntu-latest + + steps: + - name: Checkout the code + uses: actions/checkout@v3 + + - name: Get the latest commit hash + run: echo "COMMIT_HASH=$(git rev-parse HEAD)" >> $GITHUB_ENV + + - name: Update commit file + run: | + echo "{ \"commit\": \"$COMMIT_HASH\" }" > commit.json + + - name: Commit and push the update + run: | + git config --global user.name "github-actions[bot]" + git config --global user.email "github-actions[bot]@users.noreply.github.com" + git add commit.json + git commit -m "chore: update commit hash to $COMMIT_HASH" + git push \ No newline at end of file diff --git a/app/components/chat/BaseChat.tsx b/app/components/chat/BaseChat.tsx index 8c7589a..3b4be60 100644 --- a/app/components/chat/BaseChat.tsx +++ b/app/components/chat/BaseChat.tsx @@ -87,13 +87,58 @@ export const BaseChat = React.forwardRef( ref, ) => { const TEXTAREA_MAX_HEIGHT = chatStarted ? 400 : 200; - const [apiKeys, setApiKeys] = useState>({}); + const [apiKeys, setApiKeys] = useState>(() => { + const savedKeys = Cookies.get('apiKeys'); + if (savedKeys) { + try { + return JSON.parse(savedKeys); + } catch (error) { + console.error('Failed to parse API keys from cookies:', error); + return {}; + } + } + return {}; + }); const [modelList, setModelList] = useState(MODEL_LIST); const [isModelSettingsCollapsed, setIsModelSettingsCollapsed] = useState(false); const [isListening, setIsListening] = useState(false); const [recognition, setRecognition] = useState(null); const [transcript, setTranscript] = useState(''); + // Load enabled providers from cookies + const [enabledProviders, setEnabledProviders] = useState(() => { + const savedProviders = Cookies.get('providers'); + if (savedProviders) { + try { + const parsedProviders = JSON.parse(savedProviders); + return PROVIDER_LIST.filter(p => parsedProviders[p.name]); + } catch (error) { + console.error('Failed to parse providers from cookies:', error); + return PROVIDER_LIST; + } + } + return PROVIDER_LIST; + }); + + // Update enabled providers when cookies change + useEffect(() => { + const updateProvidersFromCookies = () => { + const savedProviders = Cookies.get('providers'); + if (savedProviders) { + try { + const parsedProviders = JSON.parse(savedProviders); + setEnabledProviders(PROVIDER_LIST.filter(p => parsedProviders[p.name])); + } catch (error) { + console.error('Failed to parse providers from cookies:', error); + } + } + }; + + updateProvidersFromCookies(); + const interval = setInterval(updateProvidersFromCookies, 1000); + return () => clearInterval(interval); + }, [PROVIDER_LIST]); + console.log(transcript); useEffect(() => { // Load API keys from cookies on component mount @@ -359,11 +404,15 @@ export const BaseChat = React.forwardRef( providerList={PROVIDER_LIST} apiKeys={apiKeys} /> - {provider && ( + {enabledProviders.length > 0 && provider && ( updateApiKey(provider.name, key)} + setApiKey={(key) => { + const newApiKeys = { ...apiKeys, [provider.name]: key }; + setApiKeys(newApiKeys); + Cookies.set('apiKeys', JSON.stringify(newApiKeys)); + }} /> )} diff --git a/app/components/chat/ModelSelector.tsx b/app/components/chat/ModelSelector.tsx index 1bc7a66..ae8a8c5 100644 --- a/app/components/chat/ModelSelector.tsx +++ b/app/components/chat/ModelSelector.tsx @@ -1,5 +1,7 @@ import type { ProviderInfo } from '~/types/model'; import type { ModelInfo } from '~/utils/types'; +import { useEffect, useState } from 'react'; +import Cookies from 'js-cookie'; interface ModelSelectorProps { model?: string; @@ -19,12 +21,74 @@ export const ModelSelector = ({ modelList, providerList, }: ModelSelectorProps) => { + // Load enabled providers from cookies + const [enabledProviders, setEnabledProviders] = useState(() => { + const savedProviders = Cookies.get('providers'); + if (savedProviders) { + try { + const parsedProviders = JSON.parse(savedProviders); + return providerList.filter(p => parsedProviders[p.name]); + } catch (error) { + console.error('Failed to parse providers from cookies:', error); + return providerList; + } + } + return providerList; + }); + + // Update enabled providers when cookies change + useEffect(() => { + // Function to update providers from cookies + const updateProvidersFromCookies = () => { + const savedProviders = Cookies.get('providers'); + if (savedProviders) { + try { + const parsedProviders = JSON.parse(savedProviders); + const newEnabledProviders = providerList.filter(p => parsedProviders[p.name]); + setEnabledProviders(newEnabledProviders); + + // If current provider is disabled, switch to first enabled provider + if (provider && !parsedProviders[provider.name] && newEnabledProviders.length > 0) { + const firstEnabledProvider = newEnabledProviders[0]; + setProvider?.(firstEnabledProvider); + + // Also update the model to the first available one for the new provider + const firstModel = modelList.find(m => m.provider === firstEnabledProvider.name); + if (firstModel) { + setModel?.(firstModel.name); + } + } + } catch (error) { + console.error('Failed to parse providers from cookies:', error); + } + } + }; + + // Initial update + updateProvidersFromCookies(); + + // Set up an interval to check for cookie changes + const interval = setInterval(updateProvidersFromCookies, 1000); + + return () => clearInterval(interval); + }, [providerList, provider, setProvider, modelList, setModel]); + + if (enabledProviders.length === 0) { + return ( +
+

+ No providers are currently enabled. Please enable at least one provider in the settings to start using the chat. +

+
+ ); + } + return (
setSearchTerm(e.target.value)} + className="mb-4 p-2 rounded border border-gray-300 w-full" + /> + {filteredProviders.map((provider) => ( +
+
+ {provider.name} + +
+ + {/* Base URL input for configurable providers */} + {URL_CONFIGURABLE_PROVIDERS.includes(provider.name) && provider.isEnabled && ( +
+ + handleBaseUrlChange(provider.name, e.target.value)} + placeholder={`Enter ${provider.name} base URL`} + className="w-full p-2 rounded border border-gray-600 bg-gray-700 text-white text-sm" + /> +
+ )} +
+ ))} +
+ )} + {activeTab === 'features' && ( +
+
+ Debug Info + +
+
+ {/* Your feature content here */} +
+
+ )} + {activeTab === 'debug' && isDebugEnabled && ( +
+

Debug Tab

+ + +

System Information

+

OS: {navigator.platform}

+

Browser: {navigator.userAgent}

+ +

Active Features

+
    + {providers + .filter((provider) => provider.isEnabled) + .map((provider) => ( +
  • {provider.name}
  • + ))} +
+ +

Base URLs

+
    +
  • Ollama: {process.env.REACT_APP_OLLAMA_URL}
  • +
  • OpenAI: {process.env.REACT_APP_OPENAI_URL}
  • +
  • LM Studio: {process.env.REACT_APP_LM_STUDIO_URL}
  • +
+ +

Version Information

+

Version Hash: {versionHash}

+
+ )} + + + + + + + + + + + ); +}; \ No newline at end of file diff --git a/app/components/ui/SettingsButton.tsx b/app/components/ui/SettingsButton.tsx new file mode 100644 index 0000000..24ba00b --- /dev/null +++ b/app/components/ui/SettingsButton.tsx @@ -0,0 +1,18 @@ +import { memo } from 'react'; +import { IconButton } from './IconButton'; + +interface SettingsButtonProps { + onClick: () => void; +} + +export const SettingsButton = memo(({ onClick }: SettingsButtonProps) => { + return ( + + ); +}); \ No newline at end of file diff --git a/app/components/ui/SettingsSlider.tsx b/app/components/ui/SettingsSlider.tsx new file mode 100644 index 0000000..f2fba88 --- /dev/null +++ b/app/components/ui/SettingsSlider.tsx @@ -0,0 +1,62 @@ +import { motion } from 'framer-motion'; +import { memo } from 'react'; +import { classNames } from '~/utils/classNames'; + +interface SliderOption { + value: T; + text: string; +} + +export interface SliderOptions { + left: SliderOption; + right: SliderOption; +} + +interface SettingsSliderProps { + selected: T; + options: SliderOptions; + setSelected?: (selected: T) => void; +} + +export const SettingsSlider = memo(({ selected, options, setSelected }: SettingsSliderProps) => { + const isLeftSelected = selected === options.left.value; + + return ( +
+ + + +
+ ); +}); \ No newline at end of file diff --git a/app/lib/stores/settings.ts b/app/lib/stores/settings.ts index 5e48bfe..7106cfb 100644 --- a/app/lib/stores/settings.ts +++ b/app/lib/stores/settings.ts @@ -15,10 +15,33 @@ export interface Shortcuts { toggleTerminal: Shortcut; } +export interface Provider { + name: string; + isEnabled: boolean; +} + export interface Settings { shortcuts: Shortcuts; + providers: Provider[]; } +export const providersList: Provider[] = [ + { name: 'Groq', isEnabled: false }, + { name: 'HuggingFace', isEnabled: false }, + { name: 'OpenAI', isEnabled: false }, + { name: 'Anthropic', isEnabled: false }, + { name: 'OpenRouter', isEnabled: false }, + { name: 'Google', isEnabled: false }, + { name: 'Ollama', isEnabled: false }, + { name: 'OpenAILike', isEnabled: false }, + { name: 'Together', isEnabled: false }, + { name: 'Deepseek', isEnabled: false }, + { name: 'Mistral', isEnabled: false }, + { name: 'Cohere', isEnabled: false }, + { name: 'LMStudio', isEnabled: false }, + { name: 'xAI', isEnabled: false }, +]; + export const shortcutsStore = map({ toggleTerminal: { key: 'j', @@ -29,6 +52,7 @@ export const shortcutsStore = map({ export const settingsStore = map({ shortcuts: shortcutsStore.get(), + providers: providersList, }); shortcutsStore.subscribe((shortcuts) => { diff --git a/app/types/model.ts b/app/types/model.ts index 29bff2e..c6c58d7 100644 --- a/app/types/model.ts +++ b/app/types/model.ts @@ -7,4 +7,5 @@ export type ProviderInfo = { getApiKeyLink?: string; labelForGetApiKey?: string; icon?: string; + isEnabled?: boolean; }; diff --git a/commit.json b/commit.json new file mode 100644 index 0000000..f98c1f4 --- /dev/null +++ b/commit.json @@ -0,0 +1 @@ +{ "commit": "228cf1f34fd64b6960460f84c9db47bd7ef03150" } \ No newline at end of file From a2acc77e89d151c3114f13b5d23c1f55a8692764 Mon Sep 17 00:00:00 2001 From: Dustin Loring Date: Sat, 7 Dec 2024 10:53:33 -0500 Subject: [PATCH 2/2] fix: clean up fix: typecheck / lint --- .../setup-and-build => workflows}/commit.yaml | 4 +- app/commit.json | 1 + app/components/chat/BaseChat.tsx | 28 +++---- app/components/chat/ModelSelector.tsx | 15 ++-- app/components/ui/Settings.tsx | 74 ++++++++++--------- app/components/ui/SettingsButton.tsx | 2 +- app/components/ui/SettingsSlider.tsx | 12 +-- commit.json | 1 - 8 files changed, 70 insertions(+), 67 deletions(-) rename .github/{actions/setup-and-build => workflows}/commit.yaml (87%) create mode 100644 app/commit.json delete mode 100644 commit.json diff --git a/.github/actions/setup-and-build/commit.yaml b/.github/workflows/commit.yaml similarity index 87% rename from .github/actions/setup-and-build/commit.yaml rename to .github/workflows/commit.yaml index e920a88..2f194cd 100644 --- a/.github/actions/setup-and-build/commit.yaml +++ b/.github/workflows/commit.yaml @@ -21,12 +21,12 @@ jobs: - name: Update commit file run: | - echo "{ \"commit\": \"$COMMIT_HASH\" }" > commit.json + echo "{ \"commit\": \"$COMMIT_HASH\" }" > app/commit.json - name: Commit and push the update run: | git config --global user.name "github-actions[bot]" 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 push \ No newline at end of file diff --git a/app/commit.json b/app/commit.json new file mode 100644 index 0000000..8b96fc7 --- /dev/null +++ b/app/commit.json @@ -0,0 +1 @@ +{ "commit": "228cf1f34fd64b6960460f84c9db47bd7ef03150" } diff --git a/app/components/chat/BaseChat.tsx b/app/components/chat/BaseChat.tsx index 3b4be60..67b7688 100644 --- a/app/components/chat/BaseChat.tsx +++ b/app/components/chat/BaseChat.tsx @@ -89,6 +89,7 @@ export const BaseChat = React.forwardRef( const TEXTAREA_MAX_HEIGHT = chatStarted ? 400 : 200; const [apiKeys, setApiKeys] = useState>(() => { const savedKeys = Cookies.get('apiKeys'); + if (savedKeys) { try { return JSON.parse(savedKeys); @@ -97,6 +98,7 @@ export const BaseChat = React.forwardRef( return {}; } } + return {}; }); const [modelList, setModelList] = useState(MODEL_LIST); @@ -108,15 +110,17 @@ export const BaseChat = React.forwardRef( // Load enabled providers from cookies const [enabledProviders, setEnabledProviders] = useState(() => { const savedProviders = Cookies.get('providers'); + if (savedProviders) { try { const parsedProviders = JSON.parse(savedProviders); - return PROVIDER_LIST.filter(p => parsedProviders[p.name]); + return PROVIDER_LIST.filter((p) => parsedProviders[p.name]); } catch (error) { console.error('Failed to parse providers from cookies:', error); return PROVIDER_LIST; } } + return PROVIDER_LIST; }); @@ -124,10 +128,11 @@ export const BaseChat = React.forwardRef( useEffect(() => { const updateProvidersFromCookies = () => { const savedProviders = Cookies.get('providers'); + if (savedProviders) { try { const parsedProviders = JSON.parse(savedProviders); - setEnabledProviders(PROVIDER_LIST.filter(p => parsedProviders[p.name])); + setEnabledProviders(PROVIDER_LIST.filter((p) => parsedProviders[p.name])); } catch (error) { console.error('Failed to parse providers from cookies:', error); } @@ -135,7 +140,9 @@ export const BaseChat = React.forwardRef( }; updateProvidersFromCookies(); + const interval = setInterval(updateProvidersFromCookies, 1000); + return () => clearInterval(interval); }, [PROVIDER_LIST]); @@ -228,23 +235,6 @@ export const BaseChat = React.forwardRef( } }; - 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 input = document.createElement('input'); input.type = 'file'; diff --git a/app/components/chat/ModelSelector.tsx b/app/components/chat/ModelSelector.tsx index ae8a8c5..435f4ba 100644 --- a/app/components/chat/ModelSelector.tsx +++ b/app/components/chat/ModelSelector.tsx @@ -24,15 +24,17 @@ export const ModelSelector = ({ // Load enabled providers from cookies const [enabledProviders, setEnabledProviders] = useState(() => { const savedProviders = Cookies.get('providers'); + if (savedProviders) { try { const parsedProviders = JSON.parse(savedProviders); - return providerList.filter(p => parsedProviders[p.name]); + return providerList.filter((p) => parsedProviders[p.name]); } catch (error) { console.error('Failed to parse providers from cookies:', error); return providerList; } } + return providerList; }); @@ -41,19 +43,21 @@ export const ModelSelector = ({ // Function to update providers from cookies const updateProvidersFromCookies = () => { const savedProviders = Cookies.get('providers'); + if (savedProviders) { try { const parsedProviders = JSON.parse(savedProviders); - const newEnabledProviders = providerList.filter(p => parsedProviders[p.name]); + const newEnabledProviders = providerList.filter((p) => parsedProviders[p.name]); setEnabledProviders(newEnabledProviders); // If current provider is disabled, switch to first enabled provider if (provider && !parsedProviders[provider.name] && newEnabledProviders.length > 0) { const firstEnabledProvider = newEnabledProviders[0]; setProvider?.(firstEnabledProvider); - + // 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) { setModel?.(firstModel.name); } @@ -77,7 +81,8 @@ export const ModelSelector = ({ return (

- 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.

); diff --git a/app/components/ui/Settings.tsx b/app/components/ui/Settings.tsx index fb4c4c5..e58e8b5 100644 --- a/app/components/ui/Settings.tsx +++ b/app/components/ui/Settings.tsx @@ -1,14 +1,14 @@ import * as RadixDialog from '@radix-ui/react-dialog'; import { motion } from 'framer-motion'; -import { useState, useEffect }from 'react'; +import { useState } from 'react'; import { classNames } from '~/utils/classNames'; -import { Dialog, DialogTitle, dialogVariants, dialogBackdropVariants } from './Dialog'; +import { DialogTitle, dialogVariants, dialogBackdropVariants } from './Dialog'; import { IconButton } from './IconButton'; import { providersList } from '~/lib/stores/settings'; import { db, getAll, deleteById } from '~/lib/persistence'; import { toast } from 'react-toastify'; import { useNavigate } from '@remix-run/react'; -import commit from '../../../commit.json'; +import commit from '~/commit.json'; import Cookies from 'js-cookie'; interface SettingsProps { @@ -31,6 +31,7 @@ export const Settings = ({ open, onClose }: SettingsProps) => { // Load base URLs from cookies const [baseUrls, setBaseUrls] = useState(() => { const savedUrls = Cookies.get('providerBaseUrls'); + if (savedUrls) { try { return JSON.parse(savedUrls); @@ -43,6 +44,7 @@ export const Settings = ({ open, onClose }: SettingsProps) => { }; } } + return { Ollama: 'http://localhost:11434', LMStudio: 'http://localhost:1234', @@ -51,9 +53,10 @@ export const Settings = ({ open, onClose }: SettingsProps) => { }); const handleBaseUrlChange = (provider: string, url: string) => { - setBaseUrls(prev => { + setBaseUrls((prev: Record) => { const newUrls = { ...prev, [provider]: url }; Cookies.set('providerBaseUrls', JSON.stringify(newUrls)); + return newUrls; }); }; @@ -62,46 +65,52 @@ export const Settings = ({ open, onClose }: SettingsProps) => { { id: 'chat-history', label: 'Chat History', icon: 'i-ph:book' }, { id: 'providers', label: 'Providers', icon: 'i-ph:key' }, { 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 const [providers, setProviders] = useState(() => { const savedProviders = Cookies.get('providers'); + if (savedProviders) { try { const parsedProviders = JSON.parse(savedProviders); + // Merge saved enabled states with the base provider list - return providersList.map(provider => ({ + return providersList.map((provider) => ({ ...provider, - isEnabled: parsedProviders[provider.name] || false + isEnabled: parsedProviders[provider.name] || false, })); } catch (error) { console.error('Failed to parse providers from cookies:', error); } } + return providersList; }); const handleToggleProvider = (providerName: string) => { setProviders((prevProviders) => { const newProviders = prevProviders.map((provider) => - provider.name === providerName ? { ...provider, isEnabled: !provider.isEnabled } : provider + provider.name === providerName ? { ...provider, isEnabled: !provider.isEnabled } : provider, ); - + // Save to cookies - const enabledStates = newProviders.reduce((acc, provider) => ({ - ...acc, - [provider.name]: provider.isEnabled - }), {}); + const enabledStates = newProviders.reduce( + (acc, provider) => ({ + ...acc, + [provider.name]: provider.isEnabled, + }), + {}, + ); Cookies.set('providers', JSON.stringify(enabledStates)); - + return newProviders; }); }; 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)); const handleCopyToClipboard = () => { @@ -141,11 +150,12 @@ export const Settings = ({ open, onClose }: SettingsProps) => { try { setIsDeleting(true); + const allChats = await getAll(db); - + // 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'); navigate('/', { replace: true }); } catch (error) { @@ -168,7 +178,7 @@ export const Settings = ({ open, onClose }: SettingsProps) => { chats: allChats, exportDate: new Date().toISOString(), }; - + downloadAsJson(exportData, `all-chats-${new Date().toISOString()}.json`); toast.success('Chats exported successfully'); } catch (error) { @@ -207,9 +217,7 @@ export const Settings = ({ open, onClose }: SettingsProps) => { onClick={() => setActiveTab(tab.id)} className={classNames( 'w-full flex items-center gap-2 px-4 py-3 rounded-lg text-left text-sm transition-all mb-2', - activeTab === tab.id - ? 'bg-blue-600 text-white' - : 'bg-gray-600 text-gray-200 hover:bg-blue-500' + activeTab === tab.id ? 'bg-blue-600 text-white' : 'bg-gray-600 text-gray-200 hover:bg-blue-500', )} >
@@ -239,7 +247,7 @@ export const Settings = ({ open, onClose }: SettingsProps) => {
Settings
- {activeTab === 'chat-history' && ( + {activeTab === 'chat-history' && (

Chat History

- +

Danger Area

This action cannot be undone!

@@ -256,8 +264,8 @@ export const Settings = ({ open, onClose }: SettingsProps) => { onClick={handleDeleteAllChats} disabled={isDeleting} className={classNames( - "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" + '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 ? 'Deleting...' : 'Delete All Chats'} @@ -297,7 +305,7 @@ export const Settings = ({ open, onClose }: SettingsProps) => { >
- + {/* Base URL input for configurable providers */} {URL_CONFIGURABLE_PROVIDERS.includes(provider.name) && provider.isEnabled && (
@@ -334,9 +342,7 @@ export const Settings = ({ open, onClose }: SettingsProps) => { >
-
- {/* Your feature content here */} -
+
{/* Your feature content here */}
)} {activeTab === 'debug' && isDebugEnabled && ( @@ -348,7 +354,7 @@ export const Settings = ({ open, onClose }: SettingsProps) => { > Copy to Clipboard - +

System Information

OS: {navigator.platform}

Browser: {navigator.userAgent}

@@ -358,7 +364,9 @@ export const Settings = ({ open, onClose }: SettingsProps) => { {providers .filter((provider) => provider.isEnabled) .map((provider) => ( -
  • {provider.name}
  • +
  • + {provider.name} +
  • ))} @@ -384,4 +392,4 @@ export const Settings = ({ open, onClose }: SettingsProps) => { ); -}; \ No newline at end of file +}; diff --git a/app/components/ui/SettingsButton.tsx b/app/components/ui/SettingsButton.tsx index 24ba00b..906fe52 100644 --- a/app/components/ui/SettingsButton.tsx +++ b/app/components/ui/SettingsButton.tsx @@ -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" /> ); -}); \ No newline at end of file +}); diff --git a/app/components/ui/SettingsSlider.tsx b/app/components/ui/SettingsSlider.tsx index f2fba88..1a5d410 100644 --- a/app/components/ui/SettingsSlider.tsx +++ b/app/components/ui/SettingsSlider.tsx @@ -26,24 +26,24 @@ export const SettingsSlider = memo(({ selected, options, setSelected }: Sett
    ); -}); \ No newline at end of file +}); diff --git a/commit.json b/commit.json deleted file mode 100644 index f98c1f4..0000000 --- a/commit.json +++ /dev/null @@ -1 +0,0 @@ -{ "commit": "228cf1f34fd64b6960460f84c9db47bd7ef03150" } \ No newline at end of file