├── .node-version ├── docs ├── CNAME ├── .gitignore ├── favicon.ico ├── README.md ├── assets │ └── images │ │ ├── graph.png │ │ ├── layout.png │ │ ├── theme.png │ │ ├── bed_mesh.png │ │ ├── console.png │ │ ├── macros1.png │ │ ├── macros2.png │ │ ├── ogfluidd.png │ │ ├── presets.png │ │ ├── reprint.png │ │ ├── side_menu.png │ │ ├── updates.png │ │ ├── print_stats.png │ │ ├── thumbnails.png │ │ ├── adjust_layout.png │ │ ├── auth_trusted.png │ │ ├── exclude_object.png │ │ ├── fluidd_social.png │ │ ├── gcode_preview.png │ │ ├── gcode_settings.png │ │ ├── localization.png │ │ ├── notifications.png │ │ ├── preview_sliced.png │ │ ├── print_history.png │ │ ├── slicer-upload.png │ │ ├── camera_settings.png │ │ ├── macro_visibility.png │ │ ├── physical-printer.png │ │ ├── automated-updates.png │ │ ├── gcode_display_opts.png │ │ ├── multiple-extruders.png │ │ ├── printer-selection.png │ │ ├── spoolman-multitool.png │ │ ├── spoolman-scan-spool.png │ │ ├── diagnostics_edit_card.png │ │ ├── exclude_object_modal.png │ │ ├── auth_login_multisource.png │ │ ├── spoolman-dashboard-card.png │ │ ├── multiple-extruder-steppers.png │ │ ├── auth_login_multisource_select.png │ │ ├── diagnostics_collector_config.png │ │ ├── diagnostics_metrics_explorer.png │ │ ├── logo_ldo.svg │ │ └── logo.svg ├── _sass │ └── color_schemes │ │ └── fluidd.scss ├── installation │ ├── index.md │ └── kiauh.md ├── customize │ ├── index.md │ ├── layout.md │ └── hide_outputs.md ├── updates │ ├── index.md │ └── automated.md ├── features │ ├── index.md │ ├── presets.md │ ├── med_mesh.md │ ├── updates.md │ ├── notifications.md │ ├── multiple_printers.md │ ├── chart.md │ ├── sensors.md │ ├── localization.md │ ├── multiple_extruders.md │ └── slicer-uploads.md ├── configuration │ └── index.md ├── 404.html └── sponsors │ └── index.md ├── .env.development.local.example ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ └── config.yml ├── pull_request_template.md └── workflows │ └── semantic_pull_request.yml ├── public ├── robots.txt ├── favicon.ico ├── img │ ├── icons │ │ ├── favicon.ico │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ ├── apple-touch-icon.png │ │ ├── mstile-150x150.png │ │ ├── android-chrome-192x192.png │ │ ├── android-chrome-512x512.png │ │ ├── apple-touch-icon-60x60.png │ │ ├── apple-touch-icon-76x76.png │ │ ├── shortcut-settings-96x96.png │ │ ├── apple-touch-icon-120x120.png │ │ ├── apple-touch-icon-152x152.png │ │ ├── apple-touch-icon-180x180.png │ │ ├── msapplication-icon-144x144.png │ │ ├── shortcut-configuration-96x96.png │ │ ├── android-chrome-maskable-192x192.png │ │ ├── android-chrome-maskable-512x512.png │ │ └── safari-pinned-tab.svg │ └── mmu │ │ └── mmu_ERCF.svg ├── logo_micron.svg ├── logo_eva.svg ├── logo_voron.svg ├── logo_zerog.svg ├── logo_ldo.svg ├── logo_z-bolt.svg ├── logo_pfa.svg ├── logo_vzbot.svg ├── logo_salad_fork.svg ├── logo_hevort.svg ├── logo_ratrig.svg ├── logo_fluidd.svg ├── logo_peopoly.svg └── logo_siboor.svg ├── .env ├── src ├── typings │ ├── qrcode.vue.d.ts │ ├── vue-json-viewer.d.ts │ ├── echarts-gl.d.ts │ ├── moonraker.d.ts │ ├── moonraker.websocket.d.ts │ ├── vue-virtual-scroller.d.ts │ ├── moonraker.job_queue.d.ts │ ├── moonraker.data_store.d.ts │ ├── moonraker.announcements.d.ts │ ├── moonraker.power.d.ts │ ├── moonraker.sensor.d.ts │ ├── moonraker.database.d.ts │ ├── moonraker.analysis.d.ts │ ├── moonraker.klippy_apis.d.ts │ └── moonraker.webcam.d.ts ├── store │ ├── wait │ │ ├── types.ts │ │ ├── state.ts │ │ ├── index.ts │ │ ├── actions.ts │ │ ├── mutations.ts │ │ └── getters.ts │ ├── power │ │ ├── types.ts │ │ ├── state.ts │ │ ├── index.ts │ │ ├── getters.ts │ │ ├── mutations.ts │ │ └── actions.ts │ ├── analysis │ │ ├── types.ts │ │ ├── state.ts │ │ ├── getters.ts │ │ ├── mutations.ts │ │ ├── index.ts │ │ └── actions.ts │ ├── sensors │ │ ├── types.ts │ │ ├── state.ts │ │ ├── getters.ts │ │ ├── index.ts │ │ ├── mutations.ts │ │ └── actions.ts │ ├── announcements │ │ ├── types.ts │ │ ├── state.ts │ │ ├── getters.ts │ │ ├── index.ts │ │ └── mutations.ts │ ├── afc │ │ ├── types.ts │ │ ├── getters.ts │ │ ├── state.ts │ │ ├── actions.ts │ │ ├── mutations.ts │ │ └── index.ts │ ├── mmu │ │ ├── types.ts │ │ ├── getters.ts │ │ ├── state.ts │ │ ├── actions.ts │ │ ├── mutations.ts │ │ └── index.ts │ ├── database │ │ ├── types.ts │ │ ├── state.ts │ │ ├── getters.ts │ │ ├── index.ts │ │ ├── actions.ts │ │ └── mutations.ts │ ├── files │ │ ├── types.metadata.ts │ │ ├── state.ts │ │ └── index.ts │ ├── auth │ │ ├── getters.ts │ │ ├── state.ts │ │ ├── types.ts │ │ ├── index.ts │ │ └── mutations.ts │ ├── version │ │ ├── state.ts │ │ ├── index.ts │ │ ├── types.ts │ │ └── mutations.ts │ ├── webcams │ │ ├── state.ts │ │ ├── index.ts │ │ ├── getters.ts │ │ └── types.ts │ ├── jobQueue │ │ ├── state.ts │ │ ├── types.ts │ │ ├── index.ts │ │ ├── mutations.ts │ │ └── getters.ts │ ├── charts │ │ ├── state.ts │ │ ├── types.ts │ │ └── index.ts │ ├── macros │ │ ├── state.ts │ │ ├── index.ts │ │ └── types.ts │ ├── mesh │ │ ├── state.ts │ │ ├── index.ts │ │ ├── mutations.ts │ │ ├── types.ts │ │ └── actions.ts │ ├── spoolman │ │ ├── state.ts │ │ ├── index.ts │ │ └── mutations.ts │ ├── timelapse │ │ ├── types.ts │ │ ├── index.ts │ │ ├── getters.ts │ │ └── mutations.ts │ ├── socket │ │ ├── types.ts │ │ ├── index.ts │ │ ├── getters.ts │ │ ├── state.ts │ │ └── mutations.ts │ ├── gcodePreview │ │ ├── state.ts │ │ └── index.ts │ ├── history │ │ ├── state.ts │ │ ├── index.ts │ │ └── types.ts │ ├── config │ │ └── index.ts │ ├── layout │ │ ├── index.ts │ │ └── types.ts │ ├── server │ │ ├── index.ts │ │ ├── state.ts │ │ └── types.ts │ ├── console │ │ ├── index.ts │ │ ├── state.ts │ │ └── getters.ts │ ├── printer │ │ └── index.ts │ ├── notifications │ │ ├── index.ts │ │ ├── state.ts │ │ └── types.ts │ └── diagnostics │ │ └── types.ts ├── types │ ├── files.ts │ ├── cameras.ts │ ├── flashmessage.ts │ ├── mesh.ts │ ├── tableheaders.ts │ ├── dialogs.ts │ ├── index.ts │ └── mmu.ts ├── util │ ├── is-key-of.ts │ ├── is-null-or-empty.ts │ ├── link-external-urls.ts │ ├── is-set-app-badge-supported.ts │ ├── set-url-query-param.ts │ ├── get-vue-app.ts │ ├── is-user-agent-data-mobile-supported.ts │ ├── sanitize-endpoint.ts │ ├── __tests__ │ │ ├── string-formatters.spec.ts │ │ ├── sanitize-endpoint.spec.ts │ │ └── is-loopback.spec.ts │ ├── is-web-assembly-supported.ts │ ├── promise-any.ts │ ├── download-url.ts │ ├── is-loopback.ts │ ├── gcode-macro-params.ts │ ├── get-klipper-type.ts │ ├── sleep.ts │ ├── get-file-paths.ts │ ├── fluidd-content.ts │ ├── clipboard-copy.ts │ ├── file-data-transfer.ts │ └── web-socket-wrapper.ts ├── setupConsola.ts ├── registerComponentHooks.ts ├── scss │ ├── chips.scss │ ├── dialogs.scss │ ├── helpers.scss │ ├── typeography.scss │ ├── buttons.scss │ ├── tables.scss │ ├── global.scss │ ├── draggable.scss │ ├── inputs.scss │ ├── lists.scss │ └── animation.scss ├── directives │ └── blur.ts ├── locales │ └── sv.yaml ├── views │ ├── Console.vue │ ├── GcodePreview.vue │ ├── NotFound.vue │ ├── FullscreenCamera.vue │ ├── History.vue │ ├── Icons.vue │ └── Jobs.vue ├── eventBus.ts ├── components │ ├── widgets │ │ ├── sensors │ │ │ └── SensorsCard.vue │ │ ├── macros │ │ │ └── MacrosCard.vue │ │ ├── retract │ │ │ └── RetractCard.vue │ │ ├── outputs │ │ │ └── OutputsCard.vue │ │ ├── afc │ │ │ └── AfcCardBypass.vue │ │ ├── limits │ │ │ └── PrinterLimitsCard.vue │ │ ├── filesystem │ │ │ ├── setupMonaco.features.ts │ │ │ └── FileEditorTextOnly.vue │ │ ├── job-queue │ │ │ └── JobQueueBulkActions.vue │ │ ├── status │ │ │ └── StatusLabel.vue │ │ ├── gcode-preview │ │ │ └── GcodePreviewButton.vue │ │ ├── toolhead │ │ │ └── ExtruderSelection.vue │ │ └── camera │ │ │ └── services │ │ │ └── IframeCamera.vue │ ├── ui │ │ ├── AppBtnToggle.vue │ │ ├── AppDataTableCellTemps.vue │ │ ├── AppIcon.vue │ │ ├── AppQrCode.vue │ │ ├── AppChipColor.vue │ │ ├── AppDataTableCellColors.vue │ │ ├── AppNamedSwitch.vue │ │ ├── AppDragIcon.vue │ │ ├── AppBtnCollapse.vue │ │ └── AppInlineHelp.vue │ ├── layout │ │ ├── AppFooter.vue │ │ ├── AppObservedColumn.vue │ │ └── AppToolsDrawer.vue │ └── common │ │ └── SystemLayout.vue ├── monaco │ └── README.md ├── dynamicImports.ts ├── mixins │ └── browser.ts └── plugins │ └── vuetify.ts ├── server └── nginx │ ├── common_vars.conf.template │ └── default.conf.template ├── .vscode ├── settings.default.json ├── extensions.json ├── launch.json └── tasks.json ├── Dockerfile ├── svgo.config.mjs ├── tests └── unit │ ├── utils.ts │ └── setup.ts ├── .editorconfig ├── .devcontainer ├── docker-compose.yml ├── Dockerfile └── devcontainer.json ├── tshelpers.d.ts ├── tsconfig.vitest.json ├── vitest.config.ts ├── .husky ├── pre-commit └── commit-msg ├── tsconfig.json ├── tsconfig.config.json ├── .gitignore ├── .versionrc.json ├── env.d.ts ├── CONTRIBUTING.md └── eslint.config.mjs /.node-version: -------------------------------------------------------------------------------- 1 | 24.11.0 2 | -------------------------------------------------------------------------------- /docs/CNAME: -------------------------------------------------------------------------------- 1 | docs.fluidd.xyz -------------------------------------------------------------------------------- /.env.development.local.example: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [fluidd-core] 2 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: 3 | -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | VUE_APP_I18N_LOCALE=en 2 | VUE_APP_I18N_FALLBACK_LOCALE=en -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | _site 2 | .sass-cache 3 | .jekyll-metadata 4 | -------------------------------------------------------------------------------- /src/typings/qrcode.vue.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'qrcode.vue'; 2 | -------------------------------------------------------------------------------- /src/typings/vue-json-viewer.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'vue-json-viewer'; 2 | -------------------------------------------------------------------------------- /docs/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluidd-core/fluidd/HEAD/docs/favicon.ico -------------------------------------------------------------------------------- /src/store/wait/types.ts: -------------------------------------------------------------------------------- 1 | export interface WaitState { 2 | waits: string[]; 3 | } 4 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluidd-core/fluidd/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Notes 2 | 3 | https://socialify.git.ci/fluidd-core/fluidd 4 | Font = Raleway 5 | -------------------------------------------------------------------------------- /src/types/files.ts: -------------------------------------------------------------------------------- 1 | export type FileWithPath = { 2 | file: File, 3 | path: string 4 | } 5 | -------------------------------------------------------------------------------- /docs/assets/images/graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluidd-core/fluidd/HEAD/docs/assets/images/graph.png -------------------------------------------------------------------------------- /docs/assets/images/layout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluidd-core/fluidd/HEAD/docs/assets/images/layout.png -------------------------------------------------------------------------------- /docs/assets/images/theme.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluidd-core/fluidd/HEAD/docs/assets/images/theme.png -------------------------------------------------------------------------------- /public/img/icons/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluidd-core/fluidd/HEAD/public/img/icons/favicon.ico -------------------------------------------------------------------------------- /src/store/power/types.ts: -------------------------------------------------------------------------------- 1 | export interface DevicePowerState { 2 | devices: Moonraker.Power.Device[]; 3 | } 4 | -------------------------------------------------------------------------------- /docs/assets/images/bed_mesh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluidd-core/fluidd/HEAD/docs/assets/images/bed_mesh.png -------------------------------------------------------------------------------- /docs/assets/images/console.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluidd-core/fluidd/HEAD/docs/assets/images/console.png -------------------------------------------------------------------------------- /docs/assets/images/macros1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluidd-core/fluidd/HEAD/docs/assets/images/macros1.png -------------------------------------------------------------------------------- /docs/assets/images/macros2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluidd-core/fluidd/HEAD/docs/assets/images/macros2.png -------------------------------------------------------------------------------- /docs/assets/images/ogfluidd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluidd-core/fluidd/HEAD/docs/assets/images/ogfluidd.png -------------------------------------------------------------------------------- /docs/assets/images/presets.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluidd-core/fluidd/HEAD/docs/assets/images/presets.png -------------------------------------------------------------------------------- /docs/assets/images/reprint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluidd-core/fluidd/HEAD/docs/assets/images/reprint.png -------------------------------------------------------------------------------- /docs/assets/images/side_menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluidd-core/fluidd/HEAD/docs/assets/images/side_menu.png -------------------------------------------------------------------------------- /docs/assets/images/updates.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluidd-core/fluidd/HEAD/docs/assets/images/updates.png -------------------------------------------------------------------------------- /docs/assets/images/print_stats.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluidd-core/fluidd/HEAD/docs/assets/images/print_stats.png -------------------------------------------------------------------------------- /docs/assets/images/thumbnails.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluidd-core/fluidd/HEAD/docs/assets/images/thumbnails.png -------------------------------------------------------------------------------- /public/img/icons/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluidd-core/fluidd/HEAD/public/img/icons/favicon-16x16.png -------------------------------------------------------------------------------- /public/img/icons/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluidd-core/fluidd/HEAD/public/img/icons/favicon-32x32.png -------------------------------------------------------------------------------- /docs/assets/images/adjust_layout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluidd-core/fluidd/HEAD/docs/assets/images/adjust_layout.png -------------------------------------------------------------------------------- /docs/assets/images/auth_trusted.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluidd-core/fluidd/HEAD/docs/assets/images/auth_trusted.png -------------------------------------------------------------------------------- /docs/assets/images/exclude_object.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluidd-core/fluidd/HEAD/docs/assets/images/exclude_object.png -------------------------------------------------------------------------------- /docs/assets/images/fluidd_social.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluidd-core/fluidd/HEAD/docs/assets/images/fluidd_social.png -------------------------------------------------------------------------------- /docs/assets/images/gcode_preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluidd-core/fluidd/HEAD/docs/assets/images/gcode_preview.png -------------------------------------------------------------------------------- /docs/assets/images/gcode_settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluidd-core/fluidd/HEAD/docs/assets/images/gcode_settings.png -------------------------------------------------------------------------------- /docs/assets/images/localization.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluidd-core/fluidd/HEAD/docs/assets/images/localization.png -------------------------------------------------------------------------------- /docs/assets/images/notifications.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluidd-core/fluidd/HEAD/docs/assets/images/notifications.png -------------------------------------------------------------------------------- /docs/assets/images/preview_sliced.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluidd-core/fluidd/HEAD/docs/assets/images/preview_sliced.png -------------------------------------------------------------------------------- /docs/assets/images/print_history.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluidd-core/fluidd/HEAD/docs/assets/images/print_history.png -------------------------------------------------------------------------------- /docs/assets/images/slicer-upload.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluidd-core/fluidd/HEAD/docs/assets/images/slicer-upload.png -------------------------------------------------------------------------------- /public/img/icons/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluidd-core/fluidd/HEAD/public/img/icons/apple-touch-icon.png -------------------------------------------------------------------------------- /public/img/icons/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluidd-core/fluidd/HEAD/public/img/icons/mstile-150x150.png -------------------------------------------------------------------------------- /server/nginx/common_vars.conf.template: -------------------------------------------------------------------------------- 1 | map $http_upgrade $connection_upgrade { 2 | default upgrade; 3 | '' close; 4 | } 5 | -------------------------------------------------------------------------------- /src/store/analysis/types.ts: -------------------------------------------------------------------------------- 1 | export interface AnalysisState { 2 | status: Moonraker.Analysis.StatusResponse | null; 3 | } 4 | -------------------------------------------------------------------------------- /docs/assets/images/camera_settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluidd-core/fluidd/HEAD/docs/assets/images/camera_settings.png -------------------------------------------------------------------------------- /docs/assets/images/macro_visibility.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluidd-core/fluidd/HEAD/docs/assets/images/macro_visibility.png -------------------------------------------------------------------------------- /docs/assets/images/physical-printer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluidd-core/fluidd/HEAD/docs/assets/images/physical-printer.png -------------------------------------------------------------------------------- /src/store/sensors/types.ts: -------------------------------------------------------------------------------- 1 | export interface MoonrakerSensorsState { 2 | sensors: Record; 3 | } 4 | -------------------------------------------------------------------------------- /docs/assets/images/automated-updates.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluidd-core/fluidd/HEAD/docs/assets/images/automated-updates.png -------------------------------------------------------------------------------- /docs/assets/images/gcode_display_opts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluidd-core/fluidd/HEAD/docs/assets/images/gcode_display_opts.png -------------------------------------------------------------------------------- /docs/assets/images/multiple-extruders.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluidd-core/fluidd/HEAD/docs/assets/images/multiple-extruders.png -------------------------------------------------------------------------------- /docs/assets/images/printer-selection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluidd-core/fluidd/HEAD/docs/assets/images/printer-selection.png -------------------------------------------------------------------------------- /docs/assets/images/spoolman-multitool.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluidd-core/fluidd/HEAD/docs/assets/images/spoolman-multitool.png -------------------------------------------------------------------------------- /docs/assets/images/spoolman-scan-spool.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluidd-core/fluidd/HEAD/docs/assets/images/spoolman-scan-spool.png -------------------------------------------------------------------------------- /docs/assets/images/diagnostics_edit_card.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluidd-core/fluidd/HEAD/docs/assets/images/diagnostics_edit_card.png -------------------------------------------------------------------------------- /docs/assets/images/exclude_object_modal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluidd-core/fluidd/HEAD/docs/assets/images/exclude_object_modal.png -------------------------------------------------------------------------------- /public/img/icons/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluidd-core/fluidd/HEAD/public/img/icons/android-chrome-192x192.png -------------------------------------------------------------------------------- /public/img/icons/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluidd-core/fluidd/HEAD/public/img/icons/android-chrome-512x512.png -------------------------------------------------------------------------------- /public/img/icons/apple-touch-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluidd-core/fluidd/HEAD/public/img/icons/apple-touch-icon-60x60.png -------------------------------------------------------------------------------- /public/img/icons/apple-touch-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluidd-core/fluidd/HEAD/public/img/icons/apple-touch-icon-76x76.png -------------------------------------------------------------------------------- /public/img/icons/shortcut-settings-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluidd-core/fluidd/HEAD/public/img/icons/shortcut-settings-96x96.png -------------------------------------------------------------------------------- /src/typings/echarts-gl.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'echarts-gl' 2 | declare module 'echarts-gl/components' 3 | declare module 'echarts-gl/charts' 4 | -------------------------------------------------------------------------------- /docs/assets/images/auth_login_multisource.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluidd-core/fluidd/HEAD/docs/assets/images/auth_login_multisource.png -------------------------------------------------------------------------------- /docs/assets/images/spoolman-dashboard-card.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluidd-core/fluidd/HEAD/docs/assets/images/spoolman-dashboard-card.png -------------------------------------------------------------------------------- /public/img/icons/apple-touch-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluidd-core/fluidd/HEAD/public/img/icons/apple-touch-icon-120x120.png -------------------------------------------------------------------------------- /public/img/icons/apple-touch-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluidd-core/fluidd/HEAD/public/img/icons/apple-touch-icon-152x152.png -------------------------------------------------------------------------------- /public/img/icons/apple-touch-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluidd-core/fluidd/HEAD/public/img/icons/apple-touch-icon-180x180.png -------------------------------------------------------------------------------- /public/img/icons/msapplication-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluidd-core/fluidd/HEAD/public/img/icons/msapplication-icon-144x144.png -------------------------------------------------------------------------------- /docs/assets/images/multiple-extruder-steppers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluidd-core/fluidd/HEAD/docs/assets/images/multiple-extruder-steppers.png -------------------------------------------------------------------------------- /public/img/icons/shortcut-configuration-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluidd-core/fluidd/HEAD/public/img/icons/shortcut-configuration-96x96.png -------------------------------------------------------------------------------- /src/store/announcements/types.ts: -------------------------------------------------------------------------------- 1 | export interface AnnouncementsState { 2 | entries: Moonraker.Announcements.Entry[] 3 | feeds: string[] 4 | } 5 | -------------------------------------------------------------------------------- /src/typings/moonraker.d.ts: -------------------------------------------------------------------------------- 1 | declare namespace Moonraker { 2 | export type OkResponse = 'ok' 3 | 4 | export type StringResponse = string 5 | } 6 | -------------------------------------------------------------------------------- /docs/assets/images/auth_login_multisource_select.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluidd-core/fluidd/HEAD/docs/assets/images/auth_login_multisource_select.png -------------------------------------------------------------------------------- /docs/assets/images/diagnostics_collector_config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluidd-core/fluidd/HEAD/docs/assets/images/diagnostics_collector_config.png -------------------------------------------------------------------------------- /docs/assets/images/diagnostics_metrics_explorer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluidd-core/fluidd/HEAD/docs/assets/images/diagnostics_metrics_explorer.png -------------------------------------------------------------------------------- /public/img/icons/android-chrome-maskable-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluidd-core/fluidd/HEAD/public/img/icons/android-chrome-maskable-192x192.png -------------------------------------------------------------------------------- /public/img/icons/android-chrome-maskable-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluidd-core/fluidd/HEAD/public/img/icons/android-chrome-maskable-512x512.png -------------------------------------------------------------------------------- /src/typings/moonraker.websocket.d.ts: -------------------------------------------------------------------------------- 1 | declare namespace Moonraker.Websocket { 2 | export interface ConnectionIdentifyResponse { 3 | connection_id: number; 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /src/util/is-key-of.ts: -------------------------------------------------------------------------------- 1 | const isKeyOf = (key: PropertyKey, parent: T): key is keyof T => { 2 | return key in parent 3 | } 4 | 5 | export default isKeyOf 6 | -------------------------------------------------------------------------------- /.vscode/settings.default.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.codeActionsOnSave": { 3 | "source.fixAll.eslint": true 4 | }, 5 | "i18n-ally.localesPaths": [ 6 | "src/locales" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /src/store/afc/types.ts: -------------------------------------------------------------------------------- 1 | export interface AfcState { 2 | dialog: AfcDialogState; 3 | } 4 | 5 | export interface AfcDialogState { 6 | show: boolean; 7 | filename?: string; 8 | } 9 | -------------------------------------------------------------------------------- /src/store/mmu/types.ts: -------------------------------------------------------------------------------- 1 | export interface MmuState { 2 | dialog: MmuDialogState; 3 | } 4 | 5 | export interface MmuDialogState { 6 | show: boolean; 7 | filename?: string; 8 | } 9 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Fluidd Discord 4 | url: https://discord.gg/GZ3D5tqfcF/ 5 | about: Quickest way to get in contact 6 | -------------------------------------------------------------------------------- /src/store/database/types.ts: -------------------------------------------------------------------------------- 1 | export interface DatabaseState { 2 | info: DatabaseInfo | null; 3 | } 4 | 5 | export interface DatabaseInfo { 6 | namespaces: string[]; 7 | backups: string[]; 8 | } 9 | -------------------------------------------------------------------------------- /src/setupConsola.ts: -------------------------------------------------------------------------------- 1 | import { consola, LogLevels } from 'consola' 2 | 3 | // Configure Consola 4 | consola.wrapAll() 5 | consola.level = LogLevels.warn 6 | 7 | if (import.meta.env.DEV) consola.level = LogLevels.verbose 8 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | ARG BASE_IMAGE=nginx:alpine 2 | ARG PORT=80 3 | 4 | FROM $BASE_IMAGE 5 | 6 | ARG PORT 7 | 8 | ENV PORT=$PORT 9 | 10 | COPY /dist /usr/share/nginx/html 11 | COPY /server/nginx /etc/nginx/templates 12 | -------------------------------------------------------------------------------- /src/store/wait/state.ts: -------------------------------------------------------------------------------- 1 | import type { WaitState } from './types' 2 | 3 | export const defaultState = (): WaitState => { 4 | return { 5 | waits: [] 6 | } 7 | } 8 | 9 | export const state = defaultState() 10 | -------------------------------------------------------------------------------- /src/util/is-null-or-empty.ts: -------------------------------------------------------------------------------- 1 | export type NullableOrEmpty = T | null | undefined | '' 2 | 3 | const isNullOrEmpty = (v: unknown): v is null | undefined | '' => v == null || v === '' 4 | 5 | export default isNullOrEmpty 6 | -------------------------------------------------------------------------------- /svgo.config.mjs: -------------------------------------------------------------------------------- 1 | export default { 2 | js2svg: { 3 | indent: 2, 4 | pretty: true, 5 | }, 6 | multipass: true, 7 | plugins: [ 8 | { 9 | name: 'preset-default', 10 | }, 11 | ], 12 | } 13 | -------------------------------------------------------------------------------- /src/types/cameras.ts: -------------------------------------------------------------------------------- 1 | export type CameraConnectionStatus = 'connecting' | 'connected' | 'disconnected' | 'error' 2 | 3 | export interface CameraNameMenuItem { 4 | icon: string; 5 | text: string; 6 | value: string; 7 | } 8 | -------------------------------------------------------------------------------- /src/store/files/types.metadata.ts: -------------------------------------------------------------------------------- 1 | export interface AppFileMeta extends Omit { 2 | modified: number; 3 | filament_name?: string[]; 4 | filament_type?: string[]; 5 | } 6 | -------------------------------------------------------------------------------- /src/store/afc/getters.ts: -------------------------------------------------------------------------------- 1 | import type { GetterTree } from 'vuex' 2 | import type { AfcState } from './types' 3 | import type { RootState } from '../types' 4 | 5 | export const getters = { 6 | } satisfies GetterTree 7 | -------------------------------------------------------------------------------- /src/store/analysis/state.ts: -------------------------------------------------------------------------------- 1 | import type { AnalysisState } from './types' 2 | 3 | export const defaultState = (): AnalysisState => { 4 | return { 5 | status: null 6 | } 7 | } 8 | 9 | export const state = defaultState() 10 | -------------------------------------------------------------------------------- /src/store/auth/getters.ts: -------------------------------------------------------------------------------- 1 | import type { GetterTree } from 'vuex' 2 | import type { AuthState } from './types' 3 | import type { RootState } from '../types' 4 | 5 | export const getters = { 6 | } satisfies GetterTree 7 | -------------------------------------------------------------------------------- /src/store/database/state.ts: -------------------------------------------------------------------------------- 1 | import type { DatabaseState } from './types' 2 | 3 | export const defaultState = (): DatabaseState => { 4 | return { 5 | info: null 6 | } 7 | } 8 | 9 | export const state = defaultState() 10 | -------------------------------------------------------------------------------- /src/store/mmu/getters.ts: -------------------------------------------------------------------------------- 1 | import type { GetterTree } from 'vuex' 2 | import type { MmuState } from './types' 3 | import type { RootState } from '../types' 4 | 5 | export const getters = { 6 | } satisfies GetterTree 7 | -------------------------------------------------------------------------------- /src/store/power/state.ts: -------------------------------------------------------------------------------- 1 | import type { DevicePowerState } from './types' 2 | 3 | export const defaultState = (): DevicePowerState => { 4 | return { 5 | devices: [] 6 | } 7 | } 8 | 9 | export const state = defaultState() 10 | -------------------------------------------------------------------------------- /tests/unit/utils.ts: -------------------------------------------------------------------------------- 1 | import MockDate from 'mockdate' 2 | 3 | export const timeTravel = (to: string, cb: () => any): any => { 4 | MockDate.set(to) 5 | const retVal = cb() 6 | MockDate.reset() 7 | 8 | return retVal 9 | } 10 | -------------------------------------------------------------------------------- /src/util/link-external-urls.ts: -------------------------------------------------------------------------------- 1 | const externalUrlRegExp = /(https?:\/\/\S+)/gi 2 | 3 | const linkExternalUrls = (text: string) => text.replace(externalUrlRegExp, '$1') 4 | 5 | export default linkExternalUrls 6 | -------------------------------------------------------------------------------- /docs/_sass/color_schemes/fluidd.scss: -------------------------------------------------------------------------------- 1 | $brand-color: #2196F3; 2 | 3 | $link-color: $brand-color; 4 | $btn-primary-color: $brand-color; 5 | 6 | .site-logo { 7 | margin-top: 6px; 8 | width: 80% !important; 9 | height: 80% !important; 10 | } 11 | -------------------------------------------------------------------------------- /src/store/afc/state.ts: -------------------------------------------------------------------------------- 1 | import type { AfcState } from './types' 2 | 3 | export const defaultState = (): AfcState => { 4 | return { 5 | dialog: { 6 | show: false 7 | } 8 | } 9 | } 10 | 11 | export const state = defaultState() 12 | -------------------------------------------------------------------------------- /src/store/analysis/getters.ts: -------------------------------------------------------------------------------- 1 | import type { GetterTree } from 'vuex' 2 | import type { AnalysisState } from './types' 3 | import type { RootState } from '../types' 4 | 5 | export const getters = { 6 | } satisfies GetterTree 7 | -------------------------------------------------------------------------------- /src/store/mmu/state.ts: -------------------------------------------------------------------------------- 1 | import type { MmuState } from './types' 2 | 3 | export const defaultState = (): MmuState => { 4 | return { 5 | dialog: { 6 | show: false 7 | } 8 | } 9 | } 10 | 11 | export const state = defaultState() 12 | -------------------------------------------------------------------------------- /src/store/sensors/state.ts: -------------------------------------------------------------------------------- 1 | import type { MoonrakerSensorsState } from './types' 2 | 3 | export const defaultState = (): MoonrakerSensorsState => { 4 | return { 5 | sensors: {} 6 | } 7 | } 8 | 9 | export const state = defaultState() 10 | -------------------------------------------------------------------------------- /src/registerComponentHooks.ts: -------------------------------------------------------------------------------- 1 | // registerComponentHooks.ts 2 | // import { Component } from 'vue-property-decorator' 3 | import Component from 'vue-class-component' 4 | 5 | Component.registerHooks([ 6 | 'beforeRouteEnter', 7 | 'beforeRouteUpdate' 8 | ]) 9 | -------------------------------------------------------------------------------- /src/store/version/state.ts: -------------------------------------------------------------------------------- 1 | import type { VersionState } from './types' 2 | 3 | export const defaultState = (): VersionState => { 4 | return { 5 | status: null, 6 | responses: [], 7 | } 8 | } 9 | 10 | export const state = defaultState() 11 | -------------------------------------------------------------------------------- /src/store/webcams/state.ts: -------------------------------------------------------------------------------- 1 | import type { WebcamsState } from './types' 2 | 3 | export const defaultState = (): WebcamsState => { 4 | return { 5 | webcams: [], 6 | activeWebcam: 'all' 7 | } 8 | } 9 | 10 | export const state = defaultState() 11 | -------------------------------------------------------------------------------- /src/types/flashmessage.ts: -------------------------------------------------------------------------------- 1 | export interface FlashMessage { 2 | type?: FlashMessageTypes; 3 | open: boolean; 4 | text?: string; 5 | timeout?: number; 6 | } 7 | 8 | export type FlashMessageTypes = 'success' | 'error' | 'warning' | 'primary' | 'secondary' 9 | -------------------------------------------------------------------------------- /src/store/announcements/state.ts: -------------------------------------------------------------------------------- 1 | import type { AnnouncementsState } from './types' 2 | 3 | export const defaultState = (): AnnouncementsState => { 4 | return { 5 | entries: [], 6 | feeds: [] 7 | } 8 | } 9 | 10 | export const state = defaultState() 11 | -------------------------------------------------------------------------------- /src/store/jobQueue/state.ts: -------------------------------------------------------------------------------- 1 | import type { JobQueueState } from './types' 2 | 3 | export const defaultState = (): JobQueueState => { 4 | return { 5 | queueState: 'paused', 6 | queuedJobs: [] 7 | } 8 | } 9 | 10 | export const state = defaultState() 11 | -------------------------------------------------------------------------------- /tests/unit/setup.ts: -------------------------------------------------------------------------------- 1 | import { vi } from 'vitest' 2 | 3 | vi.mock('vue', async () => { 4 | const Vue = await vi.importActual('vue') as any 5 | 6 | Vue.default.config.productionTip = false 7 | Vue.default.config.devtools = false 8 | 9 | return Vue 10 | }) 11 | -------------------------------------------------------------------------------- /docs/installation/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Installation 4 | nav_order: 2 5 | has_children: true 6 | permalink: /installation 7 | --- 8 | 9 | # Installation 10 | 11 | The recommended way to install Fluidd is to use KIAUH. 12 | {: .fs-6 .fw-300 } 13 | -------------------------------------------------------------------------------- /src/scss/chips.scss: -------------------------------------------------------------------------------- 1 | .chip-group { 2 | .v-chip { 3 | margin: 2px 2px; 4 | } 5 | .v-chip:first-child { 6 | margin: 0 2px 0 0; 7 | } 8 | .v-chip:last-child { 9 | margin: 0 0 0 2px; 10 | } 11 | .v-chip:only-child { 12 | margin: 0; 13 | } 14 | } -------------------------------------------------------------------------------- /src/store/charts/state.ts: -------------------------------------------------------------------------------- 1 | import type { ChartState } from './types' 2 | 3 | export const defaultState = (): ChartState => { 4 | return { 5 | ready: false, 6 | chart: [], 7 | selectedLegends: {} 8 | } 9 | } 10 | 11 | export const state = defaultState() 12 | -------------------------------------------------------------------------------- /src/store/macros/state.ts: -------------------------------------------------------------------------------- 1 | import type { MacrosState } from './types' 2 | 3 | export const defaultState = (): MacrosState => { 4 | return { 5 | stored: [], 6 | categories: [], 7 | expanded: [0] 8 | } 9 | } 10 | 11 | export const state = defaultState() 12 | -------------------------------------------------------------------------------- /public/logo_micron.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /docs/customize/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Customize 4 | nav_order: 3 5 | has_children: true 6 | permalink: /customize 7 | --- 8 | 9 | # Customize 10 | 11 | Fluidd allows you to adjust the layout of your dashboard, and to set a core 12 | theme color. 13 | {: .fs-6 .fw-300 } 14 | -------------------------------------------------------------------------------- /docs/updates/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Updates 4 | nav_order: 6 5 | has_children: true 6 | permalink: /updates 7 | --- 8 | 9 | # Updates 10 | 11 | Updates can be configured and managed all with Fluidd, making updating klipper, 12 | Moonraker and Fluidd a breeze. 13 | {: .fs-6 .fw-300 } 14 | -------------------------------------------------------------------------------- /public/logo_eva.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/directives/blur.ts: -------------------------------------------------------------------------------- 1 | import type { FunctionDirective } from 'vue' 2 | 3 | const blur: FunctionDirective = (element) => { 4 | element.onfocus = (event) => { 5 | if (event.target instanceof HTMLElement) { 6 | event.target.blur() 7 | } 8 | } 9 | } 10 | 11 | export default blur 12 | -------------------------------------------------------------------------------- /src/util/is-set-app-badge-supported.ts: -------------------------------------------------------------------------------- 1 | type NavigatorWithSetAppBadge = Navigator & { 2 | setAppBadge: (count?: number) => Promise 3 | } 4 | 5 | const isSetAppBadgeSupported = (navigator: Navigator): navigator is NavigatorWithSetAppBadge => 'setAppBadge' in navigator 6 | 7 | export default isSetAppBadgeSupported 8 | -------------------------------------------------------------------------------- /public/logo_voron.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /docs/features/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Features 4 | nav_order: 7 5 | has_children: true 6 | permalink: /features 7 | --- 8 | 9 | # Features 10 | 11 | Fluidd bundles many features you might not be aware of. Check them out here, 12 | along with any configuration they might require. 13 | {: .fs-6 .fw-300 } 14 | -------------------------------------------------------------------------------- /src/store/auth/state.ts: -------------------------------------------------------------------------------- 1 | import type { AuthState } from './types' 2 | 3 | export const defaultState = (): AuthState => { 4 | return { 5 | authenticated: true, 6 | token: null, 7 | refresh_token: null, 8 | currentUser: null, 9 | users: [], 10 | apiKey: '' 11 | } 12 | } 13 | 14 | export const state = defaultState() 15 | -------------------------------------------------------------------------------- /src/store/files/state.ts: -------------------------------------------------------------------------------- 1 | import type { FilesState } from './types' 2 | 3 | export const defaultState = (): FilesState => { 4 | return { 5 | uploads: [], 6 | download: null, 7 | currentPaths: {}, 8 | diskUsage: {}, 9 | rootFiles: {}, 10 | pathContent: {} 11 | } 12 | } 13 | 14 | export const state = defaultState() 15 | -------------------------------------------------------------------------------- /src/store/mesh/state.ts: -------------------------------------------------------------------------------- 1 | import type { MeshState } from './types' 2 | 3 | export const defaultState = (): MeshState => { 4 | return { 5 | range: 0, 6 | wireframe: false, 7 | scale: 0.2, 8 | boxScale: 2.0, 9 | flatSurface: false, 10 | matrix: 'mesh_matrix' 11 | } 12 | } 13 | 14 | export const state = defaultState() 15 | -------------------------------------------------------------------------------- /src/store/sensors/getters.ts: -------------------------------------------------------------------------------- 1 | import type { GetterTree } from 'vuex' 2 | import type { MoonrakerSensorsState } from './types' 3 | import type { RootState } from '../types' 4 | 5 | export const getters = { 6 | getSensors: (state) => { 7 | return Object.values(state.sensors) 8 | } 9 | } satisfies GetterTree 10 | -------------------------------------------------------------------------------- /src/store/afc/actions.ts: -------------------------------------------------------------------------------- 1 | import type { ActionTree } from 'vuex' 2 | import type { RootState } from '../types' 3 | import type { AfcState } from './types' 4 | 5 | export const actions = { 6 | /** 7 | * Reset our store 8 | */ 9 | async reset ({ commit }) { 10 | commit('setReset') 11 | } 12 | } satisfies ActionTree 13 | -------------------------------------------------------------------------------- /src/store/mmu/actions.ts: -------------------------------------------------------------------------------- 1 | import type { ActionTree } from 'vuex' 2 | import type { RootState } from '../types' 3 | import type { MmuState } from './types' 4 | 5 | export const actions = { 6 | /** 7 | * Reset our store 8 | */ 9 | async reset ({ commit }) { 10 | commit('setReset') 11 | } 12 | } satisfies ActionTree 13 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_size = 2 7 | indent_style = space 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.{js,jsx,ts,tsx,vue}] 12 | indent_style = space 13 | max_line_length = 100 14 | 15 | [*.{md,markdown}] 16 | max_line_length = 0 17 | trim_trailing_whitespace = false 18 | -------------------------------------------------------------------------------- /.devcontainer/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.8' 2 | 3 | services: 4 | fluidd: 5 | build: 6 | context: ./ 7 | volumes: 8 | - ..:/workspaces/fluidd/:cached 9 | command: sleep infinity 10 | network_mode: service:klipper-simulavr 11 | 12 | klipper-simulavr: 13 | image: ei99070/docker-klipper-simulavr:latest 14 | restart: unless-stopped 15 | -------------------------------------------------------------------------------- /src/store/jobQueue/types.ts: -------------------------------------------------------------------------------- 1 | import type { AppFile, AppFileWithMeta } from '@/store/files/types' 2 | 3 | export interface JobQueueState { 4 | queueState: Moonraker.JobQueue.QueueState; 5 | queuedJobs: Moonraker.JobQueue.QueuedJob[]; 6 | } 7 | 8 | export interface QueuedJobWithAppFile extends Moonraker.JobQueue.QueuedJob { 9 | file?: AppFile | AppFileWithMeta; 10 | } 11 | -------------------------------------------------------------------------------- /src/store/spoolman/state.ts: -------------------------------------------------------------------------------- 1 | import type { SpoolmanState } from './types' 2 | 3 | export const defaultState = (): SpoolmanState => { 4 | return { 5 | info: null, 6 | spools: [], 7 | activeSpool: null, 8 | currency: null, 9 | connected: false, 10 | dialog: { 11 | show: false 12 | } 13 | } 14 | } 15 | 16 | export const state = defaultState() 17 | -------------------------------------------------------------------------------- /src/store/timelapse/types.ts: -------------------------------------------------------------------------------- 1 | export interface TimelapseState { 2 | settings: Moonraker.Timelapse.SettingsResponse | null; 3 | lastFrame: Moonraker.Timelapse.LastFrameInfoResponse | null; 4 | renderStatus: Moonraker.Timelapse.RenderResponse | null; 5 | } 6 | 7 | export interface TimelapseLastFrame { 8 | count: number; 9 | uniqueCount: number; 10 | file: string; 11 | } 12 | -------------------------------------------------------------------------------- /src/typings/vue-virtual-scroller.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'vue-virtual-scroller' { 2 | import Vue, { PluginObject } from 'vue' 3 | 4 | declare const VueVirtualScroller: PluginObject 5 | 6 | export interface DinamicScroller extends Vue { 7 | scrollToItem: (index: number) => void, 8 | scrollToBottom: () => void 9 | } 10 | 11 | export default VueVirtualScroller 12 | } 13 | -------------------------------------------------------------------------------- /src/store/socket/types.ts: -------------------------------------------------------------------------------- 1 | export interface SocketState { 2 | apiConnected: boolean; 3 | open: boolean; 4 | connecting: boolean; 5 | disconnecting: boolean; 6 | ready: boolean; 7 | acceptingNotifications: boolean; 8 | error: SocketError | null; 9 | connectionId: string | null; 10 | } 11 | 12 | export interface SocketError { 13 | code: number; 14 | message: string; 15 | } 16 | -------------------------------------------------------------------------------- /tshelpers.d.ts: -------------------------------------------------------------------------------- 1 | declare namespace TSHelpers { 2 | type Prettify = { 3 | [K in keyof T]: T[K]; 4 | } & {} 5 | 6 | type DeepReadonly = 7 | T extends (...args: any[]) => any 8 | ? T 9 | : T extends [...any] | object 10 | ? { readonly [K in keyof T]: DeepReadonly } 11 | : T 12 | 13 | type ValueTypesOf = NonNullable 14 | } 15 | -------------------------------------------------------------------------------- /docs/features/presets.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Thermal Presets 4 | parent: Features 5 | nav_order: 11 6 | permalink: /features/presets 7 | --- 8 | 9 | # Thermal Presets 10 | {: .no_toc } 11 | 12 | --- 13 | 14 | Fluidd supports setting your own thermal presets. Click through to the UI 15 | settings page to add your own presets. 16 | 17 | ![screenshot](/assets/images/presets.png) 18 | -------------------------------------------------------------------------------- /src/store/announcements/getters.ts: -------------------------------------------------------------------------------- 1 | import type { GetterTree } from 'vuex' 2 | import type { AnnouncementsState } from './types' 3 | import type { RootState } from '../types' 4 | 5 | export const getters = { 6 | getAnnouncements: (state) => { 7 | return state.entries 8 | .filter(announcement => !announcement.dismissed) 9 | } 10 | } satisfies GetterTree 11 | -------------------------------------------------------------------------------- /src/store/gcodePreview/state.ts: -------------------------------------------------------------------------------- 1 | import type { GcodePreviewState } from './types' 2 | 3 | export const defaultState = (): GcodePreviewState => { 4 | return { 5 | moves: [], 6 | layers: [], 7 | parts: [], 8 | tools: [], 9 | bounds: null, 10 | file: null, 11 | parserProgress: 0, 12 | parserWorker: null 13 | } 14 | } 15 | 16 | export const state = defaultState() 17 | -------------------------------------------------------------------------------- /src/util/set-url-query-param.ts: -------------------------------------------------------------------------------- 1 | const setUrlQueryParam = (url: string, key: string, value: string) => { 2 | const fakeOrigin = 'http://fake.fake' 3 | const newUrl = new URL(url, fakeOrigin) 4 | 5 | newUrl.searchParams.set(key, value) 6 | 7 | return newUrl.origin === fakeOrigin 8 | ? newUrl.pathname + newUrl.search 9 | : newUrl.href 10 | } 11 | 12 | export default setUrlQueryParam 13 | -------------------------------------------------------------------------------- /tsconfig.vitest.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.app.json", 3 | "include": [ 4 | "tests/**/*", 5 | "src/**/__tests__/*", 6 | "env.d.ts", 7 | ], 8 | "exclude": [], 9 | "compilerOptions": { 10 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.vitest.tsbuildinfo", 11 | 12 | "lib": [], 13 | "types": [ 14 | "node", 15 | "jsdom" 16 | ] 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/scss/dialogs.scss: -------------------------------------------------------------------------------- 1 | // .v-dialog--scrollable > .v-card > .v-card__text { 2 | // &::-webkit-scrollbar { 3 | // transition: all .5s; 4 | // width: 5px; 5 | // height: 5px; 6 | // z-index: 10; 7 | // } 8 | 9 | // &::-webkit-scrollbar-track { 10 | // background: transparent; 11 | // } 12 | 13 | // &::-webkit-scrollbar-thumb { 14 | // background: #b3ada7; 15 | // } 16 | // } -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "davidanson.vscode-markdownlint", 4 | "dbaeumer.vscode-eslint", 5 | "eamodio.gitlens", 6 | "editorconfig.editorconfig", 7 | "esbenp.prettier-vscode", 8 | "lukas-tr.materialdesignicons-intellisense", 9 | "ms-azuretools.vscode-docker", 10 | "orta.vscode-twoslash-queries", 11 | "redhat.vscode-yaml", 12 | "vue.volar" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /src/store/database/getters.ts: -------------------------------------------------------------------------------- 1 | import type { GetterTree } from 'vuex' 2 | import type { DatabaseState } from './types' 3 | import type { RootState } from '../types' 4 | 5 | export const getters = { 6 | getBackups: (state): string[] => { 7 | const backups = [...state.info?.backups ?? []] 8 | 9 | return backups 10 | .sort((a, b) => a.localeCompare(b)) 11 | } 12 | } satisfies GetterTree 13 | -------------------------------------------------------------------------------- /src/types/mesh.ts: -------------------------------------------------------------------------------- 1 | export interface MeshData { 2 | x: number[]; 3 | y: number[]; 4 | z: number[][]; 5 | type: string; 6 | intensity: number[]; 7 | cmin: number; 8 | cmax: number; 9 | showscale: boolean; 10 | autocolorscale?: boolean; 11 | colorscale?: string | Array<(number | string)[]>; 12 | colorbar: MeshColorBar; 13 | } 14 | 15 | export interface MeshColorBar { 16 | tickfont: { [key: string]: any }; 17 | } 18 | -------------------------------------------------------------------------------- /src/typings/moonraker.job_queue.d.ts: -------------------------------------------------------------------------------- 1 | declare namespace Moonraker.JobQueue { 2 | export interface StatusResponse { 3 | queued_jobs: QueuedJob[]; 4 | queue_state: QueueState; 5 | } 6 | 7 | export interface QueuedJob { 8 | filename: string; 9 | job_id: string; 10 | time_added: number; 11 | time_in_queue: number; 12 | } 13 | 14 | export type QueueState = 'ready' | 'loading' | 'starting' | 'paused' 15 | } 16 | -------------------------------------------------------------------------------- /vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig, mergeConfig } from 'vitest/config' 2 | import viteConfig from './vite.config' 3 | 4 | export default mergeConfig(viteConfig, defineConfig({ 5 | test: { 6 | globals: true, 7 | environment: 'jsdom', 8 | setupFiles: [ 9 | './tests/unit/setup.ts' 10 | ], 11 | alias: [ 12 | { find: /^vue$/, replacement: 'vue/dist/vue.runtime.common.js' } 13 | ] 14 | } 15 | })) 16 | -------------------------------------------------------------------------------- /src/scss/helpers.scss: -------------------------------------------------------------------------------- 1 | .rotate-90 { 2 | -webkit-transform: rotate(90deg); 3 | -moz-transform: rotate(90deg); 4 | -ms-transform: rotate(90deg); 5 | -o-transform: rotate(90deg); 6 | transform: rotate(90deg); 7 | } 8 | 9 | .rotate-180 { 10 | -webkit-transform: rotate(180deg); 11 | -moz-transform: rotate(180deg); 12 | -ms-transform: rotate(180deg); 13 | -o-transform: rotate(180deg); 14 | transform: rotate(180deg); 15 | } 16 | -------------------------------------------------------------------------------- /src/store/auth/types.ts: -------------------------------------------------------------------------------- 1 | import type { JwtPayload } from 'jwt-decode' 2 | 3 | export interface AuthState { 4 | authenticated: boolean; 5 | token: JwtPayload | null; 6 | refresh_token: JwtPayload | null; 7 | currentUser: AppUser | null; 8 | users: AppUser[]; 9 | apiKey: string; 10 | } 11 | 12 | export interface AppUser { 13 | username: string; 14 | password?: string; 15 | source: string; 16 | created_on?: number; 17 | } 18 | -------------------------------------------------------------------------------- /src/locales/sv.yaml: -------------------------------------------------------------------------------- 1 | app: 2 | bedmesh: 3 | label: 4 | mesh_matrix: Mesh matrix 5 | base: bas 6 | flat_surface: Visa platt plan 7 | profile_name: Profilnamn 8 | scale: Färgskala 9 | wireframe: Trådram 10 | active: aktiv 11 | remove_profile: Ta bort %{name} profil 12 | msg: 13 | hint: Om det sparas som något annat än %{name}, kan du välja att också ta bort 14 | %{name} profilen 15 | -------------------------------------------------------------------------------- /src/store/history/state.ts: -------------------------------------------------------------------------------- 1 | import type { HistoryState } from './types' 2 | 3 | export const defaultState = (): HistoryState => { 4 | return { 5 | count: 0, 6 | jobs: [], 7 | job_totals: { 8 | total_jobs: 0, 9 | total_time: 0, 10 | total_print_time: 0, 11 | total_filament_used: 0, 12 | longest_job: 0, 13 | longest_print: 0 14 | } 15 | } 16 | } 17 | 18 | export const state = defaultState() 19 | -------------------------------------------------------------------------------- /src/store/charts/types.ts: -------------------------------------------------------------------------------- 1 | export interface ChartState { 2 | [index: string]: any; 3 | ready: boolean; // chart is ready, and we've process the initial store data. 4 | chart: Readonly[]; // chart data 5 | selectedLegends: ChartSelectedLegends; 6 | } 7 | 8 | export interface ChartData { 9 | [key: string]: number | Date; 10 | date: Date; 11 | } 12 | 13 | export interface ChartSelectedLegends { 14 | [key: string]: boolean; 15 | } 16 | -------------------------------------------------------------------------------- /src/types/tableheaders.ts: -------------------------------------------------------------------------------- 1 | import type { ConfiguredTableHeader } from '@/store/config/types' 2 | import type { DataTableHeader } from 'vuetify' 3 | 4 | /** 5 | * Vuetify table headers, extended with data so we can more easily save 6 | * and reference later. 7 | * configurable: if a user can toggle this header or not. Defaults to true if not defined. 8 | */ 9 | 10 | export interface AppDataTableHeader extends DataTableHeader, ConfiguredTableHeader { 11 | } 12 | -------------------------------------------------------------------------------- /src/store/afc/mutations.ts: -------------------------------------------------------------------------------- 1 | import type { MutationTree } from 'vuex' 2 | import type { AfcDialogState, AfcState } from './types' 3 | import { defaultState } from './state' 4 | 5 | export const mutations = { 6 | /** 7 | * Reset state 8 | */ 9 | setReset (state) { 10 | Object.assign(state, defaultState()) 11 | }, 12 | 13 | setDialogState (state, payload: AfcDialogState) { 14 | state.dialog = payload 15 | }, 16 | } satisfies MutationTree 17 | -------------------------------------------------------------------------------- /src/store/mmu/mutations.ts: -------------------------------------------------------------------------------- 1 | import type { MutationTree } from 'vuex' 2 | import type { MmuDialogState, MmuState } from './types' 3 | import { defaultState } from './state' 4 | 5 | export const mutations = { 6 | /** 7 | * Reset state 8 | */ 9 | setReset (state) { 10 | Object.assign(state, defaultState()) 11 | }, 12 | 13 | setDialogState (state, payload: MmuDialogState) { 14 | state.dialog = payload 15 | }, 16 | } satisfies MutationTree 17 | -------------------------------------------------------------------------------- /src/scss/typeography.scss: -------------------------------------------------------------------------------- 1 | 2 | // Focused text 3 | .focus--text { 4 | font-size: 1.125rem; 5 | font-weight: 300 !important; 6 | letter-spacing: 0.0125em !important; 7 | line-height: auto; 8 | } 9 | 10 | .theme--light .dim--text { 11 | color: rgba(map-get($material-light, 'text-color'), 0.45); 12 | } 13 | 14 | .theme--dark .dim--text { 15 | color: rgba(map-get($material-dark, 'text-color'), 0.45); 16 | } 17 | 18 | // .disabled--text { 19 | // opacity: 0.45; 20 | // } 21 | -------------------------------------------------------------------------------- /src/store/analysis/mutations.ts: -------------------------------------------------------------------------------- 1 | import type { MutationTree } from 'vuex' 2 | import type { AnalysisState } from './types' 3 | import { defaultState } from './state' 4 | 5 | export const mutations = { 6 | /** 7 | * Reset state 8 | */ 9 | setReset (state) { 10 | Object.assign(state, defaultState()) 11 | }, 12 | 13 | setAnalysisStatus (state, payload: Moonraker.Analysis.StatusResponse) { 14 | state.status = payload 15 | } 16 | } satisfies MutationTree 17 | -------------------------------------------------------------------------------- /src/util/get-vue-app.ts: -------------------------------------------------------------------------------- 1 | import type Vue from 'vue' 2 | 3 | type ElementWithVueApp = Element & { 4 | __vue__: Vue 5 | } 6 | 7 | const elementHasVueApp = (element: Element): element is ElementWithVueApp => '__vue__' in element 8 | 9 | const getVueApp = () => { 10 | const app = document.getElementById('app') 11 | 12 | if (!app || !elementHasVueApp(app)) { 13 | throw new Error('Vue app not found') 14 | } 15 | 16 | return app.__vue__ 17 | } 18 | 19 | export default getVueApp 20 | -------------------------------------------------------------------------------- /public/logo_zerog.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/store/afc/index.ts: -------------------------------------------------------------------------------- 1 | import type { Module } from 'vuex' 2 | import { state } from './state' 3 | import { getters } from './getters' 4 | import { actions } from './actions' 5 | import { mutations } from './mutations' 6 | import type { AfcState } from './types' 7 | import type { RootState } from '../types' 8 | 9 | const namespaced = true 10 | 11 | export const afc = { 12 | namespaced, 13 | state, 14 | getters, 15 | actions, 16 | mutations 17 | } satisfies Module 18 | -------------------------------------------------------------------------------- /src/store/mmu/index.ts: -------------------------------------------------------------------------------- 1 | import type { Module } from 'vuex' 2 | import { state } from './state' 3 | import { getters } from './getters' 4 | import { actions } from './actions' 5 | import { mutations } from './mutations' 6 | import type { MmuState } from './types' 7 | import type { RootState } from '../types' 8 | 9 | const namespaced = true 10 | 11 | export const mmu = { 12 | namespaced, 13 | state, 14 | getters, 15 | actions, 16 | mutations 17 | } satisfies Module 18 | -------------------------------------------------------------------------------- /docs/configuration/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Configuration 4 | nav_order: 4 5 | has_children: true 6 | permalink: /configuration 7 | --- 8 | 9 | # Configuration 10 | 11 | Because Fluidd relies on Moonraker and Klipper, configuration needs to happen 12 | in more than one location. 13 | {: .fs-6 .fw-300 } 14 | 15 | First steps should ensure you refer to the 16 | [initial setup](/configuration/initial_setup) section in order to ensure you 17 | have basic requirements setup first. 18 | -------------------------------------------------------------------------------- /docs/features/med_mesh.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Bed Mesh 4 | parent: Features 5 | nav_order: 9 6 | permalink: /features/bed_mesh 7 | --- 8 | 9 | # Bed Mesh 10 | {: .no_toc } 11 | 12 | --- 13 | 14 | Fluidd has a built in bed mesh viewer. Click through to the printer navigation 15 | item and calibrate a mesh to view. 16 | 17 | Note, you'll need to have configured the `bed_mesh` option in klipper for this 18 | option to be visible. 19 | 20 | ![screenshot](/assets/images/bed_mesh.png) 21 | -------------------------------------------------------------------------------- /docs/features/updates.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Automated Updates 4 | parent: Features 5 | nav_order: 4 6 | permalink: /features/updates 7 | --- 8 | 9 | # Automated Updates 10 | {: .no_toc } 11 | 12 | --- 13 | 14 | Fluidd facilitates seamless automated updates through Moonraker. For detailed 15 | information on configuring automatic updates, please refer to the 16 | [Moonraker configuration documentation](/configuration/moonraker). 17 | 18 | ![screenshot](/assets/images/updates.png) 19 | -------------------------------------------------------------------------------- /src/store/auth/index.ts: -------------------------------------------------------------------------------- 1 | import type { Module } from 'vuex' 2 | import { state } from './state' 3 | import { getters } from './getters' 4 | import { actions } from './actions' 5 | import { mutations } from './mutations' 6 | import type { AuthState } from './types' 7 | import type { RootState } from '../types' 8 | 9 | const namespaced = true 10 | 11 | export const auth = { 12 | namespaced, 13 | state, 14 | getters, 15 | actions, 16 | mutations 17 | } satisfies Module 18 | -------------------------------------------------------------------------------- /src/store/mesh/index.ts: -------------------------------------------------------------------------------- 1 | import type { Module } from 'vuex' 2 | import { state } from './state' 3 | import { getters } from './getters' 4 | import { actions } from './actions' 5 | import { mutations } from './mutations' 6 | import type { MeshState } from './types' 7 | import type { RootState } from '../types' 8 | 9 | const namespaced = true 10 | 11 | export const mesh = { 12 | namespaced, 13 | state, 14 | getters, 15 | actions, 16 | mutations 17 | } satisfies Module 18 | -------------------------------------------------------------------------------- /src/store/wait/index.ts: -------------------------------------------------------------------------------- 1 | import type { Module } from 'vuex' 2 | import { state } from './state' 3 | import { getters } from './getters' 4 | import { actions } from './actions' 5 | import { mutations } from './mutations' 6 | import type { WaitState } from './types' 7 | import type { RootState } from '../types' 8 | 9 | const namespaced = true 10 | 11 | export const wait = { 12 | namespaced, 13 | state, 14 | getters, 15 | actions, 16 | mutations 17 | } satisfies Module 18 | -------------------------------------------------------------------------------- /src/store/charts/index.ts: -------------------------------------------------------------------------------- 1 | import type { Module } from 'vuex' 2 | import { state } from './state' 3 | import { getters } from './getters' 4 | import { actions } from './actions' 5 | import { mutations } from './mutations' 6 | import type { ChartState } from './types' 7 | import type { RootState } from '../types' 8 | 9 | const namespaced = true 10 | 11 | export const charts = { 12 | namespaced, 13 | state, 14 | getters, 15 | actions, 16 | mutations 17 | } satisfies Module 18 | -------------------------------------------------------------------------------- /src/store/files/index.ts: -------------------------------------------------------------------------------- 1 | import type { Module } from 'vuex' 2 | import { state } from './state' 3 | import { getters } from './getters' 4 | import { actions } from './actions' 5 | import { mutations } from './mutations' 6 | import type { FilesState } from './types' 7 | import type { RootState } from '../types' 8 | 9 | const namespaced = true 10 | 11 | export const files = { 12 | namespaced, 13 | state, 14 | getters, 15 | actions, 16 | mutations 17 | } satisfies Module 18 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | FILES_TO_LINT=$(git diff --cached --name-only --diff-filter=ACMR | grep -iE "\.(js|mjs|ts|vue)$" || true) 2 | 3 | if [ -n "$FILES_TO_LINT" ]; then 4 | npx --no eslint -- --max-warnings 0 $FILES_TO_LINT 5 | fi 6 | 7 | FILES_TO_OPTIMIZE_SVG=$(git diff --cached --name-only --diff-filter=ACMR | grep -iE "^src\/globals\.ts$|\.(vue|svg)$" || true) 8 | 9 | if [ -n "$FILES_TO_OPTIMIZE_SVG" ]; then 10 | node tools/svgo.mjs $FILES_TO_OPTIMIZE_SVG 11 | 12 | git update-index --again 13 | fi 14 | -------------------------------------------------------------------------------- /src/store/config/index.ts: -------------------------------------------------------------------------------- 1 | import type { Module } from 'vuex' 2 | import { state } from './state' 3 | import { getters } from './getters' 4 | import { actions } from './actions' 5 | import { mutations } from './mutations' 6 | import type { ConfigState } from './types' 7 | import type { RootState } from '../types' 8 | 9 | const namespaced = true 10 | 11 | export const config = { 12 | namespaced, 13 | state, 14 | getters, 15 | actions, 16 | mutations 17 | } satisfies Module 18 | -------------------------------------------------------------------------------- /src/store/layout/index.ts: -------------------------------------------------------------------------------- 1 | import type { Module } from 'vuex' 2 | import { state } from './state' 3 | import { getters } from './getters' 4 | import { actions } from './actions' 5 | import { mutations } from './mutations' 6 | import type { LayoutState } from './types' 7 | import type { RootState } from '../types' 8 | 9 | const namespaced = true 10 | 11 | export const layout = { 12 | namespaced, 13 | state, 14 | getters, 15 | actions, 16 | mutations 17 | } satisfies Module 18 | -------------------------------------------------------------------------------- /src/store/macros/index.ts: -------------------------------------------------------------------------------- 1 | import type { Module } from 'vuex' 2 | import { state } from './state' 3 | import { getters } from './getters' 4 | import { actions } from './actions' 5 | import { mutations } from './mutations' 6 | import type { MacrosState } from './types' 7 | import type { RootState } from '../types' 8 | 9 | const namespaced = true 10 | 11 | export const macros = { 12 | namespaced, 13 | state, 14 | getters, 15 | actions, 16 | mutations 17 | } satisfies Module 18 | -------------------------------------------------------------------------------- /src/store/server/index.ts: -------------------------------------------------------------------------------- 1 | import type { Module } from 'vuex' 2 | import { state } from './state' 3 | import { getters } from './getters' 4 | import { actions } from './actions' 5 | import { mutations } from './mutations' 6 | import type { ServerState } from './types' 7 | import type { RootState } from '../types' 8 | 9 | const namespaced = true 10 | 11 | export const server = { 12 | namespaced, 13 | state, 14 | getters, 15 | actions, 16 | mutations 17 | } satisfies Module 18 | -------------------------------------------------------------------------------- /src/store/socket/index.ts: -------------------------------------------------------------------------------- 1 | import type { Module } from 'vuex' 2 | import { state } from './state' 3 | import { getters } from './getters' 4 | import { actions } from './actions' 5 | import { mutations } from './mutations' 6 | import type { SocketState } from './types' 7 | import type { RootState } from '../types' 8 | 9 | const namespaced = true 10 | 11 | export const socket = { 12 | namespaced, 13 | state, 14 | getters, 15 | actions, 16 | mutations 17 | } satisfies Module 18 | -------------------------------------------------------------------------------- /docs/404.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | --- 4 | 5 | 18 | 19 |
20 |

404

21 | 22 |

Page not found :(

23 |

The requested page could not be found.

24 |
25 | -------------------------------------------------------------------------------- /src/store/analysis/index.ts: -------------------------------------------------------------------------------- 1 | import type { Module } from 'vuex' 2 | import { state } from './state' 3 | import { getters } from './getters' 4 | import { actions } from './actions' 5 | import { mutations } from './mutations' 6 | import type { AnalysisState } from './types' 7 | import type { RootState } from '../types' 8 | 9 | const namespaced = true 10 | 11 | export const analysis = { 12 | namespaced, 13 | state, 14 | getters, 15 | actions, 16 | mutations 17 | } satisfies Module 18 | -------------------------------------------------------------------------------- /src/store/console/index.ts: -------------------------------------------------------------------------------- 1 | import type { Module } from 'vuex' 2 | import { state } from './state' 3 | import { getters } from './getters' 4 | import { actions } from './actions' 5 | import { mutations } from './mutations' 6 | import type { ConsoleState } from './types' 7 | import type { RootState } from '../types' 8 | 9 | const namespaced = true 10 | 11 | export const console = { 12 | namespaced, 13 | state, 14 | getters, 15 | actions, 16 | mutations 17 | } satisfies Module 18 | -------------------------------------------------------------------------------- /src/store/database/index.ts: -------------------------------------------------------------------------------- 1 | import type { Module } from 'vuex' 2 | import { state } from './state' 3 | import { getters } from './getters' 4 | import { actions } from './actions' 5 | import { mutations } from './mutations' 6 | import type { DatabaseState } from './types' 7 | import type { RootState } from '../types' 8 | 9 | const namespaced = true 10 | 11 | export const database = { 12 | namespaced, 13 | state, 14 | getters, 15 | actions, 16 | mutations 17 | } satisfies Module 18 | -------------------------------------------------------------------------------- /src/store/history/index.ts: -------------------------------------------------------------------------------- 1 | import type { Module } from 'vuex' 2 | import { state } from './state' 3 | import { getters } from './getters' 4 | import { actions } from './actions' 5 | import { mutations } from './mutations' 6 | import type { HistoryState } from './types' 7 | import type { RootState } from '../types' 8 | 9 | const namespaced = true 10 | 11 | export const history = { 12 | namespaced, 13 | state, 14 | getters, 15 | actions, 16 | mutations 17 | } satisfies Module 18 | -------------------------------------------------------------------------------- /src/store/jobQueue/index.ts: -------------------------------------------------------------------------------- 1 | import type { Module } from 'vuex' 2 | import { state } from './state' 3 | import { getters } from './getters' 4 | import { actions } from './actions' 5 | import { mutations } from './mutations' 6 | import type { JobQueueState } from './types' 7 | import type { RootState } from '../types' 8 | 9 | const namespaced = true 10 | 11 | export const jobQueue = { 12 | namespaced, 13 | state, 14 | getters, 15 | actions, 16 | mutations 17 | } satisfies Module 18 | -------------------------------------------------------------------------------- /src/store/power/index.ts: -------------------------------------------------------------------------------- 1 | import type { Module } from 'vuex' 2 | import { state } from './state' 3 | import { getters } from './getters' 4 | import { actions } from './actions' 5 | import { mutations } from './mutations' 6 | import type { DevicePowerState } from './types' 7 | import type { RootState } from '../types' 8 | 9 | const namespaced = true 10 | 11 | export const power = { 12 | namespaced, 13 | state, 14 | getters, 15 | actions, 16 | mutations 17 | } satisfies Module 18 | -------------------------------------------------------------------------------- /src/store/printer/index.ts: -------------------------------------------------------------------------------- 1 | import type { Module } from 'vuex' 2 | import { state } from './state' 3 | import { getters } from './getters' 4 | import { actions } from './actions' 5 | import { mutations } from './mutations' 6 | import type { PrinterState } from './types' 7 | import type { RootState } from '../types' 8 | 9 | const namespaced = true 10 | 11 | export const printer = { 12 | namespaced, 13 | state, 14 | getters, 15 | actions, 16 | mutations 17 | } satisfies Module 18 | -------------------------------------------------------------------------------- /src/store/spoolman/index.ts: -------------------------------------------------------------------------------- 1 | import type { Module } from 'vuex' 2 | import { state } from './state' 3 | import { getters } from './getters' 4 | import { actions } from './actions' 5 | import { mutations } from './mutations' 6 | import type { SpoolmanState } from './types' 7 | import type { RootState } from '../types' 8 | 9 | const namespaced = true 10 | 11 | export const spoolman = { 12 | namespaced, 13 | state, 14 | getters, 15 | actions, 16 | mutations 17 | } satisfies Module 18 | -------------------------------------------------------------------------------- /src/store/version/index.ts: -------------------------------------------------------------------------------- 1 | import type { Module } from 'vuex' 2 | import { state } from './state' 3 | import { getters } from './getters' 4 | import { actions } from './actions' 5 | import { mutations } from './mutations' 6 | import type { VersionState } from './types' 7 | import type { RootState } from '../types' 8 | 9 | const namespaced = true 10 | 11 | export const version = { 12 | namespaced, 13 | state, 14 | getters, 15 | actions, 16 | mutations 17 | } satisfies Module 18 | -------------------------------------------------------------------------------- /src/store/webcams/index.ts: -------------------------------------------------------------------------------- 1 | import type { Module } from 'vuex' 2 | import { state } from './state' 3 | import { getters } from './getters' 4 | import { actions } from './actions' 5 | import { mutations } from './mutations' 6 | import type { WebcamsState } from './types' 7 | import type { RootState } from '../types' 8 | 9 | const namespaced = true 10 | 11 | export const webcams = { 12 | namespaced, 13 | state, 14 | getters, 15 | actions, 16 | mutations 17 | } satisfies Module 18 | -------------------------------------------------------------------------------- /src/views/Console.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 21 | -------------------------------------------------------------------------------- /src/store/timelapse/index.ts: -------------------------------------------------------------------------------- 1 | import type { Module } from 'vuex' 2 | import { state } from './state' 3 | import { getters } from './getters' 4 | import { actions } from './actions' 5 | import { mutations } from './mutations' 6 | import type { TimelapseState } from './types' 7 | import type { RootState } from '../types' 8 | 9 | const namespaced = true 10 | 11 | export const timelapse = { 12 | namespaced, 13 | state, 14 | getters, 15 | actions, 16 | mutations 17 | } satisfies Module 18 | -------------------------------------------------------------------------------- /src/util/is-user-agent-data-mobile-supported.ts: -------------------------------------------------------------------------------- 1 | type NavigatorWithUserAgentDataMobile = Navigator & { 2 | userAgentData: { 3 | mobile: boolean 4 | } 5 | } 6 | 7 | const isUserAgentDataMobileSupported = (navigator: Navigator): navigator is NavigatorWithUserAgentDataMobile => { 8 | return ( 9 | navigator.userAgentData != null && 10 | 'mobile' in navigator.userAgentData && 11 | typeof navigator.userAgentData.mobile === 'boolean' 12 | ) 13 | } 14 | 15 | export default isUserAgentDataMobileSupported 16 | -------------------------------------------------------------------------------- /src/store/power/getters.ts: -------------------------------------------------------------------------------- 1 | import type { GetterTree } from 'vuex' 2 | import type { DevicePowerState } from './types' 3 | import type { RootState } from '../types' 4 | 5 | export const getters = { 6 | getDevices: (state) => { 7 | return state.devices 8 | .filter(device => !device.device.startsWith('_')) 9 | }, 10 | 11 | getDeviceByName: (state) => (name: string) => { 12 | return state.devices 13 | .find(device => device.device === name) 14 | } 15 | } satisfies GetterTree 16 | -------------------------------------------------------------------------------- /src/store/sensors/index.ts: -------------------------------------------------------------------------------- 1 | import type { Module } from 'vuex' 2 | import { state } from './state' 3 | import { getters } from './getters' 4 | import { actions } from './actions' 5 | import { mutations } from './mutations' 6 | import type { MoonrakerSensorsState } from './types' 7 | import type { RootState } from '../types' 8 | 9 | const namespaced = true 10 | 11 | export const sensors = { 12 | namespaced, 13 | state, 14 | getters, 15 | actions, 16 | mutations 17 | } satisfies Module 18 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@vue/tsconfig/tsconfig.json", 3 | "compilerOptions": { 4 | "preserveValueImports": false, 5 | 6 | "importsNotUsedAsValues": "remove", 7 | 8 | "experimentalDecorators": true, 9 | 10 | "verbatimModuleSyntax": true 11 | }, 12 | "files": [], 13 | "references": [ 14 | { 15 | "path": "./tsconfig.vitest.json" 16 | }, 17 | { 18 | "path": "./tsconfig.config.json" 19 | }, 20 | { 21 | "path": "./tsconfig.app.json" 22 | } 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /src/store/announcements/index.ts: -------------------------------------------------------------------------------- 1 | import type { Module } from 'vuex' 2 | import { state } from './state' 3 | import { getters } from './getters' 4 | import { actions } from './actions' 5 | import { mutations } from './mutations' 6 | import type { AnnouncementsState } from './types' 7 | import type { RootState } from '../types' 8 | 9 | const namespaced = true 10 | 11 | export const announcements = { 12 | namespaced, 13 | state, 14 | getters, 15 | actions, 16 | mutations 17 | } satisfies Module 18 | -------------------------------------------------------------------------------- /src/store/gcodePreview/index.ts: -------------------------------------------------------------------------------- 1 | import type { Module } from 'vuex' 2 | import { state } from './state' 3 | import { getters } from './getters' 4 | import { actions } from './actions' 5 | import { mutations } from './mutations' 6 | import type { GcodePreviewState } from './types' 7 | import type { RootState } from '../types' 8 | 9 | const namespaced = true 10 | 11 | export const gcodePreview = { 12 | namespaced, 13 | state, 14 | getters, 15 | actions, 16 | mutations 17 | } satisfies Module 18 | -------------------------------------------------------------------------------- /src/store/notifications/index.ts: -------------------------------------------------------------------------------- 1 | import type { Module } from 'vuex' 2 | import { state } from './state' 3 | import { getters } from './getters' 4 | import { actions } from './actions' 5 | import { mutations } from './mutations' 6 | import type { NotificationsState } from './types' 7 | import type { RootState } from '../types' 8 | 9 | const namespaced = true 10 | 11 | export const notifications = { 12 | namespaced, 13 | state, 14 | getters, 15 | actions, 16 | mutations 17 | } satisfies Module 18 | -------------------------------------------------------------------------------- /src/util/sanitize-endpoint.ts: -------------------------------------------------------------------------------- 1 | import { consola } from 'consola' 2 | 3 | const sanitizeEndpoint = (endpoint?: string) => { 4 | if (endpoint) { 5 | try { 6 | const url = new URL(endpoint) 7 | const path = url.pathname.endsWith('/') 8 | ? url.pathname.slice(0, -1) 9 | : url.pathname 10 | return `${url.protocol}//${url.host}${path}` 11 | } catch (e) { 12 | consola.debug('Error parsing endpoint url in config.json', e) 13 | } 14 | } 15 | } 16 | 17 | export default sanitizeEndpoint 18 | -------------------------------------------------------------------------------- /src/util/__tests__/string-formatters.spec.ts: -------------------------------------------------------------------------------- 1 | import stringFormatters from '../string-formatters' 2 | 3 | describe('prettyCase', () => { 4 | const sf = stringFormatters() 5 | 6 | it.each([ 7 | ['_ hello _ _world_ ', 'Hello World'], 8 | ['raspberry pi 3b+', 'Raspberry Pi 3b+'], 9 | ['Raspberry Pi 3B+', 'Raspberry Pi 3B+'], 10 | ['active (running)', 'Active (Running)'], 11 | ])('Expects pretty case of "%s" to be "%s"', (input, expected) => { 12 | expect(sf.prettyCase(input)).toBe(expected) 13 | }) 14 | }) 15 | -------------------------------------------------------------------------------- /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:24-alpine 2 | 3 | RUN \ 4 | apk update \ 5 | && apk add \ 6 | git \ 7 | git-zsh-completion \ 8 | sudo \ 9 | shadow \ 10 | zsh \ 11 | zsh-completions \ 12 | && echo node ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/node \ 13 | && chmod 0440 /etc/sudoers.d/node \ 14 | && chsh -s /bin/zsh node \ 15 | && rm -f /etc/apk/cache/* /root/.cache 16 | 17 | USER node 18 | 19 | RUN \ 20 | sh -c "$(wget https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh -O -)" 21 | -------------------------------------------------------------------------------- /src/eventBus.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import type { FlashMessage } from '@/types' 3 | 4 | export const EventBus = { 5 | bus: new Vue(), 6 | $emit: (text?: string, options: Partial = {}): void => { 7 | const opts: FlashMessage = { 8 | open: true, 9 | timeout: -1, 10 | ...options 11 | } 12 | if (text) opts.text = text 13 | // if (type) opts.type = type 14 | // if (timeout) opts.timeout = timeout 15 | 16 | EventBus.bus.$emit('flashMessage', opts) // custom message 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/types/dialogs.ts: -------------------------------------------------------------------------------- 1 | import type { AppDirectory, AppFile } from '@/store/files/types' 2 | import type { InputValidationRules } from 'vuetify' 3 | 4 | export interface FileSystemDialogData { 5 | type: 'rename' | 'createdir' | 'createfile' | ''; 6 | active: boolean; 7 | valid: boolean; 8 | title: string; 9 | formLabel: string; 10 | rules: InputValidationRules; 11 | item: AppDirectory | NewDirectory | AppFile; 12 | original?: AppDirectory | AppFile; 13 | } 14 | 15 | export interface NewDirectory { 16 | name: string; 17 | } 18 | -------------------------------------------------------------------------------- /src/views/GcodePreview.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 21 | -------------------------------------------------------------------------------- /src/store/version/types.ts: -------------------------------------------------------------------------------- 1 | export interface VersionState { 2 | status: Partial | null; 3 | responses: UpdateResponse[]; 4 | } 5 | 6 | export type VersionInfo = Moonraker.UpdateManager.VersionInfoEntry extends { configured_type?: infer K } 7 | ? Moonraker.UpdateManager.VersionInfoEntry & { 8 | configured_type: K, 9 | name: string 10 | } 11 | : never 12 | 13 | export interface UpdateResponse { 14 | id: number; 15 | message: string; 16 | application: string; 17 | proc_id: number; 18 | } 19 | -------------------------------------------------------------------------------- /src/store/console/state.ts: -------------------------------------------------------------------------------- 1 | import type { ConsoleState } from './types' 2 | 3 | export const defaultState = (): ConsoleState => { 4 | return { 5 | consoleCommand: '', 6 | consoleEntryCount: 0, 7 | console: [], 8 | gcodeHelp: {}, 9 | commandHistory: [], 10 | autoScroll: true, 11 | lastCleared: 0, 12 | promptDialog: { 13 | open: false, 14 | items: [], 15 | footerButtons: [] 16 | }, 17 | consoleFilters: [], 18 | consoleFiltersRegexp: [] 19 | } 20 | } 21 | 22 | export const state = defaultState() 23 | -------------------------------------------------------------------------------- /src/util/is-web-assembly-supported.ts: -------------------------------------------------------------------------------- 1 | const isWebAssemblySupported = () => { 2 | try { 3 | if (typeof WebAssembly === 'object' && typeof WebAssembly.instantiate === 'function') { 4 | const module = new WebAssembly.Module(Uint8Array.of(0x0, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00)) 5 | 6 | if (module instanceof WebAssembly.Module) { 7 | return new WebAssembly.Instance(module) instanceof WebAssembly.Instance 8 | } 9 | } 10 | } catch { 11 | } 12 | 13 | return false 14 | } 15 | 16 | export default isWebAssemblySupported 17 | -------------------------------------------------------------------------------- /src/components/widgets/sensors/SensorsCard.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 24 | -------------------------------------------------------------------------------- /tsconfig.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@vue/tsconfig/tsconfig.node.json", 3 | "include": [ 4 | "vite.config.*", 5 | "vitest.config.*", 6 | "cypress.config.*", 7 | "package.json" 8 | ], 9 | "compilerOptions": { 10 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.config.tsbuildinfo", 11 | 12 | "types": [ 13 | "node" 14 | ], 15 | 16 | "preserveValueImports": false, 17 | 18 | "importsNotUsedAsValues": "remove", 19 | 20 | "experimentalDecorators": true, 21 | 22 | "verbatimModuleSyntax": true 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | /dev-dist 5 | 6 | /tests/e2e/videos/ 7 | /tests/e2e/screenshots/ 8 | 9 | .credentials 10 | 11 | # local env files 12 | .env.local 13 | .env.*.local 14 | 15 | # Log files 16 | npm-debug.log* 17 | yarn-debug.log* 18 | yarn-error.log* 19 | pnpm-debug.log* 20 | 21 | # Editor directories and files 22 | .idea 23 | .vscode/* 24 | !.vscode/extensions.json 25 | !.vscode/launch.json 26 | !.vscode/settings.default.json 27 | !.vscode/tasks.json 28 | *.suo 29 | *.ntvs* 30 | *.njsproj 31 | *.sln 32 | *.sw? 33 | 34 | *.tsbuildinfo 35 | -------------------------------------------------------------------------------- /src/store/jobQueue/mutations.ts: -------------------------------------------------------------------------------- 1 | import type { MutationTree } from 'vuex' 2 | import type { JobQueueState } from './types' 3 | import { defaultState } from './state' 4 | 5 | export const mutations = { 6 | setReset (state) { 7 | Object.assign(state, defaultState()) 8 | }, 9 | 10 | setQueueState (state, payload: Moonraker.JobQueue.QueueState) { 11 | state.queueState = payload 12 | }, 13 | 14 | setQueuedJobs (state, payload: Moonraker.JobQueue.QueuedJob[]) { 15 | state.queuedJobs = payload || [] 16 | } 17 | } satisfies MutationTree 18 | -------------------------------------------------------------------------------- /src/components/widgets/macros/MacrosCard.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 24 | -------------------------------------------------------------------------------- /src/util/promise-any.ts: -------------------------------------------------------------------------------- 1 | const promiseAny = (iterable: Iterable>): Promise> => { 2 | if ('any' in Promise && typeof Promise.any === 'function') { 3 | return Promise.any(iterable) 4 | } 5 | 6 | return Promise.all( 7 | [...iterable].map(promise => { 8 | return new Promise((resolve, reject) => 9 | Promise.resolve(promise).then(reject, resolve) 10 | ) 11 | }) 12 | ).then( 13 | errors => Promise.reject(errors), 14 | (value: Awaited) => Promise.resolve(value) 15 | ) 16 | } 17 | 18 | export default promiseAny 19 | -------------------------------------------------------------------------------- /docs/features/notifications.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Notifications 4 | parent: Features 5 | nav_order: 13 6 | permalink: /features/notifications 7 | --- 8 | 9 | # Notifications 10 | {: .no_toc } 11 | 12 | --- 13 | 14 | Fluidd has a built-in notification system to warn you of potential issues. 15 | 16 | Fluidd will fire a warning if you have updates pending, or if 17 | you've hit a throttle condition (if running on a Pi, or otherwise your host 18 | supports `vcgencmd`). Other warnings will come as they're available. 19 | 20 | ![screenshot](/assets/images/notifications.png) 21 | -------------------------------------------------------------------------------- /src/components/widgets/retract/RetractCard.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 24 | -------------------------------------------------------------------------------- /src/util/download-url.ts: -------------------------------------------------------------------------------- 1 | import { consola } from 'consola' 2 | 3 | const downloadUrl = (filename: string, url: string) => { 4 | try { 5 | // Create a link, handle its click - and finally remove it again. 6 | const link = document.createElement('a') 7 | 8 | link.href = url 9 | link.download = filename 10 | link.target = '_blank' 11 | 12 | document.body.appendChild(link) 13 | 14 | link.click() 15 | 16 | document.body.removeChild(link) 17 | } catch (error) { 18 | consola.error('[DownloadUrl] error', error) 19 | } 20 | } 21 | 22 | export default downloadUrl 23 | -------------------------------------------------------------------------------- /docs/assets/images/logo_ldo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/scss/buttons.scss: -------------------------------------------------------------------------------- 1 | /* Prevent zoom on mobile devices when furiosly clicking buttons */ 2 | button .v-btn { 3 | touch-action: manipulation; 4 | } 5 | 6 | button .v-icon { 7 | touch-action: manipulation; 8 | } 9 | 10 | .v-btn-toggle > .v-btn.v-btn { 11 | border-top: 0 !important; 12 | border-bottom: 0 !important; 13 | 14 | &:first-child { 15 | border-left: 0 !important; 16 | } 17 | &:last-child { 18 | border-right: 0 !important; 19 | } 20 | } 21 | 22 | .v-toolbar__content .v-btn.v-btn--icon.v-size--default { 23 | width: 40px !important; 24 | height: 40px !important; 25 | } 26 | -------------------------------------------------------------------------------- /src/typings/moonraker.data_store.d.ts: -------------------------------------------------------------------------------- 1 | declare namespace Moonraker.DataStore { 2 | export interface TemperatureStoreResponse extends Record { 3 | } 4 | 5 | export interface GcodeStoreResponse { 6 | gcode_store: GcodeStoreEntry[]; 7 | } 8 | 9 | export interface TemperatureStoreEntry { 10 | temperatures: number[]; 11 | targets?: number[]; 12 | powers?: number[]; 13 | speeds?: number[]; 14 | } 15 | 16 | export interface GcodeStoreEntry { 17 | message: string; 18 | time?: number; 19 | type: 'command' | 'response'; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/components/ui/AppBtnToggle.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 28 | -------------------------------------------------------------------------------- /src/components/widgets/outputs/OutputsCard.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 25 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | 10 | -------------------------------------------------------------------------------- /src/scss/tables.scss: -------------------------------------------------------------------------------- 1 | // For those tables where a hover doesn't make sense... 2 | .theme--light.v-data-table.no-hover > .v-data-table__wrapper > table > tbody > tr:hover:not(.v-data-table__expanded__content):not(.v-data-table__empty-wrapper), 3 | .theme--dark.v-data-table.no-hover > .v-data-table__wrapper > table > tbody > tr:hover:not(.v-data-table__expanded__content):not(.v-data-table__empty-wrapper) { 4 | background-color: transparent; 5 | } 6 | 7 | // For those tables where you want to mark a row inactive... 8 | .v-data-table__wrapper > table > tbody > tr.v-data-table__inactive { 9 | color: var(--v-secondary-base); 10 | } 11 | -------------------------------------------------------------------------------- /src/util/__tests__/sanitize-endpoint.spec.ts: -------------------------------------------------------------------------------- 1 | import sanitizeEndpoint from '../sanitize-endpoint' 2 | 3 | describe('santizeEndpointChecks', () => { 4 | it.each([ 5 | ['https://localhost/', 'https://localhost'], 6 | ['https://localhost', 'https://localhost'], 7 | ['https://test.example.com/some/subpath/', 'https://test.example.com/some/subpath'], 8 | ['https://test.example.com/?query=parameter&test', 9 | 'https://test.example.com'], 10 | ['garbage', undefined] 11 | ])('Expects url Parsing of "%s" to be %s', (param, expected) => { 12 | expect(sanitizeEndpoint(param)).toBe(expected) 13 | }) 14 | }) 15 | -------------------------------------------------------------------------------- /src/components/widgets/afc/AfcCardBypass.vue: -------------------------------------------------------------------------------- 1 | 12 | 24 | -------------------------------------------------------------------------------- /src/components/widgets/limits/PrinterLimitsCard.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 24 | -------------------------------------------------------------------------------- /docs/features/multiple_printers.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Multiple Printers 4 | parent: Features 5 | nav_order: 1 6 | permalink: /features/printers 7 | --- 8 | 9 | # Multiple Printers 10 | {: .no_toc } 11 | 12 | --- 13 | 14 | Fluidd allows connecting and swapping between multiple printers. 15 | 16 | In some circumstances, moonraker must be configured to allow a connection from fluidd. Configuration may depend on your type of install. 17 | 18 | Please see the [multiple printers](/configuration/multiple_printers) configuration docs for more information on setup. 19 | 20 | ![screenshot](/assets/images/printer-selection.png) 21 | -------------------------------------------------------------------------------- /src/scss/global.scss: -------------------------------------------------------------------------------- 1 | @import './variables'; 2 | @import 'vuetify/src/styles/styles.sass'; 3 | 4 | /* Import our fonts */ 5 | @import "@fontsource/roboto/300.css"; 6 | @import "@fontsource/roboto/400.css"; 7 | @import "@fontsource/roboto/500.css"; 8 | @import "@fontsource/roboto/700.css"; 9 | @import "@fontsource/raleway/400.css"; 10 | 11 | @import "./misc"; 12 | @import "./helpers"; 13 | @import "./dialogs"; 14 | @import "./cards"; 15 | @import "./typeography"; 16 | @import "./animation"; 17 | @import "./buttons"; 18 | @import "./chips"; 19 | @import "./file-system"; 20 | @import "./inputs"; 21 | @import "./lists"; 22 | @import "./tables"; 23 | -------------------------------------------------------------------------------- /docs/features/chart.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Thermals Chart 4 | parent: Features 5 | nav_order: 7 6 | permalink: /features/chart 7 | --- 8 | 9 | # Thermals Chart 10 | {: .no_toc } 11 | 12 | --- 13 | 14 | Fluidd's chart allow you to view not just temperatures, but also power applied 15 | to your heaters and fans. 16 | 17 | 1. Click the name of a heater, sensor or fan to toggle its visibility. 18 | 2. Click the power of a heater, sensor or fan to toggle its visibility. 19 | 3. Zoom the chart by holding SHIFT and scrolling your mouse wheel whilst the 20 | cursor is over the chart. 21 | 22 | ![screenshot](/assets/images/graph.png) 23 | -------------------------------------------------------------------------------- /public/logo_ldo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/store/socket/getters.ts: -------------------------------------------------------------------------------- 1 | import type { GetterTree } from 'vuex' 2 | import type { SocketState } from './types' 3 | import type { RootState } from '../types' 4 | 5 | export const getters = { 6 | /** 7 | * Indicates if our socket is connected / open. 8 | */ 9 | getConnectionState: (state): boolean => { 10 | return state.open 11 | }, 12 | 13 | /** 14 | * Indicates if our socket is attempting to connect still.. 15 | */ 16 | getConnectingState: (state): boolean => { 17 | return state.connecting 18 | }, 19 | 20 | getApiConnected: (state) => { 21 | return state.apiConnected 22 | } 23 | } satisfies GetterTree 24 | -------------------------------------------------------------------------------- /src/typings/moonraker.announcements.d.ts: -------------------------------------------------------------------------------- 1 | declare namespace Moonraker.Announcements { 2 | export interface ListResponse { 3 | entries: Entry[]; 4 | feeds: string[]; 5 | } 6 | 7 | export interface DismissResponse { 8 | entry_id: string; 9 | } 10 | 11 | export interface Entry { 12 | entry_id: string; 13 | url: string; 14 | title: string; 15 | description: string; 16 | priority: Priority; 17 | date: number; 18 | dismissed: boolean; 19 | date_dismissed: number | null; 20 | dismiss_wake: number | null; 21 | source: string; 22 | feed: string; 23 | } 24 | 25 | export type Priority = 'normal' | 'high' 26 | } 27 | -------------------------------------------------------------------------------- /src/util/__tests__/is-loopback.spec.ts: -------------------------------------------------------------------------------- 1 | import isLoopback from '../is-loopback' 2 | 3 | describe('isLoopback', () => { 4 | it.each([ 5 | 'localhost', 6 | ' LOCALHOST ', 7 | '127.0.0.1', 8 | '127.0.0.2', 9 | '::1', 10 | '0:0:0:0:0:0:0:1', 11 | '::ffff:127.0.0.1', 12 | '::ffff:7f00:1', 13 | ])('Expects host "%s" to be loopback', host => { 14 | expect(isLoopback(host)).toBeTruthy() 15 | }) 16 | 17 | it.each([ 18 | 'somewhere', 19 | 'somewhere.localhost', 20 | '128.0.0.1', 21 | '192.168.0.1', 22 | ])('Expects host "%s" not to be loopback', host => { 23 | expect(isLoopback(host)).toBeFalsy() 24 | }) 25 | }) 26 | -------------------------------------------------------------------------------- /docs/installation/kiauh.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: KIAUH 4 | parent: Installation 5 | nav_order: 1 6 | permalink: /installation/kiauh 7 | --- 8 | 9 | # KIAUH - Klipper Installation And Update Helper 10 | {: .no_toc } 11 | 12 | --- 13 | 14 | KIAUH is the recommended way to install Fluidd. 15 | 16 | KIAUH makes installation and updates a breeze, no matter if it is just a simple 17 | install or when you'd like to trial more than one user interface on your device, 18 | or have more of an advanced setup. 19 | 20 | For more information on KIAUH, please visit its github page. 21 | 22 | [View on GitHub](https://github.com/dw-0/kiauh){: .btn .fs-5 .mb-4 .mb-md-0 } 23 | -------------------------------------------------------------------------------- /src/store/macros/types.ts: -------------------------------------------------------------------------------- 1 | export interface MacrosState { 2 | stored: Macro[]; 3 | categories: MacroCategory[]; 4 | expanded: number[]; 5 | } 6 | 7 | export interface Macro { 8 | name: string; 9 | description?: string; 10 | alias?: string; 11 | visible: boolean; 12 | categoryId?: string; 13 | category?: MacroCategory; 14 | assignTo?: string; 15 | disabledWhilePrinting?: boolean; 16 | color?: string; 17 | config?: Klipper.GcodeMacroSettings; 18 | order?: number; 19 | variables?: Record 20 | } 21 | 22 | export interface MacroCategory { 23 | id: string; 24 | name: string; 25 | count?: number; 26 | visible?: number; 27 | } 28 | -------------------------------------------------------------------------------- /public/logo_z-bolt.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/store/wait/actions.ts: -------------------------------------------------------------------------------- 1 | import type { ActionTree } from 'vuex' 2 | import type { WaitState } from './types' 3 | import type { RootState } from '../types' 4 | 5 | export const actions = { 6 | /** 7 | * Reset our store 8 | */ 9 | async reset ({ commit }) { 10 | commit('setReset') 11 | }, 12 | 13 | /** 14 | * Add's a wait to the list of waits. 15 | */ 16 | async addWait ({ commit }, wait) { 17 | commit('setAddWait', wait) 18 | }, 19 | 20 | /** 21 | * Removes a wait from the list of waits. 22 | */ 23 | async removeWait ({ commit }, wait) { 24 | commit('setRemoveWait', wait) 25 | } 26 | } satisfies ActionTree 27 | -------------------------------------------------------------------------------- /src/store/layout/types.ts: -------------------------------------------------------------------------------- 1 | export interface LayoutState { 2 | layouts: Layouts; 3 | } 4 | 5 | /** 6 | * As single layout. Should have at least one container. 7 | * Configuration id's should not be duplicated across containers. 8 | **/ 9 | export interface Layouts { 10 | [index: string]: LayoutContainer; 11 | } 12 | 13 | /** Containers, each of which is an array of configurations. */ 14 | export interface LayoutContainer { 15 | [index: string]: LayoutConfig[]; 16 | } 17 | 18 | /** Configuration of a layout item */ 19 | export interface LayoutConfig { 20 | id: string; 21 | enabled: boolean; 22 | collapsed: boolean; 23 | layout?: string; 24 | container?: string; 25 | } 26 | -------------------------------------------------------------------------------- /src/store/jobQueue/getters.ts: -------------------------------------------------------------------------------- 1 | import type { GetterTree } from 'vuex' 2 | import type { JobQueueState, QueuedJobWithAppFile } from './types' 3 | import type { RootState } from '../types' 4 | import getFilePaths from '@/util/get-file-paths' 5 | 6 | export const getters = { 7 | getQueuedJobsWithFiles: (state, getters, rootState, rootGetters) => { 8 | return state.queuedJobs.map((job): QueuedJobWithAppFile => { 9 | const { rootPath, filename } = getFilePaths(job.filename, 'gcodes') 10 | 11 | return { 12 | ...job, 13 | file: rootGetters['files/getFile'](rootPath, filename) 14 | } 15 | }) 16 | }, 17 | } satisfies GetterTree 18 | -------------------------------------------------------------------------------- /src/typings/moonraker.power.d.ts: -------------------------------------------------------------------------------- 1 | declare namespace Moonraker.Power { 2 | export interface DevicesResponse { 3 | devices: Device[]; 4 | } 5 | 6 | export interface StatusResponse { 7 | [device: string]: DeviceState; 8 | } 9 | 10 | export interface Device { 11 | device: string; 12 | status: DeviceState; 13 | locked_while_printing: boolean; 14 | type: DeviceType; 15 | } 16 | 17 | export type DeviceState = 'on' | 'off' | 'init' | 'error' 18 | 19 | export type DeviceType = 'gpio' | 'klipper_device' | 'tplink_smartplug' | 'tasmota' | 'shelly' | 'homeseer' | 'homeassistant' | 'loxonev1' | 'rf' | 'mqtt' | 'smartthings' | 'hue' | 'http' | 'uhubctl' 20 | } 21 | -------------------------------------------------------------------------------- /docs/customize/layout.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Application Layout 4 | parent: Customize 5 | nav_order: 1 6 | permalink: /customize/layout 7 | --- 8 | 9 | # Application Layout 10 | {: .no_toc } 11 | 12 | --- 13 | 14 | Fluidd allows you to adjust your dashboard layout to your liking. Use the 15 | hamburger menu and click the `adjust layout` option. 16 | 17 | Use the drag handles to move cards to / from the left and right columns. You 18 | can also easily disable cards if you have no use for them. 19 | 20 | Once you're done, click the exit layout mode button. You can reset back to 21 | the default layout by clicking reset layout. 22 | 23 | ![screenshot](/assets/images/layout.png) 24 | -------------------------------------------------------------------------------- /src/components/ui/AppDataTableCellTemps.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 29 | -------------------------------------------------------------------------------- /.versionrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "types": [ 3 | { 4 | "type": "feat", 5 | "section": "Features" 6 | }, 7 | { 8 | "type": "fix", 9 | "section": "Bug Fixes" 10 | }, 11 | { 12 | "type": "chore", 13 | "hidden": true 14 | }, 15 | { 16 | "type": "docs", 17 | "hidden": true 18 | }, 19 | { 20 | "type": "style", 21 | "hidden": true 22 | }, 23 | { 24 | "type": "perf", 25 | "section": "Performance Improvements" 26 | }, 27 | { 28 | "type": "refactor", 29 | "section": "Code Refactorings" 30 | }, 31 | { 32 | "type": "test", 33 | "hidden": true 34 | } 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /public/logo_pfa.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /public/logo_vzbot.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/views/NotFound.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 29 | -------------------------------------------------------------------------------- /src/components/ui/AppIcon.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 24 | 25 | 30 | -------------------------------------------------------------------------------- /src/store/history/types.ts: -------------------------------------------------------------------------------- 1 | import type { AppFileMeta } from '@/store/files/types.metadata' 2 | 3 | export interface HistoryState { 4 | count: number; 5 | jobs: Moonraker.History.Job[]; 6 | job_totals: Moonraker.History.JobTotals; 7 | } 8 | 9 | export interface HistoryItem extends Omit { 10 | metadata?: AppFileMeta; 11 | } 12 | 13 | export type HistoryItemStatus = 'completed' | 'cancelled' | 'error' | 'printing' | 'in_progress' | 'server_exit' | 'klippy_shutdown' | 'klippy_disconnect' | 'interrupted' 14 | 15 | export interface HistoryItemAuxiliaryData { 16 | provider: string; 17 | name: string; 18 | value: unknown; 19 | description: string; 20 | units: string | null; 21 | } 22 | -------------------------------------------------------------------------------- /public/logo_salad_fork.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/scss/draggable.scss: -------------------------------------------------------------------------------- 1 | @import 'vuetify/src/styles/styles.sass'; 2 | 3 | .flip-list-move { 4 | transition: transform 0.5s; 5 | } 6 | 7 | .no-move { 8 | transition: transform 0s; 9 | } 10 | 11 | .app-draggable__ghost { 12 | opacity: 0.5; 13 | background: #ccc; 14 | } 15 | 16 | .list-group { 17 | flex: 1 1 auto; 18 | 19 | span { 20 | display: flex; 21 | flex-direction: column; 22 | height: 100%; 23 | min-height: 50vh; 24 | } 25 | } 26 | 27 | @media #{map-get($display-breakpoints, 'sm-and-down')} { 28 | .list-group span { 29 | min-height: auto; 30 | } 31 | } 32 | 33 | .drag { 34 | .list-group { 35 | padding: 6px; 36 | border: thin dashed rgba(map-get($shades, 'white'), 0.12); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/components/ui/AppQrCode.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 26 | 27 | 34 | -------------------------------------------------------------------------------- /src/store/sensors/mutations.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import type { MutationTree } from 'vuex' 3 | import type { MoonrakerSensorsState } from './types' 4 | import { defaultState } from './state' 5 | 6 | export const mutations = { 7 | /** 8 | * Reset state 9 | */ 10 | setReset (state) { 11 | Object.assign(state, defaultState()) 12 | }, 13 | 14 | setSensorsList (state, payload: Moonraker.Sensor.ListResponse) { 15 | state.sensors = payload.sensors 16 | }, 17 | 18 | setSensorUpdate (state, payload: Record) { 19 | for (const sensorKey in payload) { 20 | Vue.set(state.sensors[sensorKey], 'values', payload[sensorKey]) 21 | } 22 | } 23 | } satisfies MutationTree 24 | -------------------------------------------------------------------------------- /public/logo_hevort.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Launch Chrome", 9 | "request": "launch", 10 | "type": "chrome", 11 | "url": "http://localhost:8080", 12 | "webRoot": "${workspaceFolder}", 13 | "preLaunchTask": "Serve" 14 | }, 15 | { 16 | "name": "Launch Edge", 17 | "request": "launch", 18 | "type": "msedge", 19 | "url": "http://localhost:8080", 20 | "webRoot": "${workspaceFolder}", 21 | "preLaunchTask": "Serve" 22 | } 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /docs/features/sensors.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Sensors 4 | parent: Features 5 | nav_order: 5 6 | permalink: /features/sensors 7 | --- 8 | 9 | # Sensors 10 | {: .no_toc } 11 | 12 | --- 13 | 14 | Fluidd supports many of the built-in sensors from Klipper. Some examples are; 15 | 16 | ## Raspberry Pi Temperature 17 | 18 | ```yaml 19 | [temperature_sensor raspberry_pi] 20 | sensor_type: temperature_host 21 | min_temp: 10 22 | max_temp: 100 23 | ``` 24 | 25 | ## ATSAM, ATAMD and STM32 temperature sensors 26 | 27 | ```yaml 28 | [temperature_sensor mcu_temp] 29 | sensor_type: temperature_mcu 30 | min_temp: 0 31 | max_temp: 100 32 | ``` 33 | 34 | More information concerning other supported sensors can be found in the 35 | [klipper documentation](http://klipper3d.org) 36 | -------------------------------------------------------------------------------- /src/store/wait/mutations.ts: -------------------------------------------------------------------------------- 1 | import type { MutationTree } from 'vuex' 2 | import type { WaitState } from './types' 3 | import { defaultState } from './state' 4 | 5 | export const mutations = { 6 | /** 7 | * Reset state 8 | */ 9 | setReset (state) { 10 | Object.assign(state, defaultState()) 11 | }, 12 | 13 | /** 14 | * Add a wait, ensuring we don't add dupes. 15 | */ 16 | setAddWait (state, payload) { 17 | const i = state.waits.indexOf(payload) 18 | if (i === -1) state.waits.push(payload) 19 | }, 20 | 21 | /** 22 | * Remove a wait, if found. 23 | */ 24 | setRemoveWait (state, payload) { 25 | const i = state.waits.indexOf(payload) 26 | if (i !== -1) state.waits.splice(i, 1) 27 | } 28 | } satisfies MutationTree 29 | -------------------------------------------------------------------------------- /src/store/mesh/mutations.ts: -------------------------------------------------------------------------------- 1 | import type { MutationTree } from 'vuex' 2 | import { defaultState } from './state' 3 | import type { MeshState } from './types' 4 | 5 | export const mutations = { 6 | /** 7 | * Reset state 8 | */ 9 | setReset (state) { 10 | Object.assign(state, defaultState()) 11 | }, 12 | 13 | setMatrix (state, payload) { 14 | state.matrix = payload 15 | }, 16 | 17 | setScale (state, payload) { 18 | state.scale = payload 19 | }, 20 | 21 | setBoxScale (state, payload) { 22 | state.boxScale = payload 23 | }, 24 | 25 | setWireframe (state, payload) { 26 | state.wireframe = payload 27 | }, 28 | 29 | setFlatSurface (state, payload) { 30 | state.flatSurface = payload 31 | } 32 | } satisfies MutationTree 33 | -------------------------------------------------------------------------------- /src/store/timelapse/getters.ts: -------------------------------------------------------------------------------- 1 | import type { GetterTree } from 'vuex' 2 | import type { TimelapseState } from './types' 3 | import type { RootState } from '../types' 4 | 5 | export const getters = { 6 | getLastFrame: (state) => { 7 | const lastFrame = state.lastFrame 8 | 9 | if (lastFrame == null) { 10 | return null 11 | } 12 | 13 | const uniqueCount = +(lastFrame.lastframefile?.match(/\d+/)?.[0] ?? 0) 14 | 15 | return { 16 | count: lastFrame.framecount, 17 | uniqueCount, 18 | file: lastFrame.lastframefile 19 | } 20 | }, 21 | 22 | isBlockedSetting: (state) => (setting: string): boolean => { 23 | return state.settings?.blockedsettings.includes(setting) ?? true 24 | } 25 | } satisfies GetterTree 26 | -------------------------------------------------------------------------------- /docs/features/localization.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Localization 4 | parent: Features 5 | nav_order: 11 6 | permalink: /features/localization 7 | --- 8 | 9 | # Localization 10 | {: .no_toc } 11 | 12 | --- 13 | 14 | Fluidd is equipped with robust localization features to cater to diverse user preferences. 15 | 16 | By default, Fluidd adopts the language set in your browser. If needed, you have the flexibility to override this by specifying a preferred language through interface settings. 17 | 18 | For those who want to contribute and enhance Fluidd's multilingual support, detailed information on localization and translation contributions is available in the [developer localization](/development/localization) docs. 19 | 20 | ![screenshot](/assets/images/localization.png) 21 | -------------------------------------------------------------------------------- /docs/features/multiple_extruders.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Multiple Extruders 4 | parent: Features 5 | nav_order: 10 6 | permalink: /features/multiple_extruders 7 | --- 8 | 9 | # Multiple Extruders 10 | {: .no_toc } 11 | 12 | --- 13 | 14 | Fluidd supports single extruder, multiple extruders, and multiple extruder 15 | stepper configurations. 16 | 17 | There is full support to set Pressure Advance values in all configurations. 18 | 19 | ![screenshot](/assets/images/multiple-extruders.png) 20 | 21 | For multiple extruder steppers configurations, Fluidd will show a section for 22 | each stepper, allowing to enable/disable it, associate with an extruder, and set 23 | the specific Pressure Advance values. 24 | 25 | ![screenshot](/assets/images/multiple-extruder-steppers.png) 26 | -------------------------------------------------------------------------------- /src/store/timelapse/mutations.ts: -------------------------------------------------------------------------------- 1 | import type { MutationTree } from 'vuex' 2 | import { defaultState } from './state' 3 | import type { TimelapseState } from './types' 4 | 5 | export const mutations = { 6 | /** 7 | * Reset state 8 | */ 9 | setReset (state) { 10 | Object.assign(state, defaultState()) 11 | }, 12 | 13 | setSettings (state, payload: Moonraker.Timelapse.SettingsResponse) { 14 | state.settings = payload 15 | }, 16 | 17 | setLastFrame (state, payload: Moonraker.Timelapse.LastFrameInfoResponse) { 18 | state.lastFrame = payload 19 | state.renderStatus = null 20 | }, 21 | 22 | setRenderStatus (state, payload: Moonraker.Timelapse.RenderResponse) { 23 | state.renderStatus = payload 24 | } 25 | } satisfies MutationTree 26 | -------------------------------------------------------------------------------- /src/types/index.ts: -------------------------------------------------------------------------------- 1 | import type { FileSystemDialogData, NewDirectory } from './dialogs' 2 | import type { FileWithPath } from './files' 3 | import type { FlashMessage, FlashMessageTypes } from './flashmessage' 4 | import type { MeshData } from './mesh' 5 | import type { AppDataTableHeader } from './tableheaders' 6 | import type { CameraConnectionStatus, CameraNameMenuItem } from './cameras' 7 | import type { MmuGateDetails, SlicerToolDetails, MmuUnitDetails } from './mmu' 8 | 9 | export type { 10 | FileSystemDialogData, 11 | NewDirectory, 12 | FlashMessage, 13 | FlashMessageTypes, 14 | MeshData, 15 | AppDataTableHeader, 16 | FileWithPath, 17 | CameraConnectionStatus, 18 | CameraNameMenuItem, 19 | MmuGateDetails, 20 | SlicerToolDetails, 21 | MmuUnitDetails 22 | } 23 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | die () { 2 | TEXT=$(cat <<'EOF' 3 | 4 | \e[31mError: Commit messages should follow the conventional commits standard, and should have a Signed-off-by line\e[0m 5 | 6 | Example: 7 | 8 | ``` 9 | feat: My feature. 10 | 11 | Some description. 12 | 13 | Signed-off-by: Your Name 14 | ``` 15 | 16 | EOF 17 | ) 18 | 19 | echo "${TEXT}" 20 | 21 | exit 1 22 | } 23 | 24 | COMMIT_MESSAGE=$(cat $1) 25 | 26 | COMMIT_HEAD="^merge.+|(feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert|types|i18n)(\(.+\))?: .{1,50}" 27 | COMMIT_SIGNED_OFF="signed-off-by: .*<.*@.*>" 28 | 29 | if ! echo "$COMMIT_MESSAGE" | grep -iqE "$COMMIT_HEAD"; then 30 | die 31 | fi 32 | 33 | if ! echo "$COMMIT_MESSAGE" | grep -iqE "$COMMIT_SIGNED_OFF"; then 34 | die 35 | fi 36 | -------------------------------------------------------------------------------- /src/store/mesh/types.ts: -------------------------------------------------------------------------------- 1 | export interface MeshState { 2 | range: number; 3 | wireframe: boolean; 4 | scale: number; 5 | boxScale: number; 6 | flatSurface: boolean; 7 | matrix: MatrixType; 8 | } 9 | 10 | export type MatrixType = 'probed_matrix' | 'mesh_matrix' 11 | 12 | export interface BedMeshProfileListEntry { 13 | name: string; 14 | active: boolean; 15 | adaptive: boolean; 16 | range: number; 17 | } 18 | 19 | export interface AppMeshes { 20 | [index: string]: ProcessedMesh; 21 | } 22 | 23 | export interface ProcessedMesh { 24 | coordinates: MeshCoordinates[]; 25 | dimensions: [number, number]; 26 | range: number; 27 | min: number; 28 | mid: number; 29 | max: number; 30 | } 31 | 32 | export interface MeshCoordinates { 33 | name: string; 34 | value: number[]; 35 | } 36 | -------------------------------------------------------------------------------- /src/monaco/README.md: -------------------------------------------------------------------------------- 1 | ### Monaco Support Credit 2 | A big thank you to; 3 | 4 | - Aerosov for the klipper-config TextMate grammar definition 5 | https://github.com/aeresov/vscode-klipper-config-syntax 6 | 7 | - Applied Engineering & Design for the gcode TextMate grammar definition 8 | https://github.com/appliedengdesign/vscode-gcode-syntax 9 | 10 | - Microsoft for the log grammar definition 11 | https://github.com/microsoft/vscode/blob/main/extensions/log/syntaxes/log.tmLanguage.json 12 | 13 | - NeekSandhu for the monaco textmate documentation and libs 14 | https://github.com/NeekSandhu/monaco-textmate 15 | https://github.com/NeekSandhu/monaco-editor-textmate 16 | 17 | - Nishkalkashyap for the vscode -> monaco theme converter 18 | https://github.com/Nishkalkashyap/monaco-vscode-textmate-theme-converter 19 | -------------------------------------------------------------------------------- /src/dynamicImports.ts: -------------------------------------------------------------------------------- 1 | import { mapKeys } from 'lodash-es' 2 | import type { LocaleMessageObject } from 'vue-i18n' 3 | 4 | const dynamicImportFixKeys = (entries: Record Promise>) => { 5 | return mapKeys( 6 | entries, 7 | (_, key) => key.split('/').pop()?.split('.')[0]) 8 | } 9 | 10 | export const MonacoLanguageImports = Object.freeze(dynamicImportFixKeys( 11 | import.meta.glob('@/monaco/language/*.tmLanguage.json', { import: 'default' }) 12 | )) 13 | 14 | export const I18nLocales = Object.freeze(dynamicImportFixKeys( 15 | import.meta.glob('@/locales/*.yaml', { import: 'default' }) 16 | )) 17 | 18 | export const CameraComponents = Object.freeze(dynamicImportFixKeys( 19 | import.meta.glob('@/components/widgets/camera/services/*Camera.vue') 20 | )) 21 | -------------------------------------------------------------------------------- /src/store/power/mutations.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import type { MutationTree } from 'vuex' 3 | import type { DevicePowerState } from './types' 4 | import { defaultState } from './state' 5 | 6 | export const mutations = { 7 | /** 8 | * Reset state 9 | */ 10 | setReset (state) { 11 | Object.assign(state, defaultState()) 12 | }, 13 | 14 | setDevices (state, payload: Moonraker.Power.DevicesResponse) { 15 | state.devices = payload.devices 16 | }, 17 | 18 | setStatus (state, payload: Moonraker.Power.StatusResponse) { 19 | for (const key in payload) { 20 | const i = state.devices.findIndex(device => device.device === key) 21 | if (i >= 0) { 22 | Vue.set(state.devices[i], 'status', payload[key]) 23 | } 24 | } 25 | } 26 | } satisfies MutationTree 27 | -------------------------------------------------------------------------------- /public/img/icons/safari-pinned-tab.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/store/sensors/actions.ts: -------------------------------------------------------------------------------- 1 | import type { ActionTree } from 'vuex' 2 | import type { MoonrakerSensorsState } from './types' 3 | import type { RootState } from '../types' 4 | import { SocketActions } from '@/api/socketActions' 5 | 6 | export const actions = { 7 | async reset ({ commit }) { 8 | commit('setReset') 9 | }, 10 | 11 | async init () { 12 | SocketActions.serverSensorsList() 13 | }, 14 | 15 | async onSensorsList ({ commit }, payload: { sensors: Moonraker.Sensor.ListResponse }) { 16 | if (payload) { 17 | commit('setSensorsList', payload) 18 | } 19 | }, 20 | 21 | async onSensorUpdate ({ commit }, payload: Record) { 22 | if (payload) { 23 | commit('setSensorUpdate', payload) 24 | } 25 | } 26 | } satisfies ActionTree 27 | -------------------------------------------------------------------------------- /src/views/FullscreenCamera.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 33 | -------------------------------------------------------------------------------- /env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | /// 4 | /// 5 | /// 6 | /// 7 | /// 8 | 9 | declare module '*.yaml' { 10 | const data: unknown 11 | export default data 12 | } 13 | 14 | declare module '@/locales/*.yaml' { 15 | import type { LocaleMessageObject } from 'vue-i18n' 16 | 17 | const data: LocaleMessageObject 18 | export default data 19 | } 20 | 21 | interface ImportMetaEnv { 22 | readonly VUE_APP_I18N_LOCALE?: string 23 | readonly VUE_APP_I18N_FALLBACK_LOCALE?: string 24 | readonly VERSION: string 25 | readonly HASH: string 26 | } 27 | 28 | interface ImportMeta { 29 | readonly env: ImportMetaEnv 30 | } 31 | -------------------------------------------------------------------------------- /src/views/History.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 31 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "Serve", 6 | "type": "npm", 7 | "script": "serve", 8 | "problemMatcher": [ 9 | "$vite" 10 | ], 11 | "isBackground": true 12 | }, 13 | { 14 | "label": "Build", 15 | "type": "npm", 16 | "script": "build", 17 | "problemMatcher": [] 18 | }, 19 | { 20 | "label": "Type check", 21 | "type": "npm", 22 | "script": "type-check", 23 | "problemMatcher": [] 24 | }, 25 | { 26 | "label": "Circular reference check", 27 | "type": "npm", 28 | "script": "circular-check", 29 | "problemMatcher": [] 30 | }, 31 | { 32 | "label": "Lint", 33 | "type": "npm", 34 | "script": "lint", 35 | "problemMatcher": [] 36 | } 37 | ] 38 | } 39 | -------------------------------------------------------------------------------- /src/store/mesh/actions.ts: -------------------------------------------------------------------------------- 1 | import type { ActionTree } from 'vuex' 2 | import type { MeshState } from './types' 3 | import type { RootState } from '../types' 4 | 5 | export const actions = { 6 | /** 7 | * Reset our store 8 | */ 9 | async reset ({ commit }) { 10 | commit('setReset') 11 | }, 12 | 13 | async onMatrix ({ commit }, payload) { 14 | commit('setMatrix', payload) 15 | }, 16 | 17 | async onScale ({ commit }, payload) { 18 | commit('setScale', payload) 19 | }, 20 | 21 | async onBoxScale ({ commit }, payload) { 22 | commit('setBoxScale', payload) 23 | }, 24 | 25 | async onWireframe ({ commit }, payload) { 26 | commit('setWireframe', payload) 27 | }, 28 | 29 | async onFlatSurface ({ commit }, payload) { 30 | commit('setFlatSurface', payload) 31 | } 32 | } satisfies ActionTree 33 | -------------------------------------------------------------------------------- /src/components/ui/AppChipColor.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 25 | 26 | 39 | -------------------------------------------------------------------------------- /src/store/diagnostics/types.ts: -------------------------------------------------------------------------------- 1 | import type { LayoutConfig } from '@/store/layout/types' 2 | 3 | export interface DiagnosticsCardContainer { 4 | [key: string]: DiagnosticsCardConfig[] 5 | } 6 | 7 | export interface DiagnosticsCardConfig extends LayoutConfig { 8 | icon: string 9 | title: string 10 | height: number 11 | 12 | axes: ChartAxis[] 13 | } 14 | 15 | export interface ChartAxis { 16 | enabled: boolean 17 | unit: string 18 | min?: number 19 | max?: number 20 | showLegend: boolean 21 | 22 | metrics: Metric[] 23 | } 24 | 25 | export interface Metric { 26 | collector: string 27 | name: string 28 | style: MetricStyle 29 | } 30 | 31 | export interface MetricStyle { 32 | lineColor: string 33 | lineStyle: 'solid' | 'dashed' | 'dotted' 34 | fillColor: string | null 35 | fillOpacity: number 36 | displayLegend: boolean 37 | } 38 | -------------------------------------------------------------------------------- /src/components/layout/AppFooter.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 31 | 32 | 40 | -------------------------------------------------------------------------------- /src/types/mmu.ts: -------------------------------------------------------------------------------- 1 | export interface MmuGateDetails { 2 | index: number 3 | status: number 4 | filamentName: string 5 | material: string 6 | color: string 7 | temperature: number 8 | spoolId: number | null 9 | speedOverride: number 10 | endlessSpoolGroup: number | null 11 | } 12 | 13 | export interface SlicerToolDetails { 14 | color: string 15 | material: string 16 | temp: number 17 | name: string 18 | inUse: boolean 19 | } 20 | 21 | export interface MmuUnitDetails { 22 | name: string 23 | vendor: string 24 | version: string 25 | numGates: number 26 | firstGate: number 27 | selectorType: string 28 | variableRotationDistances: boolean 29 | variableBowdenLengths: boolean 30 | requireBowdenMove: boolean 31 | filamentAlwaysGripped: boolean 32 | hasBypass: boolean 33 | multiGear: boolean 34 | environmentSensor: string 35 | } 36 | -------------------------------------------------------------------------------- /src/util/is-loopback.ts: -------------------------------------------------------------------------------- 1 | import { IPv4, IPv6 } from 'ipaddr.js' 2 | 3 | const isLoopback = (hostname: string) => { 4 | hostname = hostname.trim().toLowerCase() 5 | 6 | if (hostname.startsWith('[') && hostname.endsWith(']')) { 7 | hostname = hostname.slice(1, -1) 8 | } 9 | 10 | if (hostname === 'localhost') { 11 | return true 12 | } 13 | 14 | if (IPv4.isValid(hostname)) { 15 | return IPv4.parse(hostname).range() === 'loopback' 16 | } 17 | 18 | if (IPv6.isValid(hostname)) { 19 | const ipAddress = IPv6.parse(hostname) 20 | 21 | switch (ipAddress.range()) { 22 | case 'loopback': 23 | return true 24 | 25 | case 'ipv4Mapped': 26 | return ( 27 | ipAddress.toIPv4Address().range() === 'loopback' 28 | ) 29 | } 30 | } 31 | 32 | return false 33 | } 34 | 35 | export default isLoopback 36 | -------------------------------------------------------------------------------- /src/components/widgets/filesystem/setupMonaco.features.ts: -------------------------------------------------------------------------------- 1 | import 'monaco-editor/esm/vs/editor/editor.all.js' 2 | 3 | // full list of features on 'monaco-editor/esm/metadata.js' 4 | import 'monaco-editor/esm/vs/editor/standalone/browser/iPadShowKeyboard/iPadShowKeyboard.js' 5 | import 'monaco-editor/esm/vs/editor/standalone/browser/quickAccess/standaloneGotoLineQuickAccess.js' 6 | import 'monaco-editor/esm/vs/editor/standalone/browser/quickAccess/standaloneGotoSymbolQuickAccess.js' 7 | import 'monaco-editor/esm/vs/editor/standalone/browser/quickAccess/standaloneCommandsQuickAccess.js' 8 | 9 | import 'monaco-editor/esm/vs/language/css/monaco.contribution' 10 | import 'monaco-editor/esm/vs/language/json/monaco.contribution' 11 | import 'monaco-editor/esm/vs/basic-languages/css/css.contribution' 12 | import 'monaco-editor/esm/vs/basic-languages/markdown/markdown.contribution' 13 | -------------------------------------------------------------------------------- /docs/updates/automated.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Automated 4 | parent: Updates 5 | nav_order: 1 6 | permalink: /updates/automated 7 | --- 8 | 9 | # Automated Updates 10 | {: .no_toc } 11 | 12 | --- 13 | 14 | Fluidd allows you to update all of its components, including the host system in an automated way. 15 | It will notify you of available updates - and provide buttons to upgrade each individual component. 16 | 17 | The recommended order of updates should be; 18 | 19 | 1. Klipper 20 | 2. Moonraker 21 | 3. Fluidd 22 | 4. Other clients 23 | 24 | Klipper can be skipped if you have a reason to not update klipper immediately. 25 | 26 | In order for this feature to be enabled, you need to configure moonraker's update plugin. 27 | 28 | Please see here for the [moonraker configuration](/configuration/moonraker_conf) docs. 29 | 30 | ![screenshot](../assets/images/updates.png) 31 | -------------------------------------------------------------------------------- /src/components/ui/AppDataTableCellColors.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 34 | -------------------------------------------------------------------------------- /src/store/notifications/state.ts: -------------------------------------------------------------------------------- 1 | import type { NotificationsState } from './types' 2 | 3 | export const defaultState = (): NotificationsState => { 4 | return { 5 | notifications: [] 6 | 7 | // status: 8 | // printer.mcu.last_stats 9 | // printer.system_stats 10 | 11 | // notifications: 12 | // notify_cpu_throttled (to detect a throttle condition like under volt etc) 13 | 14 | // actions: 15 | // machine.proc.stats 16 | 17 | // cpu overtemp 18 | // mcu overtemp 19 | 20 | // mcu awake time graphing 21 | 22 | // If it was me, I'd probably just start with displaying everything on a single graph with 100% being one core usage. So, just graph sysload outright (sysload * 100), cputime change ((cputime - last_cputime) * 100), mcu_awake (formula above), mcu load (formula above). 23 | 24 | } 25 | } 26 | 27 | export const state = defaultState() 28 | -------------------------------------------------------------------------------- /src/store/socket/state.ts: -------------------------------------------------------------------------------- 1 | import type { SocketState } from './types' 2 | 3 | export const defaultState = (): SocketState => { 4 | return { 5 | apiConnected: true, // api is connected, socket may not be. 6 | open: false, // socket is open or closed. 7 | connecting: false, // socket is trying to connect. 8 | disconnecting: false, // indicates we know a disconnect is coming, and to retry. 9 | ready: false, // indicates the socket is ready (and has first dump of data...) 10 | acceptingNotifications: false, // indicates we're accepting notification data because we've finished subscribing to objects 11 | error: null, // if the socket has an error or not 12 | connectionId: null // connection id assigned to the socket 13 | } 14 | } 15 | 16 | export const state = defaultState() 17 | -------------------------------------------------------------------------------- /public/logo_ratrig.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /server/nginx/default.conf.template: -------------------------------------------------------------------------------- 1 | server { 2 | listen ${PORT} default_server; 3 | listen [::]:${PORT} default_server; 4 | 5 | access_log /var/log/nginx/access.log; 6 | error_log /var/log/nginx/error.log; 7 | 8 | gzip on; 9 | gzip_vary on; 10 | gzip_proxied any; 11 | gzip_proxied expired no-cache no-store private auth; 12 | gzip_comp_level 4; 13 | gzip_buffers 16 8k; 14 | gzip_http_version 1.1; 15 | gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/json application/xml; 16 | 17 | root /usr/share/nginx/html; 18 | 19 | index index.html; 20 | server_name _; 21 | 22 | client_max_body_size 0; 23 | 24 | proxy_request_buffering off; 25 | 26 | location / { 27 | try_files $uri $uri/ /index.html; 28 | } 29 | 30 | location = /index.html { 31 | add_header Cache-Control "no-store, no-cache, must-revalidate"; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/util/gcode-macro-params.ts: -------------------------------------------------------------------------------- 1 | const paramRegExp = /params\.(\w+\b)(?!\s*\()(.*)/gi 2 | const defaultValueRegExp = /\|\s*default\s*\(\s*((["'])(?:\\\2|(?!\2).)*\2|-?\d[^,)]*)/i 3 | 4 | export const gcodeMacroParamDefault = (param: string) => { 5 | const parsedValue = defaultValueRegExp.exec(param) 6 | 7 | if (!parsedValue) { 8 | return '' 9 | } 10 | 11 | const [, value, quoteChar] = parsedValue 12 | 13 | if (quoteChar) { 14 | return value.substring(1, value.length - 1) 15 | .replace(new RegExp(`\\\\${quoteChar}`, 'g'), quoteChar) 16 | .replace(/\\\\/g, '\\') 17 | } 18 | 19 | return (value || '').trim() 20 | } 21 | 22 | const gcodeMacroParams = (gcode: string) => { 23 | return [...gcode.matchAll(paramRegExp)] 24 | .map(([, name, rest]) => ({ 25 | name, 26 | value: gcodeMacroParamDefault(rest) 27 | })) 28 | } 29 | 30 | export default gcodeMacroParams 31 | -------------------------------------------------------------------------------- /src/util/get-klipper-type.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Map object prefixes and names to their generic types. 3 | * I.e., temperature_fans and heater_fans are both fans. 4 | */ 5 | export default (name: string) => { 6 | const fans = [ 7 | 'temperature_fan', 8 | 'controller_fan', 9 | 'heater_fan', 10 | 'fan_generic', 11 | 'fan' 12 | ] 13 | 14 | const sensors = [ 15 | 'temperature_sensor', 16 | 'temperature_probe', 17 | 'tmc2240', 18 | 'z_thermal_adjust' 19 | ] 20 | 21 | const heaters = [ 22 | 'heater_generic', 23 | 'extruder' 24 | ] 25 | 26 | const beds = [ 27 | 'heater_bed' 28 | ] 29 | 30 | if (fans.some(s => name.startsWith(s))) return 'fan' 31 | if (sensors.some(s => name.startsWith(s))) return 'sensor' 32 | if (heaters.some(s => name.startsWith(s))) return 'heater' 33 | if (beds.some(s => name.startsWith(s))) return 'bed' 34 | return '' 35 | } 36 | -------------------------------------------------------------------------------- /src/store/database/actions.ts: -------------------------------------------------------------------------------- 1 | import type { ActionTree } from 'vuex' 2 | import type { DatabaseInfo, DatabaseState } from './types' 3 | import type { RootState } from '../types' 4 | import { SocketActions } from '@/api/socketActions' 5 | 6 | export const actions = { 7 | async reset ({ commit }) { 8 | commit('setReset') 9 | }, 10 | 11 | async init () { 12 | SocketActions.serverDatabaseList() 13 | }, 14 | 15 | async onServerDatabaseList ({ commit }, payload: DatabaseInfo) { 16 | commit('setServerDatabaseList', payload) 17 | }, 18 | 19 | async onServerDatabasePostBackup ({ commit }, payload: { backup_path: string }) { 20 | commit('setServerDatabasePostBackup', payload) 21 | }, 22 | 23 | async onServerDatabaseDeleteBackup ({ commit }, payload: { backup_path: string }) { 24 | commit('setServerDatabaseDeleteBackup', payload) 25 | } 26 | } satisfies ActionTree 27 | -------------------------------------------------------------------------------- /src/util/sleep.ts: -------------------------------------------------------------------------------- 1 | import { consola } from 'consola' 2 | 3 | const sleep = (ms: number, signal?: AbortSignal) => { 4 | const debug = (message: string, ...args: unknown[]) => consola.debug(`[sleep] ${message}`, ...args) 5 | 6 | return new Promise((resolve, reject) => { 7 | debug(`sleeping for ${ms}...`) 8 | 9 | const dispose = () => { 10 | signal?.removeEventListener('abort', abortHandler) 11 | 12 | clearTimeout(timeoutId) 13 | } 14 | 15 | const abortHandler = () => { 16 | debug('aborted') 17 | 18 | dispose() 19 | 20 | reject(new Error('AbortError')) 21 | } 22 | 23 | const timeoutHandler = () => { 24 | debug('timed out') 25 | 26 | dispose() 27 | 28 | resolve(null) 29 | } 30 | 31 | signal?.addEventListener('abort', abortHandler) 32 | 33 | const timeoutId = setTimeout(timeoutHandler, ms) 34 | }) 35 | } 36 | 37 | export default sleep 38 | -------------------------------------------------------------------------------- /src/util/get-file-paths.ts: -------------------------------------------------------------------------------- 1 | import { Globals } from '@/globals' 2 | import type { FilePaths } from '@/store/files/types' 3 | 4 | const getFilePaths = (pathFilename: string, root = ''): FilePaths => { 5 | const pathParts = pathFilename.split('/') 6 | const filtered = Globals.FILTERED_FOLDER_NAMES.some(x => pathParts.includes(x)) 7 | const filename = pathParts.pop() ?? '' 8 | const path = pathParts.join('/') 9 | 10 | const rootPath = root && path ? `${root}/${path}` : root || path 11 | const rootPathFilename = rootPath ? `${rootPath}/${filename}` : filename 12 | 13 | const extensionIndex = filename.lastIndexOf('.') 14 | const extension = extensionIndex >= 0 ? filename.substring(extensionIndex) : '' 15 | 16 | return { 17 | root, 18 | rootPath, 19 | rootPathFilename, 20 | path, 21 | pathFilename, 22 | filename, 23 | extension, 24 | filtered 25 | } 26 | } 27 | 28 | export default getFilePaths 29 | -------------------------------------------------------------------------------- /.github/workflows/semantic_pull_request.yml: -------------------------------------------------------------------------------- 1 | name: Semantic Pull Request 2 | 3 | on: 4 | pull_request_target: 5 | types: 6 | - opened 7 | - reopened 8 | - edited 9 | - synchronize 10 | 11 | jobs: 12 | main: 13 | name: Semantic Pull Request 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Validate PR title 17 | uses: amannn/action-semantic-pull-request@v5 18 | with: 19 | types: | 20 | feat 21 | fix 22 | docs 23 | style 24 | refactor 25 | perf 26 | test 27 | build 28 | ci 29 | chore 30 | revert 31 | types 32 | i18n 33 | requireScope: false 34 | ignoreLabels: | 35 | bot 36 | ignore-semantic-pull-request 37 | env: 38 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 39 | -------------------------------------------------------------------------------- /docs/sponsors/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Sponsors 4 | nav_order: 9 5 | has_children: false 6 | permalink: /sponsors 7 | --- 8 | 9 | # Sponsors 10 | 11 | Fluidd is a free open source project. 12 | 13 | The development of Fluidd is only possible with the generous support from sponsors. 14 | 15 | ## LDO 16 | 17 | [![LDO Motors](../assets/images/logo_ldo.svg "LDO Motors")](https://ldomotors.com/) 18 | 19 | LDO, Excellence in Motion. LDO is an official Sponsor of Fluidd. 20 | 21 | ## Support Fluidd development 22 | 23 | Fluidd development is driven by passionate volunteers who dedicate their time to improving and expanding its capabilities. 24 | 25 | Your sponsorship can help us enhance Fluidd, introduce new features, and ensure it remains accessible to all Klipper users. 26 | 27 | Your support can make a significant impact on the evolution of Fluidd. Please consider [sponsoring Fluidd](https://github.com/sponsors/fluidd-core). 28 | -------------------------------------------------------------------------------- /src/store/analysis/actions.ts: -------------------------------------------------------------------------------- 1 | import type { ActionTree } from 'vuex' 2 | import type { AnalysisState } from './types' 3 | import type { RootState } from '../types' 4 | import { SocketActions } from '@/api/socketActions' 5 | import type { ObjectWithRequest } from '@/plugins/socketClient' 6 | 7 | export const actions = { 8 | async reset ({ commit }) { 9 | commit('setReset') 10 | }, 11 | 12 | async onAnalysisStatus ({ commit }, payload: Moonraker.Analysis.StatusResponse) { 13 | if (payload) { 14 | commit('setAnalysisStatus', payload) 15 | } 16 | }, 17 | 18 | async onAnalysisProcess (_, payload: ObjectWithRequest) { 19 | if (payload) { 20 | const { filename } = payload.__request__.params ?? {} 21 | 22 | if (!payload.bypassed) { 23 | SocketActions.serverFilesMetadata(filename) 24 | } 25 | } 26 | } 27 | } satisfies ActionTree 28 | -------------------------------------------------------------------------------- /src/typings/moonraker.sensor.d.ts: -------------------------------------------------------------------------------- 1 | declare namespace Moonraker.Sensor { 2 | export interface ListResponse { 3 | sensors: Record; 4 | } 5 | 6 | export interface Entry { 7 | id: string; 8 | friendly_name: string; 9 | type: string; 10 | values: Values; 11 | parameter_info?: ParameterInfo[]; 12 | history_fields?: HistoryField[]; 13 | } 14 | 15 | export interface Values extends Record { 16 | } 17 | 18 | export interface ParameterInfo extends Record { 19 | name: string; 20 | } 21 | 22 | export interface HistoryField { 23 | field: string; 24 | provider: string; 25 | description: string; 26 | strategy: string; 27 | units: string | null; 28 | init_tracker: boolean; 29 | exclude_paused: boolean; 30 | report_total: boolean; 31 | report_maximum: boolean; 32 | precision: number; 33 | parameter: string; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/store/wait/getters.ts: -------------------------------------------------------------------------------- 1 | import type { GetterTree } from 'vuex' 2 | import type { WaitState } from './types' 3 | import type { RootState } from '../types' 4 | 5 | export const getters = { 6 | /** 7 | * Determine if we have a specific wait, or list of waits active or not. 8 | */ 9 | hasWait: (state) => (wait: string | string[]): boolean => { 10 | if (Array.isArray(wait)) { 11 | return wait 12 | .some(val => state.waits.includes(val)) 13 | } else { 14 | return state.waits.includes(wait) 15 | } 16 | }, 17 | 18 | /** 19 | * Determine if we have any waits. 20 | */ 21 | hasWaits: (state) => { 22 | return state.waits.length > 0 23 | }, 24 | 25 | /** 26 | * Determine if we have any waits prefixed with... 27 | */ 28 | hasWaitsBy: (state) => (prefix: string) => { 29 | return state.waits 30 | .some(wait => wait.startsWith(prefix)) 31 | } 32 | } satisfies GetterTree 33 | -------------------------------------------------------------------------------- /public/logo_fluidd.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/scss/inputs.scss: -------------------------------------------------------------------------------- 1 | /* Extra dense text inputs */ 2 | .v-input--text-right input { 3 | text-align: right; 4 | } 5 | 6 | .v-input--width-x-small { 7 | width: 100px; 8 | } 9 | 10 | .v-input--width-small { 11 | width: 140px; 12 | } 13 | 14 | .v-input--width-medium { 15 | width: 180px; 16 | } 17 | 18 | .v-input--x-dense { 19 | .v-input__slot { 20 | min-height: 25px !important; 21 | } 22 | 23 | .v-select__selections { 24 | padding: 2px 0 !important; 25 | } 26 | 27 | .v-input__append-inner { 28 | margin-top: 6px !important; 29 | } 30 | } 31 | 32 | .v-input--dense { 33 | .v-input__slot { 34 | min-height: 36px !important; 35 | } 36 | 37 | .v-input__append-inner { 38 | margin-top: 6px !important; 39 | } 40 | } 41 | 42 | .v-text-field.v-text-field--solo .v-input__control{ 43 | min-height: 10px; 44 | } 45 | .v-input--dense .v-text-field--box .v-input__slot, .v-text-field--outline .v-input__slot { 46 | min-height: 30px !important; 47 | } 48 | -------------------------------------------------------------------------------- /src/components/widgets/filesystem/FileEditorTextOnly.vue: -------------------------------------------------------------------------------- 1 |