mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-16 01:21:20 -04:00
beginning of my ideases
This commit is contained in:
@@ -127,8 +127,8 @@ export default function ButtonBoxPanel() {
|
||||
|
||||
return (
|
||||
<section className="panel-section space-y-0.5 text-base">
|
||||
<header className="panel-muted text-center text-sm">
|
||||
<p>Button Box</p>
|
||||
<header className="panel-title-row">
|
||||
<p className="panel-title-text">Button Box</p>
|
||||
</header>
|
||||
<div className="grid grid-cols-4 gap-0.5">
|
||||
{buttons.map((button) => {
|
||||
|
||||
@@ -16,6 +16,7 @@ export default function ChatPanel({
|
||||
hideSpectatorNotice = false,
|
||||
fillHeight = false,
|
||||
allowSpectatorInput = false,
|
||||
title = 'Chat',
|
||||
}) {
|
||||
const role = useSessionSelector((state) => state.session?.role || null);
|
||||
const currentRoverId = useSessionSelector((state) => state.session?.assignment?.roverId || null);
|
||||
@@ -106,6 +107,11 @@ export default function ChatPanel({
|
||||
|
||||
return (
|
||||
<section className={`panel-section space-y-0.5 text-base ${fillHeight ? 'flex h-full flex-col overflow-hidden' : ''}`}>
|
||||
{title ? (
|
||||
<div className="panel-title-row">
|
||||
<span className="panel-title-text">{title}</span>
|
||||
</div>
|
||||
) : null}
|
||||
<div className={`surface overflow-y-auto space-y-0.5 px-0 ${listClass}`} ref={listRef}>
|
||||
{sorted.length === 0 && typingRows.length === 0 ? (
|
||||
<p className="text-sm text-slate-500">No messages yet.</p>
|
||||
|
||||
@@ -256,7 +256,7 @@ export default function HomeAssistantControls() {
|
||||
if (!ha?.enabled) {
|
||||
return (
|
||||
<section className="panel-section space-y-0.5 text-sm text-slate-400">
|
||||
<p className="text-slate-300">Room Controls</p>
|
||||
<p className="panel-title-text">Room Controls</p>
|
||||
<p className="text-slate-500">Not configured on the server.</p>
|
||||
</section>
|
||||
);
|
||||
@@ -265,7 +265,7 @@ export default function HomeAssistantControls() {
|
||||
if (entities.length === 0) {
|
||||
return (
|
||||
<section className="panel-section space-y-0.5 text-sm text-slate-400">
|
||||
<p className="text-slate-300">Room Controls</p>
|
||||
<p className="panel-title-text">Room Controls</p>
|
||||
<p className="text-slate-500">No lights or switches configured.</p>
|
||||
</section>
|
||||
);
|
||||
@@ -275,23 +275,25 @@ export default function HomeAssistantControls() {
|
||||
|
||||
return (
|
||||
<section className="panel-section space-y-0.5 text-base">
|
||||
<header className="flex items-center justify-between gap-0.5 text-sm text-slate-400">
|
||||
<div className="flex items-center gap-0.5">
|
||||
<p>Room Controls</p>
|
||||
<span className="text-xs text-slate-500">{entities.length}</span>
|
||||
{controlsLocked ? <StatusBadge label={lockState === 'off' ? 'Locked Off' : 'Locked On'} tone="warn" /> : null}
|
||||
<div className="flex items-center gap-0.5 text-xs text-slate-300 background-black">
|
||||
<header className="panel-title-row">
|
||||
<div className="panel-title-left">
|
||||
<p className="panel-title-text">Room Controls</p>
|
||||
<span className="panel-title-meta">{entities.length}</span>
|
||||
</div>
|
||||
<div className="panel-title-right">
|
||||
<div className="panel-title-actions">
|
||||
<span className="flex items-center gap-0.5">
|
||||
<span>On</span>
|
||||
<span className="panel-title-meta">On</span>
|
||||
{onKeyLabel ? <KeyPill label={onKeyLabel} /> : null}
|
||||
</span>
|
||||
<span className="flex items-center gap-0.5">
|
||||
<span>Off</span>
|
||||
<span className="panel-title-meta">Off</span>
|
||||
{offKeyLabel ? <KeyPill label={offKeyLabel} /> : null}
|
||||
</span>
|
||||
</div>
|
||||
{controlsLocked ? <StatusBadge label={lockState === 'off' ? 'Locked Off' : 'Locked On'} tone="warn" /> : null}
|
||||
<StatusBadge label={connected ? 'Connected' : 'Offline'} tone={connected ? 'success' : 'warn'} />
|
||||
</div>
|
||||
<StatusBadge label={connected ? 'Connected' : 'Offline'} tone={connected ? 'success' : 'warn'} />
|
||||
</header>
|
||||
{controlsLocked ? (
|
||||
<p className="rounded border border-amber-600/60 bg-amber-900/40 px-1 py-0.5 text-xs text-amber-100">
|
||||
@@ -319,5 +321,5 @@ export default function HomeAssistantControls() {
|
||||
|
||||
function KeyPill({ label }) {
|
||||
if (!label) return null;
|
||||
return <span className="rounded border border-white/40 px-1 text-[0.7rem] text-white">{label}</span>;
|
||||
return <span className="rounded border border-slate-600 px-1 text-[0.65rem] text-slate-300">{label}</span>;
|
||||
}
|
||||
|
||||
@@ -9,9 +9,9 @@ export default function LogPanel() {
|
||||
const rendered = useMemo(() => logs.slice().reverse(), [logs]);
|
||||
return (
|
||||
<div className="panel-section space-y-0.5 text-base">
|
||||
<div className="flex items-center justify-between text-sm text-slate-400">
|
||||
<span>Server logs</span>
|
||||
<span>{logs.length}</span>
|
||||
<div className="panel-title-row">
|
||||
<span className="panel-title-text">Server logs</span>
|
||||
<span className="panel-title-meta">{logs.length}</span>
|
||||
</div>
|
||||
<div className="surface h-64 overflow-y-auto font-mono text-xs">
|
||||
{logs.length === 0 ? (
|
||||
|
||||
@@ -22,7 +22,9 @@ export default function OverseerPreferencePanel() {
|
||||
|
||||
return (
|
||||
<section className="surface p-1 text-xs text-slate-200 text-center">
|
||||
<div className="text-[0.65rem] text-slate-400">Overseer vote</div>
|
||||
<div className="panel-title-row">
|
||||
<span className="panel-title-text">Overseer vote</span>
|
||||
</div>
|
||||
<label
|
||||
className={`flex items-center justify-center gap-1.5 rounded px-1 py-0.5 ${
|
||||
enabled ? 'bg-emerald-600/80 text-emerald-50' : 'bg-slate-600/70 text-slate-100'
|
||||
|
||||
@@ -76,9 +76,9 @@ export default function RawUserPilePanel({
|
||||
|
||||
<div className={`space-y-0.5 ${fillHeight ? 'flex flex-1 min-h-0 flex-col' : ''}`}>
|
||||
{!hideHeader && (
|
||||
<div className={`flex items-center justify-between text-sm text-slate-400 ${compact ? 'text-xs' : ''}`}>
|
||||
<span>Users</span>
|
||||
<span className="text-xs text-slate-500">{sorted.length}</span>
|
||||
<div className="panel-title-row">
|
||||
<span className="panel-title-text">Users</span>
|
||||
<span className="panel-title-meta">{sorted.length}</span>
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
|
||||
@@ -151,9 +151,9 @@ export default function ReplaySourcesPanel({ panelId = 'replay-sources', fillHei
|
||||
|
||||
return (
|
||||
<section className={`panel-section p-0.75 text-sm ${containerClass}`}>
|
||||
<header className="panel-muted flex items-center justify-between text-xs">
|
||||
<span>Replay Sources</span>
|
||||
<span>{sources.length}</span>
|
||||
<header className="panel-title-row">
|
||||
<span className="panel-title-text">Replay Sources</span>
|
||||
<span className="panel-title-meta">{sources.length}</span>
|
||||
</header>
|
||||
<div className={`grid gap-0.5 md:grid-cols-2 ${listWrapClass}`}>
|
||||
<GroupList title="Rovers" items={grouped.rovers} selected={selected} onToggle={toggleKey} />
|
||||
|
||||
@@ -101,6 +101,7 @@ function DriveDockPanel() {
|
||||
value={value}
|
||||
min={min}
|
||||
max={max}
|
||||
label="Camera tilt"
|
||||
disabled={cameraDisabled}
|
||||
onChange={setServoAngle}
|
||||
keyDownLabel={downLabel}
|
||||
|
||||
@@ -69,15 +69,15 @@ export default function RoomCameraPanel({
|
||||
return (
|
||||
<section className="panel-section space-y-0.5 text-base">
|
||||
{!hideHeader && (
|
||||
<header className="flex flex-wrap items-center justify-between gap-0.5 text-sm text-slate-400">
|
||||
<div className="flex items-center gap-0.5">
|
||||
<p>Room cameras</p>
|
||||
<span className="text-xs text-slate-500">{cameras.length}</span>
|
||||
<header className="panel-title-row">
|
||||
<div className="panel-title-left">
|
||||
<p className="panel-title-text">Room cameras</p>
|
||||
<span className="panel-title-meta">{cameras.length}</span>
|
||||
</div>
|
||||
<div className="flex flex-wrap items-center gap-0.5 text-xs">
|
||||
<div className="panel-title-right">
|
||||
{showLayoutToggle && (
|
||||
<div className="flex items-center gap-0.5">
|
||||
<span className="text-slate-500">Layout:</span>
|
||||
<div className="panel-title-actions">
|
||||
<span className="panel-title-meta">Layout</span>
|
||||
<div className="inline-flex overflow-hidden rounded border border-slate-700">
|
||||
{ORIENTATIONS.map((option) => (
|
||||
<button
|
||||
|
||||
@@ -97,7 +97,11 @@ export default function RoverQueuesPanel({ title = 'Rovers' }) {
|
||||
|
||||
return (
|
||||
<div className="space-y-0.5 text-sm">
|
||||
{title && <p className="text-sm text-slate-400">{title}</p>}
|
||||
{title && (
|
||||
<div className="panel-title-row">
|
||||
<p className="panel-title-text">{title}</p>
|
||||
</div>
|
||||
)}
|
||||
{rosterItems.length === 0 ? (
|
||||
<p className="text-sm text-slate-500">No rovers registered.</p>
|
||||
) : (
|
||||
|
||||
@@ -173,7 +173,7 @@ function SensorDetails({ sensors, dockState }) {
|
||||
function DetailCard({ title, children }) {
|
||||
return (
|
||||
<div className="surface space-y-0.5 p-1 text-sm">
|
||||
<div className="text-[0.8rem] uppercase tracking-wide text-slate-400">{title}</div>
|
||||
<div className="panel-title-text">{title}</div>
|
||||
<div className="space-y-0.5">{children}</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -20,6 +20,9 @@ export function NicknameEntryPanel({ compact = false }) {
|
||||
export function LinkButtonsPanel() {
|
||||
return (
|
||||
<section className="panel-section flex h-full min-h-0 flex-col gap-0.5 text-base">
|
||||
<div className="panel-title-row">
|
||||
<span className="panel-title-text">Socials</span>
|
||||
</div>
|
||||
<SocialButtonsGrid className="flex-1 min-h-0" />
|
||||
</section>
|
||||
);
|
||||
|
||||
@@ -71,6 +71,30 @@ body {
|
||||
@apply text-sm text-slate-400 rounded-md;
|
||||
}
|
||||
|
||||
.panel-title-row {
|
||||
@apply flex items-center justify-between gap-0.5 rounded-md bg-slate-900 px-0.5 py-0.5 text-left;
|
||||
}
|
||||
|
||||
.panel-title-text {
|
||||
@apply text-[0.78rem] font-semibold tracking-normal text-slate-200 leading-none text-left;
|
||||
}
|
||||
|
||||
.panel-title-meta {
|
||||
@apply text-[0.68rem] font-medium tracking-normal text-slate-400 leading-none;
|
||||
}
|
||||
|
||||
.panel-title-left {
|
||||
@apply flex min-w-0 items-center gap-0.5;
|
||||
}
|
||||
|
||||
.panel-title-right {
|
||||
@apply flex flex-wrap items-center justify-end gap-0.5;
|
||||
}
|
||||
|
||||
.panel-title-actions {
|
||||
@apply flex items-center gap-0.5 text-[0.68rem] text-slate-400 leading-none;
|
||||
}
|
||||
|
||||
.surface {
|
||||
@apply bg-neutral-800 text-white px-0.5 py-0.5 rounded-md;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user