Development
Fluidd is built using VueJS, and the Vuetify Framework to provide a cohesive, easy to implement UI.
Contributing
Contributions are welcome! Please review the CONTRIBUTING.md file before submitting a pull request.
Architecture overview
This section is aimed at developers comfortable with Vue and TypeScript who want a
quick map of the codebase. For an exhaustive reference, see the CLAUDE.md file at
the repository root — it was written for AI coding assistants but works as a deep
second source for humans too.
Stack
- Vue 2.7 with Vuetify 2 for UI components
- TypeScript throughout, with class-style components via
vue-property-decorator - Vuex for state management — namespaced modules mirror Klipper and Moonraker domains
- Vite 8 as the build tool and dev server
- Vitest 4 with
jsdomfor unit tests - Node.js 24 — pinned in
.node-version(engines:^22.12.0 || ^24)
How it talks to Klipper
Fluidd does not talk to Klipper directly. Instead, the browser keeps a single WebSocket connection open to Moonraker, which in turn talks to Klipper:
All printer commands and live state updates flow through that single socket. The
client lives in src/api/socketActions.ts — for printer control and state, call
its methods rather than making direct HTTP requests.
A few features still use HTTP: file upload and download (via axios for progress
reporting — fetch cannot report upload progress; see src/mixins/files.ts),
camera WebRTC signalling, and the initial config.json fetch at startup.
Repository layout
src/
├── api/ WebSocket JSON-RPC client (socketActions.ts)
├── components/
│ ├── common/ Shared dialogs and status components (auto-imported)
│ ├── layout/ App shell: AppBar, AppDrawer, etc. (auto-imported)
│ ├── ui/ Reusable widgets: AppBtn, AppDialog, etc. (auto-imported)
│ ├── settings/ Settings page components
│ └── widgets/ Feature widgets — one folder per feature
├── views/ Page components, lazy-loaded by the router
├── store/ Vuex modules — one per Klipper/Moonraker domain
├── mixins/ Shared component logic (StateMixin, FilesMixin, ...)
├── plugins/ Vue plugins (i18n, socketClient, vuetify, filters)
├── router/ Vue Router (hash mode)
├── locales/ i18n YAML files (managed via Weblate)
├── scss/ Global styles and Vuetify variable overrides
├── util/ Helper functions
├── workers/ Web Workers (G-code parser, MJPEG stream, Monaco language servers)
├── typings/ Global TypeScript declarations (Klipper, Moonraker namespaces)
└── types/ UI-specific TypeScript types
Patterns you'll meet immediately
Class-style components. Every component uses decorators — no Options API or
Composition API. Components that need printer state extend a mixin via Mixins():
@Component({ components: { /* ... */ } })
export default class PrinterWidget extends Mixins(StateMixin) {
@Prop({ type: String, required: true })
readonly label!: string
get klippyReady (): boolean {
return this.$typedGetters['printer/getKlippyReady']
}
}
Vuex modules live in src/store/<name>/ with a standard layout
(index.ts, state.ts, getters.ts, mutations.ts, actions.ts, types.ts).
Use $typedState and $typedGetters for type-safe access from components.
WebSocket calls go through SocketActions methods. Pass a wait parameter
(constants in src/globals.ts) to drive UI loading state — for example,
wait: Waits.onPrintPause.
Auto-imported components. Anything under src/components/common, layout, or
ui is registered automatically by unplugin-vue-components — no manual import
needed. The generated components.d.ts at the repo root is regenerated on every
build; do not edit it by hand.
Cross-component messaging. Use the Vuex store for shared state, or the
EventBus (src/eventBus.ts) for ephemeral events such as flash messages.
Logging. Use consola, not console.log (configured in src/setupConsola.ts).
Where to add things
- A new widget → create a folder in
src/components/widgets/<name>/and import the component where it's used. - A new route → add a lazy import to
src/router/index.tsand create the page insrc/views/<Name>.vue. - New store data → add a module under
src/store/<name>/with the six standard files (including the module'sindex.ts), register it insrc/store/index.ts, and updatesrc/store/types.tssoRootModules,RootState, andRootGetterspick it up. - A new translation key → edit
src/locales/en.yaml. Weblate handles the other languages — do not edit them directly. - A new icon → add an MDI mapping to the
Iconsobject insrc/globals.ts, then use it as<v-icon>{{ $globals.Icons.myIcon }}</v-icon>.
Going deeper
The repository's CLAUDE.md file is an exhaustive reference originally written
for AI coding assistants. It covers topics that are out of scope for this quick
orientation — the auth state machine and token-refresh policy, the full list of
build-toolchain gotchas, every Vuex module, and more. If you've outgrown this
overview, that's the next stop.
Dev Container in Visual Studio Code
Fluidd includes a Dev Container configuration to easily open with Visual Studio Code (VSCode) and have every tool and dependency installed.
Install Visual Studio Code
Follow the instruction from Visual Studio Code to install.
Make sure to also install the Dev Containers extension so that VSCode knows how to use the Dev Container configuration.
Install Docker
Follow the instruction from Docker to install.
Open the Dev Container
At this point all you need to do is open Fluidd folder in VSCode and you should see a popup indicating that it found a Dev Container configuration file; click the "Reopen in Container" to have everything configured.
The configuration includes a container running docker-klipper-simulavr, a virtualized Klipper and Moonraker image that makes it easy to debug without a real printer.
Running Fluidd locally
Install Node.js
Follow the instructions from Node.js to install Node.js, v24.x.
Check that Node.js was installed properly:
Install dependencies
Run a local development server
Browse to http://localhost:8080/ and type in the URL
of your Moonraker instance, e.g. http://192.168.0.101:7125.
Run unit tests
Running the documentation site locally
Install Python
Follow the instructions from Python to install Python 3.
Install Python dependencies
cd .../path/to/fluidd/docs
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
Serve locally
Browse to http://localhost:8000/.
Build static site
Localization
Fluidd uses vue-i18n for its localization.
Locales can be found in the src/locales folder and are in YAML format.
How to contribute
Translations are hosted on Weblate. If you want to help translating our project, please click the widget below:
Tooling
VSCode and i18n Ally
If you prefer, you can use VSCode and the i18n Ally extension to help translating offline.
If you're setup with VSCode, then this extension comes highly recommended.
Once you have a translation in hand, you can either PR the code changes directly or create an issue with the translations attached.