Skip to content

Commit 7f8f4c2

Browse files
committed
Improve UI controls and state handling
1 parent 2714181 commit 7f8f4c2

1 file changed

Lines changed: 106 additions & 30 deletions

File tree

frontend/lib/Printer.svelte

Lines changed: 106 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<script lang="ts">
2-
import { onMount, onDestroy } from "svelte";
2+
import { onMount, onDestroy, tick } from "svelte";
33
import { useRoute } from "@dvcol/svelte-simple-router/router";
44
import { getBackendUrl } from "$lib/utils.js";
55
import * as Tooltip from "$lib/components/ui/tooltip";
@@ -75,6 +75,84 @@
7575
7676
let printer = $state<PrinterResponse | null>(null);
7777
78+
function toPercent(value: number): number {
79+
// value [0, 15] → 0‑100 %
80+
return Math.round(value / 1.5) * 10;
81+
}
82+
83+
function scaleSliderToBackend(value: number, coefficient: number): number {
84+
// UI → 0‑15, Backend → 0‑255 (coefficient × value)
85+
const scaled = Math.round(value * coefficient);
86+
return Math.min(scaled, 255);
87+
}
88+
89+
function createSmartControl<T>(getBackendValue: () => T, onCommit: (value: T) => void) {
90+
let localValue = $state(getBackendValue());
91+
let isInteracting = $state(false);
92+
let timeout: number;
93+
let skipNextChange = false;
94+
95+
$effect(() => {
96+
if (!isInteracting) {
97+
skipNextChange = true;
98+
localValue = getBackendValue();
99+
tick().then(() => {
100+
skipNextChange = false;
101+
});
102+
}
103+
});
104+
105+
onDestroy(() => {
106+
clearTimeout(timeout);
107+
});
108+
109+
return {
110+
get value() {
111+
return localValue;
112+
},
113+
114+
onChange(value: T) {
115+
if (skipNextChange) {
116+
// Reset just in case several rapid backend updates happen
117+
skipNextChange = false;
118+
return;
119+
}
120+
console.log("onChange -> ", value);
121+
localValue = value;
122+
isInteracting = true;
123+
clearTimeout(timeout);
124+
},
125+
126+
onCommit(value: T) {
127+
console.log("onCommit -> ", value);
128+
localValue = value;
129+
onCommit(value);
130+
timeout = setTimeout(() => {
131+
isInteracting = false;
132+
}, 7000);
133+
},
134+
};
135+
}
136+
137+
const controls = {
138+
chamberLight: createSmartControl(
139+
() => printerStatus?.lights_report?.[0]?.mode === "on",
140+
(v) => sendWsCommand(new ChamberLight(v)),
141+
),
142+
partFan: createSmartControl(
143+
() => Number(printerStatus?.cooling_fan_speed || 0),
144+
(v) => sendWsCommand(new PartFanSpeed(scaleSliderToBackend(v, 15))),
145+
),
146+
chamberFan: createSmartControl(
147+
() => Number(printerStatus?.big_fan2_speed || 0),
148+
(v) => sendWsCommand(new ChamberFanSpeed(scaleSliderToBackend(v, 17))),
149+
),
150+
auxFan: createSmartControl(
151+
() => Number(printerStatus?.big_fan1_speed || 0),
152+
(v) => sendWsCommand(new AuxFanSpeed(scaleSliderToBackend(v, 17))),
153+
),
154+
};
155+
78156
onMount(async () => {
79157
try {
80158
const response = await fetch(`${backendUrl}/api/printer/${printerId}`);
@@ -524,7 +602,7 @@
524602
<div class="mt-6 grid grid-cols-1 gap-4">
525603
<div class="bg-400 rounded-lg p-3">
526604
<div class="mb-2 flex justify-between">
527-
<div class="flex items-center space-x-4">
605+
<div class="flex flex-row gap-1">
528606
<CircleGauge />
529607
Speed
530608
</div>
@@ -549,16 +627,16 @@
549627
<!-- Chamber Controls (Lower Right) -->
550628
<Card class="space-y-6">
551629
<CardContent class="p-4">
552-
<div class="grid grid-cols-4 gap-4">
553-
<div class="flex flex-col items-center justify-center space-y-1">
630+
<div class="flex flex-row gap-4">
631+
<div class="flex flex-col items-center justify-center space-y-1 w-full">
554632
<div class="text-400 flex items-center justify-center space-x-2 text-sm">
555633
<Thermometer /> Nozzle
556634
</div>
557635
<div class="text-center text-xl">
558636
{Math.round(printerStatus?.nozzle_temper ?? 0)}/{printerStatus?.nozzle_target_temper}°C
559637
</div>
560638
</div>
561-
<div class="flex flex-col items-center justify-center space-y-1">
639+
<div class="flex flex-col items-center justify-center space-y-1 w-full">
562640
<div class="text-400 flex items-center justify-center space-x-2 text-sm">
563641
<Thermometer /> Bed
564642
</div>
@@ -567,19 +645,20 @@
567645
</div>
568646
</div>
569647
{#if printer?.supports_chamber_temp}
570-
<div class="flex flex-col items-center justify-center space-y-1">
648+
<div class="flex flex-col items-center justify-center space-y-1 w-full">
571649
<div class="text-400 flex items-center justify-center space-x-2 text-sm">
572650
<Thermometer /> Chamber
573651
</div>
574652
<div class="text-center text-xl">{Math.round(printerStatus?.chamber_temper ?? 0)}°C</div>
575653
</div>
576654
{/if}
577-
<div class="flex items-center justify-center space-x-2">
655+
<div class="flex items-center justify-center space-x-2 w-full">
578656
<Switch
579657
id="printer-light"
580-
bind:checked={printerLightOn}
658+
checked={controls.chamberLight.value}
581659
onCheckedChange={(enabled: boolean) => {
582-
sendWsCommand(new ChamberLight(enabled));
660+
controls.chamberLight.onChange(enabled);
661+
controls.chamberLight.onCommit(enabled);
583662
}}
584663
/>
585664
<Label for="printer-light">
@@ -591,55 +670,52 @@
591670
<div class="mt-6 grid grid-cols-1 gap-4">
592671
<div class="bg-400 rounded-lg p-3">
593672
<div class="mb-2 flex justify-between">
594-
<div class="flex items-center space-x-4">
673+
<div class="flex flex-row gap-1">
595674
<Fan />
596675
Part Cooling
597676
</div>
598-
<span>{Math.round((Number(printerStatus?.cooling_fan_speed ?? 0) / 15) * 100)}%</span>
677+
<span>{toPercent(controls.partFan.value)}%</span>
599678
</div>
600679
<Slider
601-
onValueCommit={(value: number[]) => {
602-
sendWsCommand(new PartFanSpeed(value[0]));
603-
}}
604-
value={[printerStatus?.cooling_fan_speed]}
680+
onValueChange={(value: number[]) => controls.partFan.onChange(Math.round(value[0]))}
681+
onValueCommit={(value: number[]) => controls.partFan.onCommit(Math.round(value[0]))}
682+
value={[controls.partFan.value]}
605683
max={15}
606-
step={1}
684+
step={1.5}
607685
class="w-full"
608686
/>
609687
</div>
610688
<div class="bg-400 rounded-lg p-3">
611689
<div class="mb-2 flex justify-between">
612-
<div class="flex items-center space-x-4">
690+
<div class="flex flex-row gap-1">
613691
<Fan />
614692
Chamber
615693
</div>
616-
<span>{Math.round((Number(printerStatus?.big_fan2_speed ?? 0) / 15) * 100)}%</span>
694+
<span>{toPercent(controls.chamberFan.value)}%</span>
617695
</div>
618696
<Slider
619-
onValueCommit={(value: number[]) => {
620-
sendWsCommand(new ChamberFanSpeed(value[0]));
621-
}}
622-
value={[printerStatus?.big_fan2_speed || 0]}
697+
onValueChange={(value: number[]) => controls.chamberFan.onChange(Math.round(value[0]))}
698+
onValueCommit={(value: number[]) => controls.chamberFan.onCommit(Math.round(value[0]))}
699+
value={[controls.chamberFan.value]}
623700
max={15}
624-
step={1}
701+
step={1.5}
625702
class="w-full"
626703
/>
627704
</div>
628705
<div class="bg-400 rounded-lg p-3">
629706
<div class="mb-2 flex justify-between">
630-
<div class="flex items-center space-x-4">
707+
<div class="flex flex-row gap-1">
631708
<Fan />
632709
Auxillary
633710
</div>
634-
<span>{Math.round((Number(printerStatus?.big_fan1_speed ?? 0) / 15) * 100)}%</span>
711+
<span>{toPercent(controls.auxFan.value)}%</span>
635712
</div>
636713
<Slider
637-
onValueCommit={(value: number[]) => {
638-
sendWsCommand(new AuxFanSpeed(value[0]));
639-
}}
640-
value={[printerStatus?.big_fan1_speed || 0]}
714+
onValueChange={(value: number[]) => controls.auxFan.onChange(Math.round(value[0]))}
715+
onValueCommit={(value: number[]) => controls.auxFan.onCommit(Math.round(value[0]))}
716+
value={[controls.auxFan.value]}
641717
max={15}
642-
step={1}
718+
step={1.5}
643719
class="w-full"
644720
/>
645721
</div>

0 commit comments

Comments
 (0)