feat: add server process control panel to Tauri app

This commit is contained in:
Evandro Fonseca Junior committed 2026-03-26 21:16:35 -03:00
1 parent 818d9f99e6
commit 99672b33d0
7 files changed
+437 -2

No files matched your search

+127
View File
@@ -0,0 +1,127 @@
import { useState } from "react";
import type { ProcessStatus, ServerConfig } from "../hooks/useServerProcess";
import "./ServerPanel.css";
interface Props {
status: ProcessStatus;
message: string;
onStart: (cfg: ServerConfig) => void;
onStop: () => void;
}
const STATUS_LABEL: Record<ProcessStatus, string> = {
stopped: "Stopped",
starting: "Starting…",
running: "Running",
stopping: "Stopping…",
error: "Error",
};
const STATUS_COLOR: Record<ProcessStatus, string> = {
stopped: "#6b7280",
starting: "#f59e0b",
running: "#22c55e",
stopping: "#f59e0b",
error: "#ef4444",
};
export default function ServerPanel({ status, message, onStart, onStop }: Props) {
const [modelPath, setModelPath] = useState(
() => localStorage.getItem("forgelm_model_path") || ""
);
const [addr, setAddr] = useState(
() => localStorage.getItem("forgelm_server_addr") || "127.0.0.1:8080"
);
const [gpuLayers, setGpuLayers] = useState(
() => parseInt(localStorage.getItem("forgelm_gpu_layers") || "0", 10)
);
const [expanded, setExpanded] = useState(false);
const isActive = status === "running" || status === "starting" || status === "stopping";
const handleStart = () => {
localStorage.setItem("forgelm_model_path", modelPath);
localStorage.setItem("forgelm_server_addr", addr);
localStorage.setItem("forgelm_gpu_layers", String(gpuLayers));
onStart({ modelPath, addr, gpuLayers });
};
return (
<div className="server-panel">
<div className="server-panel-header" onClick={() => setExpanded(!expanded)}>
<div className="server-panel-title">
<span
className="server-status-dot"
style={{ background: STATUS_COLOR[status] }}
/>
<span className="server-panel-label">Local Server</span>
<span className="server-status-text" style={{ color: STATUS_COLOR[status] }}>
{STATUS_LABEL[status]}
</span>
</div>
<span className="server-panel-chevron">{expanded ? "▲" : "▼"}</span>
</div>
{expanded && (
<div className="server-panel-body">
<label className="sp-label">Model path (.gguf)</label>
<input
className="sp-input"
type="text"
value={modelPath}
onChange={(e) => setModelPath(e.target.value)}
placeholder="models/your-model.gguf"
disabled={isActive}
/>
<label className="sp-label">Address</label>
<input
className="sp-input"
type="text"
value={addr}
onChange={(e) => setAddr(e.target.value)}
placeholder="127.0.0.1:8080"
disabled={isActive}
/>
<label className="sp-label">GPU Layers (0 = CPU only)</label>
<input
className="sp-input sp-input-num"
type="number"
min={0}
max={999}
value={gpuLayers}
onChange={(e) => setGpuLayers(parseInt(e.target.value, 10) || 0)}
disabled={isActive}
/>
<div className="sp-actions">
{status === "stopped" || status === "error" ? (
<button
className="sp-btn sp-btn-start"
onClick={handleStart}
disabled={!modelPath.trim()}
>
▶ Start Server
</button>
) : (
<button
className="sp-btn sp-btn-stop"
onClick={onStop}
disabled={status === "starting" || status === "stopping"}
>
■ Stop Server
</button>
)}
</div>
{message && (
<div className={`sp-message ${status === "error" ? "sp-message-error" : ""}`}>
{message}
</div>
)}
</div>
)}
</div>
);
}