Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,22 @@ src/ → Core engine with modular components:
├── handlers/ → Input/output processing, error management
└── utils/ → Logging, caching, rate limiting, tokens
```
### Frontend

```
Frontend/ → Core application logic:
├── components/ → Shared UI components
├── composables/ → Shared logic/composables
├── config/ → App configuration
├── features/ → Main feature modules
├── layouts/ → Page layouts
├── lib/ → Third-party library configs
├── pages/ → Application pages
├── services/ → Shared services/providers
├── stores/ → Global state management
├── types/ → Shared type definitions
└── utils/ → Utility functions
```

### Configuration and Data

Expand Down
15 changes: 12 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,15 @@ profiles.json
*.metaproj
*.swp

# MSBuildCache
/MSBuildCacheLogs/
*.DS_Store
# MSBuildCache
/MSBuildCacheLogs/
*.DS_Store

# Node.js and npm
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*

# Build outputs
dist/
43 changes: 43 additions & 0 deletions frontend/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import React from 'react';
import { SettingsProvider } from './stores/settingsStore';
import { Navigation } from './components/Navigation';
import { SettingsPage } from './pages/SettingsPage';
import { DashboardPage } from './pages/DashboardPage';
import './styles.css';

function App() {
// Simple routing based on hash
const currentHash = window.location.hash.slice(1) || '/';

const renderPage = () => {
switch (currentHash) {
case '/settings':
return <SettingsPage />;
case '/':
default:
return <DashboardPage />;
}
};

// Update navigation links to use hash routing
React.useEffect(() => {
const handleHashChange = () => {
// Force re-render when hash changes
window.location.reload();
Copy link

Copilot AI Aug 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using window.location.reload() for hash changes causes unnecessary full page reloads. This defeats the purpose of client-side routing and impacts performance. Use React state to trigger re-renders instead.

Suggested change
window.location.reload();
useEffect(() => {
const handleHashChange = () => {
setCurrentHash(window.location.hash.slice(1) || '/');

Copilot uses AI. Check for mistakes.
Copy link

Copilot AI Aug 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using window.location.reload() for hash routing is inefficient and causes unnecessary full page reloads. Consider using React state to trigger re-renders instead of reloading the entire page.

Suggested change
window.location.reload();
// Update state to trigger re-render when hash changes
setCurrentHash(window.location.hash.slice(1) || '/');

Copilot uses AI. Check for mistakes.
};

Copy link

Copilot AI Aug 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using window.location.reload() on hash changes causes a full page reload, which is inefficient for a SPA. Consider using React state to trigger re-renders instead of reloading the entire page.

Suggested change
useEffect(() => {
const handleHashChange = () => {
setCurrentHash(window.location.hash.slice(1) || '/');
};

Copilot uses AI. Check for mistakes.
window.addEventListener('hashchange', handleHashChange);
return () => window.removeEventListener('hashchange', handleHashChange);
}, []);

Copy link

Copilot AI Aug 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using window.location.reload() forces a full page reload on hash changes, which is inefficient and will lose application state. Consider using React state to trigger re-renders instead of reloading the entire page.

Suggested change

Copilot uses AI. Check for mistakes.
Copy link

Copilot AI Aug 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using window.location.reload() for hash routing causes unnecessary full page reloads. This should use React state to trigger re-renders instead of reloading the entire page.

Suggested change

Copilot uses AI. Check for mistakes.
return (
<SettingsProvider>
<div className="min-h-screen bg-gray-50">
<Navigation />
{renderPage()}
</div>
</SettingsProvider>
);
}

export default App;
48 changes: 48 additions & 0 deletions frontend/components/Navigation.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import React from 'react';

interface NavItemProps {
href: string;
children: React.ReactNode;
isActive?: boolean;
}

const NavItem: React.FC<NavItemProps> = ({ href, children, isActive = false }) => {
return (
<a
href={href}
className={`px-3 py-2 rounded-md text-sm font-medium transition-colors ${
isActive
? 'bg-blue-100 text-blue-700'
: 'text-gray-700 hover:bg-gray-100 hover:text-gray-900'
}`}
>
{children}
</a>
);
};

export const Navigation: React.FC = () => {
const currentHash = window.location.hash.slice(1) || '/';

Copy link

Copilot AI Aug 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The currentHash is calculated once during component render but won't update when the hash changes. This will cause the active navigation state to become stale. Consider using React state or a proper routing solution to track the current route.

Suggested change
const [currentHash, setCurrentHash] = useState(() => window.location.hash.slice(1) || '/');
useEffect(() => {
const onHashChange = () => {
setCurrentHash(window.location.hash.slice(1) || '/');
};
window.addEventListener('hashchange', onHashChange);
return () => {
window.removeEventListener('hashchange', onHashChange);
};
}, []);

Copilot uses AI. Check for mistakes.
Copy link

Copilot AI Aug 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reading window.location.hash directly in render creates a static value that won't update when the hash changes. This should use React state or a custom hook to track hash changes reactively.

Suggested change
const [currentHash, setCurrentHash] = React.useState(() => window.location.hash.slice(1) || '/');
React.useEffect(() => {
const onHashChange = () => {
setCurrentHash(window.location.hash.slice(1) || '/');
};
window.addEventListener('hashchange', onHashChange);
return () => {
window.removeEventListener('hashchange', onHashChange);
};
}, []);

Copilot uses AI. Check for mistakes.
Copy link

Copilot AI Aug 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reading window.location.hash directly in render without state tracking will not trigger re-renders when the hash changes. This should be managed through React state or a proper routing library.

Suggested change
const [currentHash, setCurrentHash] = useState(() => window.location.hash.slice(1) || '/');
useEffect(() => {
const onHashChange = () => {
setCurrentHash(window.location.hash.slice(1) || '/');
};
window.addEventListener('hashchange', onHashChange);
return () => {
window.removeEventListener('hashchange', onHashChange);
};
}, []);

Copilot uses AI. Check for mistakes.
Copy link

Copilot AI Aug 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reading window.location.hash directly in render can cause hydration mismatches and doesn't update when the hash changes. This should be moved to a state variable that updates on hash changes.

Suggested change
const [currentHash, setCurrentHash] = useState<string>('/');
useEffect(() => {
// Set initial hash on mount (client-side only)
const getHash = () => window.location.hash.slice(1) || '/';
setCurrentHash(getHash());
const onHashChange = () => {
setCurrentHash(getHash());
};
window.addEventListener('hashchange', onHashChange);
return () => {
window.removeEventListener('hashchange', onHashChange);
};
}, []);

Copilot uses AI. Check for mistakes.
return (
<nav className="bg-white shadow-sm border-b border-gray-200">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex justify-between h-16">
<div className="flex items-center">
<div className="flex-shrink-0">
<h1 className="text-xl font-bold text-gray-900">SLM/LLM Frontend</h1>

Check failure

Code scanning / check-spelling

Unrecognized Spelling Error

SLM is not a recognized word. (unrecognized-spelling)
</div>
<div className="ml-10 flex items-baseline space-x-4">
<NavItem href="#/" isActive={currentHash === '/'}>
Dashboard
</NavItem>
<NavItem href="#/settings" isActive={currentHash === '/settings'}>
Settings
</NavItem>
</div>
</div>
</div>
</div>
</nav>
);
};
126 changes: 126 additions & 0 deletions frontend/features/settings/BackendSelector.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import React from 'react';
import { useSettings } from '../../stores/settingsStore';
import { BackendOption } from '../../types/settings';

const BackendCard: React.FC<{ option: BackendOption; isSelected: boolean; onSelect: () => void }> = ({
option,
isSelected,
onSelect,
}) => {
return (
<div
className={`border rounded-lg p-4 cursor-pointer transition-all duration-200 ${
isSelected
? 'border-blue-500 bg-blue-50 ring-2 ring-blue-200'
: 'border-gray-200 hover:border-gray-300 hover:bg-gray-50'
} ${!option.isAvailable ? 'opacity-50 cursor-not-allowed' : ''}`}
onClick={option.isAvailable ? onSelect : undefined}
>
<div className="flex items-center justify-between mb-2">
<h3 className="text-lg font-semibold text-gray-900">{option.name}</h3>
<div className="flex items-center space-x-2">
<span
className={`px-2 py-1 text-xs font-medium rounded-full ${
option.type === 'LLM'
? 'bg-purple-100 text-purple-800'
: 'bg-green-100 text-green-800'
}`}
>
{option.type}
</span>
<span
className={`px-2 py-1 text-xs font-medium rounded-full ${
option.isAvailable
? 'bg-green-100 text-green-800'
: 'bg-red-100 text-red-800'
}`}
>
{option.isAvailable ? 'Available' : 'Unavailable'}
</span>
</div>
</div>
<p className="text-gray-600 text-sm mb-2">{option.description}</p>
{option.provider && (
<p className="text-gray-500 text-xs">Provider: {option.provider}</p>
)}
{option.endpoint && (
<p className="text-gray-500 text-xs">Endpoint: {option.endpoint}</p>
)}
{isSelected && (
<div className="mt-3 flex items-center">
<svg className="w-4 h-4 text-blue-500 mr-2" fill="currentColor" viewBox="0 0 20 20">
<path
fillRule="evenodd"
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"
clipRule="evenodd"
/>
</svg>
<span className="text-blue-600 text-sm font-medium">Selected</span>
</div>
)}
</div>
);
};

export const BackendSelector: React.FC = () => {
const { state, selectBackend } = useSettings();

const llmOptions = state.backendOptions.filter(option => option.type === 'LLM');

Check failure

Code scanning / check-spelling

Unrecognized Spelling Error

llm is not a recognized word. (unrecognized-spelling)
const slmOptions = state.backendOptions.filter(option => option.type === 'SLM');

Check failure

Code scanning / check-spelling

Unrecognized Spelling Error

slm is not a recognized word. (unrecognized-spelling)

Check failure

Code scanning / check-spelling

Unrecognized Spelling Error

SLM is not a recognized word. (unrecognized-spelling)

return (
<div className="space-y-6">
<div>
<h2 className="text-2xl font-bold text-gray-900 mb-2">Backend Configuration</h2>
<p className="text-gray-600">
Select the backend infrastructure for your SLM/LLM processing needs.

Check failure

Code scanning / check-spelling

Unrecognized Spelling Error

SLM is not a recognized word. (unrecognized-spelling)
</p>
</div>

{state.selectedBackend && (
<div className="bg-blue-50 border border-blue-200 rounded-lg p-4">
<div className="flex items-center">
<svg className="w-5 h-5 text-blue-500 mr-2" fill="currentColor" viewBox="0 0 20 20">
<path
fillRule="evenodd"
d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z"
clipRule="evenodd"
/>
</svg>
<span className="text-blue-800 font-medium">
Currently selected: {state.backendOptions.find(opt => opt.id === state.selectedBackend)?.name}
</span>
</div>
</div>
)}

<div>
<h3 className="text-xl font-semibold text-gray-900 mb-4">Large Language Models (LLM)</h3>
<div className="grid gap-4 md:grid-cols-2">
{llmOptions.map((option) => (

Check failure

Code scanning / check-spelling

Unrecognized Spelling Error

llm is not a recognized word. (unrecognized-spelling)
<BackendCard
key={option.id}
option={option}
isSelected={state.selectedBackend === option.id}
onSelect={() => selectBackend(option.id)}
/>
))}
</div>
</div>

<div>
<h3 className="text-xl font-semibold text-gray-900 mb-4">Small Language Models (SLM)</h3>

Check failure

Code scanning / check-spelling

Unrecognized Spelling Error

SLM is not a recognized word. (unrecognized-spelling)
<div className="grid gap-4 md:grid-cols-2">
{slmOptions.map((option) => (

Check failure

Code scanning / check-spelling

Unrecognized Spelling Error

slm is not a recognized word. (unrecognized-spelling)
<BackendCard
key={option.id}
option={option}
isSelected={state.selectedBackend === option.id}
onSelect={() => selectBackend(option.id)}
/>
))}
</div>
</div>
</div>
);
};
13 changes: 13 additions & 0 deletions frontend/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Frontend - SLM/LLM Infrastructure</title>

Check failure

Code scanning / check-spelling

Unrecognized Spelling Error

SLM is not a recognized word. (unrecognized-spelling)
</head>
<body>
<div id="root"></div>
<script type="module" src="/main.tsx"></script>
</body>
</html>
9 changes: 9 additions & 0 deletions frontend/main.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'

ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<App />
</React.StrictMode>,
)
33 changes: 33 additions & 0 deletions frontend/pages/DashboardPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import React from 'react';

export const DashboardPage: React.FC = () => {
return (
<div className="min-h-screen bg-gray-50">
<div className="max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8">
<div className="bg-white shadow rounded-lg">
<div className="px-6 py-8">
<h1 className="text-3xl font-bold text-gray-900 mb-4">Dashboard</h1>
<p className="text-gray-600 mb-6">
Welcome to the SLM/LLM Infrastructure Frontend. Use the settings page to configure your backend.
</p>

<div className="bg-blue-50 border border-blue-200 rounded-lg p-4">
<div className="flex items-center">
<svg className="w-5 h-5 text-blue-500 mr-2" fill="currentColor" viewBox="0 0 20 20">
<path
fillRule="evenodd"
d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z"
clipRule="evenodd"
/>
</svg>
<span className="text-blue-800 font-medium">
Navigate to Settings to configure your preferred backend infrastructure.
</span>
</div>
</div>
</div>
</div>
</div>
</div>
);
};
23 changes: 23 additions & 0 deletions frontend/pages/SettingsPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import React from 'react';
import { BackendSelector } from '../features/settings/BackendSelector';

export const SettingsPage: React.FC = () => {
return (
<div className="min-h-screen bg-gray-50">
<div className="max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8">
<div className="bg-white shadow rounded-lg">
<div className="px-6 py-8">
<div className="border-b border-gray-200 pb-6 mb-6">
<h1 className="text-3xl font-bold text-gray-900">Settings</h1>
<p className="mt-2 text-gray-600">
Configure your application preferences and backend infrastructure.
</p>
</div>

<BackendSelector />
</div>
</div>
</div>
</div>
);
};
Loading
Loading