pj_marketplace/docs/USER_MANUAL.md
Version: 1.0.0 Last Updated: 2026-05-19 Audience: End users, developers, and LLMs assisting with the project
src/git tag v1.0.0 && git push --tags)From PlotJuggler:
Standalone (development only):
./build/pj_marketplace/pj_marketplace_app
The marketplace window shows a list of all extensions with their status:
| Column | Content |
|---|---|
| Name | Extension name |
| Version | Installed version when available, otherwise registry version; registry version is shown for comparison when they differ |
| Status | [install], [installed], [update], or [local newer] |
To see extension details: Double-click on any extension to open a detail dialog with full information (description, author, changelog).
If PlotJuggler already has the plugin loaded at startup, the marketplace is seeded with that loaded state before the first render so the card version matches what the app already opened.
Quick tip: Hover over an extension to see a brief description tooltip.
Search box: Type to filter by name, description, or tags
ros finds "ROS 2 Streaming", "ROS Bag Loader"csv finds "CSV Loader", "CSV Exporter"Category filter dropdown:
Quick filters: (planned — not yet implemented)
On Windows: You may see "Restart required to complete installation"
.backup/ and can be recovered manuallyUpdate All: Click "Update All" in the toolbar to stage updates for all extensions at once
Planned — see TODO.md. Today, removing an extension requires Uninstall.
Note on plugin shape:
Plugins are pure C++ DSOs that consume the C ABI declared in
plotjuggler_sdk/pj_plugins. They do not link Qt — only the host (PlotJuggler / the marketplace) does. Each plugin exposes a small embedded JSON manifest (id,name,version, …) through the SDK export macro; the marketplace reads that manifest to recognise installed plugins. The workflow below describes the full process for these plugins.CI Options: Extensions can live in separate repos (one per extension) or in a mono-repo with per-component releases (see Foxglove MCAP as reference). The registry supports both approaches.
Prerequisites:
Steps:
Create repo from template:
# Go to https://github.com/plotjuggler/extension-template
# Click "Use this template"
# Clone your new repo
git clone https://github.com/YOUR_USERNAME/my-extension.git
cd my-extension
Modify the plugin:
src/my_plugin.cppui/my_dialog.ui (optional)Build locally:
conan install . --profile profiles/linux_static --build=missing
cmake --preset conan-release
cmake --build --preset conan-release
Test locally:
~/.local/share/PlotJuggler/PlotJuggler4/extensions/my-extension/ — see §5.1 for other OSes)Release:
git add .
git commit -m "feat: my awesome plugin"
git tag v1.0.0
git push && git push --tags
Wait for CI:
Submit to registry:
Every plugin DSO must export an embedded manifest through the SDK macro:
PJ_DATA_SOURCE_PLUGIN(MyPlugin,
R"({"id":"my-extension","name":"My Extension","version":"1.0.0"})")
| Field | Required | Description |
|---|---|---|
id | Yes | Unique identifier (lowercase, hyphens) |
name | Yes | Human-readable plugin name |
version | Yes | Semantic version (X.Y.Z) |
encoding | Parsers only | Message encoding handled by a parser plugin |
file_extensions | No | File suffixes handled by a file source plugin |
capabilities | No | Optional capability tags |
| Type | Base class (export macro) | Purpose |
|---|---|---|
data_loader | PJ::DataSourcePluginBase (PJ_DATA_SOURCE_PLUGIN) | Load data from files |
data_streamer | PJ::DataSourcePluginBase (PJ_DATA_SOURCE_PLUGIN) | Real-time data streaming |
parser | PJ::MessageParserPluginBase (PJ_MESSAGE_PARSER_PLUGIN) | Parse binary data to fields |
toolbox | PJ::ToolboxPluginBase (PJ_TOOLBOX_PLUGIN) | Custom tools with UI |
A single DataSourcePluginBase serves both file and streaming sources; whether it loads files or streams live data is declared via capabilities()/manifest, not by a separate base class.
The marketplace toolbar shows a Details button whenever there are recent diagnostics. Click it to open a read-only log of marketplace lifecycle events (install/uninstall/staged-promotion failures, quarantine moves, registry-fetch failures). The most recent error also appears in the status bar; a successful registry refresh clears the sticky error so progress messages aren't suppressed.
When the marketplace runs inside a host application (e.g. PlotJuggler), the host can subscribe to the same diagnostic stream alongside its own plugin-load events; see ARCHITECTURE.md §3.3 ExtensionManager — Diagnostic propagation.
| Problem | Cause | Solution |
|---|---|---|
| "Extension not loading" | Incompatible version | Check min_plotjuggler_version |
| "Download failed" | Network issue | Check internet, try again |
| "Checksum mismatch" | Corrupted download | Try again, report if persistent |
| "Cannot update (Windows)" | DLL in use | Restart PlotJuggler |
| "Installed version is newer" | Local plugin is ahead of registry | Downgrade is blocked; keep the local version |
| "Update failed after backup" | New artifact did not install | Check marketplace diagnostics for the retained backup path |
| "Post-promotion validation failed" | The DSO loads in the staging area but not from extensions/ (rpath/dep issue) | The install is rolled back; check the diagnostic for the linker error |
| "Could not mark … for restart cleanup" | Marketplace could not write the .pj_pending_uninstall marker (Windows; permissions or AV) | The uninstall is not scheduled; resolve the file-permission issue and retry |
| "Moved to quarantine: …" | A previous staged update could not be removed; it has been moved aside | Inspect the quarantined directory and delete it manually once safe |
| "Invalid registry URL" | The Settings dialog rejected a malformed URL | Use a http://, https://, or file:// URL |
| OS | Path |
|---|---|
| Linux | ~/.local/share/PlotJuggler/logs/ |
| Windows | %APPDATA%/PlotJuggler/logs/ |
| macOS | ~/Library/Application Support/PlotJuggler/logs/ |
If the marketplace is broken, remove the extensions and staging directories under your platform's config root (see §5.1):
# Linux
rm -rf ~/.local/share/PlotJuggler/PlotJuggler4/extensions/
rm -rf ~/.local/share/PlotJuggler/PlotJuggler4/.extension_staging/
# macOS
rm -rf ~/Library/Application\ Support/PlotJuggler/PlotJuggler4/extensions/
# Windows
rmdir /s %LOCALAPPDATA%\PlotJuggler\PlotJuggler4\extensions
rmdir /s %LOCALAPPDATA%\PlotJuggler\PlotJuggler4\.extension_staging
The marketplace uses the application's writable data location (resolved by QStandardPaths::AppDataLocation, i.e. the PlotJuggler/PlotJuggler4 org/app pair):
| OS | Root |
|---|---|
| Linux | ~/.local/share/PlotJuggler/PlotJuggler4/ |
| macOS | ~/Library/Application Support/PlotJuggler/PlotJuggler4/ |
| Windows | %LOCALAPPDATA%/PlotJuggler/PlotJuggler4/ |
Inside that root:
<config-root>/
├── extensions/ # Active installed extensions
│ └── my-extension/
│ └── libmy_plugin.so
├── .extension_staging/ # Staging area: updates land here and are promoted
│ │ # on the next startup; a fresh install uses it
│ │ # only as the post-promotion validation gate
│ │ # (it activates immediately)
│ └── my-extension/
│ └── .pj_pending_install # Intent file for restart-time update apply
└── .backup/ # Pre-update backups (all platforms); automatic rollback deferred — restore manually
Default: https://raw.githubusercontent.com/PlotJuggler/pj-plugin-registry/refs/heads/development/registry.json
Custom registry: Open the marketplace, click ⚙ Settings, paste the new URL, click OK. The URL is persisted under QSettings("PlotJuggler", "Marketplace")/registry_url and restored on next launch.
| Platform | Architecture | Status |
|---|---|---|
| Linux | x86_64 | Full support |
| Linux | arm64 | Planned |
| Windows | x86_64 | Full support |
| macOS | arm64 (Apple Silicon) | Full support |
| macOS | x86_64 (Intel) | Planned |
| Category | Code | Examples |
|---|---|---|
| Data Loader | data_loader | CSV, MCAP, ROS bags |
| Data Streamer | data_streamer | ROS 2, MQTT, ZMQ |
| Parser | parser | Protobuf, FlatBuffers |
| Toolbox | toolbox | FFT, CSV exporter |
| Bundle | bundle | ROS 2 Complete |
This is the PlotJuggler Marketplace, an extension distribution system for PlotJuggler (a robotics data visualization tool). Key points:
| File | Purpose |
|---|---|
REQUIREMENTS.md | What the system should do |
ARCHITECTURE.md | How the system is designed |
USER_MANUAL.md | This file - how to use it |
TODO.md | Remaining work and deferred follow-ups |
"Add a new feature"
"Fix a bug"
"Help user install extension"
| Component | Path |
|---|---|
| Registry fetching | src/core/RegistryManager.cpp |
| Installation logic | src/core/ExtensionManager.cpp |
| Download handling | src/core/DownloadManager.cpp |
| Diagnostic-sink bridge | src/core/QtDiagnosticBridge.cpp |
| Main UI | src/ui/marketplace_window.{cpp,ui} |
| Extension detail dialog | src/ui/extension_detail_dialog.{cpp,ui} |
| Public headers (incl. data models) | include/pj_marketplace/ |
pj_marketplace builds as part of the PJ4 monorepo (root CMakeLists.txt add_subdirectory(pj_marketplace)); the repo defines no CMakePresets.json.
# Build (output dir build/, RelWithDebInfo)
./build.sh
# Run tests
ctest --test-dir build/pj_marketplace
# Run standalone (target pj_marketplace_app — no OUTPUT_NAME override)
./build/pj_marketplace/pj_marketplace_app
Update this manual when: