From 9950ea3b62456c7b47a43538621d8d162df1c4c1 Mon Sep 17 00:00:00 2001 From: Storme-bit Date: Sat, 18 Apr 2026 02:28:01 -0700 Subject: [PATCH] implementing model selector and info panel --- .../src/components/SettingsView.jsx | 126 +++++++++++++++++- 1 file changed, 123 insertions(+), 3 deletions(-) diff --git a/packages/chat-client/src/components/SettingsView.jsx b/packages/chat-client/src/components/SettingsView.jsx index 1207223..91a65e0 100644 --- a/packages/chat-client/src/components/SettingsView.jsx +++ b/packages/chat-client/src/components/SettingsView.jsx @@ -1,6 +1,7 @@ import React, { useState, useEffect, useCallback } from 'react'; import { useSettings } from '../hooks/useSettings'; import { getServiceHealth } from '../api/orchestration'; +import { useModels } from '../hooks/useModels'; export default function SettingsView({ onNavigate }) { const { settings, saveSetting, saving } = useSettings(); @@ -47,9 +48,7 @@ export default function SettingsView({ onNavigate }) { - } /> - } /> - } /> + @@ -237,4 +236,125 @@ function ServiceHealth() { )} ); +} + +function ModelsSection({ onNavigate }) { + const { models, selectedModel, setSelectedModel } = useModels(); + const [selectedInfo, setSelectedInfo] = useState(null); + + // Sync info panel when selection changes + useEffect(() => { + const m = models.find(m => m.value === selectedModel); + setSelectedInfo(m ?? null); + }, [selectedModel, models]); + + return ( + <> + } + /> + setSelectedModel(e.target.value)} + style={{ + padding: '6px 10px', fontSize: '13px', + background: 'var(--bg-elevated)', border: '1px solid var(--border)', + borderRadius: 'var(--radius-md)', color: 'var(--text-primary)', + cursor: 'pointer', outline: 'none', maxWidth: '220px', + }} + > + {models.map(m => ( + + ))} + + } + /> + + {/* Model info panel */} + {selectedInfo && ( +
+

Model Info

+
+ + + {selectedInfo.description && ( + + )} +
+

+ Model loading and parameter configuration coming soon +

+
+ )} + + ); +} + +function InfoLine({ label, value, mono }) { + return ( +
+ {label} + {value} +
+ ); +} + +function ModelsFolderSetting() { + const { settings, saveSetting, saving } = useSettings(); + const [local, setLocal] = useState(''); + const [error, setError] = useState(null); + + useEffect(() => { + if (settings?.modelsFolderPath) setLocal(settings.modelsFolderPath); + }, [settings?.modelsFolderPath]); + + const isDirty = local !== '' && local !== settings?.modelsFolderPath; + + async function handleSave() { + setError(null); + try { + await saveSetting('modelsFolderPath', local); + } catch (err) { + setError('Path not accessible'); + } + } + + return ( +
+
+ { setLocal(e.target.value); setError(null); }} + style={{ + width: '220px', padding: '5px 8px', fontSize: '12px', + fontFamily: 'monospace', + background: 'var(--bg-elevated)', border: `1px solid ${error ? '#e74c3c' : 'var(--border)'}`, + borderRadius: 'var(--radius-md)', color: 'var(--text-primary)', outline: 'none', + }} + /> + {isDirty && ( + + )} +
+ {error && {error}} +
+ ); } \ No newline at end of file