mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-17 01:50:47 -04:00
rover labels and theme tweakings
This commit is contained in:
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-status-bar-style" content="black-translucent" />
|
||||||
<meta name="apple-mobile-web-app-title" content="Roomba Rover" />
|
<meta name="apple-mobile-web-app-title" content="Roomba Rover" />
|
||||||
<title>Roomba Rover</title>
|
<title>Roomba Rover</title>
|
||||||
<script type="module" crossorigin src="/assets/index-PXEt8KKN.js"></script>
|
<script type="module" crossorigin src="/assets/index-Dd_cj-I7.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/assets/index-BvjNDQTC.css">
|
<link rel="stylesheet" crossorigin href="/assets/index-BLKXqzg3.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// Replay Snapshot Health
|
// Replay Snapshot Health
|
||||||
// Purpose: Defines the Replay Snapshot Health module and the local helpers/components used in this file.
|
// Purpose: Defines the Replay Snapshot Health module and the local helpers/components used in this file.
|
||||||
// Scope: Keeps behavior unchanged while isolating this concern into a clear, single-responsibility unit.
|
// Scope: Keeps behavior unchanged while isolating this concern into a clear, single-responsibility unit.
|
||||||
import { roverNameChromeStyle } from '../../lib/roverColor.js';
|
import RoverLabel from '../RoverLabel/index.jsx';
|
||||||
|
|
||||||
export default function ReplaySnapshotHealth({ health, roster = [] }) {
|
export default function ReplaySnapshotHealth({ health, roster = [] }) {
|
||||||
if (!health) return null;
|
if (!health) return null;
|
||||||
@@ -35,12 +35,11 @@ export default function ReplaySnapshotHealth({ health, roster = [] }) {
|
|||||||
<div className="space-y-0.5 text-xs text-slate-300">
|
<div className="space-y-0.5 text-xs text-slate-300">
|
||||||
{replay.sources.map((source) => (
|
{replay.sources.map((source) => (
|
||||||
<div key={`${source.type}:${source.id}`} className="flex items-center justify-between">
|
<div key={`${source.type}:${source.id}`} className="flex items-center justify-between">
|
||||||
<span
|
{source.type === 'rover' ? (
|
||||||
className={`${source.type === 'rover' ? 'rounded px-1 py-[1px] border border-transparent' : ''}`}
|
<RoverLabel name={source.label} color={roverColorFor(source.id)} fallback={source.id} />
|
||||||
style={source.type === 'rover' ? roverNameChromeStyle(roverColorFor(source.id), 0.16) : undefined}
|
) : (
|
||||||
>
|
<span>{source.label}</span>
|
||||||
{source.label}
|
)}
|
||||||
</span>
|
|
||||||
<span className={source.ready ? 'text-emerald-300' : 'text-amber-300'}>
|
<span className={source.ready ? 'text-emerald-300' : 'text-amber-300'}>
|
||||||
{source.recentCount}/{source.neededCount}
|
{source.recentCount}/{source.neededCount}
|
||||||
</span>
|
</span>
|
||||||
@@ -50,12 +49,7 @@ export default function ReplaySnapshotHealth({ health, roster = [] }) {
|
|||||||
<div className="space-y-0.5 text-xs text-slate-300">
|
<div className="space-y-0.5 text-xs text-slate-300">
|
||||||
{snapshots.rovers.map((entry) => (
|
{snapshots.rovers.map((entry) => (
|
||||||
<div key={`rover:${entry.id}`} className="flex items-center justify-between">
|
<div key={`rover:${entry.id}`} className="flex items-center justify-between">
|
||||||
<span
|
<RoverLabel name={entry.name} color={roverColorFor(entry.id)} fallback={entry.id} />
|
||||||
className="rounded px-1 py-[1px] border border-transparent"
|
|
||||||
style={roverNameChromeStyle(roverColorFor(entry.id), 0.16)}
|
|
||||||
>
|
|
||||||
{entry.name}
|
|
||||||
</span>
|
|
||||||
<span className={entry.stale ? 'text-amber-300' : 'text-emerald-300'}>{entry.stale ? 'stale' : 'ok'}</span>
|
<span className={entry.stale ? 'text-amber-300' : 'text-emerald-300'}>{entry.stale ? 'stale' : 'ok'}</span>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -50,17 +50,24 @@ export default function CardFrame({
|
|||||||
return rover?.color || null;
|
return rover?.color || null;
|
||||||
});
|
});
|
||||||
const accentRgb = hexToRgb(ownRoverColor);
|
const accentRgb = hexToRgb(ownRoverColor);
|
||||||
const cardStyle = accentRgb ? { borderColor: rgba(accentRgb, 0.5) } : undefined;
|
|
||||||
|
// swap these to toggle rover card border colors stuff
|
||||||
|
const cardStyle = accentRgb ? { borderColor: rgba(accentRgb, 0.3) } : undefined;
|
||||||
|
// const cardStyle = undefined;
|
||||||
|
|
||||||
|
|
||||||
const headerStyle = accentRgb
|
const headerStyle = accentRgb
|
||||||
? {
|
? {
|
||||||
backgroundImage: `linear-gradient(90deg, rgba(23,23,23,0.96) 0%, rgba(38,38,38,0.94) 30%, ${rgba(accentRgb, 0.28)} 100%)`,
|
backgroundImage: `linear-gradient(90deg, rgba(23,23,23,0.96) 0%, rgba(38,38,38,0.94) 0%, ${rgba(accentRgb, 0.1)} 100%)`,
|
||||||
|
// backgroundImage: `linear-gradient(90deg, ${rgba(accentRgb, 0.1)} 100%)`,
|
||||||
|
// backgroundImage: `background-color: ${rgba(accentRgb, 0.2)}`
|
||||||
}
|
}
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section
|
<section
|
||||||
className={cx(
|
className={cx(
|
||||||
'panel-section border border-neutral-500/60 bg-neutral-900/95',
|
'panel-section border border-neutral-500/60 bg-neutral-900/100',
|
||||||
clipOverflow ? 'overflow-hidden' : 'overflow-visible',
|
clipOverflow ? 'overflow-hidden' : 'overflow-visible',
|
||||||
fillHeight && 'flex h-full min-h-0 flex-col',
|
fillHeight && 'flex h-full min-h-0 flex-col',
|
||||||
className,
|
className,
|
||||||
@@ -71,7 +78,8 @@ export default function CardFrame({
|
|||||||
// Header row
|
// Header row
|
||||||
<header
|
<header
|
||||||
className={cx(
|
className={cx(
|
||||||
'flex items-center justify-between gap-0.5 border-b border-neutral-500/50 bg-gradient-to-r from-neutral-800 via-neutral-700 to-neutral-600 px-0.5 py-0.5',
|
'flex items-center justify-between gap-0.5 border-b border-neutral-500/50 bg-slate-800 px-0.5 py-0.5',
|
||||||
|
// 'flex items-center justify-between gap-0.5 border-b border-neutral-500/50 bg-gradient-to-r from-neutral-800 via-neutral-700 to-neutral-600 px-0.5 py-0.5',
|
||||||
headerClassName,
|
headerClassName,
|
||||||
)}
|
)}
|
||||||
style={headerStyle}
|
style={headerStyle}
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
// Scope: Keeps behavior unchanged while isolating this concern into a clear, single-responsibility unit.
|
// Scope: Keeps behavior unchanged while isolating this concern into a clear, single-responsibility unit.
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { FaDiscord } from 'react-icons/fa';
|
import { FaDiscord } from 'react-icons/fa';
|
||||||
import { roverBadgeStyle } from '../../lib/roverColor.js';
|
|
||||||
import { useSessionSelector } from '../../context/SessionContext.jsx';
|
import { useSessionSelector } from '../../context/SessionContext.jsx';
|
||||||
|
import RoverLabel from '../RoverLabel/index.jsx';
|
||||||
|
|
||||||
function roleColors(role) {
|
function roleColors(role) {
|
||||||
switch (role) {
|
switch (role) {
|
||||||
@@ -107,12 +107,12 @@ export function ChatIdentity({ message, toolsToggle = null }) {
|
|||||||
</span>
|
</span>
|
||||||
) : null}
|
) : null}
|
||||||
{message.roverId && (
|
{message.roverId && (
|
||||||
<span
|
<RoverLabel
|
||||||
className="shrink-0 rounded bg-slate-800 px-1 text-[0.7rem]"
|
roverId={message.roverId}
|
||||||
style={roverBadgeStyle(message.roverColor, 0.14)}
|
color={message.roverColor}
|
||||||
>
|
fallback={message.roverId}
|
||||||
{message.roverId}
|
className="shrink-0 text-[0.7rem]"
|
||||||
</span>
|
/>
|
||||||
)}
|
)}
|
||||||
{toolsToggle}
|
{toolsToggle}
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { roverNameChromeStyle } from '../../../lib/roverColor.js';
|
import RoverLabel from '../../RoverLabel/index.jsx';
|
||||||
|
|
||||||
export default function RoverLabelOverlay({
|
export default function RoverLabelOverlay({
|
||||||
variant = 'default',
|
variant = 'default',
|
||||||
@@ -20,12 +20,7 @@ export default function RoverLabelOverlay({
|
|||||||
return (
|
return (
|
||||||
<div className={`absolute ${labelPosClass} left-1/2`} style={labelWrapperStyle}>
|
<div className={`absolute ${labelPosClass} left-1/2`} style={labelWrapperStyle}>
|
||||||
<div className={`flex items-center gap-0.5 bg-black/80 text-slate-100 ${labelPadClass} ${labelTextClass}`}>
|
<div className={`flex items-center gap-0.5 bg-black/80 text-slate-100 ${labelPadClass} ${labelTextClass}`}>
|
||||||
<span
|
<RoverLabel name={label} color={roverColor} />
|
||||||
className="font-semibold text-white rounded px-1 py-[1px] border border-transparent"
|
|
||||||
style={roverNameChromeStyle(roverColor, 0.18)}
|
|
||||||
>
|
|
||||||
{label || 'No rover'}
|
|
||||||
</span>
|
|
||||||
{driverLabel ? <span className="text-slate-300">• {driverLabel}</span> : null}
|
{driverLabel ? <span className="text-slate-300">• {driverLabel}</span> : null}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -35,15 +30,7 @@ export default function RoverLabelOverlay({
|
|||||||
return (
|
return (
|
||||||
<div className={`absolute ${labelPosClass} left-1/2`} style={labelWrapperStyle}>
|
<div className={`absolute ${labelPosClass} left-1/2`} style={labelWrapperStyle}>
|
||||||
<div className={`flex gap-0.5 bg-black/80 text-slate-100 ${labelPadClass} ${labelTextClass}`}>
|
<div className={`flex gap-0.5 bg-black/80 text-slate-100 ${labelPadClass} ${labelTextClass}`}>
|
||||||
<span>
|
<RoverLabel name={label} color={roverColor} />
|
||||||
Rover:{' '}
|
|
||||||
<span
|
|
||||||
className="rounded px-1 py-[1px] border border-transparent"
|
|
||||||
style={roverNameChromeStyle(roverColor, 0.18)}
|
|
||||||
>
|
|
||||||
"{label || 'No rover'}"
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -4,8 +4,8 @@
|
|||||||
import { useEffect, useMemo, useState } from 'react';
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
import { useSessionActions, useSessionSelector } from '../../context/SessionContext.jsx';
|
import { useSessionActions, useSessionSelector } from '../../context/SessionContext.jsx';
|
||||||
import { useSettingsNamespace } from '../../settings/index.js';
|
import { useSettingsNamespace } from '../../settings/index.js';
|
||||||
import { roverNameChromeStyle } from '../../lib/roverColor.js';
|
|
||||||
import CardFrame from '../CardFrame/index.jsx';
|
import CardFrame from '../CardFrame/index.jsx';
|
||||||
|
import RoverLabel from '../RoverLabel/index.jsx';
|
||||||
|
|
||||||
function normalizeSources(list = []) {
|
function normalizeSources(list = []) {
|
||||||
return list
|
return list
|
||||||
@@ -215,12 +215,11 @@ function GroupList({ title, items, selected, onToggle }) {
|
|||||||
onChange={() => onToggle(item.key)}
|
onChange={() => onToggle(item.key)}
|
||||||
className="accent-emerald-400"
|
className="accent-emerald-400"
|
||||||
/>
|
/>
|
||||||
<span
|
{item.type === 'rover' ? (
|
||||||
className={`truncate ${item.type === 'rover' ? 'rounded px-1 py-[1px] border border-transparent' : ''}`}
|
<RoverLabel name={item.label} color={item.color} fallback={item.id} className="truncate" />
|
||||||
style={item.type === 'rover' ? roverNameChromeStyle(item.color, 0.16) : undefined}
|
) : (
|
||||||
>
|
<span className="truncate">{item.label}</span>
|
||||||
{item.label}
|
)}
|
||||||
</span>
|
|
||||||
</label>
|
</label>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -37,7 +37,9 @@ function TopDownMapPanel() {
|
|||||||
const sensors = frame?.sensors || {};
|
const sensors = frame?.sensors || {};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CardFrame hideHeader>
|
<CardFrame
|
||||||
|
title = "Roomba sensor view"
|
||||||
|
>
|
||||||
<div className="aspect-square w-full">
|
<div className="aspect-square w-full">
|
||||||
<TopDownMap sensors={sensors} />
|
<TopDownMap sensors={sensors} />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import CardFrame from '../CardFrame/index.jsx';
|
|||||||
|
|
||||||
function EmptyState() {
|
function EmptyState() {
|
||||||
return (
|
return (
|
||||||
<CardFrame title="Room cameras" bodyClassName="space-y-0.5 text-sm">
|
<CardFrame title="Room Cameras" bodyClassName="space-y-0.5 text-sm">
|
||||||
<p className="text-center text-slate-400">No room cameras configured.</p>
|
<p className="text-center text-slate-400">No room cameras configured.</p>
|
||||||
<p className="text-center text-slate-500">Add entries to server config to populate this list.</p>
|
<p className="text-center text-slate-500">Add entries to server config to populate this list.</p>
|
||||||
</CardFrame>
|
</CardFrame>
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
import { useMemo } from 'react';
|
||||||
|
import { useSessionSelector } from '../../context/SessionContext.jsx';
|
||||||
|
import { roverNameChromeStyle } from '../../lib/roverColor.js';
|
||||||
|
|
||||||
|
function classNames(...values) {
|
||||||
|
return values.filter(Boolean).join(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveText({ rover, name, fallback, roverId, rosterEntry }) {
|
||||||
|
const candidates = [
|
||||||
|
name,
|
||||||
|
rover?.name,
|
||||||
|
rosterEntry?.name,
|
||||||
|
fallback,
|
||||||
|
roverId,
|
||||||
|
rover?.id,
|
||||||
|
rosterEntry?.id,
|
||||||
|
'No rover',
|
||||||
|
];
|
||||||
|
for (const candidate of candidates) {
|
||||||
|
const text = String(candidate || '').trim();
|
||||||
|
if (text) return text;
|
||||||
|
}
|
||||||
|
return 'No rover';
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function RoverLabel({
|
||||||
|
rover = null,
|
||||||
|
roverId = null,
|
||||||
|
name = null,
|
||||||
|
color = null,
|
||||||
|
fallback = null,
|
||||||
|
className = '',
|
||||||
|
as: Component = 'span',
|
||||||
|
style = undefined,
|
||||||
|
...props
|
||||||
|
}) {
|
||||||
|
const resolvedRoverId = roverId ?? rover?.id ?? null;
|
||||||
|
const rosterEntry = useSessionSelector((state) => {
|
||||||
|
if (!resolvedRoverId) return null;
|
||||||
|
const roster = Array.isArray(state.session?.roster) ? state.session.roster : [];
|
||||||
|
return roster.find((entry) => String(entry?.id) === String(resolvedRoverId)) || null;
|
||||||
|
});
|
||||||
|
const resolvedColor = color ?? rover?.color ?? rosterEntry?.color ?? null;
|
||||||
|
const label = useMemo(
|
||||||
|
() => resolveText({ rover, name, fallback, roverId: resolvedRoverId, rosterEntry }),
|
||||||
|
[fallback, name, resolvedRoverId, rosterEntry, rover],
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Component
|
||||||
|
className={classNames(
|
||||||
|
'inline-block rounded border border-transparent px-1 py-[1px] font-semibold text-white',
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
style={{ ...(style || {}), ...(roverNameChromeStyle(resolvedColor, 0.26) || {}) }}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{label}
|
||||||
|
</Component>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -3,8 +3,8 @@
|
|||||||
// Scope: Keeps behavior unchanged while isolating this concern into a clear, single-responsibility unit.
|
// Scope: Keeps behavior unchanged while isolating this concern into a clear, single-responsibility unit.
|
||||||
import { useEffect, useMemo, useState } from 'react';
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
import { useSessionActions, useSessionSelector } from '../../context/SessionContext.jsx';
|
import { useSessionActions, useSessionSelector } from '../../context/SessionContext.jsx';
|
||||||
import { roverNameChromeStyle } from '../../lib/roverColor.js';
|
|
||||||
import CardFrame from '../CardFrame/index.jsx';
|
import CardFrame from '../CardFrame/index.jsx';
|
||||||
|
import RoverLabel from '../RoverLabel/index.jsx';
|
||||||
|
|
||||||
function classNames(...values) {
|
function classNames(...values) {
|
||||||
return values.filter(Boolean).join(' ');
|
return values.filter(Boolean).join(' ');
|
||||||
@@ -188,9 +188,7 @@ export default function RoverQueuesPanel({ title = 'Rovers' }) {
|
|||||||
<div className="flex items-center justify-between gap-0.5">
|
<div className="flex items-center justify-between gap-0.5">
|
||||||
<div className="flex min-w-0 items-center gap-0.5">
|
<div className="flex min-w-0 items-center gap-0.5">
|
||||||
<p className="min-w-0 flex items-center gap-0.5 whitespace-nowrap text-slate-200">
|
<p className="min-w-0 flex items-center gap-0.5 whitespace-nowrap text-slate-200">
|
||||||
<span className="rounded px-1 py-[1px] border border-transparent" style={roverNameChromeStyle(rover.color, 0.16)}>
|
<RoverLabel rover={rover} fallback={roverId} />
|
||||||
{rover.name}
|
|
||||||
</span>
|
|
||||||
{rover.description ? (
|
{rover.description ? (
|
||||||
<span className="min-w-0 flex-1 truncate text-[0.7rem] text-slate-400">
|
<span className="min-w-0 flex-1 truncate text-[0.7rem] text-slate-400">
|
||||||
{rover.description}
|
{rover.description}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// Rover Roster
|
// Rover Roster
|
||||||
// Purpose: Defines the Rover Roster module and the local helpers/components used in this file.
|
// Purpose: Defines the Rover Roster module and the local helpers/components used in this file.
|
||||||
// Scope: Keeps behavior unchanged while isolating this concern into a clear, single-responsibility unit.
|
// Scope: Keeps behavior unchanged while isolating this concern into a clear, single-responsibility unit.
|
||||||
import { roverNameChromeStyle } from '../../lib/roverColor.js';
|
import RoverLabel from '../RoverLabel/index.jsx';
|
||||||
|
|
||||||
function classNames(...values) {
|
function classNames(...values) {
|
||||||
return values.filter(Boolean).join(' ');
|
return values.filter(Boolean).join(' ');
|
||||||
@@ -49,10 +49,8 @@ export default function RoverRoster({
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<p className="text-slate-200">
|
<p>
|
||||||
<span className="rounded px-1 py-[1px] border border-transparent" style={roverNameChromeStyle(rover.color, 0.16)}>
|
<RoverLabel rover={rover} fallback={rover.id} />
|
||||||
{rover.name}
|
|
||||||
</span>
|
|
||||||
</p>
|
</p>
|
||||||
<p className="text-xs text-slate-500 flex flex-wrap items-center gap-0.5">
|
<p className="text-xs text-slate-500 flex flex-wrap items-center gap-0.5">
|
||||||
<span>{rover.locked ? 'locked' : 'free'}</span>
|
<span>{rover.locked ? 'locked' : 'free'}</span>
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import {
|
|||||||
function TopDownMapContent({ sensors = {}, variant = 'full', size: overrideSize, overlay = false }) {
|
function TopDownMapContent({ sensors = {}, variant = 'full', size: overrideSize, overlay = false }) {
|
||||||
const size = overrideSize || (variant === 'mini' ? 190 : 260);
|
const size = overrideSize || (variant === 'mini' ? 190 : 260);
|
||||||
const center = size / 2;
|
const center = size / 2;
|
||||||
const offsetY = size * 0.07;
|
const offsetY = size * 0.05;
|
||||||
const centerX = center;
|
const centerX = center;
|
||||||
const centerY = center + offsetY;
|
const centerY = center + offsetY;
|
||||||
const innerCircle = center * 0.8;
|
const innerCircle = center * 0.8;
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import { useEffect, useMemo, useState, useCallback } from 'react';
|
|||||||
import { useSessionSelector } from '../../context/SessionContext.jsx';
|
import { useSessionSelector } from '../../context/SessionContext.jsx';
|
||||||
import NicknameForm from '../NicknameForm/index.jsx';
|
import NicknameForm from '../NicknameForm/index.jsx';
|
||||||
import SocialButtonsGrid from '../SocialButtonsGrid/index.jsx';
|
import SocialButtonsGrid from '../SocialButtonsGrid/index.jsx';
|
||||||
import { roverBadgeStyle, roverNameChromeStyle } from '../../lib/roverColor.js';
|
|
||||||
import CardFrame from '../CardFrame/index.jsx';
|
import CardFrame from '../CardFrame/index.jsx';
|
||||||
|
import RoverLabel from '../RoverLabel/index.jsx';
|
||||||
|
|
||||||
export function NicknameEntryPanel({ compact = false }) {
|
export function NicknameEntryPanel({ compact = false }) {
|
||||||
return (
|
return (
|
||||||
@@ -130,12 +130,12 @@ export default function UserListPanel({
|
|||||||
>
|
>
|
||||||
<p className={`font-semibold ${roleColors(user.role)}`}>{formatLabel(user, selfId)}</p>
|
<p className={`font-semibold ${roleColors(user.role)}`}>{formatLabel(user, selfId)}</p>
|
||||||
{user.roverId ? (
|
{user.roverId ? (
|
||||||
<span
|
<RoverLabel
|
||||||
className="rounded bg-slate-800 px-1 text-[0.7rem]"
|
roverId={user.roverId}
|
||||||
style={roverBadgeStyle(rosterEntry(user.roverId)?.color, 0.12)}
|
color={rosterEntry(user.roverId)?.color}
|
||||||
>
|
fallback={user.roverId}
|
||||||
rover {rosterEntry(user.roverId)?.name || user.roverId}
|
className="text-[0.7rem]"
|
||||||
</span>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<span className="text-[0.7rem] text-slate-500">no rover</span>
|
<span className="text-[0.7rem] text-slate-500">no rover</span>
|
||||||
)}
|
)}
|
||||||
@@ -214,12 +214,7 @@ export default function UserListPanel({
|
|||||||
<div key={roverId} className={`flex flex-col gap-0.5 ${compact ? 'text-[0.8rem] py-0.25' : 'text-sm'}`}>
|
<div key={roverId} className={`flex flex-col gap-0.5 ${compact ? 'text-[0.8rem] py-0.25' : 'text-sm'}`}>
|
||||||
<div className="flex items-center gap-0.5">
|
<div className="flex items-center gap-0.5">
|
||||||
<p className="font-semibold text-slate-200">
|
<p className="font-semibold text-slate-200">
|
||||||
<span
|
<RoverLabel roverId={roverId} fallback={roverId} />
|
||||||
className="rounded px-1 py-[1px] border border-transparent"
|
|
||||||
style={roverNameChromeStyle(rosterEntry(roverId)?.color, 0.16)}
|
|
||||||
>
|
|
||||||
{rosterEntry(roverId)?.name || roverId}
|
|
||||||
</span>
|
|
||||||
</p>
|
</p>
|
||||||
{remaining != null && (
|
{remaining != null && (
|
||||||
<span className="rounded bg-slate-800 px-1 text-[0.7rem]">
|
<span className="rounded bg-slate-800 px-1 text-[0.7rem]">
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
import { useMemo, useState } from 'react';
|
import { useMemo, useState } from 'react';
|
||||||
import CardFrame from '../CardFrame/index.jsx';
|
import CardFrame from '../CardFrame/index.jsx';
|
||||||
import { flowWrapClass, innerFlowClass } from './constants.js';
|
import { flowWrapClass, innerFlowClass } from './constants.js';
|
||||||
|
import RoverLabel from '../RoverLabel/index.jsx';
|
||||||
|
|
||||||
export default function VipPrivateRoverAccessCard({
|
export default function VipPrivateRoverAccessCard({
|
||||||
requestableRovers = [],
|
requestableRovers = [],
|
||||||
@@ -56,7 +57,9 @@ export default function VipPrivateRoverAccessCard({
|
|||||||
return (
|
return (
|
||||||
<div key={roverId} className="surface-muted flex items-center justify-between gap-0.5 px-1 py-0.5">
|
<div key={roverId} className="surface-muted flex items-center justify-between gap-0.5 px-1 py-0.5">
|
||||||
<div className="min-w-0 text-left">
|
<div className="min-w-0 text-left">
|
||||||
<p className="truncate text-xs font-semibold text-slate-100">{rover.name || roverId}</p>
|
<p className="truncate text-xs">
|
||||||
|
<RoverLabel rover={rover} fallback={roverId} className="max-w-full truncate align-middle" />
|
||||||
|
</p>
|
||||||
<p className="truncate text-[0.7rem] text-slate-500">{roverId}</p>
|
<p className="truncate text-[0.7rem] text-slate-500">{roverId}</p>
|
||||||
</div>
|
</div>
|
||||||
{pending ? (
|
{pending ? (
|
||||||
|
|||||||
+1
-1
@@ -62,7 +62,7 @@ body {
|
|||||||
.pride-page-bg {
|
.pride-page-bg {
|
||||||
background-color: #050505;
|
background-color: #050505;
|
||||||
background-image:
|
background-image:
|
||||||
linear-gradient(rgba(0, 0, 0, 0.24), rgba(0, 0, 0, 0.24)),
|
linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, 0)),
|
||||||
repeating-linear-gradient(
|
repeating-linear-gradient(
|
||||||
45deg,
|
45deg,
|
||||||
#5bcefa 0 1.818%,
|
#5bcefa 0 1.818%,
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
// Scope: Keeps behavior unchanged while isolating this concern into a clear, single-responsibility unit.
|
// Scope: Keeps behavior unchanged while isolating this concern into a clear, single-responsibility unit.
|
||||||
import RoverMediaPlayer from '../../../components/RoverMediaPlayer/index.jsx';
|
import RoverMediaPlayer from '../../../components/RoverMediaPlayer/index.jsx';
|
||||||
import BatteryBar from '../../../components/BatteryBar/index.jsx';
|
import BatteryBar from '../../../components/BatteryBar/index.jsx';
|
||||||
import { roverNameChromeStyle } from '../../../lib/roverColor.js';
|
import RoverLabel from '../../../components/RoverLabel/index.jsx';
|
||||||
import { getBatteryVisual } from '../utils.js';
|
import { getBatteryVisual } from '../utils.js';
|
||||||
import AutoFitText from './AutoFitText.jsx';
|
import AutoFitText from './AutoFitText.jsx';
|
||||||
|
|
||||||
@@ -35,14 +35,14 @@ export default function InfoColumn({
|
|||||||
{isActiveView ? (
|
{isActiveView ? (
|
||||||
<div className="relative z-10 flex min-w-0 flex-1 flex-col justify-between text-center">
|
<div className="relative z-10 flex min-w-0 flex-1 flex-col justify-between text-center">
|
||||||
<div className="min-w-0 bg-transparent px-0 py-0 leading-none">
|
<div className="min-w-0 bg-transparent px-0 py-0 leading-none">
|
||||||
<AutoFitText
|
<RoverLabel
|
||||||
className="font-semibold leading-none text-white rounded border border-transparent px-1 py-[1px]"
|
rover={rover}
|
||||||
|
fallback={rover.id}
|
||||||
|
as={AutoFitText}
|
||||||
|
className="leading-none"
|
||||||
maxSize={1000}
|
maxSize={1000}
|
||||||
minSize={18}
|
minSize={18}
|
||||||
style={roverNameChromeStyle(rover.color, 0.18)}
|
/>
|
||||||
>
|
|
||||||
{rover.name || rover.id}
|
|
||||||
</AutoFitText>
|
|
||||||
</div>
|
</div>
|
||||||
{driverLabel ? (
|
{driverLabel ? (
|
||||||
<div className="min-w-0 bg-transparent px-0 py-0 leading-none">
|
<div className="min-w-0 bg-transparent px-0 py-0 leading-none">
|
||||||
@@ -65,14 +65,14 @@ export default function InfoColumn({
|
|||||||
<>
|
<>
|
||||||
<div className="relative z-10 flex min-w-0 flex-col gap-1 text-center">
|
<div className="relative z-10 flex min-w-0 flex-col gap-1 text-center">
|
||||||
<div className="min-w-0 bg-transparent px-0 py-0 leading-none">
|
<div className="min-w-0 bg-transparent px-0 py-0 leading-none">
|
||||||
<AutoFitText
|
<RoverLabel
|
||||||
className="font-semibold leading-none text-white rounded border border-transparent px-1 py-[1px]"
|
rover={rover}
|
||||||
|
fallback={rover.id}
|
||||||
|
as={AutoFitText}
|
||||||
|
className="leading-none"
|
||||||
maxSize={1000}
|
maxSize={1000}
|
||||||
minSize={18}
|
minSize={18}
|
||||||
style={roverNameChromeStyle(rover.color, 0.18)}
|
/>
|
||||||
>
|
|
||||||
{rover.name || rover.id}
|
|
||||||
</AutoFitText>
|
|
||||||
</div>
|
</div>
|
||||||
{driverLabel ? (
|
{driverLabel ? (
|
||||||
<div className="min-w-0 bg-transparent px-0 py-0 leading-none">
|
<div className="min-w-0 bg-transparent px-0 py-0 leading-none">
|
||||||
|
|||||||
Reference in New Issue
Block a user