mirror of
https://github.com/musistudio/claude-code-router.git
synced 2025-09-08 08:24:06 +00:00
feat: add UI build to build process
- Created separate build script to handle both CLI and UI building - Added automatic UI dependency installation - Copy built UI artifacts to dist directory 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
147
ui/src/components/ConfigProvider.tsx
Normal file
147
ui/src/components/ConfigProvider.tsx
Normal file
@@ -0,0 +1,147 @@
|
||||
import { createContext, useContext, useState, useEffect } from 'react';
|
||||
import type { ReactNode, Dispatch, SetStateAction } from 'react';
|
||||
import { api } from '@/lib/api';
|
||||
|
||||
export interface Transformer {
|
||||
path: string;
|
||||
options: {
|
||||
[key: string]: string;
|
||||
};
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export interface ProviderTransformer {
|
||||
use: (string | (string | Record<string, unknown> | { max_tokens: number })[])[];
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
[key: string]: any; // for model specific transformers
|
||||
}
|
||||
|
||||
export interface Provider {
|
||||
name: string;
|
||||
api_base_url: string;
|
||||
api_key: string;
|
||||
models: string[];
|
||||
transformer?: ProviderTransformer;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export interface RouterConfig {
|
||||
default: string;
|
||||
background: string;
|
||||
think: string;
|
||||
longContext: string;
|
||||
webSearch: string;
|
||||
}
|
||||
|
||||
export interface Config {
|
||||
LOG: boolean;
|
||||
CLAUDE_PATH: string;
|
||||
HOST: string;
|
||||
PORT: number;
|
||||
APIKEY: string;
|
||||
transformers: Transformer[];
|
||||
Providers: Provider[];
|
||||
Router: RouterConfig;
|
||||
}
|
||||
|
||||
interface ConfigContextType {
|
||||
config: Config | null;
|
||||
setConfig: Dispatch<SetStateAction<Config | null>>;
|
||||
error: Error | null;
|
||||
}
|
||||
|
||||
const ConfigContext = createContext<ConfigContextType | undefined>(undefined);
|
||||
|
||||
// eslint-disable-next-line react-refresh/only-export-components
|
||||
export function useConfig() {
|
||||
const context = useContext(ConfigContext);
|
||||
if (context === undefined) {
|
||||
throw new Error('useConfig must be used within a ConfigProvider');
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
interface ConfigProviderProps {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
export function ConfigProvider({ children }: ConfigProviderProps) {
|
||||
const [config, setConfig] = useState<Config | null>(null);
|
||||
const [error, setError] = useState<Error | null>(null);
|
||||
const [hasFetched, setHasFetched] = useState<boolean>(false);
|
||||
const [apiKey, setApiKey] = useState<string | null>(localStorage.getItem('apiKey'));
|
||||
|
||||
// Listen for localStorage changes
|
||||
useEffect(() => {
|
||||
const handleStorageChange = () => {
|
||||
setApiKey(localStorage.getItem('apiKey'));
|
||||
};
|
||||
|
||||
window.addEventListener('storage', handleStorageChange);
|
||||
return () => {
|
||||
window.removeEventListener('storage', handleStorageChange);
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchConfig = async () => {
|
||||
// Reset fetch state when API key changes
|
||||
setHasFetched(false);
|
||||
setConfig(null);
|
||||
setError(null);
|
||||
};
|
||||
|
||||
fetchConfig();
|
||||
}, [apiKey]);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchConfig = async () => {
|
||||
// Prevent duplicate API calls in React StrictMode
|
||||
// Skip if we've already fetched
|
||||
if (hasFetched) {
|
||||
return;
|
||||
}
|
||||
setHasFetched(true);
|
||||
|
||||
try {
|
||||
// Try to fetch config regardless of API key presence
|
||||
const data = await api.getConfig();
|
||||
setConfig(data);
|
||||
} catch (err) {
|
||||
console.error('Failed to fetch config:', err);
|
||||
// If we get a 401, the API client will redirect to login
|
||||
// Otherwise, set an empty config or error
|
||||
if ((err as Error).message !== 'Unauthorized') {
|
||||
// Set default empty config when fetch fails
|
||||
setConfig({
|
||||
LOG: false,
|
||||
CLAUDE_PATH: '',
|
||||
HOST: '127.0.0.1',
|
||||
PORT: 3456,
|
||||
APIKEY: '',
|
||||
transformers: [],
|
||||
Providers: [],
|
||||
Router: {
|
||||
default: '',
|
||||
background: '',
|
||||
think: '',
|
||||
longContext: '',
|
||||
webSearch: ''
|
||||
}
|
||||
});
|
||||
setError(err as Error);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
fetchConfig();
|
||||
}, [hasFetched, apiKey]);
|
||||
|
||||
return (
|
||||
<ConfigContext.Provider value={{ config, setConfig, error }}>
|
||||
{children}
|
||||
</ConfigContext.Provider>
|
||||
);
|
||||
}
|
Reference in New Issue
Block a user