mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-16 01:21:20 -04:00
pride gay theme yay
This commit is contained in:
+19
-18
@@ -43,6 +43,7 @@ import { useSessionSelector } from './context/SessionContext.jsx';
|
||||
import ButtonBoxPanel from './components/ButtonBoxPanel/index.jsx';
|
||||
import RewardRunOverlay from './components/RewardRunOverlay/index.jsx';
|
||||
import SocketConnectionPill from './components/SocketConnectionPill/index.jsx';
|
||||
import { pageBackgroundClass, themeGapClass, themeStackClass } from './themeFlags.js';
|
||||
|
||||
function useLayoutMode() {
|
||||
const [mode, setMode] = useState(() => {
|
||||
@@ -76,13 +77,13 @@ function useLayoutMode() {
|
||||
|
||||
function DesktopLayout({ layout, onOpenHelpOverlay }) {
|
||||
return (
|
||||
<div className="flex h-full gap-0.5 overflow-hidden">
|
||||
<div className="flex min-w-0 flex-[1.22] flex-col gap-0.5 overflow-y-auto pr-0">
|
||||
<div className={`flex h-full ${themeGapClass} overflow-hidden`}>
|
||||
<div className={`flex min-w-0 flex-[1.22] flex-col ${themeGapClass} overflow-y-auto pr-0`}>
|
||||
<DriverVideo />
|
||||
<TelemetryPanel />
|
||||
<LogPanel />
|
||||
</div>
|
||||
<div className="flex min-w-0 flex-1 flex-col gap-0.5 overflow-y-auto">
|
||||
<div className={`flex min-w-0 flex-1 flex-col ${themeGapClass} overflow-y-auto`}>
|
||||
<GlobalObjectiveBanner layout={layout} />
|
||||
<RightPaneTabs layout={layout} onOpenHelpOverlay={onOpenHelpOverlay} />
|
||||
{/* <SessionSnapshot /> */}
|
||||
@@ -123,7 +124,7 @@ function MobileFeatureTabs({
|
||||
ownAudioForward?.state === 'playing',
|
||||
);
|
||||
return (
|
||||
<section className="panel text-base">
|
||||
<section className="text-base">
|
||||
<Tabs defaultTab="chat" currentTab={activeTab} onTabChange={setActiveTab}>
|
||||
<TabList>
|
||||
<Tab id="chat">Chat</Tab>
|
||||
@@ -143,13 +144,13 @@ function MobileFeatureTabs({
|
||||
</TabList>
|
||||
<TabPanels>
|
||||
<TabPanel id="chat">
|
||||
<div className="space-y-0.5">
|
||||
<div className={themeStackClass}>
|
||||
<ChatPanel nicknameLayout="stacked" />
|
||||
<div className="space-y-0.5">
|
||||
<div className="grid gap-0.5 md:grid-cols-[minmax(0,1.4fr)_minmax(0,1fr)]">
|
||||
<div className={themeStackClass}>
|
||||
<div className={`grid ${themeGapClass} md:grid-cols-[minmax(0,1.4fr)_minmax(0,1fr)]`}>
|
||||
<SocialButtonsGrid />
|
||||
</div>
|
||||
<div className="grid gap-0.5 grid-cols-[minmax(0,1fr)_minmax(0,1fr)]">
|
||||
<div className={`grid ${themeGapClass} grid-cols-[minmax(0,1fr)_minmax(0,1fr)]`}>
|
||||
<OverseerPreferencePanel />
|
||||
<RawUserPilePanel hideNicknameForm />
|
||||
</div>
|
||||
@@ -160,7 +161,7 @@ function MobileFeatureTabs({
|
||||
<VipPanel isActive={activeTab === 'vip'} />
|
||||
</TabPanel>
|
||||
<TabPanel id="roomcontrols">
|
||||
<div className="space-y-0.5">
|
||||
<div className={themeStackClass}>
|
||||
{/* {showTelemetry ? <TelemetryPanel /> : null} */}
|
||||
<HomeAssistantControls />
|
||||
<ButtonBoxPanel />
|
||||
@@ -171,7 +172,7 @@ function MobileFeatureTabs({
|
||||
<HelpPanel layout={layout} onOpenOverlay={onOpenHelpOverlay} />
|
||||
</TabPanel>
|
||||
<TabPanel id="settings">
|
||||
<div className="space-y-0.5">
|
||||
<div className={themeStackClass}>
|
||||
<SettingsPanel />
|
||||
<LogPanel />
|
||||
</div>
|
||||
@@ -184,10 +185,10 @@ function MobileFeatureTabs({
|
||||
|
||||
function MobilePortraitLayout({ onOpenHelpOverlay, swapMobileControlColumns = false }) {
|
||||
return (
|
||||
<div className="flex flex-col gap-0.5">
|
||||
<div className={`flex flex-col ${themeGapClass}`}>
|
||||
<DriverVideo layoutFormat="mobile-portrait" />
|
||||
<MobileControls swapColumns={swapMobileControlColumns} />
|
||||
<div className="grid gap-0.5 grid-cols-[minmax(0,1fr)_minmax(0,1.4fr)]">
|
||||
<div className={`grid ${themeGapClass} grid-cols-[minmax(0,1fr)_minmax(0,1.4fr)]`}>
|
||||
<ReplaySourcesPanel panelId="replay-sources-mobile-portrait" />
|
||||
<RoverQueuesPanel />
|
||||
</div>
|
||||
@@ -210,12 +211,12 @@ function MobileLandscapeLayout({ onOpenHelpOverlay, swapMobileControlColumns = f
|
||||
? <MobileActionsColumn layout="landscape" className={columnClass} />
|
||||
: <MobileDriveColumn layout="landscape" className={columnClass} />;
|
||||
return (
|
||||
<div className="flex flex-col gap-0.5">
|
||||
<section className="grid min-h-screen grid-cols-[minmax(0,0.7fr)_minmax(0,2.1fr)_minmax(0,0.7fr)] gap-0.5">
|
||||
<div className={`flex flex-col ${themeGapClass}`}>
|
||||
<section className={`grid min-h-screen grid-cols-[minmax(0,0.7fr)_minmax(0,2.1fr)_minmax(0,0.7fr)] ${themeGapClass}`}>
|
||||
{firstColumn}
|
||||
<div>
|
||||
<DriverVideo layoutFormat="mobile-landscape" />
|
||||
<div className="grid gap-0.5 grid-cols-[minmax(0,1fr)_minmax(0,1.4fr)]">
|
||||
<div className={`grid ${themeGapClass} grid-cols-[minmax(0,1fr)_minmax(0,1.4fr)]`}>
|
||||
<ReplaySourcesPanel panelId="replay-sources-mobile-landscape" />
|
||||
<RoverQueuesPanel />
|
||||
</div>
|
||||
@@ -223,7 +224,7 @@ function MobileLandscapeLayout({ onOpenHelpOverlay, swapMobileControlColumns = f
|
||||
</div>
|
||||
{secondColumn}
|
||||
</section>
|
||||
<div className="flex flex-col gap-0.5 pb-0">
|
||||
<div className={`flex flex-col ${themeGapClass} pb-0`}>
|
||||
<MobileFeatureTabs
|
||||
layout="mobile-landscape"
|
||||
onOpenHelpOverlay={onOpenHelpOverlay}
|
||||
@@ -241,7 +242,7 @@ function App() {
|
||||
const fullscreen = useFullscreenPrompt(layout);
|
||||
|
||||
return (
|
||||
<div className={`bg-black text-slate-100 ${isDesktop ? 'h-screen overflow-hidden' : 'min-h-screen'}`}>
|
||||
<div className={`${pageBackgroundClass} text-slate-100 ${isDesktop ? 'h-screen overflow-hidden' : 'min-h-screen'}`}>
|
||||
<AppWithProviders layout={layout} isDesktop={isDesktop} fullscreen={fullscreen} />
|
||||
</div>
|
||||
);
|
||||
@@ -326,7 +327,7 @@ function AppWithProviders({ layout, isDesktop, fullscreen }) {
|
||||
<ControlSystemProvider>
|
||||
<KeyboardInputManager />
|
||||
<GamepadInputManager />
|
||||
<main className={`flex w-full flex-col gap-0.5 text-base ${isDesktop ? 'h-full overflow-hidden' : ''}`}>
|
||||
<main className={`flex w-full flex-col ${themeGapClass} text-base ${isDesktop ? 'h-full overflow-hidden' : ''}`}>
|
||||
{!isDesktop ? <GlobalObjectiveBanner layout={layout} /> : null}
|
||||
{renderedLayout}
|
||||
</main>
|
||||
|
||||
@@ -60,7 +60,7 @@ export default function CardFrame({
|
||||
return (
|
||||
<section
|
||||
className={cx(
|
||||
'panel-section border border-neutral-500/60 bg-neutral-900/95 shadow-[0_1px_0_rgba(255,255,255,0.05)_inset,0_10px_24px_rgba(0,0,0,0.28)]',
|
||||
'panel-section border border-neutral-500/60 bg-neutral-900/95',
|
||||
clipOverflow ? 'overflow-hidden' : 'overflow-visible',
|
||||
fillHeight && 'flex h-full min-h-0 flex-col',
|
||||
className,
|
||||
|
||||
@@ -27,6 +27,7 @@ import OverseerPreferencePanel from '../OverseerPreferencePanel/index.jsx';
|
||||
import CardFrame from '../CardFrame/index.jsx';
|
||||
import { useState } from 'react';
|
||||
import { useManualDockAssist } from '../../features/manualDockAssist/useManualDockAssist.js';
|
||||
import { themeGapClass, themeStackClass } from '../../themeFlags.js';
|
||||
|
||||
function TopDownMapPanel() {
|
||||
const {
|
||||
@@ -75,10 +76,10 @@ function DriveDockPanel() {
|
||||
const cameraDisabled = Boolean(!roverId || dockAssist.cameraLocked);
|
||||
|
||||
return (
|
||||
<section className="panel-section grid h-full min-h-0 grid-rows-[minmax(0,1fr)_auto] gap-0.5">
|
||||
<section className={`panel-section grid h-full min-h-0 grid-rows-[minmax(0,1fr)_auto] ${themeGapClass}`}>
|
||||
<DriveDockAction layout="desktop" expand driveDockState={driveDockState} />
|
||||
{!hideInlineControls ? (
|
||||
<div className="surface space-y-0.5 p-0 text-sm text-slate-200">
|
||||
<div className={`surface ${themeStackClass} p-0 text-sm text-slate-200`}>
|
||||
{nightVisionAvailable && (
|
||||
<NightVisionControl
|
||||
nightVisionOn={nightVisionState?.nightVisionOn}
|
||||
@@ -107,7 +108,7 @@ function DriveDockPanel() {
|
||||
onChange={setServoAngle}
|
||||
keyDownLabel={downLabel}
|
||||
keyUpLabel={upLabel}
|
||||
className="space-y-0.5 px-1 py-1"
|
||||
className={`${themeStackClass} px-1 py-1`}
|
||||
labelRowClass="text-xs text-slate-300"
|
||||
labelClass=""
|
||||
valueClass="font-mono text-slate-100"
|
||||
@@ -149,7 +150,7 @@ export default function RightPaneTabs({ layout, onOpenHelpOverlay }) {
|
||||
ownAudioForward?.state === 'playing',
|
||||
);
|
||||
return (
|
||||
<section className="panel text-base">
|
||||
<section className="text-base">
|
||||
<Tabs defaultTab="telemetry" currentTab={activeTab} onTabChange={setActiveTab}>
|
||||
<TabList>
|
||||
<Tab id="telemetry">Controls</Tab>
|
||||
@@ -168,19 +169,19 @@ export default function RightPaneTabs({ layout, onOpenHelpOverlay }) {
|
||||
</TabList>
|
||||
<TabPanels>
|
||||
<TabPanel id="telemetry">
|
||||
<div className="space-y-0.5">
|
||||
<div className="grid items-stretch gap-0.5 grid-cols-[minmax(0,1.35fr)_minmax(0,0.95fr)]">
|
||||
<div className={themeStackClass}>
|
||||
<div className={`grid items-stretch ${themeGapClass} grid-cols-[minmax(0,1.35fr)_minmax(0,0.95fr)]`}>
|
||||
<TopDownMapPanel />
|
||||
<DriveDockPanel />
|
||||
</div>
|
||||
<div className="grid gap-0.5 grid-cols-[minmax(0,1fr)_minmax(0,0.9fr)_minmax(0,0.75fr)]">
|
||||
<div className={`grid ${themeGapClass} grid-cols-[minmax(0,1fr)_minmax(0,0.9fr)_minmax(0,0.75fr)]`}>
|
||||
<RoverQueuesPanel />
|
||||
<ReplaySourcesPanel panelId="replay-sources-desktop" fillHeight />
|
||||
<LinkButtonsPanel />
|
||||
</div>
|
||||
<div className="grid items-stretch gap-0.5 grid-cols-[minmax(0,1.3fr)_minmax(0,0.22fr)] h-[14rem]">
|
||||
<div className={`grid items-stretch ${themeGapClass} grid-cols-[minmax(0,1.3fr)_minmax(0,0.22fr)] h-[14rem]`}>
|
||||
<ChatPanel fillHeight />
|
||||
<div className="grid min-h-0 gap-0.5 grid-rows-[auto_minmax(0,1fr)]">
|
||||
<div className={`grid min-h-0 ${themeGapClass} grid-rows-[auto_minmax(0,1fr)]`}>
|
||||
<OverseerPreferencePanel />
|
||||
<RawUserPilePanel compact hideNicknameForm fillHeight />
|
||||
</div>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// Purpose: Defines the Tabs module and the local helpers/components used in this file.
|
||||
// Scope: Keeps behavior unchanged while isolating this concern into a clear, single-responsibility unit.
|
||||
import { createContext, useCallback, useContext, useMemo, useState, useEffect } from 'react';
|
||||
import { useSessionSelector } from '../../context/SessionContext.jsx';
|
||||
import { themeGapClass, themeStackClass } from '../../themeFlags.js';
|
||||
|
||||
const TabsContext = createContext(null);
|
||||
|
||||
@@ -34,28 +34,6 @@ function classNames(...parts) {
|
||||
return parts.filter(Boolean).join(' ');
|
||||
}
|
||||
|
||||
function hexToRgb(hex) {
|
||||
const raw = String(hex || '').trim();
|
||||
const normalized = raw.startsWith('#') ? raw.slice(1) : raw;
|
||||
if (!/^[0-9a-fA-F]{3}$|^[0-9a-fA-F]{6}$/.test(normalized)) return null;
|
||||
const expanded =
|
||||
normalized.length === 3
|
||||
? normalized
|
||||
.split('')
|
||||
.map((ch) => ch + ch)
|
||||
.join('')
|
||||
: normalized;
|
||||
return {
|
||||
r: Number.parseInt(expanded.slice(0, 2), 16),
|
||||
g: Number.parseInt(expanded.slice(2, 4), 16),
|
||||
b: Number.parseInt(expanded.slice(4, 6), 16),
|
||||
};
|
||||
}
|
||||
|
||||
function rgba(rgb, alpha) {
|
||||
return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${alpha})`;
|
||||
}
|
||||
|
||||
export default function Tabs({ children, defaultTab, currentTab, onTabChange, variant = DEFAULT_VARIANT }) {
|
||||
const [internalTab, setInternalTab] = useState(defaultTab ?? null);
|
||||
const [tabOrder, setTabOrder] = useState([]);
|
||||
@@ -118,30 +96,14 @@ export default function Tabs({ children, defaultTab, currentTab, onTabChange, va
|
||||
}
|
||||
|
||||
export function TabList({ children, className = '' }) {
|
||||
const ownRoverColor = useSessionSelector((state) => {
|
||||
const roverId = String(state.session?.assignment?.roverId || '').trim();
|
||||
if (!roverId) return null;
|
||||
const roster = Array.isArray(state.session?.roster) ? state.session.roster : [];
|
||||
const rover = roster.find((entry) => String(entry?.id) === roverId);
|
||||
return rover?.color || null;
|
||||
});
|
||||
const accentRgb = hexToRgb(ownRoverColor);
|
||||
const frameStyle = accentRgb ? { borderColor: rgba(accentRgb, 0.35) } : undefined;
|
||||
const headerStyle = accentRgb
|
||||
? {
|
||||
backgroundImage: `linear-gradient(90deg, rgba(23,23,23,0.96) 0%, rgba(38,38,38,0.94) 58%, ${rgba(accentRgb, 0.18)} 100%)`,
|
||||
}
|
||||
: undefined;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
'panel-section overflow-hidden border border-neutral-500/60 bg-neutral-900/95 p-0.5 shadow-[0_1px_0_rgba(255,255,255,0.05)_inset,0_10px_24px_rgba(0,0,0,0.28)]',
|
||||
'panel-section overflow-hidden border border-neutral-500/60 bg-neutral-900/95 p-0.5',
|
||||
className
|
||||
)}
|
||||
style={frameStyle}
|
||||
>
|
||||
<div className="flex gap-0.5" style={headerStyle}>{children}</div>
|
||||
<div className={`flex ${themeGapClass}`}>{children}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -176,7 +138,7 @@ export function Tab({ id, children, className = '', disabled = false, highlight
|
||||
}
|
||||
|
||||
export function TabPanels({ children, className = '' }) {
|
||||
return <div className={classNames('mt-0 space-y-0.5', className)}>{children}</div>;
|
||||
return <div className={classNames('mt-0', themeStackClass, className)}>{children}</div>;
|
||||
}
|
||||
|
||||
export function TabPanel({ id, children, keepMounted = false }) {
|
||||
|
||||
@@ -59,6 +59,27 @@ body {
|
||||
}
|
||||
|
||||
@layer components {
|
||||
.pride-page-bg {
|
||||
background-color: #050505;
|
||||
background-image:
|
||||
linear-gradient(rgba(0, 0, 0, 0.24), rgba(0, 0, 0, 0.24)),
|
||||
repeating-linear-gradient(
|
||||
45deg,
|
||||
#5bcefa 0 1.818%,
|
||||
#f5a9b8 1.818% 3.636%,
|
||||
#ffffff 3.636% 5.455%,
|
||||
#f5a9b8 5.455% 7.273%,
|
||||
#5bcefa 7.273% 9.091%,
|
||||
#e40303 9.091% 10.909%,
|
||||
#ff8c00 10.909% 12.727%,
|
||||
#ffed00 12.727% 14.545%,
|
||||
#00a651 14.545% 16.364%,
|
||||
#0066ff 16.364% 18.182%,
|
||||
#9b2fae 18.182% 20%
|
||||
);
|
||||
background-attachment: fixed;
|
||||
}
|
||||
|
||||
.panel {
|
||||
@apply bg-black text-white p-0 rounded-md;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
export const PRIDE_THEME_ENABLED = true;
|
||||
|
||||
export const pageBackgroundClass = PRIDE_THEME_ENABLED ? 'pride-page-bg' : 'bg-black';
|
||||
export const themeGapClass = PRIDE_THEME_ENABLED ? 'gap-1' : 'gap-0.5';
|
||||
export const themeStackClass = PRIDE_THEME_ENABLED ? 'space-y-1' : 'space-y-0.5';
|
||||
Reference in New Issue
Block a user