188 lines
6.8 KiB
JavaScript
188 lines
6.8 KiB
JavaScript
const fields = {
|
|
text: document.getElementById("mobileText"),
|
|
tape: document.getElementById("mobileTape"),
|
|
margin: document.getElementById("mobileMargin"),
|
|
font: document.getElementById("mobileFont"),
|
|
copies: document.getElementById("mobileCopies"),
|
|
align: document.getElementById("mobileAlign"),
|
|
bold: document.getElementById("mobileBold"),
|
|
frame: document.getElementById("mobileFrame"),
|
|
tapeInfo: document.getElementById("mobileTapeInfo"),
|
|
tapeInfoType: document.getElementById("mobileTapeInfoType"),
|
|
tapePrintableWidth: document.getElementById("mobileTapePrintableWidth"),
|
|
tapeBgSwatch: document.getElementById("mobileTapeBgSwatch"),
|
|
tapeBgText: document.getElementById("mobileTapeBgText"),
|
|
tapeTextSwatch: document.getElementById("mobileTapeTextSwatch"),
|
|
tapeTextText: document.getElementById("mobileTapeTextText"),
|
|
preview: document.getElementById("mobilePreview"),
|
|
queueStatus: document.getElementById("mobileQueueStatus"),
|
|
message: document.getElementById("mobileMessage"),
|
|
};
|
|
|
|
const steppers = {
|
|
mobileCopies: { input: fields.copies, output: document.getElementById("mobileCopiesValue"), min: 1, max: 20, step: 1, suffix: "" },
|
|
mobileFont: { input: fields.font, output: document.getElementById("mobileFontValue"), min: 2, max: 24, step: 0.5, suffix: " mm" },
|
|
mobileMargin: { input: fields.margin, output: document.getElementById("mobileMarginValue"), min: 0, max: 20, step: 0.5, suffix: " mm" },
|
|
};
|
|
|
|
function estimateTextLengthMm(text, fontSize, margin, frame) {
|
|
const lines = String(text || " ").split("\n");
|
|
const longest = lines.reduce((max, line) => Math.max(max, line.length), 0);
|
|
const textWidth = Math.max(8, longest * fontSize * 0.62);
|
|
return Math.min(300, Math.max(20, Math.ceil(textWidth + margin * 2 + (frame ? 4 : 0))));
|
|
}
|
|
|
|
function clamp(value, min, max) {
|
|
return Math.min(max, Math.max(min, value));
|
|
}
|
|
|
|
function formatStepperValue(value) {
|
|
return Number.isInteger(value) ? String(value) : value.toFixed(1).replace(".", ",");
|
|
}
|
|
|
|
function syncStepper(id) {
|
|
const config = steppers[id];
|
|
const value = Number(config.input.value);
|
|
config.output.textContent = `${formatStepperValue(value)}${config.suffix}`;
|
|
}
|
|
|
|
function setStepperValue(id, value) {
|
|
const config = steppers[id];
|
|
const rounded = Math.round(value / config.step) * config.step;
|
|
const next = clamp(rounded, config.min, config.max);
|
|
config.input.value = String(next);
|
|
syncStepper(id);
|
|
mobileDesign();
|
|
}
|
|
|
|
function setMessage(text, type = "") {
|
|
fields.message.textContent = text;
|
|
fields.message.className = `message ${type}`;
|
|
}
|
|
|
|
function updateTapeInfo(info) {
|
|
fields.tapeInfo.hidden = !info;
|
|
if (!info) return;
|
|
const media = info.mediaType?.name || "Unbekannt";
|
|
const bg = info.tapeColor || { name: "Unbekannt", css: "#d1d5db" };
|
|
const text = info.textColor || { name: "Unbekannt", css: "#111111" };
|
|
fields.tapeInfoType.textContent = `${info.width} mm · ${media}${info.mediaType?.code ? ` (${info.mediaType.code})` : ""}`;
|
|
fields.tapePrintableWidth.textContent = info.printableWidthPx ? `${info.printableWidthPx} px` : "-";
|
|
fields.tapeBgText.textContent = `${bg.name}${bg.code ? ` (${bg.code})` : ""}`;
|
|
fields.tapeBgSwatch.style.background = bg.css;
|
|
fields.tapeTextText.textContent = `${text.name}${text.code ? ` (${text.code})` : ""}`;
|
|
fields.tapeTextSwatch.style.background = text.css;
|
|
}
|
|
|
|
function mobileDesign() {
|
|
const tapeWidth = Number(fields.tape.value);
|
|
const margin = Number(fields.margin.value);
|
|
const text = fields.text.value.trim() || " ";
|
|
const fontSize = Number(fields.font.value);
|
|
const length = estimateTextLengthMm(text, fontSize, margin, fields.frame.checked);
|
|
const objects = [];
|
|
if (fields.frame.checked) {
|
|
objects.push({
|
|
id: "frame",
|
|
type: "shape",
|
|
x: margin,
|
|
y: margin,
|
|
w: Math.max(2, length - margin * 2),
|
|
h: Math.max(2, tapeWidth - margin * 2),
|
|
lineMm: 0.25,
|
|
fill: false,
|
|
});
|
|
}
|
|
objects.push({
|
|
id: "text",
|
|
type: "text",
|
|
x: margin + (fields.frame.checked ? 1 : 0),
|
|
y: margin,
|
|
w: Math.max(2, length - margin * 2 - (fields.frame.checked ? 2 : 0)),
|
|
h: Math.max(2, tapeWidth - margin * 2),
|
|
text,
|
|
fontSizeMm: fontSize,
|
|
bold: fields.bold.checked,
|
|
align: fields.align.value,
|
|
valign: "middle",
|
|
});
|
|
return {
|
|
tapeWidthMm: tapeWidth,
|
|
lengthMm: length,
|
|
autoLength: true,
|
|
marginMm: margin,
|
|
orientation: "landscape",
|
|
cutMode: "auto",
|
|
halfcut: "off",
|
|
objects,
|
|
};
|
|
}
|
|
|
|
async function send(url) {
|
|
setMessage("Rendere Etikett...");
|
|
const response = await fetch(url, {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({ design: mobileDesign(), copies: Number(fields.copies.value) || 1 }),
|
|
});
|
|
const payload = await response.json();
|
|
if (!payload.ok) {
|
|
setMessage(payload.error || "Fehler", "error");
|
|
return;
|
|
}
|
|
fields.preview.src = `${payload.image}?t=${Date.now()}`;
|
|
setMessage(payload.message || "Vorschau erstellt.", "success");
|
|
updateQueueStatus();
|
|
}
|
|
|
|
async function updateQueueStatus() {
|
|
try {
|
|
const response = await fetch("/api/queue");
|
|
const payload = await response.json();
|
|
if (!payload.ok) return;
|
|
const firstWaiting = payload.jobs?.find((job) => job.lastError);
|
|
fields.queueStatus.textContent = payload.enabled
|
|
? `Warteschlange: ${payload.length}${firstWaiting ? " (wartet auf Drucker)" : ""}`
|
|
: "Warteschlange: aus";
|
|
} catch {
|
|
fields.queueStatus.textContent = "Warteschlange: unbekannt";
|
|
}
|
|
}
|
|
|
|
document.getElementById("mobilePreviewBtn").addEventListener("click", () => send("/api/preview"));
|
|
document.getElementById("mobilePrintBtn").addEventListener("click", () => send("/api/print"));
|
|
document.getElementById("mobileDetect").addEventListener("click", async () => {
|
|
setMessage("Erkenne Band...");
|
|
const response = await fetch("/api/tape-info");
|
|
const payload = await response.json();
|
|
if (!payload.ok) {
|
|
setMessage(payload.error || "Band konnte nicht erkannt werden.", "error");
|
|
return;
|
|
}
|
|
fields.tape.value = String(payload.width);
|
|
updateTapeInfo(payload);
|
|
const bg = payload.tapeColor?.name ? `, ${payload.tapeColor.name}` : "";
|
|
const text = payload.textColor?.name ? ` / ${payload.textColor.name}` : "";
|
|
setMessage(`Band erkannt: ${payload.width} mm${bg}${text}`, "success");
|
|
});
|
|
|
|
["text", "frame", "bold", "align", "tape"].forEach((name) => {
|
|
fields[name].addEventListener("input", () => {
|
|
mobileDesign();
|
|
});
|
|
});
|
|
|
|
document.querySelectorAll(".stepper").forEach((stepper) => {
|
|
const id = stepper.dataset.stepper;
|
|
stepper.querySelectorAll("button").forEach((button) => {
|
|
button.addEventListener("click", () => {
|
|
setStepperValue(id, Number(steppers[id].input.value) + Number(button.dataset.step));
|
|
});
|
|
});
|
|
syncStepper(id);
|
|
});
|
|
|
|
mobileDesign();
|
|
updateQueueStatus();
|
|
setInterval(updateQueueStatus, 10000);
|