Skip to content

feat: Add plugin navigation support via .theme/navi.json#1802

Open
bassco wants to merge 2 commits intofluidd-core:developfrom
bassco:develop
Open

feat: Add plugin navigation support via .theme/navi.json#1802
bassco wants to merge 2 commits intofluidd-core:developfrom
bassco:develop

Conversation

@bassco
Copy link

@bassco bassco commented Mar 18, 2026

Summary

Adds support for external navigation items in the sidebar through a .theme/navi.json configuration file. This allows plugins like KlipperFleet to add navigation links to Fluidd without modifying Fluidd's core code.

Motivation

Users running multiple web interfaces (e.g., Fluidd, Mainsail, KlipperFleet) want seamless navigation between them. This feature provides a standardized way for external applications to register navigation items in Fluidd's sidebar.

Implementation

The implementation follows the same format used by Mainsail's .theme/navi.json:

[
  {
    "title": "KlipperFleet",
    "href": "/klipperfleet.html",
    "target": "_self",
    "icon": "mdi-flash",
    "position": 201
  }
]

Files Changed

  • src/store/plugins/ - New Vuex module for plugin navigation
    • types.ts - TypeScript interfaces
    • state.ts - Default state
    • getters.ts - Computed nav points
    • actions.ts - Fetch navi.json from Moonraker
    • mutations.ts - State mutations
    • index.ts - Module export
  • src/components/ui/AppNavItemExternal.vue - New component for external nav links
  • src/components/layout/AppNavDrawer.vue - Integrate navi points into sidebar
  • src/store/index.ts - Register plugins module
  • src/store/types.ts - Add plugins state type
  • src/locales/en.yaml - Add i18n keys
  • src/store/plugins/tests/plugins.spec.ts - Unit tests

How It Works

  1. On socket connect, Fluidd fetches config/.theme/navi.json from Moonraker
  2. If the file exists and contains an array, nav items are added to the sidebar
  3. External nav items appear in the sidebar with a link icon
  4. Items are sorted by position value

Configuration

Place a navi.json file in your config directory at .theme/navi.json:

[
  {
    "title": "KlipperFleet",
    "href": "/klipperfleet.html",
    "target": "_self",
    "icon": "mdi-flash",
    "position": 201
  }
]

Related

Add support for loading external navigation items from .theme/navi.json,
enabling plugins like KlipperFleet to add sidebar navigation.

- Add plugins Vuex module for managing external nav points
- Add AppNavItemExternal component for external nav links
- Integrate with AppNavDrawer to display plugin navigation items
- Add unit tests for the plugins store module
- Add i18n keys for external navigation tooltip

Signed-off-by: changeme <changeme@users.noreply.github.com>
@bassco
Copy link
Author

bassco commented Mar 18, 2026

Testing

Unit Tests

All 91 tests pass including new tests for the plugins module covering:

  • Fetching navi.json (success and error cases)
  • Parsing nav points
  • Getter behavior
  • State reset

E2E Test Plan

Verified via Playwright automation that the full navigation flow works:

  1. Fluidd (port :81) - KlipperFleet nav item visible in sidebar
  2. Click KlipperFleet -> navigates to KlipperFleet (port :8321)
  3. Click "Return to Printer UI" -> returns to Fluidd (port :81)

Screenshots:

@bassco
Copy link
Author

bassco commented Mar 18, 2026

Screenshots

1. Fluidd sidebar with KlipperFleet navigation item:

fluidd-nav

2. KlipperFleet loaded after clicking nav item:

step2-klipperfleet

3. Returned to Fluidd after clicking Return to Printer UI:

step4-final

@bassco
Copy link
Author

bassco commented Mar 18, 2026

Addressing the build errors.

@bassco
Copy link
Author

bassco commented Mar 18, 2026

Type-check fix

The vue-tsc --build --noEmit type-check was failing with 2012 errors after the plugin navigation commit (460cb350).

Root cause: The new test file at src/store/plugins/__tests__/plugins.spec.ts is included by tsconfig.vitest.json via src/**/__tests__/*. This test imports store modules which transitively pull in Vue augmentations (declare module 'vue/types/vue') and ambient namespaces (Moonraker, Klipper). The vitest tsconfig has "types": ["node", "jsdom"] and "lib": [], creating a restricted type environment that breaks resolution of those augmentations in vue-tsc --build mode — cascading into 2012 errors across the entire codebase.

Fix: Exclude src/store/**/__tests__/* from tsconfig.vitest.json type-checking scope. Tests still run normally via vitest. Also added missing trailing commas in RootModulesType.

Changes:

  • tsconfig.vitest.json — added "exclude": ["src/store/**/__tests__/*"]
  • src/store/types.ts — trailing comma consistency

@bassco
Copy link
Author

bassco commented Mar 18, 2026

Ooh, looks like I overlooked #1786 that has similar functionality and it also adding additional links. Looks more promising that what I have come up with. Will pull it down and play with it to see if the .theme/navi.json approach can be incorporated, or how to get KlipperFleet to install into the config.

Exclude store test files from vitest tsconfig type-checking scope.
Store tests that import modules pulling in Vue augmentations and
ambient namespaces cause 2012 type errors in vue-tsc --build mode
due to the vitest project's restricted type environment. Tests
still run normally via vitest. Also add missing trailing commas
in RootModulesType.

Signed-off-by: Andrew Basson <andrew.basson@gmail.com>
@bassco
Copy link
Author

bassco commented Mar 18, 2026

Update: KlipperFleet installer approach if #1786 lands

After reviewing #1786, the CustomNavLink type and Moonraker DB storage at uiSettings.navigation.customLinks would allow KlipperFleet to inject its sidebar link directly during install — no fluidd code changes needed.

Install

# Read existing links, append KlipperFleet, write back
existing=$(curl -s "http://localhost:7125/server/database/item?namespace=fluidd&key=uiSettings.navigation.customLinks" \
  | jq -r '.result.value // []')

updated=$(echo "$existing" | jq '. + [{
  "id": "klipperfleet",
  "title": "KlipperFleet",
  "url": "/klipperfleet.html",
  "icon": "mdi-ferry",
  "position": 86
}] | unique_by(.id)')

curl -s -X POST "http://localhost:7125/server/database/item" \
  -H "Content-Type: application/json" \
  -d "{\"namespace\": \"fluidd\", \"key\": \"uiSettings.navigation.customLinks\", \"value\": $updated}"

Uninstall

# Remove KlipperFleet entry, preserve other links
existing=$(curl -s "http://localhost:7125/server/database/item?namespace=fluidd&key=uiSettings.navigation.customLinks" \
  | jq -r '.result.value // []')

updated=$(echo "$existing" | jq '[.[] | select(.id != "klipperfleet")]')

curl -s -X POST "http://localhost:7125/server/database/item" \
  -H "Content-Type: application/json" \
  -d "{\"namespace\": \"fluidd\", \"key\": \"uiSettings.navigation.customLinks\", \"value\": $updated}"

What this means

bassco added a commit to bassco/KlipperFleet that referenced this pull request Mar 18, 2026
Add Fluidd navigation integration alongside the existing Mainsail
navi.json approach. Uses Moonraker's database API to inject a
KlipperFleet CustomNavLink entry (requires fluidd-core/fluidd#1786).

- setup_fluidd_navi.py: install/remove nav link via Moonraker DB API
- setup_moonraker.py: add/remove persistent_files for any update_manager
  section, used to preserve klipperfleet.html across Fluidd updates
- install.sh: deploy redirect shim to Fluidd web root + register as
  persistent_files in [update_manager fluidd]
- uninstall.sh: reverse all of the above

Reference: fluidd-core/fluidd#1802 (comment)
Signed-off-by: Andrew Basson <andrew.basson@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant