From d93cbc024b62aee73b243134cc757392e46fc17d Mon Sep 17 00:00:00 2001 From: Steve Tarter Date: Sat, 8 Aug 2026 17:57:02 -0500 Subject: [PATCH 1/5] feat: implement contextual in-app help architecture --- .../roadrunner-view/public/guide/UserGuide.md | 17 ++- apps/roadrunner-view/src/App.tsx | 93 ++++++------ .../components/HomePage/BookmarksPanel.tsx | 6 +- .../HomePage/CreateVehiclePanel.tsx | 6 +- .../components/HomePage/CrissCrossPanel.tsx | 6 +- .../components/HomePage/SimulationTable.tsx | 134 +++++++++--------- .../src/components/NavBar/AppNavBar.tsx | 9 +- .../components/Shared/ActiveVehiclePlot.tsx | 8 +- .../src/components/Shared/HelpDrawer.tsx | 112 +++++++++++++++ .../src/components/Shared/HelpIconButton.tsx | 39 +++++ apps/roadrunner-view/src/config/helpTopics.ts | 45 ++++++ .../src/context/HelpContext.tsx | 54 +++++++ 12 files changed, 403 insertions(+), 126 deletions(-) create mode 100644 apps/roadrunner-view/src/components/Shared/HelpDrawer.tsx create mode 100644 apps/roadrunner-view/src/components/Shared/HelpIconButton.tsx create mode 100644 apps/roadrunner-view/src/config/helpTopics.ts create mode 100644 apps/roadrunner-view/src/context/HelpContext.tsx diff --git a/apps/roadrunner-view/public/guide/UserGuide.md b/apps/roadrunner-view/public/guide/UserGuide.md index 9be427f..f15f156 100644 --- a/apps/roadrunner-view/public/guide/UserGuide.md +++ b/apps/roadrunner-view/public/guide/UserGuide.md @@ -3,6 +3,10 @@ Roadrunner: How to Use the Map Interface Welcome to the Roadrunner vehicle-simulation viewer! The application provides a real-time, interactive map to monitor and manage simulated vehicle fleets. Below is a guide to the tools and panels available on the Home Page. +Contextual Help +--------------- +Look for the **`?` Help Icon** in panel header bars (such as the Simulation Table, Active Vehicle Plot, Bookmarks, and Creator panels). Clicking any help icon opens contextual documentation directly over the map without interrupting your active simulation playback. + Map View Controls ----------------- @@ -12,7 +16,7 @@ Pinned to the right side of the screen, you will find a floating toolbar to cust * ![Street/Satellite Button](./images/Satellite.png) Use this button to seamlessly switch the map background between a standard street layout and a satellite imagery view. -* ![Show All Routes Button](./images/ShowAllRoutes.png) ![Hide All Routes Button](./images/HideAllRoutes.png)These buttons allow you to globally toggle the visibility of the route lines drawn behind the vehicles. _(Note: You can also click directly near an individual vehicle on the map to toggle its specific route and popup.)_ +* ![Show All Routes Button](./images/ShowAllRoutes.png) ![Hide All Routes Button](./images/HideAllRoutes.png) These buttons allow you to globally toggle the visibility of the route lines drawn behind the vehicles. _(Note: You can also click directly near an individual vehicle on the map to toggle its specific route and popup.)_ * ![Interpolation Button](./images/Interpolation.png) This tool toggles vehicle movement interpolation on and off. Enable smoothing for fluid vehicle animations, or disable it to see the raw, un-interpolated data points coming from the backend. @@ -26,17 +30,18 @@ The top navigation bar contains additional management panels for running and mon ### Standard Features -* **Bookmarks:** Opens a panel where you can select from a curated list of previously saved simulation sessions. Selecting a bookmark will adjust the application clock and transport you to the Driver View for that specific vehicle's historical run. [Learn more about the Bookmarks panel.](/guide/bookmarks) +* **Bookmarks:** Opens a panel where you can select from a curated list of previously saved simulation sessions. Selecting a bookmark will adjust the application clock and transport you to the Driver View for that specific vehicle's historical run. -* **Sim Table:** Opens a tabular layout displaying simulation sessions that have occured within the retention period. [Learn more about the Sim Table.](/guide/sim-table) +* **Sim Table:** Opens a tabular layout displaying simulation sessions that have occurred within the retention period. -* **Active Vehicle Plot:** Opens a chart displaying vehicle activity over the retention period. Allows an alternate way to select a timeline to view past or current simulations. [Learn more about the Active Vehicle Plot.](/guide/active-vehicle-plot) +* **Active Vehicle Plot:** Opens a chart displaying vehicle activity over the retention period. Allows an alternate way to select a timeline to view past or current simulations. ### Creator Features _Note: The following features require your user account to be assigned to the 'creator' group in Amazon Cognito._ -* **Create Vehicle:** Opens the creation panel, allowing you to establish a starting point, destination, and parameters for a new vehicle simulation. [Learn more about the Create Vehicle Panel.](/guide/create-vehicle-panel) +* **Create Vehicle:** Opens the creation panel, allowing you to establish a starting point, destination, and parameters for a new vehicle simulation. + +* **Create Criss-Cross:** Opens a specialized panel designed to generate a "criss-cross" simulation scenario, ideal for testing intersection routing or specific demonstration paths. -* **Create criss-cross:** Opens a specialized panel designed to generate a "criss-cross" simulation scenario, ideal for testing intersection routing or specific demonstration paths. [Learn more about the Create Criss-Cross Panel.](/guide/create-criss-cross-panel) diff --git a/apps/roadrunner-view/src/App.tsx b/apps/roadrunner-view/src/App.tsx index 74d93a1..3c4d32b 100644 --- a/apps/roadrunner-view/src/App.tsx +++ b/apps/roadrunner-view/src/App.tsx @@ -10,7 +10,7 @@ import { ProfilePage } from './components/ProfilePage/ProfilePage'; import { DriverViewPage } from './components/DriverViewPage/DriverViewPage'; import { library } from '@fortawesome/fontawesome-svg-core'; import fontawesome from '@fortawesome/fontawesome' -import { faHome, faSatellite, faMap, faUpRightAndDownLeftFromCenter, faEye, faEyeSlash, faArrowLeft, faArrowRight, fa0 } from '@fortawesome/free-solid-svg-icons'; +import { faHome, faSatellite, faMap, faUpRightAndDownLeftFromCenter, faEye, faEyeSlash, faArrowLeft, faArrowRight, fa0, faQuestionCircle } from '@fortawesome/free-solid-svg-icons'; import { faPowerOff, faUser } from '@fortawesome/fontawesome-free-solid' import { AboutPage } from './components/AboutPage/AboutPage'; import { GuidePage } from './components/GuidePage/GuidePage'; @@ -18,8 +18,10 @@ import { Vehicle3DMapPage } from './components/Vehicle3DMapPage/Vehicle3DMapPage import { GoogleHomePage } from './components/GoogleHomePage/GoogleHomePage'; import { GoogleVehicle3DMapPage } from './components/GoogleVehicle3DMapPage/GoogleVehicle3DMapPage'; import { GoogleDriverViewPage } from './components/GoogleDriverViewPage/GoogleDriverViewPage'; +import { HelpProvider } from './context/HelpContext'; +import { HelpDrawer } from './components/Shared/HelpDrawer'; -library.add(faHome, faSatellite, faMap, faUpRightAndDownLeftFromCenter, faEye, faEyeSlash, faArrowLeft, faArrowRight, fa0 ); +library.add(faHome, faSatellite, faMap, faUpRightAndDownLeftFromCenter, faEye, faEyeSlash, faArrowLeft, faArrowRight, fa0, faQuestionCircle ); fontawesome.library.add(faPowerOff, faUser); const HomeRedirect = () => { @@ -70,48 +72,51 @@ const WrappedGoogleDriverViewPage = () => ( export const App = () => { return ( -
-
- - } /> - } />} - /> - } />} - /> - } />} - /> - } />} - /> - } - /> - } - /> - } - /> - } />} - /> - } />} - /> - + +
+
+ + } /> + } />} + /> + } />} + /> + } />} + /> + } />} + /> + } + /> + } + /> + } + /> + } />} + /> + } />} + /> + +
+
-
+ ); } diff --git a/apps/roadrunner-view/src/components/HomePage/BookmarksPanel.tsx b/apps/roadrunner-view/src/components/HomePage/BookmarksPanel.tsx index 5dd89c6..4d81919 100644 --- a/apps/roadrunner-view/src/components/HomePage/BookmarksPanel.tsx +++ b/apps/roadrunner-view/src/components/HomePage/BookmarksPanel.tsx @@ -6,6 +6,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faTimes } from '@fortawesome/free-solid-svg-icons'; import { Button } from "react-bootstrap"; import { usePlayback } from "../../context/PlaybackContext"; +import { HelpIconButton } from "../Shared/HelpIconButton"; export type Bookmark = { vehicleId: string; @@ -69,7 +70,10 @@ export const BookmarksPanel: React.FC = ({ onClose, onSelec -
Bookmarks
+
+ Bookmarks + +
diff --git a/apps/roadrunner-view/src/components/HomePage/CreateVehiclePanel.tsx b/apps/roadrunner-view/src/components/HomePage/CreateVehiclePanel.tsx index 793a19b..358fe41 100644 --- a/apps/roadrunner-view/src/components/HomePage/CreateVehiclePanel.tsx +++ b/apps/roadrunner-view/src/components/HomePage/CreateVehiclePanel.tsx @@ -9,6 +9,7 @@ import { PointPicker } from '../Shared/PointPicker'; import { useState } from "react"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faTimes } from "@fortawesome/free-solid-svg-icons"; +import { HelpIconButton } from "../Shared/HelpIconButton"; export const CreateVehiclePanel = (props: { setIsCreateVehicleActive: any, @@ -114,7 +115,10 @@ export const CreateVehiclePanel = (props: { -
Create Vehicle
+
+ Create Vehicle + +
diff --git a/apps/roadrunner-view/src/components/HomePage/CrissCrossPanel.tsx b/apps/roadrunner-view/src/components/HomePage/CrissCrossPanel.tsx index adcb640..07836ff 100644 --- a/apps/roadrunner-view/src/components/HomePage/CrissCrossPanel.tsx +++ b/apps/roadrunner-view/src/components/HomePage/CrissCrossPanel.tsx @@ -6,6 +6,7 @@ import { CONFIG, getRestUrl } from "../../config"; import { PointPicker } from '../Shared/PointPicker'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faTimes } from "@fortawesome/free-solid-svg-icons"; +import { HelpIconButton } from "../Shared/HelpIconButton"; export const CrissCrossPanel = (props: { setIsCrissCrossActive: any, @@ -234,7 +235,10 @@ export const CrissCrossPanel = (props: { return ( -
Create Criss-Cross
+
+ Create Criss-Cross + +
diff --git a/apps/roadrunner-view/src/components/HomePage/SimulationTable.tsx b/apps/roadrunner-view/src/components/HomePage/SimulationTable.tsx index 92410a0..819bf72 100644 --- a/apps/roadrunner-view/src/components/HomePage/SimulationTable.tsx +++ b/apps/roadrunner-view/src/components/HomePage/SimulationTable.tsx @@ -4,7 +4,7 @@ import { MaterialReactTable } from 'material-react-table'; import { getRestUrl } from "../../config"; import { usePlayback } from "../../context/PlaybackContext"; import { Button } from "react-bootstrap"; -import { useNavigate } from "react-router-dom"; +import { HelpIconButton } from "../Shared/HelpIconButton"; const timeFormatOptions: Intl.DateTimeFormatOptions = { timeZone: 'UTC', @@ -21,8 +21,6 @@ export const SimulationTable = (props: { toggleSimTable: any, returnToNow: any, }) => { - const navigate = useNavigate(); - const [data, setData] = useState([]); const [rowCount, setRowCount] = useState(0); @@ -58,88 +56,80 @@ export const SimulationTable = (props: { "Content-Type": "application/json", Authorization: `Bearer ${accessToken}`, }, - credentials: 'include', - signal: controller.signal, + signal: controller.signal }); - - const pagedModel = await res.json(); - const sessions = pagedModel._embedded?.simulationSessions ?? []; - - setData(sessions); - setRowCount(pagedModel.page?.totalElements ?? 0); - } catch (err:any) { - if (err.name !== "AbortError") { - console.error("Fetch error:", err); + const result = await res.json(); + + // Handle single-object response if API returns array or object + if (result && Array.isArray(result.sessions)) { + setData(result.sessions); + setRowCount(result.totalCount || result.sessions.length); + } else if (Array.isArray(result)) { + setData(result as any); + setRowCount(result.length); + } + } catch (err: any) { + if (err.name !== 'AbortError') { + console.error("Failed to load simulation sessions", err); } } } loadPage(); - return () => controller.abort(); }, [pagination.pageIndex, pagination.pageSize, dataSource]); - const [isMobile, setIsMobile] = useState(window.innerWidth < 768); - - useEffect(() => { - const handleResize = () => { - setIsMobile(window.innerWidth < 768); - }; - window.addEventListener('resize', handleResize); - return () => window.removeEventListener('resize', handleResize); - }, []); - - const columns = useMemo(() => { - const cols = [ + const columns = useMemo( + () => [ { - accessorKey: 'id', - header: 'Vehicle', - size: isMobile ? 85 : 150, - Cell: ({ cell }: any) => { - const val = cell.getValue() || ""; - return isMobile && val.length > 8 ? val.substring(0, 8) : val; - } - } - ]; - - if (!isMobile) { - cols.push({ - accessorKey: 'username', - header: 'Username', - size: 200, - Cell: ({ cell }: any) => cell.getValue() || "unknown-user" - }); - } - - cols.push({ - accessorKey: 'start', - header: 'Start Time', - size: isMobile ? 120 : 200, - Cell: ({ cell }: any) => new Date(cell.getValue()).toLocaleTimeString([], timeFormatOptions) + 'Z' - }); - - cols.push({ - header: 'Actions', - size: isMobile ? 80 : 120, - Cell: ({ row }: any) => { - return ( + accessorKey: 'vehicleId', + header: 'Vehicle ID', + size: 150, + }, + { + accessorFn: (row: any) => { + const date = new Date(row.start); + return date.toLocaleString('en-US', timeFormatOptions); + }, + id: 'start', + header: 'Start Time (UTC)', + size: 180, + }, + { + accessorFn: (row: any) => { + const date = new Date(row.end); + return date.toLocaleString('en-US', timeFormatOptions); + }, + id: 'end', + header: 'End Time (UTC)', + size: 180, + }, + { + accessorFn: (row: any) => `${row.pointCount ?? 0} pts`, + id: 'pointCount', + header: 'Data Points', + size: 120, + }, + { + id: 'actions', + header: 'Actions', + size: 120, + Cell: ({ row }: { row: any }) => ( - ); - } - }); - - return cols; - }, [navigate, setPlaybackSession, isMobile]); + ), + }, + ], + [props, setPlaybackSession] + ); return (
+
+
+ Simulation Sessions + +
+
{ cancelled = true; }; }, []); + const { openHelp } = useHelp(); + const logoutAndReturn = async () => { try { await signOut({ global: true }); @@ -139,8 +142,9 @@ export const AppNavBar = ({ }, [navigate]); const gotoGuidePage = useCallback(() => { - navigate('/guide/overview'); - }, [navigate]); + openHelp('overview'); + setIsOpen(false); + }, [openHelp]); const gotoHomePage = useCallback(() => { @@ -172,7 +176,6 @@ export const AppNavBar = ({ {additionalMenuItems && additionalMenuItems(() => setIsOpen(false))} gotoGuidePage()} style={{ cursor: "pointer" }} diff --git a/apps/roadrunner-view/src/components/Shared/ActiveVehiclePlot.tsx b/apps/roadrunner-view/src/components/Shared/ActiveVehiclePlot.tsx index 630c1dd..0963d72 100644 --- a/apps/roadrunner-view/src/components/Shared/ActiveVehiclePlot.tsx +++ b/apps/roadrunner-view/src/components/Shared/ActiveVehiclePlot.tsx @@ -2,6 +2,7 @@ import React, { useMemo, useState, useEffect, useCallback } from 'react'; import { usePlayback } from "../../context/PlaybackContext"; import { useSimulationSessionData } from '../../hooks/useSimulationSessionData'; import { Button } from "react-bootstrap"; +import { HelpIconButton } from "./HelpIconButton"; import { LineChart, Line, @@ -385,7 +386,12 @@ export const ActiveVehiclePlot = (props: { boxShadow: '0 4px 15px rgba(0,0,0,0.2)' }} > -
{getDynamicTitle(domain[0], domain[1])}
+
+
+ {getDynamicTitle(domain[0], domain[1])} + +
+
{ + const { activeTopicId, isOpen, closeHelp, openHelp } = useHelp(); + const [content, setContent] = useState(''); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(null); + + const topicKey = activeTopicId && HELP_TOPICS[activeTopicId] ? activeTopicId : 'overview'; + const topic = HELP_TOPICS[topicKey]; + + useEffect(() => { + if (!isOpen || !topic) return; + + setLoading(true); + setError(null); + + fetch(topic.markdownUrl) + .then((res) => { + if (!res.ok) { + throw new Error(`Failed to load documentation (${res.status})`); + } + return res.text(); + }) + .then((text) => { + setContent(text); + setLoading(false); + }) + .catch((err) => { + console.error('Help loading error:', err); + setError(`Unable to load guide content for "${topic.title}".`); + setLoading(false); + }); + }, [isOpen, topic]); + + return ( + + +
+ openHelp(e.target.value)} + className="fw-bold" + > + {Object.entries(HELP_TOPICS).map(([key, t]) => ( + + ))} + +
+
+ + + {loading && ( +
+ +
Loading guide...
+
+ )} + + {error && ( + + {error} + + )} + + {!loading && !error && ( +
+ ( + {props.alt + ) + }} + > + {content} + +
+ )} +
+
+ ); +}; diff --git a/apps/roadrunner-view/src/components/Shared/HelpIconButton.tsx b/apps/roadrunner-view/src/components/Shared/HelpIconButton.tsx new file mode 100644 index 0000000..0b21ff8 --- /dev/null +++ b/apps/roadrunner-view/src/components/Shared/HelpIconButton.tsx @@ -0,0 +1,39 @@ +import React from 'react'; +import { Button } from 'react-bootstrap'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faQuestionCircle } from '@fortawesome/free-solid-svg-icons'; +import { useHelp } from '../../context/HelpContext'; + +interface HelpIconButtonProps { + topicId: string; + title?: string; + className?: string; + variant?: string; + size?: 'sm' | 'lg'; +} + +export const HelpIconButton: React.FC = ({ + topicId, + title = 'View Help', + className = '', + variant = 'link', + size +}) => { + const { openHelp } = useHelp(); + + return ( + + ); +}; diff --git a/apps/roadrunner-view/src/config/helpTopics.ts b/apps/roadrunner-view/src/config/helpTopics.ts new file mode 100644 index 0000000..c0cf4f8 --- /dev/null +++ b/apps/roadrunner-view/src/config/helpTopics.ts @@ -0,0 +1,45 @@ +export interface HelpTopic { + id: string; + title: string; + markdownUrl: string; + summary?: string; +} + +export const HELP_TOPICS: Record = { + 'overview': { + id: 'overview', + title: 'RoadRunner Help & Overview', + markdownUrl: '/guide/UserGuide.md', + summary: 'Overview of RoadRunner features and navigation.' + }, + 'sim-table': { + id: 'sim-table', + title: 'Simulation Table Guide', + markdownUrl: '/guide/SimTable.md', + summary: 'Search, filter, and inspect simulated vehicle sessions.' + }, + 'active-vehicle-plot': { + id: 'active-vehicle-plot', + title: 'Active Vehicle Plot Guide', + markdownUrl: '/guide/ActiveVehiclePlot.md', + summary: 'Analyze vehicle concurrency over time and scrub playback.' + }, + 'bookmarks': { + id: 'bookmarks', + title: 'Bookmarks Panel Guide', + markdownUrl: '/guide/BookmarksPanel.md', + summary: 'Quickly access curated simulation scenarios.' + }, + 'create-vehicle': { + id: 'create-vehicle', + title: 'Create Vehicle Panel Guide', + markdownUrl: '/guide/CreateVehiclePanel.md', + summary: 'Configure and spawn individual simulated vehicles.' + }, + 'create-criss-cross': { + id: 'create-criss-cross', + title: 'Criss-Cross Pattern Panel Guide', + markdownUrl: '/guide/CreateCrissCrossPanel.md', + summary: 'Generate complex intersection and criss-cross vehicle patterns.' + } +}; diff --git a/apps/roadrunner-view/src/context/HelpContext.tsx b/apps/roadrunner-view/src/context/HelpContext.tsx new file mode 100644 index 0000000..1d93708 --- /dev/null +++ b/apps/roadrunner-view/src/context/HelpContext.tsx @@ -0,0 +1,54 @@ +import React, { createContext, useContext, useState, useCallback } from 'react'; + +interface HelpContextType { + activeTopicId: string | null; + isOpen: boolean; + openHelp: (topicId: string) => void; + closeHelp: () => void; + toggleHelp: (topicId?: string) => void; +} + +const HelpContext = createContext({ + activeTopicId: null, + isOpen: false, + openHelp: () => {}, + closeHelp: () => {}, + toggleHelp: () => {}, +}); + +export const HelpProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => { + const [activeTopicId, setActiveTopicId] = useState(null); + const [isOpen, setIsOpen] = useState(false); + + const openHelp = useCallback((topicId: string) => { + setActiveTopicId(topicId); + setIsOpen(true); + }, []); + + const closeHelp = useCallback(() => { + setIsOpen(false); + }, []); + + const toggleHelp = useCallback((topicId?: string) => { + setIsOpen((prevOpen) => { + if (!prevOpen) { + if (topicId) setActiveTopicId(topicId); + return true; + } + // If already open and topic matches, close it; if topic is different, switch topic + if (topicId && topicId !== activeTopicId) { + setActiveTopicId(topicId); + return true; + } + return false; + }); + }, [activeTopicId]); + + return ( + + {children} + + ); +}; + +export const useHelp = () => useContext(HelpContext); From 058bf943e8441dee03fa13201bb8dbff5b393d52 Mon Sep 17 00:00:00 2001 From: Steve Tarter Date: Sun, 9 Aug 2026 11:34:29 -0500 Subject: [PATCH 2/5] fix: resolve relative image paths in markdown help drawer --- .../public/guide/ActiveVehiclePlot.md | 6 +-- .../public/guide/BookmarksPanel.md | 2 +- .../public/guide/CreateCrissCrossPanel.md | 4 +- .../public/guide/CreateVehiclePanel.md | 2 +- apps/roadrunner-view/public/guide/SimTable.md | 2 +- .../roadrunner-view/public/guide/UserGuide.md | 10 ++--- .../src/components/Shared/HelpDrawer.tsx | 42 +++++++++++------- .../components/Shared/MarkdownPageLayout.tsx | 44 ++++++++++++------- 8 files changed, 67 insertions(+), 45 deletions(-) diff --git a/apps/roadrunner-view/public/guide/ActiveVehiclePlot.md b/apps/roadrunner-view/public/guide/ActiveVehiclePlot.md index 2092555..cfc86b7 100644 --- a/apps/roadrunner-view/public/guide/ActiveVehiclePlot.md +++ b/apps/roadrunner-view/public/guide/ActiveVehiclePlot.md @@ -4,10 +4,10 @@ When invoked from the Home page, the plot opens with the plot zoomed out to show Below is a typical view after invoking from the Home Page: -![Active Vehicle Plot Unzoomed](./images/VehiclePlot-7Day.png) +![Active Vehicle Plot Unzoomed](/guide/images/VehiclePlot-7Day.png) You can zoom in on a particular run using the mouse wheel, or by using pinch gestures. Zooming in allows easier selection of the specific time desired. When the chart is clicked or double tapped, the playback time is set to that time. Below shows the plot zoomed in: -![Active Vehicle Plot Zoomed](./images/VehiclePlot-Zoomed.png) +![Active Vehicle Plot Zoomed](/guide/images/VehiclePlot-Zoomed.png) When accessed from the Driver View, the Active Vehicle Plot zooms in to the time period that the selected vehicle ran. This enables jumping back or forward in that vehicle's life, allowing "getting to the good part" easier. Since criss-cross patterns are often centered on "interesting" places, this is useful. Below is a typical Active Vehicle Plot accessed from the Driver Page. The red vertical line shows the current playback time. -![Active Vehicle Plot Driver View](./images/VehiclePlot-DriverView.png) +![Active Vehicle Plot Driver View](/guide/images/VehiclePlot-DriverView.png) diff --git a/apps/roadrunner-view/public/guide/BookmarksPanel.md b/apps/roadrunner-view/public/guide/BookmarksPanel.md index 32e77c6..b472f51 100644 --- a/apps/roadrunner-view/public/guide/BookmarksPanel.md +++ b/apps/roadrunner-view/public/guide/BookmarksPanel.md @@ -1,2 +1,2 @@ The Bookmarks Panel displays a curated list of past simulation sessions, with a description of what you are about to get into. The list is curated by the administrator. Below is typical content: -![Bookmarks Panel](./images/BookmarksPanel.png) +![Bookmarks Panel](/guide/images/BookmarksPanel.png) diff --git a/apps/roadrunner-view/public/guide/CreateCrissCrossPanel.md b/apps/roadrunner-view/public/guide/CreateCrissCrossPanel.md index 059522c..6b8c774 100644 --- a/apps/roadrunner-view/public/guide/CreateCrissCrossPanel.md +++ b/apps/roadrunner-view/public/guide/CreateCrissCrossPanel.md @@ -1,7 +1,7 @@ _Note: The following feature require your user account to be assigned to the 'creator' group in Amazon Cognito. This access must be requested from the administrator. Users in this group may create up to 30 sessions a day._ The Create Criss-Cross Panel allows a 'creator' user to start a number of vehicles at once. The user specifies a center point by specifying an address or clicking on the map, the number of vehicles to start, and the radius of the circle. Once the center has been selected, a circle appears on the map showing the selected radius. Along the circle are dots that show where a vehicle will be created. -![Create Criss-Cross Panel Select](./images/CrissCross-Select.png) +![Create Criss-Cross Panel Select](/guide/images/CrissCross-Select.png) When the "Generate" button is pressed, vehicles will be created in the requested pattern. Each vehicle is given a route that takes it from its starting point, through the center point, and ending at the point on the opposite side of the circle. -![Create Criss-Cross Panel Generate](./images/CrissCross-Generate.png) +![Create Criss-Cross Panel Generate](/guide/images/CrissCross-Generate.png) Pro Tip: Press the Generate button multiple times so you can watch a vehicle travel the route just in front of you. \ No newline at end of file diff --git a/apps/roadrunner-view/public/guide/CreateVehiclePanel.md b/apps/roadrunner-view/public/guide/CreateVehiclePanel.md index ef99965..056febf 100644 --- a/apps/roadrunner-view/public/guide/CreateVehiclePanel.md +++ b/apps/roadrunner-view/public/guide/CreateVehiclePanel.md @@ -6,4 +6,4 @@ If specifying a point by address, autocomplete is used to aid choosing the point Below is the Create Vehicle Panel: -![Create Vehicle Panel](./images/CreateVehiclePanel.png) +![Create Vehicle Panel](/guide/images/CreateVehiclePanel.png) diff --git a/apps/roadrunner-view/public/guide/SimTable.md b/apps/roadrunner-view/public/guide/SimTable.md index a7a9f52..c43f300 100644 --- a/apps/roadrunner-view/public/guide/SimTable.md +++ b/apps/roadrunner-view/public/guide/SimTable.md @@ -3,4 +3,4 @@ Sim Table Panel The Sim Table Panel provides a listing of all vehicle simulations that have occurred during the retention period. For each simulation session, the vehicle ID, initiating user, and start time are listed. A playback button is provided to allow starting playback of that simulation session. The table has controls that allow paging through the sessions. There is also a button to set the playback time to "Now". A typical Sim Table Panel is shown below: -![Sim Table Panel](./images/SimTable.png) +![Sim Table Panel](/guide/images/SimTable.png) diff --git a/apps/roadrunner-view/public/guide/UserGuide.md b/apps/roadrunner-view/public/guide/UserGuide.md index f15f156..e7363d3 100644 --- a/apps/roadrunner-view/public/guide/UserGuide.md +++ b/apps/roadrunner-view/public/guide/UserGuide.md @@ -12,15 +12,15 @@ Map View Controls Pinned to the right side of the screen, you will find a floating toolbar to customize your map view. -* ![Fit All Button](./images/FitAll.png) Click this button to automatically adjust the map's zoom and pan to fit all currently active vehicles within your viewing area. +* ![Fit All Button](/guide/images/FitAll.png) Click this button to automatically adjust the map's zoom and pan to fit all currently active vehicles within your viewing area. -* ![Street/Satellite Button](./images/Satellite.png) Use this button to seamlessly switch the map background between a standard street layout and a satellite imagery view. +* ![Street/Satellite Button](/guide/images/Satellite.png) Use this button to seamlessly switch the map background between a standard street layout and a satellite imagery view. -* ![Show All Routes Button](./images/ShowAllRoutes.png) ![Hide All Routes Button](./images/HideAllRoutes.png) These buttons allow you to globally toggle the visibility of the route lines drawn behind the vehicles. _(Note: You can also click directly near an individual vehicle on the map to toggle its specific route and popup.)_ +* ![Show All Routes Button](/guide/images/ShowAllRoutes.png) ![Hide All Routes Button](/guide/images/HideAllRoutes.png) These buttons allow you to globally toggle the visibility of the route lines drawn behind the vehicles. _(Note: You can also click directly near an individual vehicle on the map to toggle its specific route and popup.)_ -* ![Interpolation Button](./images/Interpolation.png) This tool toggles vehicle movement interpolation on and off. Enable smoothing for fluid vehicle animations, or disable it to see the raw, un-interpolated data points coming from the backend. +* ![Interpolation Button](/guide/images/Interpolation.png) This tool toggles vehicle movement interpolation on and off. Enable smoothing for fluid vehicle animations, or disable it to see the raw, un-interpolated data points coming from the backend. -* ![Active Vehicle Chart Button](./images/VehicleChart.png) Clicking this opens a detailed data chart/plot for active vehicles directly over the map view. +* ![Active Vehicle Chart Button](/guide/images/VehicleChart.png) Clicking this opens a detailed data chart/plot for active vehicles directly over the map view. Application Menu (Manage Panels) diff --git a/apps/roadrunner-view/src/components/Shared/HelpDrawer.tsx b/apps/roadrunner-view/src/components/Shared/HelpDrawer.tsx index 8996e73..e2b3063 100644 --- a/apps/roadrunner-view/src/components/Shared/HelpDrawer.tsx +++ b/apps/roadrunner-view/src/components/Shared/HelpDrawer.tsx @@ -85,21 +85,33 @@ export const HelpDrawer: React.FC = () => {
( - {props.alt - ) + img: ({ node, ...props }) => { + let imageSrc = props.src || ''; + if (imageSrc.startsWith('./images/')) { + imageSrc = '/guide/' + imageSrc.substring(2); + } else if (imageSrc.startsWith('images/')) { + imageSrc = '/guide/' + imageSrc; + } else if (imageSrc.startsWith('./')) { + imageSrc = '/guide/' + imageSrc.substring(2); + } + + return ( + {props.alt + ); + } }} > {content} diff --git a/apps/roadrunner-view/src/components/Shared/MarkdownPageLayout.tsx b/apps/roadrunner-view/src/components/Shared/MarkdownPageLayout.tsx index d93fe8d..cf2830a 100644 --- a/apps/roadrunner-view/src/components/Shared/MarkdownPageLayout.tsx +++ b/apps/roadrunner-view/src/components/Shared/MarkdownPageLayout.tsx @@ -79,23 +79,33 @@ export function MarkdownPageLayout({ title, markdownUrl, onClose }: MarkdownPage ( - {props.alt - ) + img: ({node, ...props}) => { + let imageSrc = props.src || ''; + if (imageSrc.startsWith('./images/')) { + imageSrc = '/guide/' + imageSrc.substring(2); + } else if (imageSrc.startsWith('images/')) { + imageSrc = '/guide/' + imageSrc; + } else if (imageSrc.startsWith('./')) { + imageSrc = '/guide/' + imageSrc.substring(2); + } + + return ( + {props.alt + ); + } }} > {content} From 490c05f8c309d9f332a12971e72f628a82d71d72 Mon Sep 17 00:00:00 2001 From: Steve Tarter Date: Sun, 9 Aug 2026 11:47:52 -0500 Subject: [PATCH 3/5] fix: remove Fit All Vehicles button from 3D view pages --- .../GoogleVehicle3DMapPage.tsx | 45 ------------------- .../Vehicle3DMapPage/Vehicle3DMapPage.tsx | 38 ---------------- 2 files changed, 83 deletions(-) diff --git a/apps/roadrunner-view/src/components/GoogleVehicle3DMapPage/GoogleVehicle3DMapPage.tsx b/apps/roadrunner-view/src/components/GoogleVehicle3DMapPage/GoogleVehicle3DMapPage.tsx index dd91463..7b7496d 100644 --- a/apps/roadrunner-view/src/components/GoogleVehicle3DMapPage/GoogleVehicle3DMapPage.tsx +++ b/apps/roadrunner-view/src/components/GoogleVehicle3DMapPage/GoogleVehicle3DMapPage.tsx @@ -10,7 +10,6 @@ import { faHome, faMagic, faBars, - faUpRightAndDownLeftFromCenter, faChartLine, faChevronDown, faChevronUp, @@ -527,43 +526,7 @@ export const GoogleVehicle3DMapPage = () => { } }, [isDataLoaded, isMapReady, vehicleList, hasCenteredInitially]); - // Fit view bounds to contain all active vehicles - const fitAllOnScreen = useCallback(() => { - if (!isDataLoaded || vehicleList.length === 0) return; - let minLng = 180, maxLng = -180, minLat = 90, maxLat = -90; - vehicleList.forEach(v => { - minLng = Math.min(minLng, v.degLongitude); - maxLng = Math.max(maxLng, v.degLongitude); - minLat = Math.min(minLat, v.degLatitude); - maxLat = Math.max(maxLat, v.degLatitude); - }); - - const centerLat = (minLat + maxLat) / 2; - const centerLng = (minLng + maxLng) / 2; - const deltaLat = maxLat - minLat; - const deltaLng = maxLng - minLng; - - const distLat = deltaLat * 111000; - const distLng = deltaLng * 111000 * Math.cos(centerLat * Math.PI / 180); - const maxDist = Math.max(distLat, distLng, 500); - - const mapEl = mapRef.current; - if (mapEl) { - isProgrammaticUpdateRef.current = true; - mapEl.flyCameraTo({ - endCamera: { - center: { lat: centerLat, lng: centerLng, altitude: 0 }, - range: maxDist * 1.5, - heading: -20, - tilt: 65, - altitudeMode: 'RELATIVE_TO_GROUND' - }, - durationMillis: 0 - }); - isProgrammaticUpdateRef.current = false; - } - }, [isDataLoaded, vehicleList]); @@ -778,14 +741,6 @@ export const GoogleVehicle3DMapPage = () => { )} - )} -