Welcome, and thank you for your interest in contributing to Lichtblick! We value your contributions and want to make the contributing experience enjoyable and rewarding for you.
Lichtblick is an integrated visualization and diagnosis tool for robotics, built primarily with TypeScript and React. It is available as a desktop app (Electron) and a web app.
- Code of Conduct
- Prerequisites
- Getting Started
- Project Structure
- Component Structure
- Development Workflow
- Branching Strategy
- Code Style & Standards
- Testing
- Pull Request Guidelines
- Reporting Issues
- Version Increment
- Localization
- License
- Credits
This project adheres to the Contributor Covenant Code of Conduct. By participating, you are expected to uphold this code. Please report unacceptable behavior to lichtblick@bmwgroup.com.
Before contributing, ensure you have the following installed:
| Tool | Version | Notes |
|---|---|---|
| Node.js | >= 20 | LTS recommended |
| Corepack | (bundled with Node.js) | Must be enabled: corepack enable |
| Yarn | 3.6.3 | Managed via Corepack — do not install separately |
| Git | Latest | Required for version control |
Note: If you encounter issues with Corepack after enabling it, try uninstalling and reinstalling Node.js. Ensure Yarn is installed via Corepack, not from another source.
External contributors do not have write access to the repository. To contribute, you must fork the project and open a Pull Request from your fork:
-
Fork the repository on GitHub.
-
Clone your fork:
git clone https://github.com/<your-username>/lichtblick.git cd lichtblick
-
Add the upstream remote (to keep your fork up to date):
git remote add upstream https://github.com/lichtblick-suite/lichtblick.git
Core team members with write access can clone the repository directly and create branches following the Branching Strategy:
git clone https://github.com/lichtblick-suite/lichtblick.git
cd lichtblick-
Enable Corepack and install dependencies:
corepack enable yarn install -
Launch the development environment:
# Desktop app (run in separate terminals): yarn desktop:serve # Start webpack dev server yarn desktop:start # Launch Electron (wait for desktop:serve to finish) # Web app: yarn web:serve # Available at http://localhost:8080
-
Explore available commands:
yarn run # List all available commands
# Launch Storybook for component development
yarn storybook
# Lint all files (with auto-fix)
yarn lint
# Run all unit tests
yarn test
# Run tests in watch mode (for active development)
yarn test:watch
# Run E2E tests
yarn test:e2e:desktop
yarn test:e2e:web# Running webpack and Electron on different machines on the same network
yarn desktop:serve --host 192.168.xxx.yyy
yarn dlx electron@22.1.0 .webpackLichtblick is organized as a monorepo managed with Yarn Workspaces:
lichtblick/
├── desktop/ # Electron desktop app entry points
│ ├── main/ # Main process
│ ├── preload/ # Preload scripts
│ ├── renderer/ # Renderer process
│ └── quicklook/ # Quick Look support
├── web/ # Web app entry point
├── packages/
│ ├── suite/ # Core application logic
│ ├── suite-base/ # Shared base components, panels, and players
│ ├── suite-desktop/ # Desktop-specific functionality
│ ├── suite-web/ # Web-specific functionality
│ ├── theme/ # Theming and styling
│ ├── hooks/ # Shared React hooks
│ ├── log/ # Logging utilities
│ ├── den/ # Data utilities and async helpers
│ ├── mcap-support/ # MCAP file format support
│ ├── message-path/ # Message path parsing
│ └── ... # Other internal packages
├── e2e/ # End-to-end tests (Playwright)
├── benchmark/ # Performance benchmarks
└── ci/ # CI scripts and utilities
To ensure consistency, scalability, and a clear separation of concerns, all React components should follow the structure outlined below.
| File / Directory | Purpose |
|---|---|
index.tsx |
Entry point — manages exports and provides a simplified integration interface |
ComponentName.tsx |
Primary logic and rendering of the component |
ComponentName.test.tsx |
Unit tests for the component |
ComponentName.style.ts |
Styles specific to the component (using tss-react) |
types.ts |
TypeScript type definitions, interfaces, and enums for the component |
constants.ts |
Constants specific to the component (avoids magic numbers and scattered hardcoded values) |
hooks/ |
Custom hooks related to the component (e.g., useComponentData.ts) |
builders/ |
Builder classes for creating mock data, test props, and reusable configurations |
utils/ |
Utility functions specific to the component |
shared/ |
Shared functionalities reusable across sibling components |
panels/
├── Plot/
│ ├── index.tsx # Entry point for exposing the component
│ ├── Plot.tsx # Primary logic and rendering
│ ├── Plot.style.ts # Styles specific to the Plot component
│ ├── Plot.test.tsx # Unit tests for the Plot component
│ ├── PlotLegend.tsx # Sub-component: logic and rendering
│ ├── PlotLegend.style.ts # Styles for the PlotLegend sub-component
│ ├── PlotLegend.test.tsx # Unit tests for the PlotLegend sub-component
│ ├── types.ts # Contracts/Schemas for Plot components
│ ├── constants.ts # Constants specific to Plot components
│ ├── hooks/
│ │ ├── usePlotData.ts # Custom hook for the Plot component
│ │ └── usePlotData.test.ts # Unit tests for the hook
│ ├── builders/
│ │ ├── PlotBuilder.ts # Builder for mock data and test props
│ │ └── PlotBuilder.test.ts # Unit tests for the builder
│ └── utils/
│ ├── formatPlotValues.ts # Utility function for the Plot component
│ └── formatPlotValues.test.ts # Unit tests for the utility
└── shared/
├── formatDate.ts # Shared function across panel components
└── formatDate.test.ts # Unit tests for the shared function
index.tsxshould focus exclusively on managing exports. Primary component logic belongs inComponentName.tsx.types.tscentralizes type definitions, making them easily accessible and reusable.constants.tsand*.style.tsfiles can be excluded from code coverage tools (e.g., SonarQube) to focus metrics on relevant files.- Builders follow the Builder pattern to simplify creation of complex objects step-by-step — especially useful for test setups.
shared/promotes reusability across sibling components and reduces duplication of common logic.
- Internal team: Create a branch directly in the repository following the Branching Strategy. Always branch off
developfor features and bugfixes. - Community contributors: Create a branch in your fork. When opening a PR, target the
developbranch of the upstream repository.
- Write clean, well-documented TypeScript code.
- Follow the Code Style & Standards and Component Structure.
- Add or update unit tests for your changes.
- If your change affects the UI, verify it in both web and desktop modes.
Before pushing, ensure your changes pass all checks:
yarn lint # Linting (ESLint + Prettier)
yarn test # Unit tests (Jest)
yarn run tsc --noEmit # TypeScript type checking- Community contributors: Open a PR from your fork targeting the
developbranch of the upstream repository. - Internal team: Open a PR directly in the repository targeting
developfor features/bugfixes, ormainforrelease/major/,release/minor/, andhotfix/branches. - Fill in the PR template completely.
- Ensure CI checks pass.
Branch naming is enforced by CI. PRs with non-compliant branch names will be automatically rejected.
| Prefix | Purpose | Example |
|---|---|---|
feature/ |
New features or significant improvements | feature/user-authentication |
bugfix/ |
Non-critical bug fixes | bugfix/fix-login-redirect |
dependabot/ |
Automated dependency updates | (auto-generated) |
| Prefix | Purpose | Example |
|---|---|---|
release/major/ |
Major production releases (breaking changes or reworked APIs) | release/major/2.0.0 |
release/minor/ |
Minor production releases (new functionality, no breaking changes) | release/minor/1.3.0 |
hotfix/ |
Urgent critical fixes | hotfix/security-patch |
Code quality is enforced through automated tooling. All checks run in CI and must pass before merging.
- Prettier is used for code formatting with a
printWidthof 100 characters. - Prettier runs automatically as part of linting in CI.
- ESLint with the
@lichtblickplugin suite enforces consistent code patterns. - Run
yarn lintto auto-fix issues locally (Prettier integration is disabled locally to speed up linting, but enforced in CI).
- Strict mode is enabled — avoid
anytypes where possible. - Prefer
undefinedovernull. When required for React refs, use theReactNullalias. - Do not use property getters or setters — use function syntax instead.
- Unused variables must have a
_prefix (e.g.,_unusedParam). - Avoid
@emotion/styledand MUI'sstyled/sx/Box— use tss-react/mui for styling instead. - Use
@lichtblick/den/asyncraceinstead ofPromise.race(see V8 bug). - Allowed console methods:
console.warn,console.error,console.debug,console.assert.
All source files must include the MPL-2.0 license header. This is enforced by ESLint via the @lichtblick/license-header rule.
- Keep constants in
constants.ts, types intypes.ts, and styles in*.style.tsfiles. - Organize styles using tss-react.
- See the Component Structure section for a complete reference.
- All tests should follow the Given-When-Then (GWT) structure for improved readability and maintainability. This pattern is used consistently across both unit and E2E tests.
- Use reusable mock builders to create test data instead of manually setting up mocks in each test. Builders are centralized in
builders/directories and provide consistent, flexible, and configurable data for all test levels.
The project uses a Builder pattern to streamline the creation of test data across unit, integration, and E2E tests:
/testing
├── builders/
│ ├── BasicBuilder.ts # Basic mocks (dates, strings, lists, floats, etc.)
│ └── EntityBuilder.ts # Mocks for specific application entities
BasicBuilderprovides foundational mock data types.- Entity builders generate consistent domain-specific mock data.
- Component-specific builders live in the component's own
builders/directory (see Component Structure).
-
All new features and bug fixes should be covered by unit tests.
-
Tests are located alongside source files with the
.test.ts/.test.tsxextension. -
Run tests:
yarn test # Run all tests yarn test:watch # Run tests on changed files yarn test:coverage # Run tests with coverage report yarn test:debug # Run tests with debugger attached
E2E tests cover both desktop (Electron) and web versions, with the following strategy:
- Primary focus on desktop testing — The desktop version is the final stage of the software, so the majority of E2E tests target it.
- Web-specific tests only when necessary — Tests are written for the web version only when there is a distinct behavior that needs separate validation.
- Shared test architecture — Page Object Models (POM) and helper functions are reused across both platforms.
- Playwright is the chosen E2E framework.
{feature-name}.{platform}.spec.ts
Example: install-multiple-extensions.web.spec.ts
e2e/
├── tests/
│ ├── desktop/ # Desktop E2E tests
│ │ ├── open-files/
│ │ │ └── open-mcap-via-ui.desktop.spec.ts
│ │ ├── sidebar/
│ │ ├── layout/
│ │ ├── extension/
│ │ ├── panel/
│ │ ├── utils/ # Shared desktop test utilities
│ │ └── playwright.config.ts # Desktop Playwright configuration
│ └── web/ # Web E2E tests
│ ├── open-files/
│ │ └── open-mcap-via-url.web.spec.ts
│ ├── utils/
│ └── playwright.config.ts # Web Playwright configuration
├── fixtures/ # Test fixtures and data mocks
└── helpers/ # Generic test helper functions
yarn test:e2e:desktop # Desktop E2E tests
yarn test:e2e:web # Web E2E tests
yarn test:e2e:desktop:debug # Desktop E2E with debug mode
yarn test:e2e:web:debug # Web E2E with debug modeThe following checks run automatically on every PR:
| Check | Description |
|---|---|
| npm audit | Security vulnerability scan |
| lint | License check, dedup check, TypeScript type checking, ESLint, unused exports, dependency checks |
| packages | Build all internal packages |
| web | Production web build |
| desktop | Production desktop build |
| benchmark | Production benchmark build |
| test | Full unit test suite |
| e2e-desktop | Desktop E2E tests (parallelized across 4 shards) |
| e2e-web | Web E2E tests |
All CI checks must pass before a PR can be merged.
🔒 Direct PRs to the repository are restricted to the internal development team. Community contributors must submit PRs from a fork of the repository. All contributions — internal and external — follow the same review and CI requirements.
When opening a PR, fill in the template provided:
- The web version was tested and is running correctly
- The desktop version was tested and is running correctly
- Changes are covered by unit tests
- Files
constants.ts,types.ts, and*.style.tshave been checked — relevant code snippets have been relocated appropriately
- Title: Use a clear, descriptive title summarizing the change.
- User-Facing Changes: Describe changes visible to end users — this will be used as a changelog entry.
- Description: Link relevant GitHub issues. Add the
docslabel if documentation updates are needed. - Size: Keep PRs focused. Smaller PRs are reviewed faster and have fewer merge conflicts.
- Reviewers: PRs require at least one approving review before merging.
- Bug reports: Use the Bug Report template on GitHub.
- Feature requests: Start a Discussion in the repository.
- Questions: Search existing Discussions or ask on Robotics Stack Exchange.
When reporting a bug, please include:
- Lichtblick version
- Platform (OS, browser, desktop)
- Data source type (e.g., MCAP file, ROS 1/2 native, rosbridge)
- Steps to reproduce
- Expected vs. actual behavior
The version format: <major>.<minor>.<patch>. Version bumps are determined automatically by the CI release workflow based on the branch name used:
| Component | Branch Prefix | Description |
|---|---|---|
| MAJOR | release/major/ |
Breaking changes — removed or reworked APIs. Users should expect a non-trivial update. |
| MINOR | release/minor/ |
New functionality added without breaking backward compatibility. |
| PATCH | hotfix/ |
Bug fixes, security patches, and minor improvements. |
First-class support is provided in English only. Translations into other languages are community-driven and available on a best-effort basis.
Translation support is implemented using react-i18next.
- We value high-quality translations over complete coverage. Every PR must have up-to-date English translations, but updating other languages is optional.
- If you update an English translation and cannot provide accurate non-English translations, delete the outdated non-English versions in your PR. Optionally, open follow-up PRs with accurate translations.
The i18n directory contains translated strings organized by namespaces — e.g., i18n/en/appSettings.ts contains translations for the Settings tab.
- Call the
useTranslation(namespace)hook to get thetfunction. - Use
t("key")to render translated strings. - Use
camelCasefor all new localization keys.
| Before | After |
|---|---|
function MyComponent() {
return <p>Hello!</p>;
} |
function MyComponent() {
const { t } = useTranslation("myComponent");
return <p>{t("hello")}</p>;
}// i18n/en/myComponent.ts
export const myComponent = {
hello: "Hello!",
}; |
// MyComponent.ts
import { useTranslation } from "react-i18next";
function MyComponent(props: Props): React.JSX.Element {
const { t } = useTranslation("myComponent");
return <p>{t("hello")}</p>;
}// i18n/en/myComponent.ts
export const myComponent = {
hello: "Hello!",
};
// i18n/en/index.ts
export * from "./myComponent";// i18n/zh/myComponent.ts
export const myComponent: Partial<TypeOptions["resources"]["myComponent"]> = {
hello: "你好!",
};
// i18n/zh/index.ts
export * from "./myComponent";| English | Chinese |
|---|---|
<p>Hello!</p> |
<p>你好!</p> |
Lichtblick is licensed under the Mozilla Public License v2.0. All contributions must comply with this license.
Lichtblick originally began as a fork of Foxglove Studio, an open-source project developed by Foxglove.
Thank you for contributing to Lichtblick! Your efforts help build better tools for the robotics community.