mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-16 01:21:20 -04:00
chat bridge command fix lol AND chat box resizing!!!
This commit is contained in:
+4
-2
@@ -1,6 +1,8 @@
|
|||||||
1. Overseer personality swapping.
|
1. Overseer personality swapping.
|
||||||
2. make google tts the default everywhere but roverd
|
2. make rs commands work from site chat, not just discord
|
||||||
3. fix rover request spam queue cheat
|
3. FIX RS BRIDGE DISABLE FOR NON ADMINS AAAAAH
|
||||||
|
4. make google tts the default everywhere but roverd
|
||||||
|
5. fix rover request spam queue cheat
|
||||||
|
|
||||||
|
|
||||||
# relative pipe dreams:
|
# relative pipe dreams:
|
||||||
|
|||||||
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-BhkoVBkE.js"></script>
|
<script type="module" crossorigin src="/assets/index-BkTL8xYG.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/assets/index-BcnYeuah.css">
|
<link rel="stylesheet" crossorigin href="/assets/index-Des_xuUP.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
|||||||
@@ -42,9 +42,15 @@ function createBridgeCommand({ getGuildConfig, setGuildConfig, removeGuildConfig
|
|||||||
if (mode && !VALID_MODES.has(mode)) return message.reply({ content: 'Invalid mode. Use `global` or `private`.', allowedMentions: { parse: [], repliedUser: false } });
|
if (mode && !VALID_MODES.has(mode)) return message.reply({ content: 'Invalid mode. Use `global` or `private`.', allowedMentions: { parse: [], repliedUser: false } });
|
||||||
|
|
||||||
if (action === 'status') return message.reply({ content: status(getGuildConfig(guildId)), allowedMentions: { parse: [], repliedUser: false } });
|
if (action === 'status') return message.reply({ content: status(getGuildConfig(guildId)), allowedMentions: { parse: [], repliedUser: false } });
|
||||||
if (action === 'off') { removeGuildConfig(guildId); return message.reply({ content: 'Chat bridge disabled for this server.', allowedMentions: { parse: [], repliedUser: false } }); }
|
|
||||||
|
// Every command below this point mutates the guild bridge configuration.
|
||||||
|
// Keeping the authorization check in one shared gate prevents destructive
|
||||||
|
// actions, especially `rs bridge off`, from accidentally bypassing the same
|
||||||
|
// Manage Server/admin requirement used by `here` and `mode`.
|
||||||
if (!canManageBridge(message)) return message.reply({ content: 'You need Manage Server permissions to change the chat bridge.', allowedMentions: { parse: [], repliedUser: false } });
|
if (!canManageBridge(message)) return message.reply({ content: 'You need Manage Server permissions to change the chat bridge.', allowedMentions: { parse: [], repliedUser: false } });
|
||||||
|
|
||||||
|
if (action === 'off') { removeGuildConfig(guildId); return message.reply({ content: 'Chat bridge disabled for this server.', allowedMentions: { parse: [], repliedUser: false } }); }
|
||||||
|
|
||||||
if (action === 'here') {
|
if (action === 'here') {
|
||||||
try {
|
try {
|
||||||
const entry = await ensureBridgeWebhook(message.channel, guildId);
|
const entry = await ensureBridgeWebhook(message.channel, guildId);
|
||||||
|
|||||||
@@ -26,10 +26,15 @@ import { useSettingsNamespace } from '../../settings/index.js';
|
|||||||
import ButtonBoxPanel from '../ButtonBoxPanel/index.jsx';
|
import ButtonBoxPanel from '../ButtonBoxPanel/index.jsx';
|
||||||
import OverseerPreferencePanel from '../OverseerPreferencePanel/index.jsx';
|
import OverseerPreferencePanel from '../OverseerPreferencePanel/index.jsx';
|
||||||
import CardFrame from '../CardFrame/index.jsx';
|
import CardFrame from '../CardFrame/index.jsx';
|
||||||
import { useState } from 'react';
|
import { useLayoutEffect, useRef, useState } from 'react';
|
||||||
import { useManualDockAssist } from '../../features/manualDockAssist/useManualDockAssist.js';
|
import { useManualDockAssist } from '../../features/manualDockAssist/useManualDockAssist.js';
|
||||||
import { themeGapClass, themeStackClass } from '../../themeFlags.js';
|
import { themeGapClass, themeStackClass } from '../../themeFlags.js';
|
||||||
|
|
||||||
|
const CHAT_DOCK_INITIAL_HEIGHT = 224;
|
||||||
|
const CHAT_DOCK_MIN_HEIGHT = 144;
|
||||||
|
const CHAT_DOCK_MAX_HEIGHT = 300;
|
||||||
|
const CHAT_DOCK_BOTTOM_INSET = 8;
|
||||||
|
|
||||||
function TopDownMapPanel() {
|
function TopDownMapPanel() {
|
||||||
const {
|
const {
|
||||||
state: { roverId },
|
state: { roverId },
|
||||||
@@ -133,6 +138,8 @@ function DriveDockPanel() {
|
|||||||
|
|
||||||
export default function RightPaneTabs({ layout, onOpenHelpOverlay }) {
|
export default function RightPaneTabs({ layout, onOpenHelpOverlay }) {
|
||||||
const [activeTab, setActiveTab] = useState('telemetry');
|
const [activeTab, setActiveTab] = useState('telemetry');
|
||||||
|
const chatDockRef = useRef(null);
|
||||||
|
const [chatDockHeight, setChatDockHeight] = useState(CHAT_DOCK_INITIAL_HEIGHT);
|
||||||
const isVerified = useSessionSelector((state) => Boolean(state.session?.isVerified));
|
const isVerified = useSessionSelector((state) => Boolean(state.session?.isVerified));
|
||||||
const ownRoverId = useSessionSelector((state) => String(state.session?.assignment?.roverId || '').trim());
|
const ownRoverId = useSessionSelector((state) => String(state.session?.assignment?.roverId || '').trim());
|
||||||
const ownAudioForward = useSessionSelector((state) => {
|
const ownAudioForward = useSessionSelector((state) => {
|
||||||
@@ -157,6 +164,90 @@ export default function RightPaneTabs({ layout, onOpenHelpOverlay }) {
|
|||||||
ownAudioForward?.source === 'upload' &&
|
ownAudioForward?.source === 'upload' &&
|
||||||
ownAudioForward?.state === 'playing',
|
ownAudioForward?.state === 'playing',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
const chatDock = chatDockRef.current;
|
||||||
|
if (!chatDock) return undefined;
|
||||||
|
let animationFrame = 0;
|
||||||
|
let settledFirstFrame = 0;
|
||||||
|
let settledSecondFrame = 0;
|
||||||
|
|
||||||
|
const measureChatDock = () => {
|
||||||
|
animationFrame = 0;
|
||||||
|
const { top } = chatDock.getBoundingClientRect();
|
||||||
|
|
||||||
|
/*
|
||||||
|
Keep this as pixels because getBoundingClientRect() and innerHeight are
|
||||||
|
pixel-based browser measurements. The row stays in normal document flow:
|
||||||
|
when its top moves upward during right-column scrolling, the available
|
||||||
|
viewport space grows and the chat panel expands until the max height.
|
||||||
|
*/
|
||||||
|
const availableHeight = window.innerHeight - top - CHAT_DOCK_BOTTOM_INSET;
|
||||||
|
const nextHeight = Math.round(
|
||||||
|
Math.max(CHAT_DOCK_MIN_HEIGHT, Math.min(CHAT_DOCK_MAX_HEIGHT, availableHeight)),
|
||||||
|
);
|
||||||
|
setChatDockHeight((currentHeight) => (Math.abs(currentHeight - nextHeight) > 1 ? nextHeight : currentHeight));
|
||||||
|
};
|
||||||
|
|
||||||
|
const scheduleMeasure = () => {
|
||||||
|
if (animationFrame) return;
|
||||||
|
animationFrame = window.requestAnimationFrame(measureChatDock);
|
||||||
|
};
|
||||||
|
|
||||||
|
const scheduleSettledMeasure = () => {
|
||||||
|
settledFirstFrame = window.requestAnimationFrame(() => {
|
||||||
|
settledFirstFrame = 0;
|
||||||
|
settledSecondFrame = window.requestAnimationFrame(() => {
|
||||||
|
settledSecondFrame = 0;
|
||||||
|
measureChatDock();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const resizeObserver =
|
||||||
|
typeof ResizeObserver === 'function'
|
||||||
|
? new ResizeObserver(() => {
|
||||||
|
/*
|
||||||
|
Some panels above chat settle after the first React commit as data,
|
||||||
|
images, or intrinsic layout measurements arrive. Watching the
|
||||||
|
parent group means those height changes recalculate the chat row
|
||||||
|
immediately instead of requiring a user scroll to repair the
|
||||||
|
initial bottom alignment.
|
||||||
|
*/
|
||||||
|
scheduleMeasure();
|
||||||
|
})
|
||||||
|
: null;
|
||||||
|
|
||||||
|
scheduleMeasure();
|
||||||
|
scheduleSettledMeasure();
|
||||||
|
if (resizeObserver && chatDock.parentElement) {
|
||||||
|
resizeObserver.observe(chatDock.parentElement);
|
||||||
|
}
|
||||||
|
window.addEventListener('resize', scheduleMeasure);
|
||||||
|
/*
|
||||||
|
The desktop layout scrolls inside the right-column pane, not the window.
|
||||||
|
Capturing scroll events at the document level keeps the effect small while
|
||||||
|
still noticing that nested scroll movement without wiring a dedicated ref
|
||||||
|
through App.jsx just for this row.
|
||||||
|
*/
|
||||||
|
document.addEventListener('scroll', scheduleMeasure, { capture: true, passive: true });
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (animationFrame) {
|
||||||
|
window.cancelAnimationFrame(animationFrame);
|
||||||
|
}
|
||||||
|
if (settledFirstFrame) {
|
||||||
|
window.cancelAnimationFrame(settledFirstFrame);
|
||||||
|
}
|
||||||
|
if (settledSecondFrame) {
|
||||||
|
window.cancelAnimationFrame(settledSecondFrame);
|
||||||
|
}
|
||||||
|
resizeObserver?.disconnect();
|
||||||
|
window.removeEventListener('resize', scheduleMeasure);
|
||||||
|
document.removeEventListener('scroll', scheduleMeasure, { capture: true });
|
||||||
|
};
|
||||||
|
}, [activeTab]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="text-base">
|
<section className="text-base">
|
||||||
<Tabs defaultTab="telemetry" currentTab={activeTab} onTabChange={setActiveTab}>
|
<Tabs defaultTab="telemetry" currentTab={activeTab} onTabChange={setActiveTab}>
|
||||||
@@ -177,21 +268,44 @@ export default function RightPaneTabs({ layout, onOpenHelpOverlay }) {
|
|||||||
</TabList>
|
</TabList>
|
||||||
<TabPanels>
|
<TabPanels>
|
||||||
<TabPanel id="telemetry">
|
<TabPanel id="telemetry">
|
||||||
<div className={themeStackClass}>
|
<div className={`flex flex-col ${themeGapClass}`}>
|
||||||
<div className={`grid items-stretch ${themeGapClass} grid-cols-[minmax(0,1.35fr)_minmax(0,0.95fr)]`}>
|
{/*
|
||||||
<TopDownMapPanel />
|
The first desktop telemetry group is intentionally a viewport
|
||||||
<DriveDockPanel />
|
filler instead of a sticky overlay. The rows above chat keep
|
||||||
</div>
|
their natural height, and the chat row receives only the
|
||||||
<div className={`grid ${themeGapClass} grid-cols-[minmax(0,1fr)_minmax(0,0.9fr)_minmax(0,0.75fr)]`}>
|
leftover room between those rows and the bottom of the visible
|
||||||
<RoverQueuesPanel />
|
right column. That keeps the chat composer at the bottom of the
|
||||||
<ReplaySourcesPanel panelId="replay-sources-desktop" fillHeight />
|
screen when space is available while still letting later panels
|
||||||
<LinkButtonsPanel />
|
sit below it in normal scroll flow instead of being covered.
|
||||||
</div>
|
*/}
|
||||||
<div className={`grid items-stretch ${themeGapClass} grid-cols-[minmax(0,1.3fr)_minmax(0,0.22fr)] h-[14rem]`}>
|
<div className={`flex flex-col ${themeGapClass}`}>
|
||||||
<ChatPanel fillHeight />
|
<div className={`grid items-stretch ${themeGapClass} grid-cols-[minmax(0,1.35fr)_minmax(0,0.95fr)]`}>
|
||||||
<div className={`grid min-h-0 ${themeGapClass} grid-rows-[auto_minmax(0,1fr)]`}>
|
<TopDownMapPanel />
|
||||||
<OverseerPreferencePanel />
|
<DriveDockPanel />
|
||||||
<RawUserPilePanel compact hideNicknameForm fillHeight />
|
</div>
|
||||||
|
<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>
|
||||||
|
{/*
|
||||||
|
This row gets an explicit measured height because the target
|
||||||
|
behavior depends on the row's live viewport position during
|
||||||
|
right-column scrolling. Pure CSS can size against the viewport
|
||||||
|
itself, but it cannot calculate the remaining visible distance
|
||||||
|
from this particular row's current top edge to the bottom of
|
||||||
|
the nested scroll viewport.
|
||||||
|
*/}
|
||||||
|
<div
|
||||||
|
ref={chatDockRef}
|
||||||
|
className={`grid min-h-0 items-stretch ${themeGapClass} grid-cols-[minmax(0,1.3fr)_minmax(0,0.22fr)]`}
|
||||||
|
style={{ height: `${chatDockHeight}px` }}
|
||||||
|
>
|
||||||
|
<ChatPanel fillHeight />
|
||||||
|
<div className={`grid min-h-0 ${themeGapClass} grid-rows-[auto_minmax(0,1fr)]`}>
|
||||||
|
<OverseerPreferencePanel />
|
||||||
|
<RawUserPilePanel compact hideNicknameForm fillHeight />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<HomeAssistantControls />
|
<HomeAssistantControls />
|
||||||
|
|||||||
Reference in New Issue
Block a user