Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6b9ad9d426 |
@@ -1,5 +1,46 @@
|
||||
# Changelog
|
||||
|
||||
## [0.9.23] – 2026-06-16
|
||||
|
||||
### Neu
|
||||
- **Druckdialog nach Upload automatisch öffnen.** Eine neue Einstellung
|
||||
`print_start_dialog` (Einstellungen → Drucker → „Druckstart-Verhalten") steuert,
|
||||
was nach einem Upload bei leerlaufendem Drucker passiert: „Print-Dialog" öffnet
|
||||
den Slot-Zuordnungs-Dialog automatisch, „Print-Leiste" behält das bisherige
|
||||
Banner. Basiert auf PR #56 von @gangoke.
|
||||
- **Auto-Leveling-Schalter pro Druck.** Der Druckdialog hat jetzt eine eigene
|
||||
Auto-Leveling-Checkbox, die den globalen Standard für einen einzelnen Druck
|
||||
überschreibt.
|
||||
|
||||
### Behoben
|
||||
- **Objekt-Skip wurde beim Druckstart still ignoriert (PR #56, @gangoke).** Der
|
||||
Skip-Befehl wurde gesendet, *bevor* der Drucker im `printing`-Status war, und
|
||||
daher verworfen. Der Skip wird nun in einer Retry-Schleife erneut angewendet,
|
||||
sobald der Druck bestätigt läuft — mit einer Pending-Sperre, damit die UI den
|
||||
Skip-Status nicht vorzeitig zurücksetzt.
|
||||
- **Upload während eines laufenden Drucks überschrieb die Vorschau des laufenden
|
||||
Auftrags.** Ein neuer Upload während des Drucks ersetzt nicht mehr Thumbnail /
|
||||
file_ready des Auftrags auf dem Druckbett.
|
||||
|
||||
## [0.9.22] – 2026-06-16
|
||||
|
||||
### Neu
|
||||
- **Neu strukturiertes Einstellungs-Panel.** Das Einstellungs-Modal wurde durch
|
||||
ein dauerhaftes Master-Detail-Panel mit fünf Kategorien ersetzt: Verbindung,
|
||||
Drucker, Darstellung, Filament und System. Das Poll-Intervall ist nun live
|
||||
einstellbar.
|
||||
- **Vendor-Sichtbarkeitsfilter (Issue #41).** Eine neue Checkliste in den
|
||||
Filament-Einstellungen beschränkt das Slot-Profil-Dropdown auf bestimmte
|
||||
Hersteller. „Generic" und eigene importierte Profile sind immer sichtbar.
|
||||
- **Idle-Datei-Aktionen in der Fortschritts-Karte (Issue #55).** Nach einem
|
||||
Upload bei leerlaufendem Drucker erscheinen drei Schnellaktionen direkt in der
|
||||
Fortschritts-Karte: ▶ Drucken, ⚙ Slots zuordnen und ✕ Leeren.
|
||||
|
||||
### Behoben
|
||||
- **Mobileraker-Kompatibilität (Issue #48).** Absturz in `ConfigExtruder.fromJson`
|
||||
(leeres `configfile.config`), Hänger beim Refresh (Metadata-Endlosschleife) und
|
||||
fehlende ETA/Restzeit behoben.
|
||||
|
||||
## [0.9.21] – 2026-06-14
|
||||
|
||||
### Behoben
|
||||
|
||||
21
CHANGELOG.md
21
CHANGELOG.md
@@ -1,5 +1,26 @@
|
||||
# Changelog
|
||||
|
||||
## [0.9.23] – 2026-06-16
|
||||
|
||||
### New
|
||||
- **Auto-open print dialog after upload.** A new `print_start_dialog` setting
|
||||
(Settings → Printer → "Start Print Behavior") controls what happens after a
|
||||
file is uploaded while the printer is idle: `Print Dialog` opens the
|
||||
slot-assignment dialog automatically, `Print Bar` keeps the previous banner
|
||||
behaviour. Based on PR #56 by @gangoke.
|
||||
- **Per-print auto-leveling toggle.** The print dialog now has its own
|
||||
auto-leveling checkbox that overrides the global default for a single print.
|
||||
|
||||
### Fixed
|
||||
- **Object skip was silently ignored at print start (PR #56, @gangoke).** The
|
||||
skip command was sent *before* the printer entered the `printing` state, so it
|
||||
was dropped. The skip is now re-applied in a retry loop once the print is
|
||||
confirmed running, with a pending-lock so the UI doesn't reset the skip state
|
||||
prematurely.
|
||||
- **Upload during an active print overwrote the running job's preview.**
|
||||
Uploading a new file while printing no longer replaces the thumbnail /
|
||||
file_ready of the job currently on the bed.
|
||||
|
||||
## [0.9.22] – 2026-06-16
|
||||
|
||||
### New
|
||||
|
||||
@@ -61,6 +61,7 @@ def _load_config_file(path: pathlib.Path):
|
||||
"AUTO_LEVELING": (CONFIG_SECTION_PRINT, "auto_leveling"),
|
||||
"CAMERA_ON_PRINT": (CONFIG_SECTION_PRINT, "camera_on_print"),
|
||||
"WEB_UPLOAD_WARNING": (CONFIG_SECTION_PRINT, "web_upload_warning"),
|
||||
"PRINT_START_DIALOG": (CONFIG_SECTION_PRINT, "print_start_dialog"),
|
||||
"BRIDGE_PRINTER_NAME": (CONFIG_SECTION_BRIDGE, "printer_name"),
|
||||
}
|
||||
for env_key, (section, option) in mapping.items():
|
||||
@@ -73,6 +74,18 @@ def _load_config_file(path: pathlib.Path):
|
||||
pass
|
||||
|
||||
|
||||
# Backward compatibility: old key FILE_READY_DIALOG → PRINT_START_DIALOG
|
||||
if "PRINT_START_DIALOG" not in os.environ:
|
||||
try:
|
||||
legacy = cfg.get(CONFIG_SECTION_PRINT, "file_ready_dialog")
|
||||
if legacy:
|
||||
os.environ["PRINT_START_DIALOG"] = legacy
|
||||
except (configparser.NoSectionError, configparser.NoOptionError):
|
||||
pass
|
||||
if "PRINT_START_DIALOG" not in os.environ and "FILE_READY_DIALOG" in os.environ:
|
||||
os.environ["PRINT_START_DIALOG"] = os.environ["FILE_READY_DIALOG"]
|
||||
|
||||
|
||||
def migrate_env_to_config(env_path: pathlib.Path, config_path: pathlib.Path):
|
||||
"""Einmalige Migration: .env → config.ini anlegen."""
|
||||
env_vals: dict[str, str] = {}
|
||||
@@ -303,3 +316,4 @@ DEFAULT_AMS_SLOT = get("DEFAULT_AMS_SLOT", "auto")
|
||||
AUTO_LEVELING = int(get("AUTO_LEVELING","1"))
|
||||
CAMERA_ON_PRINT = int(get("CAMERA_ON_PRINT","0"))
|
||||
WEB_UPLOAD_WARNING = int(get("WEB_UPLOAD_WARNING", "1"))
|
||||
PRINT_START_DIALOG = int(get("PRINT_START_DIALOG", get("FILE_READY_DIALOG", "1")))
|
||||
|
||||
@@ -48,3 +48,4 @@ MODE_ID = get("MODE_ID", "")
|
||||
DEVICE_ID = get("DEVICE_ID", "")
|
||||
DEFAULT_AMS_SLOT = get("DEFAULT_AMS_SLOT", "auto")
|
||||
AUTO_LEVELING = int(get("AUTO_LEVELING", "1"))
|
||||
PRINT_START_DIALOG = int(get("PRINT_START_DIALOG", get("FILE_READY_DIALOG", "1")))
|
||||
|
||||
@@ -791,6 +791,7 @@ class KobraXBridge:
|
||||
"print_speed_mode": 2,
|
||||
"connection_error": "",
|
||||
"file_ready": "",
|
||||
"print_start_dialog": getattr(args, "print_start_dialog", 1),
|
||||
"filament_mode": "toolhead",
|
||||
"ace_drying": {"status": 0, "target_temp": 0, "duration": 0, "remain_time": 0, "humidity": None, "current_temp": None},
|
||||
}
|
||||
@@ -814,6 +815,9 @@ class KobraXBridge:
|
||||
|
||||
# Part-Skip: zuletzt vom Drucker gemeldete Skip-Liste (v0.9.10)
|
||||
self._skip_state: dict = {"objects": [], "skipped": [], "ts": 0}
|
||||
# Pre-Print-Skip: pending until printer enters printing state
|
||||
self._pending_preprint_skip: list[str] = []
|
||||
self._pending_preprint_skip_deadline: float = 0.0
|
||||
|
||||
# Theme-Name prüfen (keine Sonderzeichen oder Umlaute)
|
||||
raw_theme = (getattr(args, "ui_theme", None) or "default").strip()
|
||||
@@ -1067,7 +1071,16 @@ class KobraXBridge:
|
||||
"""
|
||||
d = payload.get("data") or {}
|
||||
skipped = d.get("objects_skip_parts") or d.get("skipped") or d.get("skipped_parts") or []
|
||||
# Liste immer (auch leer) übernehmen – sonst bleibt sie auf alten Stand
|
||||
# Während ein Pre-Print-Skip noch pending ist, leere Früh-Reports ignorieren
|
||||
# damit die UI nicht sofort zurückspringt bevor der Drucker den Skip bestätigt.
|
||||
now = time.time()
|
||||
if (not skipped and self._pending_preprint_skip
|
||||
and now <= self._pending_preprint_skip_deadline):
|
||||
return
|
||||
# Pending-Lock aufheben sobald Drucker die gewünschten Objekte bestätigt
|
||||
if self._pending_preprint_skip and set(skipped) >= set(self._pending_preprint_skip):
|
||||
self._pending_preprint_skip = []
|
||||
self._pending_preprint_skip_deadline = 0.0
|
||||
self._skip_state = {
|
||||
"skipped": list(skipped),
|
||||
"ts": int(time.time()),
|
||||
@@ -1079,14 +1092,19 @@ class KobraXBridge:
|
||||
d = payload.get("data") or {}
|
||||
details = d.get("file_details") or {}
|
||||
thumb = details.get("thumbnail") or details.get("png_image") or ""
|
||||
if thumb:
|
||||
file_name = d.get("filename") or details.get("filename") or self._last_uploaded_file
|
||||
active_print = self._state.get("print_state") in ("printing", "paused")
|
||||
current_print_file = self._state.get("filename") or ""
|
||||
# Uploads während eines laufenden Drucks dürfen die aktive
|
||||
# Fortschritts-Vorschau nicht überschreiben.
|
||||
if thumb and (not active_print or (file_name and file_name == current_print_file)):
|
||||
self._thumbnail_b64 = thumb
|
||||
log.info(f"Vorschaubild empfangen: {len(thumb)} Zeichen base64")
|
||||
# Part-Skip: Objekt-Liste + optionales SVG (v0.9.10)
|
||||
objs = details.get("objects_skip_parts") or []
|
||||
svg = details.get("svg_image") or ""
|
||||
if objs:
|
||||
filename = d.get("filename") or details.get("filename") or self._last_uploaded_file
|
||||
filename = file_name
|
||||
if filename:
|
||||
try:
|
||||
self._store.update_file_objects(filename, objs, svg)
|
||||
@@ -1095,6 +1113,33 @@ class KobraXBridge:
|
||||
log.warning(f"update_file_objects fehlgeschlagen: {e}")
|
||||
self._push_status_update()
|
||||
|
||||
def _apply_preprint_skip_after_start(self, names: list[str], retries: int = 20, delay_s: float = 0.75):
|
||||
"""Sendet Skip-Befehl erst nachdem Drucker in printing-State gewechselt hat.
|
||||
|
||||
Vorher sendet der Drucker den Befehl ins Leere (kein aktiver Druck).
|
||||
"""
|
||||
wanted = [str(n) for n in (names or []) if isinstance(n, str) and n]
|
||||
if not wanted:
|
||||
return False
|
||||
for i in range(max(1, int(retries))):
|
||||
try:
|
||||
if self._state.get("kobra_state") != "printing":
|
||||
time.sleep(max(0.1, float(delay_s)))
|
||||
continue
|
||||
resp = self.client.skip_objects(wanted)
|
||||
if resp is not None:
|
||||
log.info(f"Pre-Print skip applied ({len(wanted)} objects) on attempt {i+1}/{retries}")
|
||||
self._pending_preprint_skip = []
|
||||
self._pending_preprint_skip_deadline = 0.0
|
||||
return True
|
||||
except Exception as e:
|
||||
log.debug(f"Pre-Print skip attempt {i+1}/{retries} failed: {e}")
|
||||
time.sleep(max(0.1, float(delay_s)))
|
||||
log.warning(f"Pre-Print skip could not be confirmed after {retries} attempts")
|
||||
self._pending_preprint_skip = []
|
||||
self._pending_preprint_skip_deadline = 0.0
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def _detect_filament_mode(boxes: list, head_tools_model: int = -1) -> str:
|
||||
"""Detect active filament topology mode.
|
||||
@@ -2538,7 +2583,7 @@ class KobraXBridge:
|
||||
ams_box_mapping = self._build_auto_ams_box_mapping()
|
||||
|
||||
use_ams = len(ams_box_mapping) > 0
|
||||
auto_leveling = getattr(self._args, "auto_leveling", 1)
|
||||
auto_leveling = int(body.get("auto_leveling", getattr(self._args, "auto_leveling", 1)))
|
||||
filename = gcode_file["filename"]
|
||||
file_path = gcode_file["path"]
|
||||
|
||||
@@ -2570,6 +2615,15 @@ class KobraXBridge:
|
||||
},
|
||||
}
|
||||
|
||||
# Pre-Print-Skip sofort im UI-Status spiegeln
|
||||
self._skip_state = {"skipped": list(excluded_objects), "ts": int(time.time())}
|
||||
if excluded_objects:
|
||||
self._pending_preprint_skip = [str(n) for n in excluded_objects if isinstance(n, str) and n]
|
||||
self._pending_preprint_skip_deadline = time.time() + 12.0
|
||||
else:
|
||||
self._pending_preprint_skip = []
|
||||
self._pending_preprint_skip_deadline = 0.0
|
||||
|
||||
log.info(f"KX-Store Druckstart: {filename} ams={len(ams_box_mapping)} slots assignments={bool(assignments)} excluded={len(excluded_objects)}")
|
||||
loop = asyncio.get_event_loop()
|
||||
result = await loop.run_in_executor(
|
||||
@@ -2578,6 +2632,9 @@ class KobraXBridge:
|
||||
if result is None:
|
||||
return self._json_cors({"error": "Keine Antwort vom Drucker"}, status=504)
|
||||
|
||||
if excluded_objects:
|
||||
loop.run_in_executor(None, lambda: self._apply_preprint_skip_after_start(excluded_objects))
|
||||
|
||||
# Job in History starten
|
||||
self._current_job_id = self._store.start_job(
|
||||
gcode_file_id=gcode_file["id"],
|
||||
@@ -3759,6 +3816,7 @@ class KobraXBridge:
|
||||
"thumbnail": thumbnail,
|
||||
"connection_error": s["connection_error"],
|
||||
"file_ready": s["file_ready"],
|
||||
"print_start_dialog": s.get("print_start_dialog", getattr(self._args, "print_start_dialog", 1)),
|
||||
"version": self._read_version(),
|
||||
})
|
||||
|
||||
@@ -3897,6 +3955,7 @@ class KobraXBridge:
|
||||
"auto_leveling": getattr(self._args, "auto_leveling", 1),
|
||||
"camera_on_print": getattr(self._args, "camera_on_print", 0),
|
||||
"web_upload_warning": getattr(self._args, "web_upload_warning", 1),
|
||||
"print_start_dialog": getattr(self._args, "print_start_dialog", 1),
|
||||
"poll_interval": getattr(self._args, "poll_interval", 3),
|
||||
"filament_profiles": {str(k): v for k, v in self._filament_profiles.items()},
|
||||
"visible_vendors": self._visible_vendors,
|
||||
@@ -3930,6 +3989,7 @@ class KobraXBridge:
|
||||
cfg.set("print", "auto_leveling", str(data.get("auto_leveling", getattr(self._args, "auto_leveling", 1))))
|
||||
cfg.set("print", "camera_on_print", str(int(bool(data.get("camera_on_print", getattr(self._args, "camera_on_print", 0))))))
|
||||
cfg.set("print", "web_upload_warning", str(int(bool(data.get("web_upload_warning", getattr(self._args, "web_upload_warning", 1))))))
|
||||
cfg.set("print", "print_start_dialog", str(int(bool(data.get("print_start_dialog", getattr(self._args, "print_start_dialog", 1))))))
|
||||
if "poll_interval" in data:
|
||||
try:
|
||||
pi = max(1, min(60, int(data["poll_interval"])))
|
||||
@@ -4102,7 +4162,8 @@ class KobraXBridge:
|
||||
# die alten Werte statt der geänderten config.ini.
|
||||
for _k in ("PRINTER_IP", "MQTT_PORT", "MQTT_USERNAME", "MQTT_PASSWORD",
|
||||
"MODE_ID", "DEVICE_ID", "DEFAULT_AMS_SLOT", "AUTO_LEVELING",
|
||||
"CAMERA_ON_PRINT", "WEB_UPLOAD_WARNING", "BRIDGE_PRINTER_NAME"):
|
||||
"CAMERA_ON_PRINT", "WEB_UPLOAD_WARNING", "PRINT_START_DIALOG",
|
||||
"FILE_READY_DIALOG", "BRIDGE_PRINTER_NAME"):
|
||||
os.environ.pop(_k, None)
|
||||
|
||||
in_docker = os.path.exists("/.dockerenv") or os.environ.get("KX_IN_DOCKER")
|
||||
@@ -4987,6 +5048,8 @@ def main():
|
||||
parser.add_argument("--auto-leveling", type=int, default=env_loader.AUTO_LEVELING)
|
||||
parser.add_argument("--camera-on-print", type=int, default=env_loader.CAMERA_ON_PRINT)
|
||||
parser.add_argument("--web-upload-warning", type=int, default=env_loader.WEB_UPLOAD_WARNING)
|
||||
parser.add_argument("--print-start-dialog", dest="print_start_dialog", type=int, default=env_loader.PRINT_START_DIALOG)
|
||||
parser.add_argument("--file-ready-dialog", dest="print_start_dialog", type=int)
|
||||
|
||||
parser.add_argument("--host", default="0.0.0.0",
|
||||
help="Bind-Adresse für den Bridge-Server")
|
||||
|
||||
@@ -9,6 +9,9 @@ var camOn=false;
|
||||
var camUserStopped=false; // user stopped camera manually — suppress auto-restart for this print
|
||||
var _camPollInterval=null; // snapshot-polling interval for Android (no MJPEG support)
|
||||
var _lastLoadedFile=null; // zuletzt geladene/gedruckte Datei für Progress-Karten-Aktionen (Issue #55)
|
||||
var _fdDialogOpen=false; // Dialog ist gerade offen
|
||||
var _fdAutoOpenedFile=null; // Dateiname für den der Dialog auto-geöffnet wurde
|
||||
var _fdUserCancelled=false; // User hat den Auto-Open-Dialog abgebrochen
|
||||
var currentStep=1;
|
||||
var currentPanel='dashboard';
|
||||
var aceAutoRefillPrefs=(function(){
|
||||
@@ -355,8 +358,13 @@ function applyLang(){
|
||||
setText('lbl-default-slot',T.settings_default_slot);
|
||||
setText('opt-slot-auto',T.settings_slot_auto);
|
||||
setText('lbl-auto-leveling',T.settings_auto_leveling);
|
||||
setText('lbl-file-ready-mode',T.settings_file_ready_mode);
|
||||
setText('opt-file-ready-dialog',T.settings_file_ready_dialog);
|
||||
setText('opt-file-ready-banner',T.settings_file_ready_banner);
|
||||
setText('lbl-camera-on-print',T.settings_camera_on_print);
|
||||
setText('lbl-web-upload-warning',T.settings_web_upload_warning);
|
||||
setText('fd-options-title',T.fd_options_title);
|
||||
setText('fd-lbl-auto-leveling',T.print_auto_leveling);
|
||||
|
||||
setText('lbl-update-check',T.update_check);
|
||||
setText('lbl-update-apply',T.update_apply);
|
||||
@@ -652,10 +660,18 @@ function applyState(){
|
||||
var bannerVisible=false;
|
||||
var frb=document.getElementById('file-ready-banner');
|
||||
if(frb){
|
||||
var shouldAutoOpen=(s.print_start_dialog===undefined?true:!!s.print_start_dialog);
|
||||
if(s.file_ready&&s.print_state==='standby'){
|
||||
document.getElementById('file-ready-name').textContent=s.file_ready;
|
||||
frb.style.display='flex';
|
||||
bannerVisible=true;
|
||||
// Neue Datei → Abbruch-Sperre aufheben
|
||||
if(_fdAutoOpenedFile&&_fdAutoOpenedFile!==s.file_ready) _fdUserCancelled=false;
|
||||
if(shouldAutoOpen&&!_fdDialogOpen&&!_fdUserCancelled&&_fdAutoOpenedFile!==s.file_ready){
|
||||
_fdAutoOpenedFile=s.file_ready;
|
||||
startReadyFileWithSlots(s.file_ready,true);
|
||||
} else {
|
||||
frb.style.display='flex';
|
||||
bannerVisible=true;
|
||||
}
|
||||
}else{frb.style.display='none';}
|
||||
}
|
||||
// skip-button (mid-print) – nur sichtbar wenn aktuell gedruckt wird
|
||||
@@ -958,6 +974,7 @@ function openSettings(){
|
||||
document.getElementById('s-default-slot').value=d.default_ams_slot||'auto';
|
||||
document.getElementById('s-auto-leveling').checked=(d.auto_leveling===undefined?true:!!d.auto_leveling);
|
||||
var cop=document.getElementById('s-camera-on-print');if(cop)cop.checked=!!d.camera_on_print;
|
||||
var frm=document.getElementById('s-file-ready-mode');if(frm)frm.value=(d.print_start_dialog===undefined?'1':String(d.print_start_dialog?1:0));
|
||||
var wuw=document.getElementById('s-web-upload-warning');if(wuw)wuw.checked=(d.web_upload_warning===undefined?true:!!d.web_upload_warning);
|
||||
// Poll-Intervall (Sekunden) — Backend hat Vorrang vor localStorage
|
||||
var pi=document.getElementById('s-poll-interval');
|
||||
@@ -1455,6 +1472,7 @@ function saveSettings(){
|
||||
default_ams_slot: document.getElementById('s-default-slot').value,
|
||||
auto_leveling: document.getElementById('s-auto-leveling').checked?1:0,
|
||||
camera_on_print: (document.getElementById('s-camera-on-print')||{}).checked?1:0,
|
||||
print_start_dialog: parseInt((document.getElementById('s-file-ready-mode')||{}).value||'1',10),
|
||||
web_upload_warning:webUploadWarning,
|
||||
poll_interval: Math.min(60,Math.max(1,parseInt((document.getElementById('s-poll-interval')||{}).value,10)||3)),
|
||||
}).then(function(){
|
||||
@@ -2239,7 +2257,8 @@ function confirmStoreWebVerify(){
|
||||
});
|
||||
}
|
||||
|
||||
function startReadyFileWithSlots(filename){
|
||||
function startReadyFileWithSlots(filename,_autoOpen){
|
||||
if(!_autoOpen) _fdAutoOpenedFile=null; // manueller Aufruf → Auto-Open-Sperre aufheben
|
||||
var fn=filename||S.file_ready;
|
||||
var currentFile=(storeFiles||[]).find(function(f){return f.filename===fn;});
|
||||
if(currentFile && currentFile.web_unverified && webUploadWarningEnabled()){
|
||||
@@ -2252,10 +2271,16 @@ function startReadyFileWithSlots(filename){
|
||||
_storeFileId=null;
|
||||
_gcodeFilaments=[];
|
||||
|
||||
var _autoOpenFile=_autoOpen?fn:null;
|
||||
if(_autoOpen) _fdDialogOpen=true; // bereits während Fetch sperren
|
||||
function openWithSlots(){
|
||||
fetch(_apiUrl('/kx/filament/slots')).then(function(r){return r.json()}).then(function(d){
|
||||
if(_autoOpenFile && _fdUserCancelled){_fdDialogOpen=false;return;}
|
||||
openFilamentDialog(d.result||[]);
|
||||
}).catch(function(){openFilamentDialog([]);});
|
||||
}).catch(function(){
|
||||
if(_autoOpenFile && _fdUserCancelled){_fdDialogOpen=false;return;}
|
||||
openFilamentDialog([]);
|
||||
});
|
||||
}
|
||||
|
||||
var fileObj=(storeFiles||[]).find(function(f){return f.filename===_storeFilename;});
|
||||
@@ -2325,6 +2350,9 @@ function openFilamentDialog(slots){
|
||||
var title=document.getElementById('fd-title');
|
||||
var body=document.getElementById('fd-slots');
|
||||
if(title)title.textContent='▶ '+_storeFilename;
|
||||
// Auto-Leveling-Checkbox mit globalem Default vorbelegen
|
||||
var fdAl=document.getElementById('fd-auto-leveling');
|
||||
if(fdAl) fdAl.checked=(S.auto_leveling===undefined?true:!!S.auto_leveling);
|
||||
// Objekt-Liste laden (nur Store-Modus: per File-ID; Banner-Modus hat keine ID)
|
||||
_printObjects=[];
|
||||
_printObjectsSvg='';
|
||||
@@ -2432,6 +2460,8 @@ function openFilamentDialog(slots){
|
||||
function closeFilamentDialog(){
|
||||
var dlg=document.getElementById('filament-dialog');
|
||||
if(dlg)dlg.classList.remove('open');
|
||||
_fdDialogOpen=false;
|
||||
if(_fdAutoOpenedFile) _fdUserCancelled=true;
|
||||
}
|
||||
|
||||
function confirmFilamentPrint(){
|
||||
@@ -2471,12 +2501,14 @@ function confirmFilamentPrint(){
|
||||
}
|
||||
// Pre-Print Skip: Namen der abgehakten Objekte sammeln
|
||||
var excludedObjects=_printObjects.filter(function(o){return o.skip;}).map(function(o){return o.name;});
|
||||
var fdAlEl=document.getElementById('fd-auto-leveling');
|
||||
var fdAutoLeveling=fdAlEl?( fdAlEl.checked?1:0):(S.auto_leveling===undefined?1:S.auto_leveling?1:0);
|
||||
closeFilamentDialog();
|
||||
if(_filamentDialogMode==='banner'){
|
||||
// Banner-Modus: normaler print/start mit Slot-Override
|
||||
var btn=document.getElementById('file-ready-btn');
|
||||
if(btn){btn.disabled=true;btn.textContent='…';}
|
||||
post('/printer/print/start',{filename:S.file_ready,filament_assignments:assignments,excluded_objects:excludedObjects})
|
||||
post('/printer/print/start',{filename:S.file_ready,filament_assignments:assignments,excluded_objects:excludedObjects,auto_leveling:fdAutoLeveling})
|
||||
.then(function(r){return r.json();})
|
||||
.then(function(){
|
||||
document.getElementById('file-ready-banner').style.display='none';
|
||||
@@ -2491,7 +2523,7 @@ function confirmFilamentPrint(){
|
||||
fetch(_apiUrl('/kx/print'),{
|
||||
method:'POST',
|
||||
headers:{'Content-Type':'application/json'},
|
||||
body:JSON.stringify({file_id:_storeFileId,filament_assignments:assignments,excluded_objects:excludedObjects})
|
||||
body:JSON.stringify({file_id:_storeFileId,filament_assignments:assignments,excluded_objects:excludedObjects,auto_leveling:fdAutoLeveling})
|
||||
}).then(function(r){return r.json()}).then(function(d){
|
||||
if(d.result==='ok'){clog('Druckstart: '+_storeFilename,'msg-ok');showPanel('dashboard');}
|
||||
else{clog('Druckfehler: '+(d.error||'?'),'msg-err');}
|
||||
|
||||
@@ -492,6 +492,13 @@
|
||||
<input type="checkbox" id="s-auto-leveling" style="width:auto;margin:0">
|
||||
<label id="lbl-auto-leveling" style="margin:0;cursor:pointer" for="s-auto-leveling">Auto-Leveling vor Druck</label>
|
||||
</div>
|
||||
<div class="modal-field">
|
||||
<label id="lbl-file-ready-mode">Nach Upload: Druckstart-Verhalten</label>
|
||||
<select id="s-file-ready-mode">
|
||||
<option value="1" id="opt-file-ready-dialog">Print-Dialog</option>
|
||||
<option value="0" id="opt-file-ready-banner">Print-Leiste</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="modal-field" style="flex-direction:row;align-items:center;gap:10px">
|
||||
<input type="checkbox" id="s-camera-on-print" style="width:auto;margin:0">
|
||||
<label id="lbl-camera-on-print" style="margin:0;cursor:pointer" for="s-camera-on-print">Kamera bei Druckstart einschalten</label>
|
||||
@@ -623,6 +630,13 @@
|
||||
<div id="fd-objects-svg" style="display:none;background:var(--raised);border:1px solid var(--border);border-radius:8px;padding:6px;margin-bottom:8px;text-align:center"></div>
|
||||
<div id="fd-objects" style="display:flex;flex-direction:column;gap:6px;max-height:140px;overflow-y:auto"></div>
|
||||
</div>
|
||||
<div style="margin-bottom:14px;padding:10px 12px;background:var(--raised);border-radius:8px;border:1px solid var(--border)">
|
||||
<div style="font-size:11px;font-weight:600;color:var(--txt2);margin-bottom:8px;text-transform:uppercase;letter-spacing:.05em" id="fd-options-title">Druckoptionen</div>
|
||||
<div style="display:flex;align-items:center;gap:8px">
|
||||
<input type="checkbox" id="fd-auto-leveling" style="width:auto;margin:0">
|
||||
<label for="fd-auto-leveling" style="margin:0;cursor:pointer;font-size:13px" id="fd-lbl-auto-leveling">Auto-Leveling</label>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display:flex;gap:8px;justify-content:flex-end">
|
||||
<button id="fd-cancel" onclick="closeFilamentDialog()" style="padding:8px 16px;background:var(--raised);border:1px solid var(--border);border-radius:8px;color:var(--txt);cursor:pointer">Abbrechen</button>
|
||||
<button id="fd-print" onclick="confirmFilamentPrint()" style="padding:8px 18px;background:var(--accent);color:#fff;border:none;border-radius:8px;cursor:pointer;font-weight:600">▶ Drucken</button>
|
||||
|
||||
@@ -257,5 +257,33 @@
|
||||
"sf_new": "Neu",
|
||||
"ss_date": "↓ Datum",
|
||||
"ss_name": "A–Z Name",
|
||||
"ss_dur": "⏱ Druckzeit"
|
||||
"ss_dur": "⏱ Druckzeit",
|
||||
"ace_dry_preset_pla": "PLA",
|
||||
"ace_dry_preset_pla_plus": "PLA+",
|
||||
"ace_dry_preset_petg": "PETG",
|
||||
"ace_dry_preset_tpu": "TPU",
|
||||
"ace_dry_preset_abs_asa": "ABS / ASA",
|
||||
"ace_dry_preset_pa_pc": "PA / PC",
|
||||
"ace_dry_preset_custom": "Custom",
|
||||
"fd_options_title": "Optionen",
|
||||
"print_auto_leveling": "Auto-Leveling für diesen Druck",
|
||||
"settings_file_ready_mode": "Druckdialog starten",
|
||||
"settings_file_ready_banner": "Druckleiste",
|
||||
"settings_file_ready_dialog": "Druckdialog",
|
||||
"log_dir_rx": "RX",
|
||||
"log_dir_tx": "TX",
|
||||
"log_dir_label": "Richtung:",
|
||||
"log_lvl_err": "⛔ Fehler",
|
||||
"log_lvl_warn": "⚠ Warnung",
|
||||
"log_topic_label": "Thema:",
|
||||
"log_topic_ams": "AMS",
|
||||
"log_topic_print": "Druck",
|
||||
"log_topic_info": "Info",
|
||||
"log_topic_status": "Status",
|
||||
"log_download": "⬇ Download",
|
||||
"log_auto": "⬇ Auto",
|
||||
"log_clear": "✕ Leeren",
|
||||
"log_filter_placeholder": "Filtern…",
|
||||
"skip_cancel": "Abbrechen",
|
||||
"skip_confirm": "Überspringen"
|
||||
}
|
||||
|
||||
@@ -69,6 +69,13 @@
|
||||
"ace_dry_dialog_save_restart": "Save & Restart",
|
||||
"ace_dry_dialog_custom_name": "Custom Name",
|
||||
"ace_dry_dialog_reset_default": "Reset to Default",
|
||||
"ace_dry_preset_pla": "PLA",
|
||||
"ace_dry_preset_pla_plus": "PLA+",
|
||||
"ace_dry_preset_petg": "PETG",
|
||||
"ace_dry_preset_tpu": "TPU",
|
||||
"ace_dry_preset_abs_asa": "ABS / ASA",
|
||||
"ace_dry_preset_pa_pc": "PA / PC",
|
||||
"ace_dry_preset_custom": "Custom",
|
||||
"cam_placeholder": "📷 Camera not started",
|
||||
"cam_stream_unavailable": "Stream unavailable",
|
||||
"btn_cam_start": "▶ Camera",
|
||||
@@ -153,7 +160,12 @@
|
||||
"hint_ip_no_port": "IP address only, no port (e.g. 192.168.1.102)",
|
||||
"settings_default_slot": "Default Slot (single color)",
|
||||
"settings_slot_auto": "Auto (all loaded slots)",
|
||||
"settings_auto_leveling": "Auto-Leveling before print",
|
||||
"settings_auto_leveling": "Auto-Leveling Default",
|
||||
"fd_options_title": "Print Options",
|
||||
"print_auto_leveling": "Auto-Leveling",
|
||||
"settings_file_ready_mode": "Start Print Behavior",
|
||||
"settings_file_ready_banner": "Print Bar",
|
||||
"settings_file_ready_dialog": "Print Dialog",
|
||||
"settings_camera_on_print": "Turn camera on at print start",
|
||||
"settings_web_upload_warning": "Show warning when printing web uploads",
|
||||
"update_check": "Check for Updates",
|
||||
@@ -192,7 +204,21 @@
|
||||
"orca_profile_done": "Imported",
|
||||
"orca_profile_skipped": "skipped",
|
||||
"log_dir_all": "All",
|
||||
"log_dir_rx": "RX",
|
||||
"log_dir_tx": "TX",
|
||||
"log_dir_label": "Dir:",
|
||||
"log_lvl_label": "Level:",
|
||||
"log_lvl_err": "⛔ Errors",
|
||||
"log_lvl_warn": "⚠ Warn",
|
||||
"log_topic_label": "Topic:",
|
||||
"log_topic_ams": "AMS",
|
||||
"log_topic_print": "Print",
|
||||
"log_topic_info": "Info",
|
||||
"log_topic_status": "Status",
|
||||
"log_download": "⬇ Download",
|
||||
"log_auto": "⬇ Auto",
|
||||
"log_clear": "✕ Clear",
|
||||
"log_filter_placeholder": "Filter…",
|
||||
"file_ready_btn": "▶ Start Print",
|
||||
"file_slots_btn": "🎨 Select Slots",
|
||||
"file_cancel_btn": "✕ Cancel",
|
||||
@@ -202,6 +228,8 @@
|
||||
"skip_btn_label": "Objects",
|
||||
"skip_no_objects": "No objects in this print.",
|
||||
"skip_already": "skipped",
|
||||
"skip_cancel": "Cancel",
|
||||
"skip_confirm": "Skip",
|
||||
"skip_select_at_least_one": "Please pick at least one object.",
|
||||
"skip_sending": "Sending …",
|
||||
"skip_success": "Objects will be skipped.",
|
||||
|
||||
@@ -257,5 +257,33 @@
|
||||
"sf_new": "Nuevo",
|
||||
"ss_date": "↓ Fecha",
|
||||
"ss_name": "A–Z Nombre",
|
||||
"ss_dur": "⏱ Tiempo de impresión"
|
||||
"ss_dur": "⏱ Tiempo de impresión",
|
||||
"ace_dry_preset_pla": "PLA",
|
||||
"ace_dry_preset_pla_plus": "PLA+",
|
||||
"ace_dry_preset_petg": "PETG",
|
||||
"ace_dry_preset_tpu": "TPU",
|
||||
"ace_dry_preset_abs_asa": "ABS / ASA",
|
||||
"ace_dry_preset_pa_pc": "PA / PC",
|
||||
"ace_dry_preset_custom": "Personalizado",
|
||||
"fd_options_title": "Opciones",
|
||||
"print_auto_leveling": "Autonivelado para esta impresión",
|
||||
"settings_file_ready_mode": "Iniciar diálogo de impresión",
|
||||
"settings_file_ready_banner": "Barra de impresión",
|
||||
"settings_file_ready_dialog": "Diálogo de impresión",
|
||||
"log_dir_rx": "RX",
|
||||
"log_dir_tx": "TX",
|
||||
"log_dir_label": "Dirección:",
|
||||
"log_lvl_err": "⛔ Errores",
|
||||
"log_lvl_warn": "⚠ Avisos",
|
||||
"log_topic_label": "Tema:",
|
||||
"log_topic_ams": "AMS",
|
||||
"log_topic_print": "Impresión",
|
||||
"log_topic_info": "Info",
|
||||
"log_topic_status": "Estado",
|
||||
"log_download": "⬇ Descargar",
|
||||
"log_auto": "⬇ Auto",
|
||||
"log_clear": "✕ Limpiar",
|
||||
"log_filter_placeholder": "Filtrar…",
|
||||
"skip_cancel": "Cancelar",
|
||||
"skip_confirm": "Omitir"
|
||||
}
|
||||
|
||||
@@ -257,6 +257,33 @@
|
||||
"sf_new": "Nouveau",
|
||||
"ss_date": "↓ Date",
|
||||
"ss_name": "A–Z Nom",
|
||||
"ss_dur": "⏱ Durée d'impression"
|
||||
"ss_dur": "⏱ Durée d'impression",
|
||||
"ace_dry_preset_pla": "PLA",
|
||||
"ace_dry_preset_pla_plus": "PLA+",
|
||||
"ace_dry_preset_petg": "PETG",
|
||||
"ace_dry_preset_tpu": "TPU",
|
||||
"ace_dry_preset_abs_asa": "ABS / ASA",
|
||||
"ace_dry_preset_pa_pc": "PA / PC",
|
||||
"ace_dry_preset_custom": "Personnalisé",
|
||||
"fd_options_title": "Options",
|
||||
"print_auto_leveling": "Mise à niveau auto pour cette impression",
|
||||
"settings_file_ready_mode": "Démarrer le dialogue d'impression",
|
||||
"settings_file_ready_banner": "Barre d'impression",
|
||||
"settings_file_ready_dialog": "Dialogue d'impression",
|
||||
"log_dir_rx": "RX",
|
||||
"log_dir_tx": "TX",
|
||||
"log_dir_label": "Sens :",
|
||||
"log_lvl_err": "⛔ Erreurs",
|
||||
"log_lvl_warn": "⚠ Avert.",
|
||||
"log_topic_label": "Sujet :",
|
||||
"log_topic_ams": "AMS",
|
||||
"log_topic_print": "Impression",
|
||||
"log_topic_info": "Info",
|
||||
"log_topic_status": "Statut",
|
||||
"log_download": "⬇ Télécharger",
|
||||
"log_auto": "⬇ Auto",
|
||||
"log_clear": "✕ Effacer",
|
||||
"log_filter_placeholder": "Filtrer…",
|
||||
"skip_cancel": "Annuler",
|
||||
"skip_confirm": "Ignorer"
|
||||
}
|
||||
|
||||
|
||||
@@ -257,5 +257,33 @@
|
||||
"sf_new": "新",
|
||||
"ss_date": "↓ 日期",
|
||||
"ss_name": "A–Z 名称",
|
||||
"ss_dur": "⏱ 打印时间"
|
||||
"ss_dur": "⏱ 打印时间",
|
||||
"ace_dry_preset_pla": "PLA",
|
||||
"ace_dry_preset_pla_plus": "PLA+",
|
||||
"ace_dry_preset_petg": "PETG",
|
||||
"ace_dry_preset_tpu": "TPU",
|
||||
"ace_dry_preset_abs_asa": "ABS / ASA",
|
||||
"ace_dry_preset_pa_pc": "PA / PC",
|
||||
"ace_dry_preset_custom": "自定义",
|
||||
"fd_options_title": "选项",
|
||||
"print_auto_leveling": "本次打印自动调平",
|
||||
"settings_file_ready_mode": "开始打印对话框",
|
||||
"settings_file_ready_banner": "打印栏",
|
||||
"settings_file_ready_dialog": "打印对话框",
|
||||
"log_dir_rx": "RX",
|
||||
"log_dir_tx": "TX",
|
||||
"log_dir_label": "方向:",
|
||||
"log_lvl_err": "⛔ 错误",
|
||||
"log_lvl_warn": "⚠ 警告",
|
||||
"log_topic_label": "主题:",
|
||||
"log_topic_ams": "AMS",
|
||||
"log_topic_print": "打印",
|
||||
"log_topic_info": "信息",
|
||||
"log_topic_status": "状态",
|
||||
"log_download": "⬇ 下载",
|
||||
"log_auto": "⬇ 自动",
|
||||
"log_clear": "✕ 清空",
|
||||
"log_filter_placeholder": "筛选…",
|
||||
"skip_cancel": "取消",
|
||||
"skip_confirm": "跳过"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user