From fbed6a904a792a0767c6ada433311a8976235ad9 Mon Sep 17 00:00:00 2001
From: paxx12 <245230251+paxx12@users.noreply.github.com>
Date: Mon, 1 Jun 2026 10:11:07 +0200
Subject: [PATCH 1/3] feat(afc): Enhance Spoolman integration in AFC widgets
Align AFC lane body with Spoolman data: use information from Spoolman
or directly from the AFC component instead of deducing values or
hardcoding defaults such as `initial_weight` of 1000.
Expand the spool reel tooltip to show ID, vendor, filament name,
material with extruder/bed temps, and remaining/used weight from
Spoolman when available.
Make the filament name row a clickable link to `/spool/show/{id}`
on the Spoolman UI when configured; falls back to plain text otherwise.
Add `initial_weight` and `filament_name` to `AfcLaneState` typings.
Signed-off-by: paxx12 <245230251+paxx12@users.noreply.github.com>
---
.../widgets/afc/AfcCardUnitLaneBody.vue | 89 ++++++++++++++-----
src/locales/en.yaml | 1 -
src/typings/klipper.d.ts | 2 +
3 files changed, 67 insertions(+), 25 deletions(-)
diff --git a/src/components/widgets/afc/AfcCardUnitLaneBody.vue b/src/components/widgets/afc/AfcCardUnitLaneBody.vue
index 80626eabfd..a4792455ea 100644
--- a/src/components/widgets/afc/AfcCardUnitLaneBody.vue
+++ b/src/components/widgets/afc/AfcCardUnitLaneBody.vue
@@ -21,9 +21,26 @@
- #{{ spoolId }} | {{ spoolVendor }}
+ ID #{{ spoolId }}
- {{ spoolFilamentName }}
+ {{ spoolFilamentVendor }} — {{ spoolFilamentName }}
+
+
+ {{ spoolMaterial }}
+
+ | {{ spoolExtruderTemp }}°C
+
+
+ | {{ spoolBedTemp }}°C
+
+
+
+
+ {{ Math.round(spoolRemainingWeight) }} g remaining
+
+ ({{ Math.round(spoolUsedWeight) }} g used)
+
+
- {{ spoolRemainingWeightOutput }}
+ {{ Math.round(spoolRemainingWeight) }} g
+ --
@@ -147,39 +172,51 @@ export default class AfcCardUnitLaneBody extends Mixins(StateMixin, AfcMixin) {
return this.lane?.color || '#000000'
}
- get spoolRemainingWeight (): number {
- if (this.afcExistsSpoolman && this.spool?.remaining_weight != null) {
- return Math.round(this.spool.remaining_weight)
- }
- return Math.round(this.lane?.weight ?? 0)
- }
-
- get spoolRemainingWeightOutput (): string {
- return `${this.spoolRemainingWeight} g`
+ get spoolRemainingWeight (): number | undefined {
+ return this.spool?.remaining_weight ?? this.lane?.weight
}
- get spoolFullWeight (): number {
- return this.spool?.initial_weight ?? 1000
+ get spoolFullWeight (): number | undefined {
+ return this.spool?.initial_weight ?? this.lane?.initial_weight
}
get spoolPercent (): number {
+ if (this.spoolRemainingWeight == null || this.spoolFullWeight == null) return 100
if (this.spoolFullWeight === 0) return 100
return Math.round((this.spoolRemainingWeight / this.spoolFullWeight) * 100)
}
get spoolMaterial (): string {
- return this.lane?.material || '--'
+ return this.spool?.filament?.material ?? this.lane?.material ?? ''
+ }
+
+ get spoolFilamentVendor (): string | undefined {
+ return this.spool?.filament?.vendor?.name
+ }
+
+ get spoolFilamentName (): string | undefined {
+ return this.spool?.filament?.name ||
+ this.lane?.filament_name ||
+ undefined
+ }
+
+ get spoolUrl (): string | undefined {
+ const base: string | undefined = this.$typedGetters['spoolman/getSpoolmanUrl']
+ if (!base || !this.spoolId) return undefined
+ return `${base.replace(/\/$/, '')}/spool/show/${this.spoolId}`
}
- get spoolVendor (): string {
- return this.spool?.filament?.vendor?.name ?? this.$t('app.afc.Unknown').toString()
+ get spoolExtruderTemp (): number | undefined {
+ return this.spool?.filament?.settings_extruder_temp
}
- get spoolFilamentName (): string {
- return this.afcExistsSpoolman
- ? this.spool?.filament?.name ?? this.$t('app.afc.Unknown').toString()
- : ''
+ get spoolBedTemp (): number | undefined {
+ return this.spool?.filament?.settings_bed_temp
+ }
+
+ get spoolUsedWeight (): number | undefined {
+ return this.spool?.used_weight
}
get tdPresent (): boolean {
@@ -246,4 +283,8 @@ export default class AfcCardUnitLaneBody extends Mixins(StateMixin, AfcMixin) {
.position-relative {
position: relative !important;
}
+
+.filament-link {
+ color: inherit !important;
+}
diff --git a/src/locales/en.yaml b/src/locales/en.yaml
index a11cdd2c06..9258f7c8cc 100644
--- a/src/locales/en.yaml
+++ b/src/locales/en.yaml
@@ -86,7 +86,6 @@ app:
ShowTd1Color: "Show TD-1 Color"
Unloading: "Unloading"
UnloadLane: "Unload Lane"
- Unknown: "Unknown"
Weight: "Weight"
WeightSubtitle: "The weight of the filament in grams (without spool)."
bedmesh:
diff --git a/src/typings/klipper.d.ts b/src/typings/klipper.d.ts
index 0a22a085eb..872279e69c 100644
--- a/src/typings/klipper.d.ts
+++ b/src/typings/klipper.d.ts
@@ -815,9 +815,11 @@ declare namespace Klipper {
density?: number;
diameter?: number;
empty_spool_weight?: number;
+ initial_weight?: number;
spool_id: number | null;
color: string | null;
weight: number;
+ filament_name?: string;
extruder_temp: number | null;
runout_lane: string | null;
filament_status: 'In Tool' | 'Ready' | 'Prep' | 'Not Ready';
From b441fec53c509e41b826c1dc09b8e5eb95f27023 Mon Sep 17 00:00:00 2001
From: paxx12 <245230251+paxx12@users.noreply.github.com>
Date: Mon, 1 Jun 2026 12:19:48 +0200
Subject: [PATCH 2/3] fix(afc): Address PR review comments in
`AfcCardUnitLaneBody`
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- Add `rel="noopener noreferrer"` to external Spoolman link to prevent
reverse-tabnabbing via `target="_blank"`
- Keep `!this.spoolId` guard in `spoolUrl` — spool ID `0` is invalid in
Spoolman so the falsy check is correct and consistent with `spool` getter
- Replace hard-coded English strings in tooltip with i18n keys
(`app.afc.WeightRemaining`, `app.afc.WeightUsed`); fix `spoolUsedWeight`
guard from truthy to `!= null` so `0 g used` is displayed correctly
Signed-off-by: paxx12 <245230251+paxx12@users.noreply.github.com>
---
src/components/widgets/afc/AfcCardUnitLaneBody.vue | 7 ++++---
src/locales/en.yaml | 2 ++
2 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/src/components/widgets/afc/AfcCardUnitLaneBody.vue b/src/components/widgets/afc/AfcCardUnitLaneBody.vue
index a4792455ea..7a5eef63bf 100644
--- a/src/components/widgets/afc/AfcCardUnitLaneBody.vue
+++ b/src/components/widgets/afc/AfcCardUnitLaneBody.vue
@@ -36,9 +36,9 @@
- {{ Math.round(spoolRemainingWeight) }} g remaining
-
- ({{ Math.round(spoolUsedWeight) }} g used)
+ {{ $t('app.afc.WeightRemaining', { weight: Math.round(spoolRemainingWeight) }) }}
+
+ ({{ $t('app.afc.WeightUsed', { weight: Math.round(spoolUsedWeight) }) }})
@@ -106,6 +106,7 @@
v-if="spoolUrl"
:href="spoolUrl"
target="_blank"
+ rel="noopener noreferrer"
class="position-absolute text-truncate text-truncate-element text-center text-decoration-none filament-link"
>{{ spoolFilamentName }}
Date: Mon, 1 Jun 2026 14:50:27 +0200
Subject: [PATCH 3/3] fix(afc): Address second round of PR review comments in
`AfcCardUnitLaneBody`
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- Fix `v-if="spoolExtruderTemp"` / `v-if="spoolBedTemp"` to use `!= null`
so `0°C` renders correctly instead of being hidden by falsy check
- Show `--` when `spoolMaterial` is empty to match AFC UI conventions
- Add hover/focus underline and `cursor: pointer` to `.filament-link` so
the Spoolman link is discoverable and keyboard-accessible
Signed-off-by: paxx12 <245230251+paxx12@users.noreply.github.com>
---
src/components/widgets/afc/AfcCardUnitLaneBody.vue | 12 +++++++++---
1 file changed, 9 insertions(+), 3 deletions(-)
diff --git a/src/components/widgets/afc/AfcCardUnitLaneBody.vue b/src/components/widgets/afc/AfcCardUnitLaneBody.vue
index 7a5eef63bf..9ae9deb754 100644
--- a/src/components/widgets/afc/AfcCardUnitLaneBody.vue
+++ b/src/components/widgets/afc/AfcCardUnitLaneBody.vue
@@ -27,10 +27,10 @@
{{ spoolMaterial }}
-
+
| {{ spoolExtruderTemp }}°C
-
+
| {{ spoolBedTemp }}°C
@@ -70,7 +70,7 @@
:name="name"
/>
- {{ spoolMaterial }}
+ {{ spoolMaterial || '--' }}
{{ Math.round(spoolRemainingWeight) }} g
@@ -287,5 +287,11 @@ export default class AfcCardUnitLaneBody extends Mixins(StateMixin, AfcMixin) {
.filament-link {
color: inherit !important;
+ cursor: pointer;
+}
+
+.filament-link:hover,
+.filament-link:focus {
+ text-decoration: underline !important;
}