More work on the management UI, multi printer is working, temps is sometimes working

This commit is contained in:
2025-10-24 17:18:57 +11:00
parent cf5edd1329
commit 41431277cf
12 changed files with 679 additions and 169 deletions

View File

@@ -1,26 +0,0 @@
<div id="connect-Button">
{% if connected %}
<form
hx-post="/disconnect"
hx-target="#connect-Button"
hx-swap="outerHTML"
>
<button class="px-4 py-2 rounded bg-green-600 text-white hover:bg-red-600">
Printer is connected. (Press to disconnect)
</button>
</form>
<!-- Live status panel, will load once when revealed and then the returned fragment polls itself -->
<div id="live-status" hx-post="/status" hx-trigger="revealed" hx-swap="outerHTML" class="mt-4"></div>
{% else %}
<form
hx-post="/connect"
hx-target="#connect-Button"
hx-swap="outerHTML"
>
<button class="px-4 py-2 rounded bg-indigo-600 text-white hover:bg-indigo-500">
Connect to Printer
</button>
</form>
{% endif %}
</div>

View File

@@ -0,0 +1,34 @@
<div id="printer-{{ printer.id }}" class="p-6 rounded border border-slate-700 bg-slate-800 w-full h-fit">
<h2 class="text-xl font-bold mb-4 text-white" title="{{ printer.name }}">{{ printer.name }}</h2>
<!-- Connect/Disconnect button -->
{% if printer.connected %}
<form
hx-post="/disconnect/{{ printer.id }}"
hx-target="#printer-{{ printer.id }}"
hx-swap="outerHTML"
>
<button class="w-full px-4 py-2 rounded bg-green-600 text-white hover:bg-red-600">
{{ printer.name }} is connected (Press to disconnect)
</button>
</form>
<!-- Live status panel -->
<div id="status-{{ printer.id }}"
hx-post="/status/{{ printer.id }}"
hx-trigger="every 500ms[!document.activeElement.matches('#status-{{ printer.id }} input')]"
hx-swap="outerHTML"
class="mt-4">
</div>
{% else %}
<form
hx-post="/connect/{{ printer.id }}"
hx-target="#printer-{{ printer.id }}"
hx-swap="outerHTML"
>
<button class="w-full px-4 py-2 rounded bg-indigo-600 text-white hover:bg-indigo-500">
Connect to {{ printer.name }}
</button>
</form>
{% endif %}
</div>

View File

@@ -0,0 +1,14 @@
<div class="grid grid-cols-1 gap-6 md:grid-cols-2 xl:grid-cols-3 2xl:grid-cols-4 auto-rows-fr">
{% for printer in printers.values() %}
{% include "_printer_card.html" %}
{% else %}
<div class="col-span-full text-center text-slate-400">
No printers configured. Check config.json file.
</div>
{% endfor %}
</div>
<!-- Debug info -->
<div class="mt-8 text-sm text-slate-500">
Total Printers: {{ printers|length }}
</div>

View File

@@ -1,37 +1,104 @@
<div id="status-panel" hx-post="/status" hx-trigger="every 500ms" hx-swap="outerHTML" class="rounded bg-slate-800 border border-slate-700 p-4 text-slate-100">
<div id="status-{{ printer.id }}"
hx-post="/status/{{ printer.id }}"
hx-trigger="every 1s"
hx-swap="outerHTML"
hx-target="#status-{{ printer.id }}"
hx-select="#status-{{ printer.id }}">
{% if status.error %}
<div class="text-red-400">Error: {{ status.error }}</div>
{% if status.debug_info %}
<div class="mt-2 text-sm text-slate-400">
<div>Printer ID: {{ status.debug_info.id }}</div>
<div>Name: {{ status.debug_info.name }}</div>
<div>Connected: {{ status.debug_info.connected }}</div>
</div>
{% endif %}
{% else %}
<div class="grid grid-cols-2 gap-4">
<div class="col-span-2">
<div class="text-sm text-slate-400">Current File</div>
<div class="font-medium">{{ status.current_file }}</div>
</div>
<div>
<div class="text-sm text-slate-400">Nozzle Temperature</div>
<div class="font-medium">{{ status.nozzle_temp }}</div>
</div>
<div>
<div class="text-sm text-slate-400">Bed Temperature</div>
<div class="font-medium">{{ status.bed_temp }}</div>
<div class="w-full space-y-2">
<!-- Current File -->
<div class="flex justify-between items-center">
<span class="text-sm text-slate-400">Current File</span>
<span class="font-medium">{{ status.current_file }}</span>
</div>
<div>
<div class="text-sm text-slate-400">Chamber Temperature</div>
<div class="font-medium">{{ status.chamber_temp }}</div>
<!-- Status Items -->
<style>
/* Remove arrows/spinners from number inputs */
input[type=number]::-webkit-inner-spin-button,
input[type=number]::-webkit-outer-spin-button {
-webkit-appearance: none;
margin: 0;
}
input[type=number] {
-moz-appearance: textfield;
appearance: textfield;
}
</style>
<div class="flex justify-between items-center">
<span class="text-sm text-slate-400">Nozzle Temp</span>
<div class="flex items-center gap-1">
<span class="font-medium">{{ status.nozzle_temp|int }}</span>
<span class="text-slate-400">/</span>
<form class="flex items-center"
hx-post="/set-temperature/{{ printer.id }}"
hx-trigger="blur from:input, keyup[key=='Enter'] from:input"
hx-swap="none"
onsubmit="event.preventDefault();">
<input type="hidden" name="type" value="nozzle">
<input type="number"
name="value"
class="w-[3ch] font-medium bg-transparent focus:outline-none focus:ring-1 focus:ring-blue-500 focus:rounded"
value="{{ status.nozzle_target|default(status.nozzle_temp|int) }}"
step="1"
onblur="if(!this.value) { this.value = '0'; this.form.requestSubmit(); }"
maxlength="3">
</form>
</div>
</div>
<div>
<div class="text-sm text-slate-400">Progress</div>
<div class="font-medium">{{ status.percentage }}</div>
<div class="flex justify-between items-center">
<span class="text-sm text-slate-400">Bed Temp</span>
<div class="flex items-center gap-1">
<span class="font-medium">{{ status.bed_temp|int }}</span>
<span class="text-slate-400">/</span>
<form class="flex items-center"
hx-post="/set-temperature/{{ printer.id }}"
hx-trigger="blur from:input, keyup[key=='Enter'] from:input"
hx-swap="none"
onsubmit="event.preventDefault();">
<input type="hidden" name="type" value="bed">
<input type="number"
name="value"
class="w-[3ch] font-medium bg-transparent focus:outline-none focus:ring-1 focus:ring-blue-500 focus:rounded"
value="{{ status.bed_target|default(status.bed_temp|int) }}"
step="1"
onblur="if(!this.value) { this.value = '0'; this.form.requestSubmit(); }"
maxlength="3">
</form>
</div>
</div>
<div>
<div class="text-sm text-slate-400">Remaining Time</div>
<div class="font-medium">{{ status.remaining_time }}</div>
<div class="flex justify-between items-center">
<span class="text-sm text-slate-400">Chamber Temp</span>
<span class="font-medium">{{ status.chamber_temp }}</span>
</div>
<div>
<div class="text-sm text-slate-400">Time Done</div>
<div class="font-medium">{{ status.finish_time }}</div>
<div class="flex justify-between items-center">
<span class="text-sm text-slate-400">Progress</span>
<span class="font-medium">{{ status.percentage }}</span>
</div>
<div class="flex justify-between items-center">
<span class="text-sm text-slate-400">Time Left</span>
<span class="font-medium">{{ status.remaining_time }}</span>
</div>
<div class="flex justify-between items-center">
<span class="text-sm text-slate-400">Est. Done</span>
<span class="font-medium">{{ status.finish_time }}</span>
</div>
</div>
</div>
{% endif %}
</div>

View File

@@ -10,9 +10,21 @@
<!-- HTMX: lets you call endpoints and swap HTML fragments, no custom JS needed -->
<script src="https://unpkg.com/htmx.org@2.0.3"></script>
<script>
// Handle messages from server
document.body.addEventListener('showMessage', function(event) {
const message = event.detail;
const div = document.createElement('div');
div.className = 'fixed top-4 right-4 bg-red-500 text-white px-6 py-3 rounded shadow-lg';
div.textContent = message;
document.body.appendChild(div);
setTimeout(() => div.remove(), 3000);
});
</script>
</head>
<body class="bg-slate-900 text-slate-100">
<div class="max-w-3xl mx-auto p-6 bg-slate-800 rounded-lg shadow-md">
<div class="w-11/12 max-w-[90%] lg:max-w-[95%] mx-auto p-6 bg-slate-800 rounded-lg shadow-md">
<h1 class="text-2xl font-bold mb-4 text-white">X1 Carbon Connection</h1>
{% block connect %}{% endblock %}
<br>

View File

@@ -1,52 +1,47 @@
{% extends "base.html" %}
{% block connect %}
<!-- Run action button -->
<div id="connect-Button">
{% if connected %}
<div class="container mx-auto px-4 py-6">
<!-- Connect All Button -->
<div class="max-w-3xl mx-auto mb-8">
<form
hx-post="/disconnect"
hx-target="#connect-Button"
hx-swap="outerHTML"
hx-post="/connect-all"
hx-target="#printers-container"
hx-swap="innerHTML"
>
<button class="px-4 py-2 rounded bg-green-600 text-white hover:bg-red-600">
Printer is connected. (Press to disconnect)
<button class="w-full px-6 py-3 rounded bg-indigo-600 text-white hover:bg-indigo-500 font-bold text-lg">
Connect All Printers
</button>
</form>
</div>
<!-- Live status panel, will load once when revealed and then the returned fragment polls itself -->
<div id="live-status" hx-post="/status" hx-trigger="load" hx-swap="outerHTML" class="mt-4"></div>
{% else %}
<form
hx-post="/connect"
hx-target="#connect-Button"
hx-swap="outerHTML"
>
<button class="px-4 py-2 rounded bg-indigo-600 text-white hover:bg-indigo-500">
Connect to Printer
</button>
</form>
{% endif %}
<!-- Printer Grid Container -->
<div id="printers-container" class="w-full">
<div class="grid gap-6 grid-cols-1 md:grid-cols-2 xl:grid-cols-3 2xl:grid-cols-4">
{% for printer in printers.values() %}
<div class="h-fit">
{% include "_printer_card.html" %}
</div>
{% else %}
<div class="col-span-full text-center text-slate-400">
No printers configured. Check config.json file.
</div>
{% endfor %}
</div>
<!-- Debug info -->
<div class="mt-8 text-sm text-slate-500">
Total Printers: {{ printers|length }}
</div>
</div>
</div>
{% endblock %}
{% block status %}
<!--
<form
hx-post="/status"
hx-target="#status-result"
hx-swap="innerHTML"
>
<button class="px-4 py-2 rounded bg-indigo-600 text-white hover:bg-indigo-500">
Get Printer Status
</button>
</form>
<div id="status-result" class="mt-4"></div>
-->
<style>
/* Ensure grid items don't overlap at any screen size */
@media (min-width: 1024px) {
#printers-container .grid {
grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
}
}
</style>
{% endblock %}