Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
c1c14e3
Merge pull request #67 from Sentience-Robotics/fix/demo
Mael-RABOT Jun 18, 2026
25decbf
Merge pull request #69 from Sentience-Robotics/mra/hotfix-documentati…
Mael-RABOT Jun 23, 2026
7372cbc
evol(sensors): live pressure graphs from hardware config topics
charlesmadjeri Jun 25, 2026
f144910
fix(hardware-config): keep delete column below sticky page header on …
charlesmadjeri Jun 25, 2026
8f662a3
evol(routes): rename configuration page to robot configuration
charlesmadjeri Jun 25, 2026
7328dfc
fix(lcp): unify page shell layout and align control toolbar
charlesmadjeri Jun 25, 2026
ed22003
fix(sensors): merge in one flotGraph component the pressure sensor ones
charlesmadjeri Jun 25, 2026
66a4bca
Merge pull request #70 from Sentience-Robotics/cma/evol-sensors
charlesmadjeri Jun 26, 2026
85c7941
evol: changing lcp default port to 4000
sambrus Aug 13, 2026
1816ac4
Merge remote-tracking branch 'origin/dev' into sbr/evol-change-lcp-de…
sambrus Aug 13, 2026
0511c0d
evol(readme): update ubuntu version refs for Jazzy match
charlesmadjeri Aug 29, 2026
d51eaa9
Merge pull request #72 from Sentience-Robotics/cma/update-readme-afte…
charlesmadjeri Aug 29, 2026
c1bf16b
Merge remote-tracking branch 'origin/dev' into sbr/evol-change-lcp-de…
sambrus Aug 31, 2026
a7ffee2
wip: updating port to port 4004g
sambrus Aug 31, 2026
59f0f9f
Merge pull request #71 from Sentience-Robotics/sbr/evol-change-lcp-de…
sambrus Aug 31, 2026
d9d6d17
fix(ci): commit yarn.lock for immutable installs on PRs
charlesmadjeri Aug 30, 2026
76e6480
feat: adding button for resetting every actuator to its rest stage
sambrus Sep 1, 2026
684acda
Merge pull request #74 from Sentience-Robotics/sbr/feat-reset-actuator
sambrus Sep 1, 2026
b18f215
Merge pull request #73 from Sentience-Robotics/cma/fix-commit-lock-file
charlesmadjeri Sep 1, 2026
8ac37f3
Macos pixi fix (#75)
Arcod7 Sep 1, 2026
03964e3
evol: shorten comment (#76)
Arcod7 Sep 2, 2026
8f42da1
feat(debug): add connection and command pipeline modals (#77)
Arcod7 Sep 2, 2026
b8ac58c
feat: explaining rosbridge purpose and fixing case where we deleted e…
sambrus Sep 3, 2026
716a829
fix: useless comms
sambrus Sep 3, 2026
0c9771c
fix: adding a text explaining purpose of blue and green tooltips
sambrus Sep 3, 2026
f5cb1f0
Merge pull request #78 from Sentience-Robotics/sbr/feat-explain-rosbr…
sambrus Sep 3, 2026
763c90c
fix: read the URDF from robot_state_publisher's parameter (#79)
Arcod7 Sep 3, 2026
074ad30
Merge pull request #80 from Sentience-Robotics/sbr/fix-tooltip-hover
sambrus Sep 3, 2026
45a9d2c
fix: activate reset all button only when control is on
sambrus Sep 3, 2026
e5272b8
Merge pull request #81 from Sentience-Robotics/sbr/fix-resetall-butto…
sambrus Sep 3, 2026
c1096da
fix: made configuration table header visible when scrolling
sambrus Sep 3, 2026
9bd909f
Merge pull request #82 from Sentience-Robotics/sbr/fix-configuration-…
sambrus Sep 3, 2026
198c1c8
evol: adding a modal to confirm we want to take the control from anot…
sambrus Sep 3, 2026
03734c0
fix: incorrect information
sambrus Sep 3, 2026
d80e278
fix: useless comms
sambrus Sep 3, 2026
9b69ae1
fix: made the control page header sticky while scrolling down
sambrus Sep 3, 2026
ee51db8
Merge pull request #83 from Sentience-Robotics/sbr/evol-modal-before-…
sambrus Sep 3, 2026
c6e1405
Merge pull request #84 from Sentience-Robotics/sbr/fix-sticky-control…
sambrus Sep 3, 2026
d33e29f
evol: turning color of switch buttons to red when they are off
sambrus Sep 3, 2026
b0d0f2c
fix: removed useless property
sambrus Sep 3, 2026
110ef20
fix: box shadow weird
sambrus Sep 3, 2026
b487ae3
feat(ui): getting started
Mael-RABOT Sep 4, 2026
b1016c2
evol(components): redo style of movable modal
Mael-RABOT Sep 4, 2026
a15a2d4
refactor(component): usage of movable modal as base
Mael-RABOT Sep 4, 2026
cab3438
fix(movable modal): spawning position
Mael-RABOT Sep 4, 2026
4e8c771
evol(getting started): instruction in .md
Mael-RABOT Sep 4, 2026
14d4999
fix(control page): takeover modal
Mael-RABOT Sep 4, 2026
34bbe97
evol(theme): main text in off-white
Mael-RABOT Sep 4, 2026
c951a66
fix(docs): typos
Mael-RABOT Sep 7, 2026
438da84
fix(modals): settings
Mael-RABOT Sep 7, 2026
cd70063
Merge pull request #87 from Sentience-Robotics/mra/feat-getting-started
Mael-RABOT Sep 7, 2026
bb11dcc
Merge pull request #86 from Sentience-Robotics/sbr/evol-change-color-…
sambrus Sep 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Dev server port (default 5000). Use http://localhost:5000 if VITE_HTTPS is false.
VITE_PORT=5000
# Dev server port (default 4004). Use http://localhost:4004 if VITE_HTTPS is false.
VITE_PORT=4004
VITE_OVERRIDE_ROS_BRIDGE_SERVER_URL=

# Set false for local dev without SSL (avoids ERR_SSL_PROTOCOL_ERROR). Set true and add certs for HTTPS.
Expand Down
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ pnpm-debug.log*
lerna-debug.log*

node_modules
.yarn/
.pnp.cjs
.pnp.loader.mjs
dist
dist-ssr
*.local
Expand All @@ -32,7 +35,6 @@ dist-ssr
.env.test.local
.env.production.local

yarn.lock
package-lock.json

todo.md
1 change: 1 addition & 0 deletions .yarnrc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
nodeLinker: node-modules
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ The application features a distinctive green-on-black cyberpunk aesthetic remini

> **Quick Node upgrade with nvm**
>
> If you have Node < 22 (e.g. the Ubuntu 22.04 default 18.x), install nvm and switch:
> If you have Node < 22 (e.g. the Ubuntu 24.04 Noble default 18.x), install nvm and switch:
> ```sh
> # Install nvm (skip if already installed)
> curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.2/install.sh | bash
Expand Down Expand Up @@ -140,7 +140,7 @@ VITE_OVERRIDE_ROS_BRIDGE_SERVER_URL=https://100.100.100.100:5000/rosbridge

VITE_ENABLE_LOGS=true

# Hardware YAML editor (`/configuration`): header shows `robot_name` from loaded YAML (via ROS config/get).
# Hardware YAML editor (`/robot-configuration`): header shows `robot_name` from loaded YAML (via ROS config/get).
```

### Authentication Setup
Expand Down
92 changes: 92 additions & 0 deletions docs/getting_started.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
<-- Welcome -->

## Welcome to the Lucy control panel.

This short getting-started guide will walk you through the basics of the control panel and help you get familiar with the main features.

> You can skip this guide with the button below.
> If you want to replay the guide, you can find it in the setting popup at the top right corner.

---

If you need more in-depth information, you can click the 'Documentation' button at any time or go to [this address](https://docs.sentience-robotics.fr/share/p1x9ikjkhf/p/public-documentation-EExgMX2REV).

Additionally, if you need specific information or want to exchange on the project, you can join our [community discord server](https://discord.gg/g4KNZ3eeBd), we'll be more than happy to exchange with you!

---

Thanks for using Lucy, it means a lot to us ❤️.

With that being said, let's get started!

<-- ROS 2 -->

Let's have a quick word about ROS 2.

> [ROS](https://www.ros.org/) (Robot Operating System) is a standard in the field of robotic.

Lucy use ROS 2 internaly to manage everything, from connecting to the physical actuators to displaying the number of connected controller.

---

The control panel has to connect to this bridge everytime, which is made automatically for you by default.
> If at any point you need to update the connection URL, or toggle the auto-connect feature, you can visit the setting popup.

<-- Control -->

> This is where you'll control your robot, and it's full of stuff, so let's go over the basics!

First thing first, you will need to take control over the robot. This can be achieved by using the 'Control robot' toggle.
> This security system ensure that no more than one controller is sending instruction to your robot. When activated, this will take control from other control applications.

---

## Sliders

The main part of the screen is occupied by the 'actuators categories', each of them contains multiple actuators.

> You will be able to assign each & every actuator to a category in the configuration page.

Each of these actuators has its own control box, letting you update its position in real time.
Every slider comes with two indicators:
- The top green one indicates the default position for this actuator.
- The bottom blue one indicates the actual position. It will move more or less slowly depending on the physical configuration of the actuator.

<-- Poses & Animations -->

## Poses

In the top bar controls, you can find the 'Save pose' & 'Load pose' popups.

They will let you save every actuators position and load them at a later time when needed.

> Every pose is saved locally in your browser's localStorage.

---

## Animations

The animation system uses the saved poses.

> Once at least two poses are created, you will be able to select them in the 'Manage animation' popup and create an animation with them.

<-- Sensors -->

For this part, we will need to travel a bit.

- On the bottom right corner, you will find the navigation bar, select the second option 'Sensors'.

> It sure look empty, let's fix it!

Using the drop-down menu, you can select every sensors you want to watch.

---

> We currently support direct data display & temperatures, with a graph.
> If you have sensors whose data cannot be displayed, feel free to create an [issue](https://github.com/Sentience-Robotics/lucy_control_panel/issues) or to [contact us](https://discord.gg/g4KNZ3eeBd) directly.

<-- Configuration -->

The configuration page is undergoing a complete refactor, usage guide will be updated soon.

In the meantime, if you need more information, please refer to our [online documentation](https://docs.sentience-robotics.fr/share/p1x9ikjkhf/p/public-documentation-EExgMX2REV).
43 changes: 28 additions & 15 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,14 @@ import { RobotControlPanel } from './Pages/RobotControlPanel';
import { Navigation } from './Components/Navigation';
import { NotFound } from './Pages/NotFound';
import { ActiveHardwareRosProvider } from './contexts/ActiveHardwareRosContext';
import { GettingStartedModal } from './Components/GettingStartedModal';
/* Components */
import { AuthForm } from './Components/AuthForm';
import { LucyLoader } from './Components/LucyLoader';
import { Page } from './Components/Page';
import { ROUTES } from './Constants/routes.ts';
import {
PAGE_CONTENT_STYLE,
UI_ACCENT_GREEN,
UI_BG_BLACK,
UI_BORDER_STRONG,
Expand All @@ -20,21 +24,21 @@ import {
UI_TEXT_SUBTLE,
} from './Constants/uiTheme.ts';

const { Content } = Layout;
const { useBreakpoint } = Grid;

const Configuration = lazy(() => import('./Pages/Configuration').then(module => ({ default: module.default })));
const SensorDisplay = lazy(() => import('./Pages/SensorDisplay').then(module => ({ default: module.default })));

const CONTROL_PATH = '/';
const CONFIG_PATH = '/configuration';
const SENSORS_PATH = '/sensors';
const KNOWN_PATHS = new Set<string>([CONTROL_PATH, CONFIG_PATH, SENSORS_PATH]);
const KNOWN_PATHS = new Set<string>([
ROUTES.control,
ROUTES.robotConfiguration,
ROUTES.sensors,
]);

/**
* Render all main pages once and toggle visibility with `display: none`.
* Keeps page-local state (sliders, edits, expanded panels) alive when the
* user switches between CONTROL / CONFIGURATION. Lazy pages are only mounted
* user switches between CONTROL / ROBOT CONFIGURATION. Lazy pages are only mounted
* after their first visit so the initial chunk stays small.
*/
const PersistentPages: FC = () => {
Expand All @@ -43,8 +47,8 @@ const PersistentPages: FC = () => {
const [sensorsVisited, setSensorsVisited] = useState(false);

useEffect(() => {
if (pathname === CONFIG_PATH) setConfigVisited(true);
if (pathname === SENSORS_PATH) setSensorsVisited(true);
if (pathname === ROUTES.robotConfiguration) setConfigVisited(true);
if (pathname === ROUTES.sensors) setSensorsVisited(true);
}, [pathname]);

const isKnown = KNOWN_PATHS.has(pathname);
Expand All @@ -55,16 +59,16 @@ const PersistentPages: FC = () => {

return (
<>
<div style={pageStyle(pathname === CONTROL_PATH)}>
<div style={pageStyle(pathname === ROUTES.control)}>
<RobotControlPanel />
</div>
{configVisited ? (
<div style={pageStyle(pathname === CONFIG_PATH)}>
<div style={pageStyle(pathname === ROUTES.robotConfiguration)}>
<Suspense
fallback={
<LucyLoader
label="LOADING CONFIGURATION"
detail="Preparing the hardware configuration page."
label="LOADING ROBOT CONFIGURATION"
detail="Preparing the robot configuration page."
/>
}
>
Expand All @@ -73,7 +77,7 @@ const PersistentPages: FC = () => {
</div>
) : null}
{sensorsVisited ? (
<div style={pageStyle(pathname === SENSORS_PATH)}>
<div style={pageStyle(pathname === ROUTES.sensors)}>
<Suspense
fallback={
<LucyLoader
Expand Down Expand Up @@ -179,10 +183,19 @@ function App() {
<Router>
<ActiveHardwareRosProvider>
<Layout style={{ minHeight: '100vh', backgroundColor: UI_BG_BLACK }}>
<Content style={{ paddingBottom: isMobile ? '60px' : '0' }}>
<Page
showHeader
title
removeScrollbars={false}
contentStyle={{
...PAGE_CONTENT_STYLE,
paddingBottom: isMobile ? 72 : PAGE_CONTENT_STYLE.padding,
}}
>
<PersistentPages />
</Content>
</Page>
<Navigation />
<GettingStartedModal />
</Layout>
</ActiveHardwareRosProvider>
</Router>
Expand Down
48 changes: 27 additions & 21 deletions src/Components/AnimationManager.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import React, { useState, useCallback, useEffect } from 'react';
import {
Modal,
Button,
Input,
Space,
Expand All @@ -16,16 +15,16 @@ import {
DeleteOutlined
} from '@ant-design/icons';
import { storageService, type SavedPose, type SavedAnimation } from '../Services/storage.service.ts';
import { MovableModal } from './MovableModal';
import {
UI_ACCENT_GREEN,
UI_BORDER_SOFT,
UI_COLOR_TRANSPARENT,
UI_INPUT_SURFACE,
UI_MODAL_MASK_BG,
UI_TEXT_PRIMARY_ON_DARK,
} from '../Constants/uiTheme.ts';

const { Text, Title } = Typography;
const { Text } = Typography;
const { Option } = Select;

interface AnimationManagerProps {
Expand Down Expand Up @@ -168,16 +167,16 @@ export const AnimationManager: React.FC<AnimationManagerProps> = ({ onPlayAnimat
</Space>

{/* Manage Animation Modal */}
<Modal
title={
<Title level={4} style={{ color: UI_ACCENT_GREEN, margin: 0 }}>
<SettingOutlined /> Manage Animations
</Title>
}
open={manageModalVisible}
onCancel={() => setManageModalVisible(false)}
footer={[
editingAnimId ? (
<MovableModal
modalName="MANAGE ANIMATIONS"
isVisible={manageModalVisible}
onClose={() => setManageModalVisible(false)}
initialPosition={{ x: 760, y: 120 }}
initialSize={{ w: 350, h: 650 }}
header={<SettingOutlined style={{ color: UI_ACCENT_GREEN }} />}
footer={
<>
{editingAnimId ? (
<Popconfirm
key="delete"
title="Delete this animation?"
Expand All @@ -187,17 +186,24 @@ export const AnimationManager: React.FC<AnimationManagerProps> = ({ onPlayAnimat
>
<Button danger icon={<DeleteOutlined />}>Delete</Button>
</Popconfirm>
) : null,
) : null}
<Button key="cancel" onClick={() => setManageModalVisible(false)}>
Cancel
</Button>,
<Button key="save" type="primary" loading={loading} onClick={() => void handleSaveAnimation()} style={{ backgroundColor: UI_ACCENT_GREEN, borderColor: UI_ACCENT_GREEN }}>
</Button>
<Button
key="save"
type="primary"
loading={loading}
onClick={() => void handleSaveAnimation()}
style={{
backgroundColor: UI_COLOR_TRANSPARENT,
borderColor: UI_BORDER_SOFT,
color: UI_TEXT_PRIMARY_ON_DARK
}}>
{editingAnimId ? 'Update Animation' : 'Create Animation'}
</Button>
]}
style={{ top: 100 }}
styles={{ mask: { backgroundColor: UI_MODAL_MASK_BG } }}
className="dark-modal"
</>
}
>
<Space direction="vertical" style={{ width: '100%', marginTop: 16 }} size="large">
<div>
Expand Down Expand Up @@ -255,7 +261,7 @@ export const AnimationManager: React.FC<AnimationManagerProps> = ({ onPlayAnimat
<Switch checked={loop} onChange={setLoop} />
</div>
</Space>
</Modal>
</MovableModal>
</>
);
};
19 changes: 18 additions & 1 deletion src/Components/AppHeader.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { useEffect, useState, useRef } from 'react';
import { Button, Tooltip, Typography, Grid } from 'antd';
import { SettingOutlined, ReadOutlined } from '@ant-design/icons';
import { SettingOutlined, ReadOutlined, BugOutlined, NodeIndexOutlined } from '@ant-design/icons';
import { useRosConnection } from '../hooks/useRosConnection.hook';
import { ConnectedClientsHandler } from '../Services/ros/handlers/ConnectedClients.handler';
import { ControlModeHandler } from '../Services/ros/handlers/ControlMode.handler';
Expand All @@ -13,6 +13,7 @@ import {
UI_TEXT_SECONDARY_MUTED,
} from '../Constants/uiTheme';
import { SettingsModal, isAutoConnectEnabled } from './SettingsModal';
import { ConnectionDebugModal, CommandDebugModal } from './PipelineDebugModal';

const { Text } = Typography;
const { useBreakpoint } = Grid;
Expand All @@ -21,6 +22,8 @@ export const AppHeader: React.FC = () => {
const { connectionStatus, isConnected, connect, currentUrl } = useRosConnection();
const [countState, setCountState] = useState<number>(0);
const [isSettingsModalVisible, setIsSettingsModalVisible] = useState(false);
const [isConnectionDebugVisible, setIsConnectionDebugVisible] = useState(false);
const [isCommandDebugVisible, setIsCommandDebugVisible] = useState(false);
const [activeControllerId, setActiveControllerId] = useState<string>(
() => ControlModeHandler.getInstance().currentControllerId
);
Expand Down Expand Up @@ -180,13 +183,27 @@ export const AppHeader: React.FC = () => {
{!isMobile && 'Documentation'}
</Button>
</Tooltip>
<Tooltip title="Debug: connection pipeline">
<Button icon={<NodeIndexOutlined />} onClick={() => setIsConnectionDebugVisible(true)} />
</Tooltip>
<Tooltip title="Debug: command pipeline (slider to joint)">
<Button icon={<BugOutlined />} onClick={() => setIsCommandDebugVisible(true)} />
</Tooltip>
<Tooltip title="Settings">
<Button icon={<SettingOutlined />} onClick={() => setIsSettingsModalVisible(true)} />
</Tooltip>
<SettingsModal
visible={isSettingsModalVisible}
onClose={() => setIsSettingsModalVisible(false)}
/>
<ConnectionDebugModal
isVisible={isConnectionDebugVisible}
onClose={() => setIsConnectionDebugVisible(false)}
/>
<CommandDebugModal
isVisible={isCommandDebugVisible}
onClose={() => setIsCommandDebugVisible(false)}
/>
</div>
);
};
Loading