This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
FlashForgeWebUI is a standalone web-based interface for controlling and monitoring FlashForge 3D printers. It provides a lightweight deployment option for low-spec devices like Raspberry Pi, without Electron dependencies.
Current Status: Production-ready. Core functionality tested and working including multi-printer support, Spoolman integration, Discord webhook notifications, go2rtc-based camera streaming, and cross-platform binary distribution.
npm run dev # Build and watch with hot reload (concurrent backend + webui + server)
npm run build # Full production build (backend + webui)
npm run build:watch # Watch backend and frontend builds without starting the server
npm run start # Run the built application
npm run start:dev # Run with nodemon (watches for changes)npm run build:backend # Bundle backend with esbuild (scripts/build-backend.ts)
npm run build:backend:watch # Watch backend files
npm run build:webui # Compile frontend TS + copy static assets
npm run build:webui:watch # Watch frontend files
npm run build:webui:copy # Copy HTML/CSS and vendor libraries to distnpm run build:linux # Linux x64 executable (using pkg)
npm run build:linux-arm # Linux ARM64 executable
npm run build:linux-armv7 # Linux ARMv7 executable
npm run build:win # Windows x64 executable
npm run build:mac # macOS x64 executable
npm run build:mac-arm # macOS ARM64 executable
npm run build:all # Build for all platforms
npm run build:wrapper # Run the platform build wrapper directly
npm run build:win:wrapped # Windows x64 build via wrapper
npm run build:linux:wrapped # Linux x64 build via wrapper
npm run build:linux-arm:wrapped # Linux ARM64 build via wrapper
npm run build:linux-armv7:wrapped # Linux ARMv7 build via wrapper
npm run build:mac:wrapped # macOS x64 build via wrapper
npm run build:mac-arm:wrapped # macOS ARM64 build via wrapper
npm run build:all:wrapped # All wrapped platform buildsnpm run lint # Run Biome lint checks
npm run lint:fix # Auto-fix Biome lint issues
npm run format # Preview Biome formatting changes
npm run format:fix # Apply Biome formatting changes
npm run check # Run Biome check (lint + format combined)
npm run check:fix # Auto-fix Biome check issues
npm run type-check # TypeScript type checking (app + e2e)
npm run type-check:app # Type check main application only
npm run type-check:e2e # Type check e2e tests only (tsconfig.e2e.json)
npm run docs:check # Validate @fileoverview coverage in source files
npm run docs:check:debug # Debug fileoverview validation output
npm run clean # Remove dist directory
npm run download:go2rtc # Manually download go2rtc binary# Jest app tests
npm test # Run all Jest tests
npm run test:watch # Jest watch mode
npm run test:coverage # Jest with coverage
npm run test:verbose # Jest verbose output
# Playwright fixture E2E (fast, stub server)
npm run test:e2e:install # Install Chromium for Playwright
npm run test:e2e # Run all fixture E2E specs
npm run test:e2e:smoke # Smoke tests only
npm run test:e2e:auth # Auth tests only
# Playwright emulator E2E (full server + emulator, workers=1)
npm run test:e2e:emulator # Run all emulator E2E specs
npm run test:e2e:emulator:direct # Direct connection spec
npm run test:e2e:emulator:discovery # Discovery spec
npm run test:e2e:emulator:multi # Multi-printer spec
# Combined
npm run test:e2e:all # All Playwright suites (fixture + emulator)
npm run test:all # Everything (Jest + all Playwright)
# Passthrough: append extra args after --
# npm run test:e2e -- --grep "login"
# npm run test:e2e:emulator:direct -- --grep "connect"
# npm test -- --testPathPattern=ConfigThe application supports multiple startup modes via CLI arguments:
# Connect to last used printer
node dist/index.js --last-used
# Connect to all saved printers
node dist/index.js --all-saved-printers
# Connect to specific printers
node dist/index.js --printers="192.168.1.100:new:12345678,192.168.1.101:legacy"
# Start without printer connections (WebUI only)
node dist/index.js --no-printers
# Override WebUI settings
node dist/index.js --last-used --webui-port=3001 --webui-password=mypasswordPrinter spec format: IP:TYPE:CHECKCODE where TYPE is new or legacy.
The system is built on a multi-context singleton architecture:
- Singleton Managers: Global coordinators for major subsystems.
- Multi-Context Design: Support for simultaneous connections to multiple printers, each isolated in its own context.
- Event-Driven Communication: EventEmitter-based communication between services and managers.
- Service-Oriented: Clear separation between managers, backends, and runtime services.
src/index.ts # Entry point - initializes all singletons, connects printers, starts WebUI
|-- managers/ # Singleton coordinators (ConfigManager, PrinterContextManager, etc.)
|-- printer-backends/ # Printer-specific API implementations
|-- services/ # Background services (polling, camera, spoolman, monitoring)
|-- webui/ # Web server and frontend
| |-- server/ # Express server, WebSocket, API routes, auth
| `-- static/ # Frontend TypeScript (separate tsconfig, compiled to ES modules)
|-- types/ # TypeScript type definitions
`-- utils/ # Shared utilities
Managers (singleton coordinators):
ConfigManager- Global configuration fromdata/config.jsonPrinterContextManager- Multi-printer context lifecycle (create, switch, remove contexts)PrinterBackendManager- Backend instantiation based on printer model and capabilitiesConnectionFlowManager- Connection orchestration (discovery, pairing, reconnect, disconnect)PrinterDetailsManager- Persistent printer metadata and saved printer details
Contexts: Each connected printer gets a unique context containing:
- Unique ID and printer details
- Backend instance (
AD5XBackend,Adventurer5MBackend,GenericLegacyBackend, etc.) - Polling service instance
- Connection state
- Spoolman spool assignment
Backends: Abstraction layer over printer APIs (all extend BasePrinterBackend):
AD5XBackend- Adventurer 5M X series (uses AD5X API)Adventurer5MBackend- Adventurer 5M (FiveMClient)Adventurer5MProBackend- Adventurer 5M Pro (FiveMClient)DualAPIBackend- Base for printers supporting both FiveMClient and FlashForgeClientGenericLegacyBackend- Fallback for older printers (FlashForgeClient only)
Services:
MultiContextPollingCoordinator- Manages per-context polling (3s for all contexts)MultiContextPrintStateMonitor- Tracks print progress and state transitionsMultiContextTemperatureMonitor- Temperature anomaly detectionMultiContextSpoolmanTracker- Filament usage trackingMultiContextNotificationCoordinator- Aggregates print notifications across contextsGo2rtcService- Unified camera streaming via go2rtc (WebRTC, MSE, MJPEG)Go2rtcBinaryManager- Manages go2rtc binary lifecycle (download, start, stop)DiscordNotificationService- Discord webhook notifications for print events and periodic status updatesSavedPrinterService- Persistent printer storage indata/printer_details.jsonSpoolmanIntegrationService/SpoolmanService- Spoolman connectivity and synchronizationPrinterDiscoveryService- Network printer discovery protocolConnectionEstablishmentService- Connection establishment flow orchestrationConnectionStateManager- Tracks connection state per contextEnvironmentService- Package detection, path resolution, environment statePrinterPollingService- Per-context status polling (used byMultiContextPollingCoordinator)AutoConnectService- Auto-reconnect logic for saved printersPrinterDataTransformer- Normalizes raw printer data to unified status formatThumbnailRequestQueue- Queued thumbnail fetching for print files
WebUI:
WebUIManager- Express HTTP server and static file servingWebSocketManager- Real-time bidirectional communication with the frontendAuthManager- Optional password authentication- API routes organized by feature (context, printer-control, printer-status, printer-detection, printer-management, job, camera, temperature, filtration, spoolman, theme, discovery)
- Frontend uses GridStack for dashboard layout and the bundled
video-rtcplayer for go2rtc-backed camera streaming
The application stores runtime state in data/ under the project working directory. In the current repo, only data/runtime/ is ignored by .gitignore, so local edits to top-level files like data/config.json can appear in git status.
Key files:
data/config.json- User settings (WebUI, Discord, Spoolman, theme, debug settings)data/printer_details.json- Saved printer details and printer-specific overrides used for reconnects and camera resolution
Default config values live in src/types/config.ts and are loaded through ConfigManager.
Backend Bundling (scripts/build-backend.ts):
- Bundles
src/index.tstodist/index.jsas a CommonJS entrypoint - Uses
packages: 'external'to keepnode_modulesseparate for pkg compatibility tscis still used for type checking vianpm run type-check
Frontend Compilation (src/webui/static/tsconfig.json):
- Compiles frontend TypeScript to
dist/webui/static/as browser ES modules
Asset Pipeline (scripts/copy-webui-assets.js):
- Copies
index.html,webui.css,gridstack-extra.min.css, andfavicon.pngfromsrc/webui/static/ - Copies vendor libraries from
node_modules/including GridStack, Lucide, andvideo-rtc
Documentation Validation (scripts/check-fileoverview.go):
- Powers
npm run docs:checkandnpm run docs:check:debug - Verifies
@fileoverviewcoverage across the source tree
Wrapped Platform Builds (scripts/platform-build-wrapper.ts):
- Provides wrapper entrypoints for the
build:*:wrappedscripts
go2rtc Binary (scripts/download-go2rtc.cjs):
- Runs at
npm installtime via thepostinstallhook - Downloads the platform-specific go2rtc binary to
resources/bin/
pkg Bundling:
- Production builds use
@yao-pkg/pkgto create standalone executables - Packaged assets include
dist/webui/**/*andresources/bin/**/*
Why @yao-pkg/pkg instead of official pkg?
- The official
pkgpackage is no longer maintained and lacks newer Node.js and ARMv7 support @yao-pkg/pkgsupportsnode20-linuxstatic-armv7, which is required for Raspberry Pi 32-bit builds- It remains compatible with the existing
pkgconfiguration used by this project
Why esbuild instead of tsc for the backend?
pkgexpects a CJS-friendly backend bundle- esbuild produces a single bundled entrypoint that packages reliably
- Keeping packages external lets
pkgembed the needed runtime assets and scripts cleanly
Core:
@ghosttypes/ff-api- FlashForge printer API clients (FiveMClient,FlashForgeClient)@parallel-7/slicer-meta- Printer metadata and model detectionexpress- HTTP serverws- WebSocket serverzod- Schema validationaxios- HTTP client for go2rtc API callsform-data- Multipart form handling
Frontend:
gridstack- Dashboard layout systemlucide- Icon libraryvideo-rtc- go2rtc video player bundled into static assets
Dev:
- TypeScript 5.7
- Biome 2 for linting and formatting
esbuildfor backend bundlingjest,ts-jest,supertestfor unit/integration testing@playwright/testfor E2E browser testingconcurrentlyfor parallel build tasksnodemonfor dev server reloadstsxfor build scripts@yao-pkg/pkgfor executable packaging
FlashForge printers have two API generations:
- Legacy API (
FlashForgeClient) - Older printers using a line-based TCP protocol - New API (
FiveMClient) - Newer printers using structured commands with JSON responses
Some printers support both (dual API). The backend system abstracts these differences.
Backend Selection (in PrinterBackendManager.createBackend()):
- Adventurer 5M X ->
AD5XBackend - Adventurer 5M ->
Adventurer5MBackend - Adventurer 5M Pro ->
Adventurer5MProBackend - Other new-API printers -> backend selected from discovery metadata and
clientType - Legacy printers ->
GenericLegacyBackend
Feature Detection: Each backend declares supported features via getBaseFeatures(). The UI shows or hides controls based on those features, including LEDs, power toggle, material station support, and camera support.
-
Startup (
src/index.ts)- Initialize data directory
- Parse CLI arguments
- Load config
- Initialize go2rtc, Spoolman, Discord, and monitoring services
- Connect printers and create contexts/backends
- Start the WebUI server
- Forward polling updates to WebSocket clients and Discord status tracking
- Reconcile camera streams per context
- Register signal handlers for graceful shutdown
-
Polling Lifecycle
MultiContextPollingCoordinatorcreates aPrinterPollingServiceper context- Each polling service calls
backend.getPrinterStatus()every 3 seconds - Status data is emitted as a
polling-dataevent withcontextId WebUIManagerforwards polling data toWebSocketManagerDiscordNotificationServiceuses the same status stream for periodic updates
-
WebSocket Communication
- The frontend connects via WebSocket once the server starts
- The server pushes printer status and notification updates
- The client sends commands for printer control, job actions, and context switching
- The API contract is defined in
src/webui/schemas/web-api.schemas.tsandsrc/webui/types/web-api.types.ts
Singleton Pattern:
type ManagerBrand = { readonly __brand: 'Manager' };
type ManagerInstance = Manager & ManagerBrand;
class Manager {
private static instance: ManagerInstance | null = null;
private constructor() {}
static getInstance(): ManagerInstance {
if (!this.instance) {
this.instance = new Manager() as ManagerInstance;
}
return this.instance;
}
}
export function getManager(): ManagerInstance {
return Manager.getInstance();
}Context Access:
const contextManager = getPrinterContextManager();
const activeContextId = contextManager.getActiveContextId();
const context = contextManager.getContext(activeContextId);
const backend = context?.backend;Typed EventEmitter Usage:
interface EventMap extends Record<string, unknown[]> {
'event-name': [arg1: Type1, arg2: Type2];
}
class Service extends EventEmitter<EventMap> {
doSomething() {
this.emit('event-name', arg1, arg2);
}
}- Dual Build System: Backend uses esbuild bundling, frontend uses a separate
tsconfigfor browser modules. - Data Directory Tracking: Runtime state lives in
<project>/data/, but onlydata/runtime/is ignored by the current.gitignore. - Camera Streams: go2rtc manages camera streams per context; there is no user-facing global
CameraProxyPortsetting. - go2rtc Binary: The binary is downloaded at
npm installtime and stored underresources/bin/. If download or packaging fails, camera streaming will not work. - Polling Frequency: All contexts poll every 3 seconds to avoid inactive-context TCP keep-alive failures.
- Context IDs: IDs are UUID-based and generated at connection time; they are not tied to IP or serial number.
- Backend Lifecycle: Backends are created per context and maintain their own TCP connections.
- Graceful Shutdown: Shutdown stops polling, Discord, printer connections, go2rtc, and the WebUI with layered timeouts.
- Windows Compatibility: Ctrl+C handling includes a readline bridge on Windows.
- ARMv7 Builds: Raspberry Pi 32-bit builds target
node20-linuxstatic-armv7, which depends on@yao-pkg/pkg.
Jest unit/integration tests (src/):
- ConfigManager, EnvironmentService, DiscordNotificationService, error utilities, WebUIManager integration
Playwright fixture E2E (e2e/):
- Fast headless tests against the built WebUI served by a stub HTTP+WebSocket server
- Specs: smoke (asset versioning, context switching), auth (login, token persistence, logout)
- Global setup runs
npm run buildbefore the suite - Config:
playwright.config.ts(30s timeout, single worker)
Playwright emulator E2E (e2e-emulator/):
- Full integration tests using the standalone server and
flashforge-emulator-v2printer emulator - Specs: direct connection (all 5 printer models), discovery flow, multi-printer context switching
- Helpers: emulator harness, standalone server harness, lifecycle runner, scenario definitions, page object model
- Config:
playwright.emulator.config.ts(180s timeout, single worker) - Requires
flashforge-emulator-v2cloned at../flashforge-emulator-v2(orFF_EMULATOR_ROOTenv var)
Verified and tested:
- Multi-printer context switching
- Spoolman integration
- Platform-specific binary builds (Linux ARM, Linux x64, Windows, macOS)
- WebUI authentication
- Static file serving in packaged binaries
- Discord notification service behavior
- Direct connection, discovery, and multi-printer E2E workflows via Playwright
Areas for continued testing:
- go2rtc binary download and startup across all supported platforms
- Temperature anomaly detection edge cases
- Wrapped build flows and packaging verification
- @ghosttypes/ff-api: FlashForge API client library
- @parallel-7/slicer-meta: Printer metadata and model utilities