Spaces:
Build error
Build error
File size: 3,707 Bytes
36ebfb6 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 | import { useMemo, useState } from "react";
import { StreamStatus, useAudioStreamer } from "./hooks/useAudioStreamer";
import { StatusBadge } from "./components/StatusBadge";
import SessionControls from "./components/SessionControls";
import StatBoard from "./components/StatBoard";
import TranscriptSurface from "./components/TranscriptSurface";
const DEFAULT_WS_URI = import.meta.env.VITE_WS_URI ?? "ws://localhost:8000/ws";
const statusHint: Record<StreamStatus, string> = {
idle: "Ready to connect.",
"requesting-permission": "Requesting microphone access...",
connecting: "Connecting to the backend...",
recording: "Streaming audio...",
error: "An error occurred.",
};
export default function App() {
const [wsUri, setWsUri] = useState<string>(DEFAULT_WS_URI);
const { status, transcript, error, stats, isRecording, start, stop, clearTranscript } =
useAudioStreamer(wsUri);
const statusDescription = useMemo(() => statusHint[status], [status]);
const isBusy = status === "requesting-permission" || status === "connecting";
const showReset = wsUri !== DEFAULT_WS_URI;
const handleToggleStreaming = async () => {
if (isRecording) {
await stop();
return;
}
clearTranscript();
await start();
};
const handleStop = async () => {
await stop();
};
const handleResetEndpoint = () => {
setWsUri(DEFAULT_WS_URI);
};
return (
<div className="app-shell">
<header className="hero" style={{ position: "relative" }}>
<div style={{ position: "absolute", top: 0, right: 0 }}>
<a
href="/docs/index.html"
target="_blank"
rel="noreferrer"
className="button button--ghost"
style={{ textDecoration: "none", fontSize: "0.9rem" }}
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z" />
<path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z" />
</svg>
Docs
</a>
</div>
<span className="hero__eyebrow">Gemini Live API</span>
<h1 className="hero__title">Cosmic Flow</h1>
<p className="hero__subtitle">
A real-time audio streaming interface powered by Google Gemini.
</p>
<div className="hero__status">
<StatusBadge status={status} />
<p className="hero__status-hint">{statusDescription}</p>
</div>
</header>
<main className="panels">
<section className="panel">
<header className="panel__header">
<div>
<h2 className="panel__title">Session Controls</h2>
<p className="panel__subtitle">Configure and manage your audio stream.</p>
</div>
</header>
<SessionControls
wsUri={wsUri}
setWsUri={setWsUri}
isRecording={isRecording}
isBusy={isBusy}
startRecording={handleToggleStreaming}
stopRecording={handleStop}
showReset={showReset}
handleResetEndpoint={handleResetEndpoint}
/>
<StatBoard
stats={stats}
isRecording={isRecording}
/>
</section>
<TranscriptSurface
transcript={transcript}
error={error}
clearTranscript={clearTranscript}
/>
</main>
</div>
);
}
|