This commit is contained in:
legop3
2026-03-15 22:43:05 -04:00
parent 2ea222355f
commit 95972af558
20 changed files with 552 additions and 134 deletions
+52 -1
View File
@@ -20,6 +20,8 @@ export default function AdminPanel() {
setAdminReason,
rebootRover,
rebootServer,
playTestAudio,
stopTestAudio,
llmControl,
adminLogs,
llmCommentaryState,
@@ -27,6 +29,7 @@ export default function AdminPanel() {
const roster = useMemo(() => session?.roster ?? [], [session?.roster]);
const [lockStates, setLockStates] = useState({});
const [rebootStates, setRebootStates] = useState({});
const [audioStates, setAudioStates] = useState({});
const [serverRebooting, setServerRebooting] = useState(false);
const [clearingLlmHistory, setClearingLlmHistory] = useState(false);
const health = session?.health || null;
@@ -84,6 +87,30 @@ export default function AdminPanel() {
}
};
const handlePlayTestAudio = async (roverId) => {
if (!roverId) return;
setAudioStates((prev) => ({ ...prev, [roverId]: true }));
try {
await playTestAudio(roverId);
} catch (err) {
alert(err.message);
} finally {
setAudioStates((prev) => ({ ...prev, [roverId]: false }));
}
};
const handleStopTestAudio = async (roverId) => {
if (!roverId) return;
setAudioStates((prev) => ({ ...prev, [roverId]: true }));
try {
await stopTestAudio(roverId);
} catch (err) {
alert(err.message);
} finally {
setAudioStates((prev) => ({ ...prev, [roverId]: false }));
}
};
const handleServerReboot = async () => {
const ok = window.confirm('Reboot the server host now? This will disconnect all users.');
if (!ok) return;
@@ -232,7 +259,7 @@ export default function AdminPanel() {
<RoverRoster
roster={roster}
renderActions={(rover) => (
<div className="flex flex-wrap gap-0.5 text-xs">
<div className="flex flex-wrap items-center gap-0.5 text-xs">
<button
type="button"
onClick={() => handleLockToggle(rover.id, !lockMap[rover.id])}
@@ -251,6 +278,30 @@ export default function AdminPanel() {
>
{rebootStates[rover.id] ? 'Rebooting...' : 'Reboot'}
</button>
<button
type="button"
onClick={() => handlePlayTestAudio(rover.id)}
disabled={Boolean(audioStates[rover.id])}
className="button-dark disabled:cursor-not-allowed disabled:opacity-60"
>
Play Test Audio
</button>
<button
type="button"
onClick={() => handleStopTestAudio(rover.id)}
disabled={Boolean(audioStates[rover.id])}
className="button-dark disabled:cursor-not-allowed disabled:opacity-60"
>
Stop Test Audio
</button>
<span className="surface-muted">
audio: {session?.audioForward?.[rover.id]?.state || 'idle'}
</span>
{session?.audioForward?.[rover.id]?.error ? (
<span className="surface-muted text-rose-300">
{String(session.audioForward[rover.id].error)}
</span>
) : null}
</div>
)}
/>
+4
View File
@@ -26,6 +26,8 @@ const SessionContext = createContext({
setAdminReason: async () => {},
rebootRover: async () => {},
rebootServer: async () => {},
playTestAudio: async () => {},
stopTestAudio: async () => {},
llmControl: async () => {},
});
@@ -141,6 +143,8 @@ export function SessionProvider({ children }) {
rebootRover: (roverId) =>
emitWithAck('command', { roverId, type: 'reboot', data: { reboot: {} } }),
rebootServer: () => emitWithAck('server:reboot'),
playTestAudio: (roverId) => emitWithAck('audio:testPlay', { roverId }),
stopTestAudio: (roverId) => emitWithAck('audio:testStop', { roverId }),
llmControl: (action, controls = {}) =>
emitWithAck('llm:control', { controls: { action, ...controls } }),
pushAlert: (alert) =>