keet-streaming / src /components /ContextPanel.tsx
ysdede's picture
feat(space): migrate Hugging Face Space to keet SolidJS app
b8cc2bf
import { Component, For, Show, createEffect, onCleanup } from 'solid-js';
import { appStore } from '../stores/appStore';
import { getModelDisplayName, MODELS } from './ModelLoadingOverlay';
interface ContextPanelProps {
isOpen: boolean;
onClose: () => void;
onLoadModel: () => void;
onOpenDebug: () => void;
onDeviceSelect?: (id: string) => void;
}
export const ContextPanel: Component<ContextPanelProps> = (props) => {
createEffect(() => {
if (!props.isOpen) return;
const handler = (e: KeyboardEvent) => {
if (e.key === 'Escape') {
e.preventDefault();
props.onClose();
}
};
document.addEventListener('keydown', handler);
onCleanup(() => document.removeEventListener('keydown', handler));
});
return (
<Show when={props.isOpen}>
<div
class="fixed inset-0 z-40 flex items-center justify-center bg-[var(--color-earthy-dark-brown)]/30 backdrop-blur-sm"
role="dialog"
aria-modal="true"
aria-label="Context and settings"
onClick={(e) => e.target === e.currentTarget && props.onClose()}
>
<div
class="w-full max-w-md mx-4 bg-[var(--color-earthy-bg)] rounded-2xl border border-[var(--color-earthy-sage)] shadow-xl overflow-hidden"
onClick={(e) => e.stopPropagation()}
>
<div class="px-6 py-4 border-b border-[var(--color-earthy-sage)]/30 flex items-center justify-between">
<h2 class="text-lg font-semibold tracking-tight text-[var(--color-earthy-dark-brown)]">Context</h2>
<button
type="button"
onClick={props.onClose}
class="p-2 rounded-full text-[var(--color-earthy-muted-green)] hover:bg-[var(--color-earthy-sage)]/30 transition-colors"
aria-label="Close"
>
<span class="material-symbols-outlined text-xl">close</span>
</button>
</div>
<div class="p-6 space-y-6">
<section>
<h3 class="text-[10px] font-bold uppercase tracking-widest text-[var(--color-earthy-soft-brown)] mb-3">Model</h3>
<div class="flex flex-col gap-2">
<select
class="w-full text-sm bg-white border border-[var(--color-earthy-sage)] rounded-xl px-3 py-2 text-[var(--color-earthy-dark-brown)] focus:outline-none focus:ring-2 focus:ring-[var(--color-earthy-coral)]/30"
value={appStore.selectedModelId()}
onInput={(e) => appStore.setSelectedModelId((e.target as HTMLSelectElement).value)}
disabled={appStore.modelState() === 'loading'}
>
<For each={MODELS}>
{(m) => <option value={m.id}>{m.name}</option>}
</For>
</select>
<p class="text-xs text-[var(--color-earthy-soft-brown)]">
{appStore.modelState() === 'ready' ? getModelDisplayName(appStore.selectedModelId()) : appStore.modelState()}
</p>
<button
type="button"
onClick={props.onLoadModel}
disabled={appStore.modelState() === 'ready' || appStore.modelState() === 'loading'}
class="flex items-center gap-2 px-4 py-2 rounded-full border border-[var(--color-earthy-sage)] text-[var(--color-earthy-muted-green)] hover:bg-[var(--color-earthy-muted-green)] hover:text-white transition-all text-sm font-medium disabled:opacity-50 disabled:cursor-not-allowed"
>
<span class="material-symbols-outlined text-lg">power_settings_new</span>
{appStore.modelState() === 'ready' ? 'Model loaded' : appStore.modelState() === 'loading' ? 'Loading...' : 'Load model'}
</button>
</div>
</section>
<section>
<h3 class="text-[10px] font-bold uppercase tracking-widest text-[var(--color-earthy-soft-brown)] mb-3">Audio input</h3>
<select
class="w-full text-sm bg-white border border-[var(--color-earthy-sage)] rounded-xl px-3 py-2 text-[var(--color-earthy-dark-brown)] focus:outline-none focus:ring-2 focus:ring-[var(--color-earthy-coral)]/30"
value={appStore.selectedDeviceId()}
onInput={(e) => {
const id = (e.target as HTMLSelectElement).value;
appStore.setSelectedDeviceId(id);
props.onDeviceSelect?.(id);
}}
>
<For each={appStore.availableDevices()}>
{(device) => (
<option value={device.deviceId}>
{device.label || `Device ${device.deviceId.slice(0, 8)}`}
</option>
)}
</For>
</select>
</section>
<section>
<h3 class="text-[10px] font-bold uppercase tracking-widest text-[var(--color-earthy-soft-brown)] mb-2">Backend</h3>
<p class="text-sm text-[var(--color-earthy-dark-brown)] font-medium">{appStore.backend().toUpperCase()}</p>
</section>
<div class="pt-2 border-t border-[var(--color-earthy-sage)]/30 flex items-center justify-between">
<span class="text-xs text-[var(--color-earthy-soft-brown)]">Developer</span>
<button
type="button"
onClick={() => {
props.onOpenDebug();
props.onClose();
}}
class="flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-xs font-medium text-[var(--color-earthy-muted-green)] hover:bg-[var(--color-earthy-sage)]/30 transition-colors"
>
<span class="material-symbols-outlined text-base">bug_report</span>
Open Debug panel
</button>
</div>
</div>
</div>
</div>
</Show>
);
};