Grotti 3.3.0: Projekte mit Ordner, Datei-Löschen/Verschieben, Material-Archiv, UI-Polish.
This commit is contained in:
1 parent
4ddffbbeb1
commit
67a89d1794
45 files changed
+2856
-320
No files matched your search
+221
-68
@@ -78,11 +78,10 @@
|
||||
</div>
|
||||
|
||||
<div class="flow-step ok" data-pane="send" id="step-mode">
|
||||
<h3><span class="step-num">3</span>Wann</h3>
|
||||
<h3><span class="step-num">3</span>Start</h3>
|
||||
<div class="segmented" id="mode" role="radiogroup">
|
||||
<button type="button" data-mode="now" class="on">Jetzt<small>sofort starten</small></button>
|
||||
<button type="button" data-mode="free">Wenn frei<small>auf diesem Drucker</small></button>
|
||||
<button type="button" data-mode="next">Nächster freier<small>Grotti wählt</small></button>
|
||||
<button type="button" data-mode="this" class="on">Dieser Drucker<small>frei → start, sonst Warteschlange</small></button>
|
||||
<button type="button" data-mode="next">Nächster passender<small>Grotti wählt nach Sorte</small></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -189,7 +188,7 @@ let ablaufDefaults = {
|
||||
check: { max_delta_mm: 3.0, timeout_min: 2, x: 125, y: 125, ref_x: 25, ref_y: 125, z_clear: 20 },
|
||||
};
|
||||
let ablaufSteps = [];
|
||||
const sel = { fileId: null, hosts: new Set(), mode: "now", tab: "send" };
|
||||
const sel = { fileId: null, hosts: new Set(), mode: "this", tab: "send" };
|
||||
let aceHostsKey = "";
|
||||
|
||||
/* ---------- 3D-Pfad ---------- */
|
||||
@@ -202,6 +201,25 @@ function ensureViewer() {
|
||||
return pathViewer;
|
||||
}
|
||||
|
||||
let toolpathGen = 0;
|
||||
|
||||
function scheduleToolpath(file, progress, deferMs) {
|
||||
const gen = ++toolpathGen;
|
||||
const run = () => {
|
||||
if (gen !== toolpathGen) return;
|
||||
showToolpath(file, progress);
|
||||
};
|
||||
if (deferMs > 0) {
|
||||
if (window.requestIdleCallback) {
|
||||
requestIdleCallback(run, { timeout: Math.max(800, deferMs) });
|
||||
} else {
|
||||
setTimeout(run, deferMs);
|
||||
}
|
||||
return;
|
||||
}
|
||||
run();
|
||||
}
|
||||
|
||||
async function showToolpath(file, progress) {
|
||||
const card = document.getElementById("viewer-card");
|
||||
const caption = document.getElementById("viewer-caption");
|
||||
@@ -221,6 +239,7 @@ async function showToolpath(file, progress) {
|
||||
meta.textContent = "Pfad wird gelesen …";
|
||||
try {
|
||||
const data = await api("/dateien/" + file.id + "/pfad?tp=8");
|
||||
if (String(sel.fileId) !== String(file.id)) return;
|
||||
viewer.setPath(data);
|
||||
viewerFileId = file.id;
|
||||
meta.textContent = (data.count || 0).toLocaleString("de-DE") + " Punkte";
|
||||
@@ -262,7 +281,9 @@ function saveSendMemory() {
|
||||
function loadSendMemory() {
|
||||
try {
|
||||
const data = JSON.parse(localStorage.getItem(SEND_KEY) || "{}");
|
||||
if (["now", "free", "next"].includes(data.mode)) sel.mode = data.mode;
|
||||
let mode = data.mode;
|
||||
if (mode === "now" || mode === "free") mode = "this";
|
||||
if (["this", "next"].includes(mode)) sel.mode = mode;
|
||||
if (["send", "ablauf"].includes(data.tab)) sel.tab = data.tab;
|
||||
if (Array.isArray(data.hosts)) sel.hosts = new Set(data.hosts);
|
||||
} catch (error) { /* ignore */ }
|
||||
@@ -309,10 +330,22 @@ function slotTitle(host, slot) {
|
||||
return bits.join(" · ");
|
||||
}
|
||||
|
||||
function paintIndexOf(filament, fallback) {
|
||||
const raw = filament && filament.paint_index;
|
||||
if (raw === 0 || raw === "0") return 0;
|
||||
const n = Number(raw);
|
||||
return Number.isFinite(n) && n >= 0 ? n : fallback;
|
||||
}
|
||||
|
||||
function filamentLabel(filament, index) {
|
||||
const kind = filament.type || "Filament";
|
||||
const paint = paintIndexOf(filament, index);
|
||||
const kind = filament.profile || filament.type || "Filament";
|
||||
const color = filament.color ? " · " + filament.color : "";
|
||||
return "Farbe " + (index + 1) + " · " + kind + color;
|
||||
return "T" + paint + " · " + kind + color;
|
||||
}
|
||||
|
||||
function canStartNow(p) {
|
||||
return isFree(p) && !p.needs_clear;
|
||||
}
|
||||
|
||||
function spoolsOnHost(host) {
|
||||
@@ -326,6 +359,19 @@ function spoolWarnText() {
|
||||
const maps = collectAceMaps();
|
||||
const hosts = selectedHosts();
|
||||
const fileTypes = ((file.filaments || []).map((f) => (f.type || "").toLowerCase()).filter(Boolean));
|
||||
const fileProfiles = ((file.filaments || []).map((f) => (f.profile || "").toLowerCase()).filter(Boolean));
|
||||
function profileOk(spoolRows) {
|
||||
if (!fileProfiles.length) return true;
|
||||
const names = spoolRows.map((s) => (s.name || "").toLowerCase()).filter(Boolean);
|
||||
if (!names.length) return true;
|
||||
return fileProfiles.every((want) => names.some((n) => {
|
||||
if (n === want) return true;
|
||||
if (!n.startsWith(want) || /[a-z0-9]/i.test(n.charAt(want.length) || "")) return false;
|
||||
const rest = n.slice(want.length).replace(/^[\s·\-_|/]+/, "");
|
||||
const first = (rest.split(/\s+/)[0] || "").toLowerCase();
|
||||
return !["meta", "matte", "matt", "silk", "plus", "hf", "cf"].includes(first);
|
||||
}));
|
||||
}
|
||||
for (const host of hosts) {
|
||||
const name = (printers.find((p) => p.host === host) || {}).name || host;
|
||||
const mapping = maps[host] || [];
|
||||
@@ -334,18 +380,23 @@ function spoolWarnText() {
|
||||
let left = 0;
|
||||
let any = false;
|
||||
const types = [];
|
||||
const mappedSpools = [];
|
||||
mapping.forEach((item) => {
|
||||
const slot = Number(item.ams_index) + 1;
|
||||
const spool = onHost.find((s) => Number(s.ace_slot) === slot);
|
||||
if (spool) {
|
||||
any = true;
|
||||
left += Number(spool.remaining_g) || 0;
|
||||
mappedSpools.push(spool);
|
||||
if (spool.material_type) types.push(spool.material_type.toLowerCase());
|
||||
}
|
||||
});
|
||||
if (!any) return name + ": keine Grotti-Spule auf den gewählten ACE-Plätzen — Abzug entfällt.";
|
||||
if (left + 0.5 < need) return "ACE auf " + name + ": nur " + grams(left) + ", Datei braucht " + grams(need) + ".";
|
||||
if (fileTypes.length && types.length && !fileTypes.some((t) => types.includes(t))) {
|
||||
if (!profileOk(mappedSpools)) {
|
||||
return name + ": Sorte passt nicht (Datei " + fileProfiles.join("/") + ").";
|
||||
}
|
||||
if (!fileProfiles.length && fileTypes.length && types.length && !fileTypes.some((t) => types.includes(t))) {
|
||||
return name + ": Material passt nicht (Datei " + fileTypes.join("/") + ", ACE " + types.join("/") + ").";
|
||||
}
|
||||
continue;
|
||||
@@ -359,8 +410,11 @@ function spoolWarnText() {
|
||||
if (left + 0.5 < need) {
|
||||
return name + ": nur " + grams(left) + ", Datei braucht " + grams(need) + ".";
|
||||
}
|
||||
if (!profileOk(external)) {
|
||||
return name + ": Sorte passt nicht (Datei " + fileProfiles.join("/") + ").";
|
||||
}
|
||||
const types = external.map((s) => (s.material_type || "").toLowerCase()).filter(Boolean);
|
||||
if (fileTypes.length && types.length && !fileTypes.some((t) => types.includes(t))) {
|
||||
if (!fileProfiles.length && fileTypes.length && types.length && !fileTypes.some((t) => types.includes(t))) {
|
||||
return name + ": Material passt nicht (Datei " + fileTypes.join("/") + ", Drucker " + types.join("/") + ").";
|
||||
}
|
||||
}
|
||||
@@ -432,16 +486,17 @@ function renderAceMaps(force) {
|
||||
title.className = "ace-printer-name";
|
||||
title.textContent = printer.name;
|
||||
block.appendChild(title);
|
||||
paints.forEach((filament, paintIndex) => {
|
||||
paints.forEach((filament, uiIndex) => {
|
||||
const paint = paintIndexOf(filament, uiIndex);
|
||||
const row = document.createElement("label");
|
||||
row.className = "ace-row";
|
||||
const swatch = document.createElement("span");
|
||||
swatch.className = "ace-swatch";
|
||||
if (filament.color) swatch.style.background = filament.color;
|
||||
const text = document.createElement("span");
|
||||
text.textContent = filamentLabel(filament, paintIndex);
|
||||
text.textContent = filamentLabel(filament, uiIndex);
|
||||
const select = document.createElement("select");
|
||||
select.dataset.paint = String(paintIndex);
|
||||
select.dataset.paint = String(paint);
|
||||
select.innerHTML = '<option value="">Platz wählen</option>';
|
||||
printer.slots.forEach((slot) => {
|
||||
const option = document.createElement("option");
|
||||
@@ -452,16 +507,30 @@ function renderAceMaps(force) {
|
||||
const hex = (spool && spool.colors && spool.colors[0] && spool.colors[0].hex) || slot.color || "";
|
||||
if (hex) option.style.borderLeft = "4px solid " + hex;
|
||||
const spoolType = (spool && spool.material_type) || slot.type || "";
|
||||
// Vorauswahl: gleiche Sorte (Grotti-Spule bevorzugt), sonst Platz in Reihenfolge
|
||||
if (!select.dataset.picked && filament.type && spoolType
|
||||
const spoolName = ((spool && spool.name) || "").toLowerCase();
|
||||
const wantProfile = (filament.profile || "").toLowerCase();
|
||||
// Vorauswahl: Katalog-Sorte exakt (Meta ≠ Matte), sonst grober Typ
|
||||
let profileHit = false;
|
||||
if (wantProfile && spoolName) {
|
||||
if (spoolName === wantProfile) profileHit = true;
|
||||
else if (spoolName.startsWith(wantProfile) && !/[a-z0-9]/i.test(spoolName.charAt(wantProfile.length) || "")) {
|
||||
const rest = spoolName.slice(wantProfile.length).replace(/^[\s·\-_|/]+/, "");
|
||||
const first = (rest.split(/\s+/)[0] || "").toLowerCase();
|
||||
if (!["meta", "matte", "matt", "silk", "plus", "hf", "cf"].includes(first)) profileHit = true;
|
||||
}
|
||||
}
|
||||
if (!select.dataset.picked && profileHit) {
|
||||
option.selected = true;
|
||||
select.dataset.picked = "1";
|
||||
} else if (!select.dataset.picked && !wantProfile && filament.type && spoolType
|
||||
&& filament.type.toLowerCase() === String(spoolType).toLowerCase()) {
|
||||
option.selected = true;
|
||||
select.dataset.picked = "1";
|
||||
}
|
||||
select.appendChild(option);
|
||||
});
|
||||
if (!select.dataset.picked && printer.slots[paintIndex]) {
|
||||
select.value = String(printer.slots[paintIndex].index);
|
||||
if (!select.dataset.picked && printer.slots[uiIndex]) {
|
||||
select.value = String(printer.slots[uiIndex].index);
|
||||
}
|
||||
select.addEventListener("change", updateSpoolWarn);
|
||||
row.appendChild(swatch);
|
||||
@@ -638,8 +707,9 @@ function fillFolderFilter() {
|
||||
}
|
||||
|
||||
function moveSelectHtml(file) {
|
||||
if (!hasWatchDir || file.source !== "watch") return "";
|
||||
const current = folderKey(file);
|
||||
if (!hasWatchDir) return "";
|
||||
if (file.source !== "watch" && file.source !== "upload") return "";
|
||||
const current = file.source === "upload" ? "" : folderKey(file);
|
||||
const opts = [
|
||||
`<option value="" selected disabled>Verschieben…</option>`,
|
||||
`<option value="__root__">Wurzel</option>`,
|
||||
@@ -701,8 +771,8 @@ function sliceOf(file) {
|
||||
}
|
||||
|
||||
function materialText(file) {
|
||||
const types = [...new Set(((file && file.filaments) || []).map((f) => f.type).filter(Boolean))];
|
||||
return types.join("+");
|
||||
const labels = [...new Set(((file && file.filaments) || []).map((f) => f.profile || f.type).filter(Boolean))];
|
||||
return labels.join("+");
|
||||
}
|
||||
|
||||
function layerHeightText(file) {
|
||||
@@ -757,8 +827,7 @@ function thumbHtml(file) {
|
||||
|
||||
function libItemHtml(file) {
|
||||
const project = projects.find((p) => String(p.id) === String(file.project_id));
|
||||
const del = file.source === "upload"
|
||||
? `<button type="button" class="icon-btn" data-delete="${file.id}" title="Hochgeladene Datei löschen">${icon("x")}</button>` : "";
|
||||
const del = `<button type="button" class="icon-btn" data-delete="${file.id}" title="Datei löschen">${icon("trash")}</button>`;
|
||||
return `<div class="lib-item ${String(file.id) === String(sel.fileId) ? "on" : ""}" role="button" tabindex="0" data-file="${file.id}">
|
||||
${thumbHtml(file)}
|
||||
<span style="min-width:0"><span class="t" title="${escapeHtml(file.name)}">${escapeHtml(file.name)}</span><span class="m">${alwaysMeta(file, project)}</span></span>
|
||||
@@ -882,17 +951,25 @@ function updateSendButton() {
|
||||
let note = "";
|
||||
if (!file) problem = "Erst eine Datei wählen.";
|
||||
else if (file.kind === "3mf") problem = "Die 3MF hat keinen G-code. Mit G-code slicen.";
|
||||
if (sel.mode === "now") {
|
||||
if (sel.mode === "this") {
|
||||
if (!problem && !chosen.length) problem = "Einen oder mehrere Drucker wählen.";
|
||||
const busy = chosen.filter((p) => !isFree(p));
|
||||
if (!problem && busy.length) note = namesText(busy) + " " + (busy.length === 1 ? "ist" : "sind") + " nicht frei. „Wenn frei“ reiht stattdessen ein.";
|
||||
text = chosen.length ? "Jetzt starten auf " + namesText(chosen) : "Jetzt starten";
|
||||
} else if (sel.mode === "free") {
|
||||
text = chosen.length ? "Einreihen für " + namesText(chosen) : "Einreihen: nächster freier " + familyOfSelection();
|
||||
note = chosen.length ? "Startet von selbst, sobald der Drucker bereit und die Platte frei ist." : "Ohne Drucker: der nächste freie passende übernimmt.";
|
||||
const ready = chosen.filter(canStartNow);
|
||||
const later = chosen.filter((p) => !canStartNow(p));
|
||||
if (ready.length && !later.length) {
|
||||
text = "Jetzt starten auf " + namesText(ready);
|
||||
note = "";
|
||||
} else if (!ready.length && later.length) {
|
||||
text = "Einreihen für " + namesText(later);
|
||||
note = "Startet, sobald der Drucker bereit und die Platte frei ist.";
|
||||
} else if (ready.length && later.length) {
|
||||
text = "Starten / einreihen auf " + namesText(chosen);
|
||||
note = namesText(ready) + " sofort, " + namesText(later) + " in die Warteschlange.";
|
||||
} else {
|
||||
text = "Starten";
|
||||
}
|
||||
} else {
|
||||
text = "Einreihen: nächster freier " + familyOfSelection();
|
||||
note = "Grotti nimmt den ersten freien Drucker mit passender Maschine.";
|
||||
text = "Einreihen: nächster passender " + familyOfSelection();
|
||||
note = "Grotti wählt den nächsten freien Drucker mit passender Sorte (z. B. PLA Meta ≠ Matte).";
|
||||
}
|
||||
const mismatch = chosen.filter((p) => familyMismatch(p, file));
|
||||
if (!problem && mismatch.length && !document.getElementById("send-override").checked) {
|
||||
@@ -902,7 +979,7 @@ function updateSendButton() {
|
||||
button.disabled = !!problem || !!sendPoll;
|
||||
hint.textContent = problem || note;
|
||||
hint.classList.toggle("bad", !!problem);
|
||||
targetHint.textContent = sel.mode === "next" && sel.tab === "send" ? "Ziel wählt Grotti." : chosen.length > 1 ? "Je Drucker ein eigener Auftrag." : "";
|
||||
targetHint.textContent = sel.mode === "next" && sel.tab === "send" ? "Ziel wählt Grotti nach Sorte." : chosen.length > 1 ? "Je Drucker ein eigener Auftrag." : "";
|
||||
}
|
||||
|
||||
function updateAblaufButton() {
|
||||
@@ -971,7 +1048,7 @@ function selectFile(id) {
|
||||
}
|
||||
document.querySelectorAll("#lib-list .lib-item").forEach((n) => n.classList.toggle("on", n.dataset.file === String(id)));
|
||||
refreshFlow();
|
||||
showToolpath(file, 0);
|
||||
scheduleToolpath(file, 0, 0);
|
||||
if (window.matchMedia("(max-width: 1080px)").matches) {
|
||||
document.querySelector(".send-panel").scrollIntoView({ behavior: "smooth", block: "start" });
|
||||
}
|
||||
@@ -1015,19 +1092,72 @@ async function load() {
|
||||
renderLibrary();
|
||||
renderTabs();
|
||||
refreshFlow();
|
||||
const file = selectedFile();
|
||||
if (file) showToolpath(file, 0);
|
||||
// Deep-Link von Orca /orca_senden: Senden in den Blick, Optionen offen für ACE
|
||||
if (new URLSearchParams(location.search).get("file") && file) {
|
||||
let file = selectedFile();
|
||||
// Orca/Anycubic: Metadaten oft ohne paint_index → ACE zeigt T0/T1 statt T1/T10
|
||||
if (file && file.id && filamentsNeedPaintIndex(file.filaments)) {
|
||||
try {
|
||||
const fresh = await api("/dateien/" + file.id + "/meta", { method: "POST" });
|
||||
if (fresh && fresh.id) {
|
||||
const idx = files.findIndex((row) => String(row.id) === String(fresh.id));
|
||||
if (idx >= 0) files[idx] = fresh;
|
||||
else files.unshift(fresh);
|
||||
file = selectedFile();
|
||||
renderLibrary();
|
||||
refreshFlow();
|
||||
}
|
||||
} catch (error) { /* manuell: Ordner neu lesen */ }
|
||||
}
|
||||
const q = new URLSearchParams(location.search);
|
||||
const deepLink = !!(q.get("file") || q.get("orca"));
|
||||
// Deep-Link / Orca-App: ACE/Senden zuerst — 3D-Pfad friert sonst den Tab ein
|
||||
if (file) scheduleToolpath(file, 0, deepLink ? 900 : 0);
|
||||
if (deepLink) {
|
||||
sel.tab = "send";
|
||||
renderTabs();
|
||||
const opts = document.getElementById("options");
|
||||
if (opts) opts.open = true;
|
||||
const panel = document.querySelector(".send-panel");
|
||||
if (panel) panel.scrollIntoView({ behavior: "smooth", block: "start" });
|
||||
if (file) lastOrcaFocusId = String(file.id);
|
||||
if (q.get("orca")) pullOrcaFocus();
|
||||
}
|
||||
}
|
||||
|
||||
let lastOrcaFocusId = null;
|
||||
|
||||
async function pullOrcaFocus() {
|
||||
try {
|
||||
const data = await api("/orca-fokus");
|
||||
if (!data || data.file_id == null) return;
|
||||
const id = String(data.file_id);
|
||||
const age = Date.now() / 1000 - Number(data.ts || 0);
|
||||
if (age > 180) return;
|
||||
const already = id === String(sel.fileId) && selectedFile();
|
||||
if (id === lastOrcaFocusId && already) return;
|
||||
const switched = lastOrcaFocusId != null && id !== lastOrcaFocusId;
|
||||
lastOrcaFocusId = id;
|
||||
if (already) return;
|
||||
let file = files.find((row) => String(row.id) === id);
|
||||
if (!file) {
|
||||
files = await api("/dateien");
|
||||
renderLibrary();
|
||||
file = files.find((row) => String(row.id) === id);
|
||||
}
|
||||
if (file) {
|
||||
selectFile(file.id);
|
||||
sel.tab = "send";
|
||||
renderTabs();
|
||||
if (switched) toast("Neue Slice-Datei ausgewählt.", "good");
|
||||
}
|
||||
} catch (error) { /* nächster Tick */ }
|
||||
}
|
||||
|
||||
function filamentsNeedPaintIndex(filaments) {
|
||||
const rows = filaments || [];
|
||||
if (rows.length < 2) return false;
|
||||
return rows.some((item) => !item || item.paint_index == null);
|
||||
}
|
||||
|
||||
async function refreshPrinters() {
|
||||
if (document.hidden) return;
|
||||
try {
|
||||
@@ -1105,32 +1235,36 @@ async function watchJobs(ids) {
|
||||
await tick();
|
||||
}
|
||||
|
||||
async function postSenden(hosts, whenFree) {
|
||||
return api("/senden", {
|
||||
method: "POST",
|
||||
json: {
|
||||
file_id: Number(sel.fileId),
|
||||
hosts,
|
||||
confirm: true,
|
||||
override_machine: document.getElementById("send-override").checked,
|
||||
bed_leveling: document.getElementById("send-bed-level").checked,
|
||||
ace_maps: collectAceMaps(),
|
||||
when_free: whenFree,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async function doSend() {
|
||||
const button = document.getElementById("send-go");
|
||||
const whenFree = sel.mode !== "now";
|
||||
button.disabled = true;
|
||||
document.getElementById("send-note").textContent = "";
|
||||
try {
|
||||
const payload = await api("/senden", {
|
||||
method: "POST",
|
||||
json: {
|
||||
file_id: Number(sel.fileId),
|
||||
hosts: selectedHosts(),
|
||||
confirm: true,
|
||||
override_machine: document.getElementById("send-override").checked,
|
||||
bed_leveling: document.getElementById("send-bed-level").checked,
|
||||
ace_maps: collectAceMaps(),
|
||||
when_free: whenFree,
|
||||
},
|
||||
});
|
||||
const projectId = document.getElementById("send-project")?.value;
|
||||
if (projectId && sel.fileId) {
|
||||
try {
|
||||
await api("/dateien/" + sel.fileId, { method: "PATCH", json: { project_id: Number(projectId) } });
|
||||
} catch (error) { /* Projektbindung optional */ }
|
||||
}
|
||||
const jobs = payload.jobs || [];
|
||||
if (whenFree || payload.mode === "when_free") {
|
||||
|
||||
if (sel.mode === "next") {
|
||||
const payload = await postSenden([], true);
|
||||
const jobs = payload.jobs || [];
|
||||
document.getElementById("send-progress").hidden = true;
|
||||
toast((jobs.length > 1 ? jobs.length + " Aufträge" : "Auftrag") + " eingereiht. Steht unter „Wartet“.", "good");
|
||||
updateSendButton();
|
||||
@@ -1138,9 +1272,35 @@ async function doSend() {
|
||||
refreshPrinters();
|
||||
return;
|
||||
}
|
||||
showJobsProgress(jobs);
|
||||
|
||||
const chosen = selectedPrinters();
|
||||
const ready = chosen.filter(canStartNow).map((p) => p.host);
|
||||
const later = chosen.filter((p) => !canStartNow(p)).map((p) => p.host);
|
||||
let queued = 0;
|
||||
let nowJobs = [];
|
||||
if (later.length) {
|
||||
const payload = await postSenden(later, true);
|
||||
queued += (payload.jobs || []).length;
|
||||
}
|
||||
if (ready.length) {
|
||||
const payload = await postSenden(ready, false);
|
||||
nowJobs = payload.jobs || [];
|
||||
}
|
||||
if (queued && !nowJobs.length) {
|
||||
document.getElementById("send-progress").hidden = true;
|
||||
toast((queued > 1 ? queued + " Aufträge" : "Auftrag") + " eingereiht. Steht unter „Wartet“.", "good");
|
||||
updateSendButton();
|
||||
if (typeof refreshPrintQueue === "function") refreshPrintQueue();
|
||||
refreshPrinters();
|
||||
return;
|
||||
}
|
||||
if (queued && nowJobs.length) {
|
||||
toast(queued + " eingereiht, " + nowJobs.length + " startet jetzt.", "good");
|
||||
if (typeof refreshPrintQueue === "function") refreshPrintQueue();
|
||||
}
|
||||
showJobsProgress(nowJobs);
|
||||
if (typeof refreshSendQueue === "function") refreshSendQueue();
|
||||
await watchJobs(jobs.map((job) => job.id));
|
||||
await watchJobs(nowJobs.map((job) => job.id));
|
||||
} catch (error) {
|
||||
updateSendButton();
|
||||
toast(error.message || String(error), "bad");
|
||||
@@ -1445,7 +1605,7 @@ document.getElementById("targets").addEventListener("click", (event) => {
|
||||
toast("Abläufe gehen nur auf Kobra 3.", "bad");
|
||||
return;
|
||||
}
|
||||
if (sel.tab === "send" && sel.mode === "next") sel.mode = "free";
|
||||
if (sel.tab === "send" && sel.mode === "next") sel.mode = "this";
|
||||
if (sel.hosts.has(host)) sel.hosts.delete(host);
|
||||
else sel.hosts.add(host);
|
||||
saveSendMemory();
|
||||
@@ -1479,19 +1639,11 @@ document.getElementById("send-bed-level").addEventListener("change", updateOptio
|
||||
document.getElementById("send-override").addEventListener("change", () => { refreshFlow(); });
|
||||
document.getElementById("send-project")?.addEventListener("change", updateOptionsSummary);
|
||||
|
||||
document.getElementById("send-go").addEventListener("click", (event) => {
|
||||
const button = event.currentTarget;
|
||||
const warn = spoolWarnText();
|
||||
if (sel.mode === "now") {
|
||||
armButton(button, warn ? "Warnung — trotzdem starten?" : "Wirklich starten? Nochmal klicken", doSend);
|
||||
} else if (warn) {
|
||||
armButton(button, "Warnung — trotzdem einreihen?", doSend);
|
||||
} else {
|
||||
doSend();
|
||||
}
|
||||
document.getElementById("send-go").addEventListener("click", () => {
|
||||
doSend();
|
||||
});
|
||||
document.getElementById("ablauf-go").addEventListener("click", (event) => {
|
||||
armButton(event.currentTarget, "Ablauf wirklich einreihen? Nochmal klicken", doAblauf);
|
||||
document.getElementById("ablauf-go").addEventListener("click", () => {
|
||||
doAblauf();
|
||||
});
|
||||
|
||||
document.getElementById("ablauf-add-print").addEventListener("click", () => addStep(defaultPrintStep()));
|
||||
@@ -1591,5 +1743,6 @@ loadSendMemory();
|
||||
loadAblaufMemory();
|
||||
load().catch((error) => toast(error.message || String(error), "bad"));
|
||||
setInterval(refreshPrinters, 5000);
|
||||
setInterval(pullOrcaFocus, 2000);
|
||||
</script>
|
||||
{% endblock %}
|
||||
Reference in new issue
Block a user