| import React, { useState, useEffect } from 'react'; |
| const { ipcRenderer } = window.require('electron'); |
|
|
| export default function App() { |
| const [messages, setMessages] = useState([]); |
| const [input, setInput] = useState(""); |
| const [memoryActive, setMemoryActive] = useState(false); |
| const [models, setModels] = useState({ local: [], presets: [] }); |
| const [selectedModel, setSelectedModel] = useState("ibm-granite/granite-3.0-2b-instruct"); |
|
|
| useEffect(() => { |
| |
| ipcRenderer.send('list-models'); |
| |
| ipcRenderer.on('model-list', (e, data) => setModels(data)); |
| |
| ipcRenderer.on('receive-token', (event, token) => { |
| setMessages(prev => { |
| const last = prev[prev.length - 1]; |
| if (last && last.role === 'assistant') { |
| return [...prev.slice(0, -1), { role: 'assistant', text: last.text + token }]; |
| } |
| return [...prev, { role: 'assistant', text: token }]; |
| }); |
| }); |
| |
| ipcRenderer.on('stream-end', () => setMemoryActive(false)); |
| }, []); |
|
|
| const sendMessage = () => { |
| if (!input) return; |
| setMessages(prev => [...prev, { role: 'user', text: input }]); |
| ipcRenderer.send('send-message', { model: selectedModel, text: input }); |
| setInput(""); |
| setMemoryActive(true); |
| }; |
|
|
| return ( |
| <div style={{ display: 'flex', height: '100vh', background: '#1e1e1e', color: 'white' }}> |
| |
| {/* LEFT: Chat Panel */} |
| <div style={{ flex: 1, display: 'flex', flexDirection: 'column', borderRight: '1px solid #333' }}> |
| <div style={{ padding: 20, borderBottom: '1px solid #333', display: 'flex', justifyContent: 'space-between' }}> |
| <h2>🪔 OV-Studio</h2> |
| <select |
| value={selectedModel} |
| onChange={e => setSelectedModel(e.target.value)} |
| style={{ background: '#333', color: 'white', padding: 5, borderRadius: 5 }} |
| > |
| <optgroup label="Preset Models"> |
| {models.presets.map(m => <option key={m} value={m}>{m}</option>)} |
| </optgroup> |
| <optgroup label="Local GGUF"> |
| {models.local.map(m => <option key={m} value={m}>{m}</option>)} |
| </optgroup> |
| </select> |
| </div> |
| |
| <div style={{ flex: 1, padding: 20, overflowY: 'auto' }}> |
| {messages.map((m, i) => ( |
| <div key={i} style={{ |
| marginBottom: 15, |
| alignSelf: m.role === 'user' ? 'flex-end' : 'flex-start', |
| background: m.role === 'user' ? '#007acc' : '#333', |
| padding: 10, borderRadius: 10, maxWidth: '80%' |
| }}> |
| <strong>{m.role === 'user' ? 'You' : 'OV-Engine'}:</strong> |
| <p style={{ margin: '5px 0 0 0' }}>{m.text}</p> |
| </div> |
| ))} |
| </div> |
| |
| <div style={{ padding: 20, background: '#252526' }}> |
| <input |
| value={input} |
| onChange={e => setInput(e.target.value)} |
| onKeyPress={e => e.key === 'Enter' && sendMessage()} |
| placeholder="Ask OV-Engine..." |
| style={{ width: '100%', padding: 10, borderRadius: 5, border: 'none' }} |
| /> |
| </div> |
| </div> |
| |
| {/* RIGHT: Memory Visualizer (Honeycomb) */} |
| <div style={{ width: 400, background: '#111', padding: 20 }}> |
| <h3>🕸️ Memory Graph</h3> |
| <p>Status: {memoryActive ? "Scanning C++ Kernel..." : "Idle"}</p> |
| |
| {/* Placeholder for 3D Graph */} |
| <div style={{ |
| marginTop: 20, height: 300, |
| background: memoryActive ? '#004400' : '#222', |
| display: 'flex', alignItems: 'center', justifyContent: 'center', |
| transition: 'background 0.5s' |
| }}> |
| {memoryActive ? "Searching 20,000 Vectors..." : "Waiting for Query"} |
| </div> |
| |
| <div style={{ marginTop: 20 }}> |
| <h4>Active Context:</h4> |
| <div style={{ fontSize: 12, color: '#aaa' }}> |
| {memoryActive ? "Calculating P = S * C * R * W" : "None"} |
| </div> |
| </div> |
| </div> |
| |
| </div> |
| ); |
| } |
|
|