mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-15 17:12:59 -04:00
pride gay theme yay
This commit is contained in:
+6
-1
@@ -1,4 +1,9 @@
|
||||
1. allow private rover replays for people who are allowed to drive them
|
||||
1. pride month themeing (and forever probably)
|
||||
1. in the background of the web UI
|
||||
1. slightly wider gaps between cards
|
||||
2. pride flag background, vertical stripes
|
||||
3. add toggle to disable it that doesnt work or crashes the page lol
|
||||
2. redo logo in inkscape, make it gay
|
||||
2. add faster way for admins to login, like an invisible button in top left or something
|
||||
3. add discord bot typing thing for when someone requests a replay
|
||||
4. change replay title for ones requested from discord, something other than "requester driving rover"
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -11,8 +11,8 @@
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||||
<meta name="apple-mobile-web-app-title" content="Roomba Rover" />
|
||||
<title>Roomba Rover</title>
|
||||
<script type="module" crossorigin src="/assets/index-DC5PqMFo.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-CIj_suyF.css">
|
||||
<script type="module" crossorigin src="/assets/index-BwoVmbyy.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-0REyzzxs.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 43 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 43 KiB |
+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