forked from viewit/KX-Bridge-Release
build: sources for v0.9.22
This commit is contained in:
@ -8,6 +8,7 @@ var tempHistory={n:[],b:[]};
|
||||
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 currentStep=1;
|
||||
var currentPanel='dashboard';
|
||||
var aceAutoRefillPrefs=(function(){
|
||||
@ -167,6 +168,8 @@ async function setLanguage(lang){
|
||||
localStorage.setItem('lang',l);
|
||||
var langSel=document.getElementById('lang-select');
|
||||
if(langSel)langSel.value=l;
|
||||
var sLangSel=document.getElementById('s-lang-select');
|
||||
if(sLangSel)sLangSel.value=l;
|
||||
document.documentElement.setAttribute('lang',l);
|
||||
applyLang();
|
||||
}
|
||||
@ -310,12 +313,26 @@ function applyLang(){
|
||||
document.querySelectorAll('.temp-input').forEach(e=>e.setAttribute('placeholder',T.label_target_c.replace(':','')));
|
||||
// Console
|
||||
setText('ptitle-console',T.panel_console_title);
|
||||
// Settings modal
|
||||
setText('modal-title-settings',T.settings_title);
|
||||
// Settings-Panel
|
||||
setText('modal-sec-connection',T.settings_connection);
|
||||
setText('modal-sec-print',T.settings_print);
|
||||
setText('modal-sec-poll',T.settings_poll);
|
||||
setText('modal-sec-version',T.settings_version);
|
||||
// Nav + Kategorie-Labels (mit Fallback falls i18n-Key noch fehlt)
|
||||
setText('nav-settings',T.nav_settings||'Einstellungen');
|
||||
setText('setcat-lbl-connection',T.settings_connection||'Verbindung');
|
||||
setText('setcat-lbl-printer',T.settings_print||'Drucker');
|
||||
setText('setcat-lbl-display',T.settings_cat_display||'Darstellung');
|
||||
setText('setcat-lbl-display2',T.settings_cat_display||'Darstellung');
|
||||
setText('setcat-lbl-filament',T.settings_cat_filament||'Filament');
|
||||
setText('setcat-lbl-system',T.settings_version||'System');
|
||||
setText('lbl-set-lang',T.settings_cat_language||'Sprache');
|
||||
setText('lbl-set-theme',T.settings_cat_theme||'Hell / Dunkel umschalten');
|
||||
setText('lbl-poll-interval',T.settings_poll||'Poll-Intervall (Sekunden)');
|
||||
setText('lbl-filament-mapping',T.settings_filament_mapping||'Filament-Profil-Mapping (pro Slot)');
|
||||
setText('lbl-filament-mapping-save',T.settings_filament_mapping_save||'Mapping speichern');
|
||||
setText('lbl-visible-vendors',T.settings_visible_vendors||'Sichtbare Hersteller (Profil-Dropdown)');
|
||||
setText('visible-vendors-hint',T.settings_visible_vendors_hint||'Nur diese Hersteller erscheinen im Slot-Profil-Dropdown. Nichts ausgewählt = alle anzeigen. „Generic" und eigene Profile sind immer sichtbar.');
|
||||
setText('lbl-visible-vendors-save',T.settings_visible_vendors_save||'Auswahl speichern');
|
||||
// Custom-Profile-Import (Issue #41)
|
||||
setText('modal-sec-orca-profiles',T.orca_profile_section);
|
||||
setText('orca-profiles-hint',T.orca_profile_hint);
|
||||
@ -343,6 +360,10 @@ function applyLang(){
|
||||
|
||||
setText('lbl-update-check',T.update_check);
|
||||
setText('lbl-update-apply',T.update_apply);
|
||||
// Progress-Karten-Aktionen für geladene/idle Datei (Issue #55)
|
||||
setText('d-idle-print-lbl',T.progress_action_print||'Drucken');
|
||||
setText('d-idle-slots-lbl',T.progress_action_slots||'Slots zuordnen');
|
||||
setText('d-idle-clear-lbl',T.progress_action_clear||'Leeren');
|
||||
// Speed buttons
|
||||
setText('d-spd-lbl-1',T.speed_silent.replace(/^\S+\s/,''));
|
||||
setText('d-spd-lbl-2',T.speed_normal.replace(/^\S+\s/,''));
|
||||
@ -462,6 +483,15 @@ function showPanel(id){
|
||||
var nb=document.getElementById('nb-'+id);if(nb)nb.classList.add('active');
|
||||
var bnb=document.getElementById('bnb-'+id);if(bnb)bnb.classList.add('active');
|
||||
currentPanel=id;
|
||||
if(id==='settings')openSettings();
|
||||
}
|
||||
|
||||
// Settings-Kategorie umschalten (Master-Detail)
|
||||
function showSettingsCat(name){
|
||||
document.querySelectorAll('.set-group').forEach(g=>g.classList.remove('active'));
|
||||
document.querySelectorAll('.set-cat').forEach(b=>b.classList.remove('active'));
|
||||
var g=document.getElementById('setgrp-'+name);if(g)g.classList.add('active');
|
||||
var c=document.getElementById('setcat-'+name);if(c)c.classList.add('active');
|
||||
}
|
||||
|
||||
// ── Console log ──
|
||||
@ -619,11 +649,13 @@ function applyState(){
|
||||
// connection error banner – nur wenn überhaupt ein Drucker konfiguriert ist
|
||||
var banner=document.getElementById('conn-error-banner');
|
||||
if(banner){if(s.connection_error&&_printers.length>0){banner.textContent='⚠ '+tr('lbl_conn_error')+' '+s.connection_error;banner.style.display='block';}else{banner.style.display='none';}}
|
||||
var bannerVisible=false;
|
||||
var frb=document.getElementById('file-ready-banner');
|
||||
if(frb){
|
||||
if(s.file_ready&&s.print_state==='standby'){
|
||||
document.getElementById('file-ready-name').textContent=s.file_ready;
|
||||
frb.style.display='flex';
|
||||
bannerVisible=true;
|
||||
}else{frb.style.display='none';}
|
||||
}
|
||||
// skip-button (mid-print) – nur sichtbar wenn aktuell gedruckt wird
|
||||
@ -635,6 +667,25 @@ function applyState(){
|
||||
var ctrlBtns=document.getElementById('d-ctrl-btns');
|
||||
if(ctrlBtns) ctrlBtns.style.display=printing?'':'none';
|
||||
updatePauseResumeBtn();
|
||||
// Zuletzt geladene Datei merken (Issue #55): solange sie über den State
|
||||
// sichtbar ist. Beim Druckende/Abbruch leert die Bridge file_ready+filename
|
||||
// (Issue #29) — die gemerkte Referenz bleibt für die Karten-Aktionen.
|
||||
if(s.file_ready) _lastLoadedFile=s.file_ready;
|
||||
else if(s.filename) _lastLoadedFile=s.filename;
|
||||
// Idle-Aktionen (Drucken/Slots/Leeren) nur wenn nicht gedruckt wird, eine
|
||||
// Datei bekannt ist und der grüne Banner nicht ohnehin schon dieselbe Aktion
|
||||
// anbietet.
|
||||
var idleBtns=document.getElementById('d-idle-btns');
|
||||
if(idleBtns){
|
||||
var showIdle=(!printing && _lastLoadedFile && !bannerVisible);
|
||||
idleBtns.style.display=showIdle?'':'none';
|
||||
if(showIdle){
|
||||
var dfn=document.getElementById('d-fname');
|
||||
if(dfn && (!dfn.textContent || dfn.textContent==='–')){
|
||||
dfn.textContent=_lastLoadedFile;dfn.title=_lastLoadedFile;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// header
|
||||
var b=document.getElementById('h-badge');
|
||||
@ -896,10 +947,6 @@ function drawChart(id,_,series){
|
||||
var _updateTag='';
|
||||
var _updateUrl='';
|
||||
function openSettings(){
|
||||
// Titel mit aktivem Drucker-Namen aktualisieren
|
||||
var pname=_activePrinter&&_activePrinter.name?_activePrinter.name:null;
|
||||
var title=document.getElementById('modal-title-settings');
|
||||
if(title)title.textContent=T.settings_title+(pname?' – '+pname:'');
|
||||
fetch(_apiUrl('/api/settings')).then(function(r){return r.json()}).then(function(d){
|
||||
document.getElementById('s-printer-name').value=d.printer_name||'';
|
||||
document.getElementById('s-printer-ip').value=d.printer_ip||'';
|
||||
@ -912,23 +959,118 @@ function openSettings(){
|
||||
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 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');
|
||||
if(pi){
|
||||
var sec=d.poll_interval||Math.round((parseInt(localStorage.getItem('pollInterval')||'2000'))/1000)||3;
|
||||
pi.value=sec;
|
||||
}
|
||||
renderFilamentMapping(d.filament_profiles||{});
|
||||
});
|
||||
var v=localStorage.getItem('pollInterval')||'2000';
|
||||
document.querySelectorAll('.poll-btn').forEach(function(b){b.classList.remove('active')});
|
||||
var pb=document.getElementById('poll-'+Math.round(parseInt(v)/1000));
|
||||
if(pb)pb.classList.add('active');
|
||||
// Sprach-Select im Settings-Panel mit aktueller Sprache spiegeln
|
||||
var ls=document.getElementById('s-lang-select');
|
||||
if(ls)ls.value=(localStorage.getItem('lang')||document.documentElement.lang||'de');
|
||||
document.getElementById('s-version-label').textContent='v'+('__VERSION__'||'?');
|
||||
document.getElementById('update-status').textContent='';
|
||||
document.getElementById('btn-update-apply').style.display='none';
|
||||
var cl=document.getElementById('update-changelog');if(cl)cl.style.display='none';
|
||||
_updateTag='';_updateUrl='';
|
||||
document.getElementById('settings-modal').classList.add('open');
|
||||
// Custom-Profile-Liste laden (Issue #41 — Verwaltung von User-importierten
|
||||
// OrcaSlicer-Filament-Profilen)
|
||||
// Custom-Profile-Liste laden (Issue #41)
|
||||
refreshUserProfileList();
|
||||
// Vendor-Sichtbarkeitsfilter (Issue #41 Option A)
|
||||
loadVendorChecklist();
|
||||
}
|
||||
function closeSettings(){
|
||||
document.getElementById('settings-modal').classList.remove('open');
|
||||
// Panel-Variante: zurück zum Dashboard
|
||||
showPanel('dashboard');
|
||||
}
|
||||
|
||||
// Poll-Intervall-Feld → Live-Poll sofort übernehmen (Persistenz erst beim Speichern)
|
||||
function onPollIntervalInput(){
|
||||
var pi=document.getElementById('s-poll-interval');
|
||||
if(!pi)return;
|
||||
var sec=parseInt(pi.value,10);
|
||||
if(sec>=1&&sec<=60)setPoll(sec*1000);
|
||||
}
|
||||
|
||||
// ── Filament-Profil-Mapping pro Slot ([filament_profiles]) ──
|
||||
function renderFilamentMapping(map){
|
||||
var el=document.getElementById('filament-mapping-list');
|
||||
if(!el)return;
|
||||
var rows='';
|
||||
for(var i=0;i<4;i++){
|
||||
var m=map[i]||map[String(i)]||{};
|
||||
var idHint=m.id?' <span style="color:var(--txt2);font-size:11px">('+m.id+')</span>':'';
|
||||
rows+='<div class="modal-field" style="margin-bottom:8px">'
|
||||
+'<label>Slot '+(i+1)+idHint+'</label>'
|
||||
+'<div style="display:flex;gap:6px;flex-wrap:wrap">'
|
||||
+'<input type="text" id="fmap-'+i+'-vendor" placeholder="Vendor (z.B. Polymaker)" value="'+(m.vendor||'')+'" style="flex:1;min-width:120px">'
|
||||
+'<input type="text" id="fmap-'+i+'-name" placeholder="Name (z.B. PolyTerra PLA)" value="'+(m.name||'')+'" style="flex:1;min-width:140px">'
|
||||
+'</div></div>';
|
||||
}
|
||||
el.innerHTML=rows;
|
||||
}
|
||||
function saveFilamentMapping(){
|
||||
// Nutzt den per-Slot-Endpoint (vendor,name → ID-Lookup im Backend).
|
||||
// Leere Felder = Mapping entfernen.
|
||||
var chain=Promise.resolve();
|
||||
for(var i=0;i<4;i++){
|
||||
(function(slot){
|
||||
var vendor=((document.getElementById('fmap-'+slot+'-vendor')||{}).value||'').trim();
|
||||
var name=((document.getElementById('fmap-'+slot+'-name')||{}).value||'').trim();
|
||||
chain=chain.then(function(){
|
||||
return fetch(_apiUrl('/kx/filament/slots/'+slot+'/profile'),
|
||||
{method:'POST',headers:{'Content-Type':'application/json'},
|
||||
body:JSON.stringify({vendor:vendor,name:name})});
|
||||
});
|
||||
})(i);
|
||||
}
|
||||
chain.then(function(){
|
||||
clog(tr('log_filament_mapping_saved')||'Filament-Mapping gespeichert','msg-ok');
|
||||
openSettings(); // neu laden → ID-Hints aktualisieren
|
||||
}).catch(function(e){clog('Mapping-Fehler: '+e,'msg-err');});
|
||||
}
|
||||
|
||||
// ── Vendor-Sichtbarkeitsfilter (Issue #41 Option A) ──
|
||||
var _vendorChecklistSel={}; // {vendor:true} — laufende Auswahl im UI
|
||||
function loadVendorChecklist(){
|
||||
// aktuelle Auswahl aus Backend, dann alle verfügbaren Vendoren rendern
|
||||
_visibleVendors=null; // Cache invalidieren
|
||||
_loadVisibleVendors(function(vis){
|
||||
_vendorChecklistSel={};
|
||||
(vis||[]).forEach(function(v){_vendorChecklistSel[v]=true;});
|
||||
renderVendorChecklist();
|
||||
});
|
||||
}
|
||||
function renderVendorChecklist(){
|
||||
var el=document.getElementById('visible-vendors-list');
|
||||
if(!el)return;
|
||||
_loadOrcaFilaments(function(profiles){
|
||||
// alle System-Vendoren (ohne Generic — der ist immer sichtbar) sammeln
|
||||
var set={};
|
||||
profiles.forEach(function(p){ if(!p.is_user && p.vendor && p.vendor!=='Generic') set[p.vendor]=1; });
|
||||
var vendors=Object.keys(set).sort();
|
||||
var q=((document.getElementById('vendor-filter-search')||{}).value||'').toLowerCase();
|
||||
if(q)vendors=vendors.filter(function(v){return v.toLowerCase().indexOf(q)>=0;});
|
||||
el.innerHTML=vendors.map(function(v){
|
||||
var ck=_vendorChecklistSel[v]?'checked':'';
|
||||
var safe=v.replace(/"/g,'"');
|
||||
return '<label style="display:flex;align-items:center;gap:8px;padding:3px 0;cursor:pointer;font-size:13px">'
|
||||
+'<input type="checkbox" data-vendor="'+safe+'" '+ck+' onchange="_vendorCheck(this)" style="width:auto;margin:0"> '+v+'</label>';
|
||||
}).join('')||'<i style="color:var(--txt2)">–</i>';
|
||||
});
|
||||
}
|
||||
function _vendorCheck(cb){
|
||||
var v=cb.getAttribute('data-vendor');
|
||||
if(cb.checked)_vendorChecklistSel[v]=true; else delete _vendorChecklistSel[v];
|
||||
}
|
||||
function saveVisibleVendors(){
|
||||
var vendors=Object.keys(_vendorChecklistSel);
|
||||
fetch(_apiUrl('/kx/filament/visible_vendors'),{method:'POST',headers:{'Content-Type':'application/json'},
|
||||
body:JSON.stringify({vendors:vendors})}).then(function(r){return r.json();}).then(function(){
|
||||
_visibleVendors=vendors.slice(); // Dropdown-Cache aktualisieren
|
||||
clog(tr('log_visible_vendors_saved')||'Hersteller-Auswahl gespeichert','msg-ok');
|
||||
}).catch(function(e){clog('Vendor-Filter-Fehler: '+e,'msg-err');});
|
||||
}
|
||||
|
||||
// ── Custom Filament Profile Import (Issue #41) ──
|
||||
@ -1002,6 +1144,9 @@ function doProfileImportUpload(files){
|
||||
_orcaFilamentCache=null;
|
||||
refreshImportDialogList();
|
||||
refreshUserProfileList();
|
||||
// Vendor-Checkliste neu aufbauen — ein Import kann einen bisher
|
||||
// unbekannten System-Vendor mitbringen (Issue #41).
|
||||
if(document.getElementById('visible-vendors-list')) renderVendorChecklist();
|
||||
// Wenn Slot-Edit offen ist, Dropdown gleich neu befüllen
|
||||
var mat=document.getElementById('slot-edit-mat');
|
||||
if(mat && document.getElementById('slot-edit-modal').classList.contains('open')){
|
||||
@ -1042,6 +1187,14 @@ function updateSlotEditFeedButton(){
|
||||
btn.textContent=_slotEditLoaded?tr('slot_edit_unload'):tr('slot_edit_load');
|
||||
}
|
||||
var _orcaFilamentCache=null; // [{id,name,vendor,type,color}, …]
|
||||
var _visibleVendors=null; // Vendor-Sichtbarkeitsfilter (Issue #41); [] = alle
|
||||
function _loadVisibleVendors(cb){
|
||||
if(_visibleVendors!==null){ cb(_visibleVendors); return; }
|
||||
fetch(_apiUrl('/kx/filament/visible_vendors')).then(function(r){return r.json();}).then(function(d){
|
||||
_visibleVendors=d.result||[];
|
||||
cb(_visibleVendors);
|
||||
}).catch(function(){ _visibleVendors=[]; cb([]); });
|
||||
}
|
||||
function _loadOrcaFilaments(cb){
|
||||
if(_orcaFilamentCache){ cb(_orcaFilamentCache); return; }
|
||||
fetch(_apiUrl('/kx/filament/profiles')).then(function(r){return r.json();}).then(function(d){
|
||||
@ -1087,13 +1240,23 @@ function _fillSlotProfileDropdown(material, currentVendor, currentName){
|
||||
userProfs.forEach(function(p){ _appendOption(gUser, p); });
|
||||
sel.appendChild(gUser);
|
||||
}
|
||||
// System-Profile nach Vendor gruppieren
|
||||
var byVendor={};
|
||||
systemProfs.forEach(function(p){ (byVendor[p.vendor]=byVendor[p.vendor]||[]).push(p); });
|
||||
Object.keys(byVendor).sort().forEach(function(v){
|
||||
var g=document.createElement('optgroup'); g.label=v;
|
||||
byVendor[v].forEach(function(p){ _appendOption(g, p); });
|
||||
sel.appendChild(g);
|
||||
// Vendor-Sichtbarkeitsfilter (Issue #41 Option A): nur gewählte Vendoren +
|
||||
// Generic. Leere Liste = alle (rückwärtskompatibel). Eigene Profile (is_user)
|
||||
// sind oben bereits unkonditional drin.
|
||||
_loadVisibleVendors(function(vis){
|
||||
var filtered=systemProfs;
|
||||
if(vis&&vis.length){
|
||||
var allow={};vis.forEach(function(v){allow[v]=1;});
|
||||
allow['Generic']=1;
|
||||
filtered=systemProfs.filter(function(p){return allow[p.vendor];});
|
||||
}
|
||||
var byVendor={};
|
||||
filtered.forEach(function(p){ (byVendor[p.vendor]=byVendor[p.vendor]||[]).push(p); });
|
||||
Object.keys(byVendor).sort().forEach(function(v){
|
||||
var g=document.createElement('optgroup'); g.label=v;
|
||||
byVendor[v].forEach(function(p){ _appendOption(g, p); });
|
||||
sel.appendChild(g);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -1147,15 +1310,16 @@ function slotEditFeed(){
|
||||
})
|
||||
.catch(function(){});
|
||||
}
|
||||
function startReadyFile(){
|
||||
var currentFile=(storeFiles||[]).find(function(f){return f.filename===S.file_ready;});
|
||||
function startReadyFile(filename){
|
||||
var fn=filename||S.file_ready;
|
||||
var currentFile=(storeFiles||[]).find(function(f){return f.filename===fn;});
|
||||
if(currentFile && currentFile.web_unverified && webUploadWarningEnabled()){
|
||||
maybeGateWebUpload(currentFile, function(){ startReadyFile(); });
|
||||
maybeGateWebUpload(currentFile, function(){ startReadyFile(fn); });
|
||||
return;
|
||||
}
|
||||
var btn=document.getElementById('file-ready-btn');
|
||||
if(btn){btn.disabled=true;btn.textContent='…';}
|
||||
post('/printer/print/start',{filename:S.file_ready})
|
||||
post('/printer/print/start',{filename:fn})
|
||||
.then(function(r){return r.json();})
|
||||
.then(function(r){
|
||||
document.getElementById('file-ready-banner').style.display='none';
|
||||
@ -1170,6 +1334,20 @@ function cancelReadyFile(){
|
||||
post('/api/file_ready/clear',{})
|
||||
.then(function(){document.getElementById('file-ready-banner').style.display='none';});
|
||||
}
|
||||
|
||||
// ── Aktionen für geladene/idle Datei in der Progress-Karte (Issue #55) ──
|
||||
function startIdleFile(){
|
||||
if(_lastLoadedFile) startReadyFile(_lastLoadedFile);
|
||||
}
|
||||
function startIdleFileWithSlots(){
|
||||
if(_lastLoadedFile) startReadyFileWithSlots(_lastLoadedFile);
|
||||
}
|
||||
function clearIdleFile(){
|
||||
_lastLoadedFile=null;
|
||||
var ib=document.getElementById('d-idle-btns');if(ib)ib.style.display='none';
|
||||
var fn=document.getElementById('d-fname');if(fn){fn.textContent='–';fn.title='';}
|
||||
post('/api/file_ready/clear',{}).catch(function(){});
|
||||
}
|
||||
function selectMatPreset(m){
|
||||
document.getElementById('slot-edit-mat').value=m;
|
||||
highlightMatBtn(m);
|
||||
@ -1257,9 +1435,6 @@ document.addEventListener('DOMContentLoaded',function(){
|
||||
});
|
||||
});
|
||||
function setPoll(ms){
|
||||
document.querySelectorAll('.poll-btn').forEach(function(b){b.classList.remove('active')});
|
||||
var id='poll-'+Math.round(ms/1000);
|
||||
var pb=document.getElementById(id);if(pb)pb.classList.add('active');
|
||||
localStorage.setItem('pollInterval',ms);
|
||||
clearInterval(pollTimer);
|
||||
pollTimer=setInterval(poll,ms);
|
||||
@ -1281,6 +1456,7 @@ function saveSettings(){
|
||||
auto_leveling: document.getElementById('s-auto-leveling').checked?1:0,
|
||||
camera_on_print: (document.getElementById('s-camera-on-print')||{}).checked?1:0,
|
||||
web_upload_warning:webUploadWarning,
|
||||
poll_interval: Math.min(60,Math.max(1,parseInt((document.getElementById('s-poll-interval')||{}).value,10)||3)),
|
||||
}).then(function(){
|
||||
btn.textContent=T.update_restarting;
|
||||
setTimeout(function(){
|
||||
@ -2063,14 +2239,15 @@ function confirmStoreWebVerify(){
|
||||
});
|
||||
}
|
||||
|
||||
function startReadyFileWithSlots(){
|
||||
var currentFile=(storeFiles||[]).find(function(f){return f.filename===S.file_ready;});
|
||||
function startReadyFileWithSlots(filename){
|
||||
var fn=filename||S.file_ready;
|
||||
var currentFile=(storeFiles||[]).find(function(f){return f.filename===fn;});
|
||||
if(currentFile && currentFile.web_unverified && webUploadWarningEnabled()){
|
||||
maybeGateWebUpload(currentFile, function(){ startReadyFileWithSlots(); });
|
||||
maybeGateWebUpload(currentFile, function(){ startReadyFileWithSlots(fn); });
|
||||
return;
|
||||
}
|
||||
_filamentDialogMode='banner';
|
||||
_storeFilename=S.file_ready||'';
|
||||
_storeFilename=fn||'';
|
||||
// Banner must never reuse stale store-file context.
|
||||
_storeFileId=null;
|
||||
_gcodeFilaments=[];
|
||||
|
||||
@ -42,115 +42,12 @@
|
||||
<option value="zh-cn">中文(简体)</option>
|
||||
</select>
|
||||
</div>
|
||||
<button class="theme-btn" onclick="openSettings()" id="settings-btn" title="Einstellungen">⚙</button>
|
||||
<button class="theme-btn" onclick="showPanel('settings')" id="settings-btn" title="Einstellungen">⚙</button>
|
||||
<button class="conn-btn disconnected" id="conn-btn" onclick="toggleConnection()">⚡ Verbinden</button>
|
||||
</header>
|
||||
|
||||
<!-- ═══ SETTINGS MODAL ═══ -->
|
||||
<div class="modal-overlay" id="settings-modal" onclick="if(event.target===this)closeSettings()">
|
||||
<div class="modal-box">
|
||||
<div class="modal-header">
|
||||
<span class="modal-title" id="modal-title-settings">Einstellungen</span>
|
||||
<button class="modal-close" onclick="closeSettings()">✕</button>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="modal-field" style="margin-bottom:12px">
|
||||
<label id="lbl-printer-name" style="font-weight:600">Drucker-Name</label>
|
||||
<input type="text" id="s-printer-name" placeholder="z.B. Kobra X Links">
|
||||
</div>
|
||||
<div class="modal-section" id="modal-sec-connection">Verbindung</div>
|
||||
<div class="modal-field">
|
||||
<label id="lbl-printer-ip">Drucker-IP</label>
|
||||
<input type="text" id="s-printer-ip" placeholder="192.168.x.x">
|
||||
<small id="lbl-ip-hint" style="color:#f80;display:none"></small>
|
||||
</div>
|
||||
<div class="modal-field">
|
||||
<label id="lbl-mqtt-port">MQTT-Port</label>
|
||||
<input type="number" id="s-mqtt-port" placeholder="9883">
|
||||
</div>
|
||||
<div class="modal-field">
|
||||
<label id="lbl-username">MQTT-Benutzername</label>
|
||||
<input type="text" id="s-username" placeholder="userXXXXXXXX" autocomplete="new-password">
|
||||
</div>
|
||||
<div class="modal-field">
|
||||
<label id="lbl-password">MQTT-Passwort</label>
|
||||
<input type="password" id="s-password" autocomplete="new-password">
|
||||
</div>
|
||||
<div class="modal-field">
|
||||
<label id="lbl-device-id">Device-ID</label>
|
||||
<input type="text" id="s-device-id" placeholder="32 Hex-Zeichen">
|
||||
</div>
|
||||
<div class="modal-field">
|
||||
<label id="lbl-mode-id">Mode-ID</label>
|
||||
<input type="text" id="s-mode-id" placeholder="20030">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="modal-section" id="modal-sec-print">Druckeinstellungen</div>
|
||||
<div class="modal-field">
|
||||
<label id="lbl-default-slot">Standard-Slot (Einfarbdruck)</label>
|
||||
<select id="s-default-slot">
|
||||
<option value="auto" id="opt-slot-auto">Auto (alle belegten Slots)</option>
|
||||
<option value="0" id="opt-slot-0">Slot 1</option>
|
||||
<option value="1" id="opt-slot-1">Slot 2</option>
|
||||
<option value="2" id="opt-slot-2">Slot 3</option>
|
||||
<option value="3" id="opt-slot-3">Slot 4</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="modal-field" style="flex-direction:row;align-items:center;gap:10px">
|
||||
<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" 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>
|
||||
</div>
|
||||
<div class="modal-field" style="flex-direction:row;align-items:center;gap:10px">
|
||||
<input type="checkbox" id="s-web-upload-warning" style="width:auto;margin:0">
|
||||
<label id="lbl-web-upload-warning" style="margin:0;cursor:pointer" for="s-web-upload-warning">Warnung bei Web-Upload-Druck anzeigen</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="modal-section" id="modal-sec-poll">Poll-Intervall</div>
|
||||
<div class="poll-btns">
|
||||
<button class="poll-btn" onclick="setPoll(1000)" id="poll-1">1s</button>
|
||||
<button class="poll-btn active" onclick="setPoll(2000)" id="poll-2">2s</button>
|
||||
<button class="poll-btn" onclick="setPoll(5000)" id="poll-5">5s</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- OrcaSlicer-Profile (Custom-Profile-Import, Issue #41) -->
|
||||
<div>
|
||||
<div class="modal-section" id="modal-sec-orca-profiles">OrcaSlicer-Profile</div>
|
||||
<div style="font-size:11px;color:var(--txt2);margin-bottom:8px" id="orca-profiles-hint">
|
||||
Eigene Profile aus OrcaSlicer importieren (User-Dir öffnen via Help → Show Configuration Folder)
|
||||
</div>
|
||||
<div id="orca-profiles-list" style="margin-bottom:8px;font-size:12px;color:var(--txt2)"></div>
|
||||
<button class="btn btn-sm" id="btn-orca-profiles-import" onclick="openProfileImport()"
|
||||
style="background:var(--raised);color:var(--txt)">
|
||||
⬆ <span id="lbl-orca-profiles-import">Profile importieren</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="modal-section" id="modal-sec-version">Version</div>
|
||||
<div class="update-row">
|
||||
<span id="s-version-label" style="font-size:13px;color:var(--txt)">–</span>
|
||||
<button class="btn btn-sm" style="background:var(--raised);color:var(--txt)" onclick="checkUpdate()" id="btn-update-check">🔄 <span id="lbl-update-check">Auf Updates prüfen</span></button>
|
||||
</div>
|
||||
<div class="update-status" id="update-status" style="margin-top:6px"></div>
|
||||
<button class="btn btn-sm btn-accent" id="btn-update-apply" style="display:none;margin-top:8px" onclick="applyUpdate()">
|
||||
<span id="lbl-update-apply">Jetzt installieren</span>
|
||||
</button>
|
||||
<div id="update-changelog" style="display:none;margin-top:10px;background:var(--raised);border-radius:6px;padding:10px;font-size:11px;font-family:var(--mono);color:var(--txt2);white-space:pre-wrap;max-height:180px;overflow-y:auto;line-height:1.6"></div>
|
||||
</div>
|
||||
|
||||
<button class="modal-save" onclick="saveSettings()" id="btn-save-settings">Speichern & Neustart</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Settings-Modal entfernt — jetzt #panel-settings (Master-Detail im Main-Bereich) -->
|
||||
|
||||
<!-- ═══ AMS SLOT EDIT DIALOG ═══ -->
|
||||
<div class="modal-overlay" id="slot-edit-modal" onclick="if(event.target===this)closeSlotEdit()">
|
||||
@ -230,6 +127,8 @@
|
||||
<span class="nav-icon">🗂</span><span class="nav-text">Browser</span></button>
|
||||
<button class="nav-btn" onclick="showPanel('console');clearLogBadge()" id="nb-console">
|
||||
<span class="nav-icon">≡</span><span class="nav-text">Konsole</span><span id="log-badge" style="display:none;margin-left:4px;background:var(--err);color:#fff;border-radius:10px;font-size:10px;padding:1px 5px;font-weight:700"></span></button>
|
||||
<button class="nav-btn" onclick="showPanel('settings')" id="nb-settings">
|
||||
<span class="nav-icon">⚙</span><span class="nav-text" id="nav-settings">Einstellungen</span></button>
|
||||
</nav>
|
||||
|
||||
<main>
|
||||
@ -298,6 +197,12 @@
|
||||
<button class="btn btn-skip btn-sm" id="d-btn-skip" onclick="openSkipDialog()" style="display:none">✂ <span id="d-btn-skip-label">Objekte</span></button>
|
||||
<button class="btn btn-cancel btn-sm" id="d-btn-cancel" onclick="confirmCancel()">✕ Stopp</button>
|
||||
</div>
|
||||
<!-- Aktionen für eine geladene, aber nicht laufende Datei (Issue #55) -->
|
||||
<div class="ctrl-btns" id="d-idle-btns" style="margin-top:12px;display:none">
|
||||
<button class="btn btn-accent btn-sm" id="d-idle-print" onclick="startIdleFile()">▶ <span id="d-idle-print-lbl">Drucken</span></button>
|
||||
<button class="btn btn-sm" id="d-idle-slots" onclick="startIdleFileWithSlots()" style="background:var(--raised);color:var(--txt)">⚙ <span id="d-idle-slots-lbl">Slots zuordnen</span></button>
|
||||
<button class="btn btn-cancel btn-sm" id="d-idle-clear" onclick="clearIdleFile()">✕ <span id="d-idle-clear-lbl">Leeren</span></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Temperatursteuerung + Verlauf -->
|
||||
@ -520,6 +425,162 @@
|
||||
<div class="console" id="console-log" style="height:calc(100vh - 260px);min-height:160px" onscroll="onLogScroll()"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ═══ EINSTELLUNGEN ═══ -->
|
||||
<div class="panel" id="panel-settings">
|
||||
<div class="settings-wrap">
|
||||
<div class="settings-cats">
|
||||
<button class="set-cat active" id="setcat-connection" onclick="showSettingsCat('connection')"><span>🔌</span> <span id="setcat-lbl-connection">Verbindung</span></button>
|
||||
<button class="set-cat" id="setcat-printer" onclick="showSettingsCat('printer')"><span>🖨</span> <span id="setcat-lbl-printer">Drucker</span></button>
|
||||
<button class="set-cat" id="setcat-display" onclick="showSettingsCat('display')"><span>🎨</span> <span id="setcat-lbl-display">Darstellung</span></button>
|
||||
<button class="set-cat" id="setcat-filament" onclick="showSettingsCat('filament')"><span>🧵</span> <span id="setcat-lbl-filament">Filament</span></button>
|
||||
<button class="set-cat" id="setcat-system" onclick="showSettingsCat('system')"><span>⚙</span> <span id="setcat-lbl-system">System</span></button>
|
||||
</div>
|
||||
|
||||
<div class="settings-content">
|
||||
<!-- Verbindung -->
|
||||
<div class="set-group active" id="setgrp-connection">
|
||||
<div class="card">
|
||||
<div class="card-title"><span>🔌</span> <span id="modal-sec-connection">Verbindung</span></div>
|
||||
<div class="modal-field" style="margin-bottom:12px">
|
||||
<label id="lbl-printer-name" style="font-weight:600">Drucker-Name</label>
|
||||
<input type="text" id="s-printer-name" placeholder="z.B. Kobra X Links">
|
||||
</div>
|
||||
<div class="modal-field">
|
||||
<label id="lbl-printer-ip">Drucker-IP</label>
|
||||
<input type="text" id="s-printer-ip" placeholder="192.168.x.x">
|
||||
<small id="lbl-ip-hint" style="color:#f80;display:none"></small>
|
||||
</div>
|
||||
<div class="modal-field">
|
||||
<label id="lbl-mqtt-port">MQTT-Port</label>
|
||||
<input type="number" id="s-mqtt-port" placeholder="9883">
|
||||
</div>
|
||||
<div class="modal-field">
|
||||
<label id="lbl-username">MQTT-Benutzername</label>
|
||||
<input type="text" id="s-username" placeholder="userXXXXXXXX" autocomplete="new-password">
|
||||
</div>
|
||||
<div class="modal-field">
|
||||
<label id="lbl-password">MQTT-Passwort</label>
|
||||
<input type="password" id="s-password" autocomplete="new-password">
|
||||
</div>
|
||||
<div class="modal-field">
|
||||
<label id="lbl-device-id">Device-ID</label>
|
||||
<input type="text" id="s-device-id" placeholder="32 Hex-Zeichen">
|
||||
</div>
|
||||
<div class="modal-field">
|
||||
<label id="lbl-mode-id">Mode-ID</label>
|
||||
<input type="text" id="s-mode-id" placeholder="20030">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Drucker -->
|
||||
<div class="set-group" id="setgrp-printer">
|
||||
<div class="card">
|
||||
<div class="card-title"><span>🖨</span> <span id="modal-sec-print">Druckeinstellungen</span></div>
|
||||
<div class="modal-field">
|
||||
<label id="lbl-default-slot">Standard-Slot (Einfarbdruck)</label>
|
||||
<select id="s-default-slot">
|
||||
<option value="auto" id="opt-slot-auto">Auto (alle belegten Slots)</option>
|
||||
<option value="0" id="opt-slot-0">Slot 1</option>
|
||||
<option value="1" id="opt-slot-1">Slot 2</option>
|
||||
<option value="2" id="opt-slot-2">Slot 3</option>
|
||||
<option value="3" id="opt-slot-3">Slot 4</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="modal-field" style="flex-direction:row;align-items:center;gap:10px">
|
||||
<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" 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>
|
||||
</div>
|
||||
<div class="modal-field" style="flex-direction:row;align-items:center;gap:10px">
|
||||
<input type="checkbox" id="s-web-upload-warning" style="width:auto;margin:0">
|
||||
<label id="lbl-web-upload-warning" style="margin:0;cursor:pointer" for="s-web-upload-warning">Warnung bei Web-Upload-Druck anzeigen</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Darstellung -->
|
||||
<div class="set-group" id="setgrp-display">
|
||||
<div class="card">
|
||||
<div class="card-title"><span>🎨</span> <span id="setcat-lbl-display2">Darstellung</span></div>
|
||||
<div class="modal-field">
|
||||
<label id="lbl-set-lang">Sprache</label>
|
||||
<select id="s-lang-select" onchange="setLanguage(this.value)">
|
||||
<option value="de">Deutsch</option>
|
||||
<option value="en">English</option>
|
||||
<option value="es">Espanol</option>
|
||||
<option value="fr">Français</option>
|
||||
<option value="zh-cn">中文(简体)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="modal-field" style="flex-direction:row;align-items:center;gap:10px">
|
||||
<button class="btn btn-sm" style="background:var(--raised);color:var(--txt)" onclick="toggleTheme()"><span id="lbl-set-theme">Hell / Dunkel umschalten</span></button>
|
||||
</div>
|
||||
<div class="modal-field">
|
||||
<label id="lbl-poll-interval">Poll-Intervall (Sekunden)</label>
|
||||
<input type="number" id="s-poll-interval" min="1" max="60" step="1" placeholder="3" oninput="onPollIntervalInput()">
|
||||
<small style="color:var(--txt2)" id="lbl-poll-hint">Wie oft die Bridge den Drucker-Status abfragt</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Filament -->
|
||||
<div class="set-group" id="setgrp-filament">
|
||||
<div class="card">
|
||||
<div class="card-title"><span>🧵</span> <span id="modal-sec-orca-profiles">OrcaSlicer-Profile</span></div>
|
||||
<div style="font-size:11px;color:var(--txt2);margin-bottom:8px" id="orca-profiles-hint">
|
||||
Eigene Profile aus OrcaSlicer importieren (User-Dir öffnen via Help → Show Configuration Folder)
|
||||
</div>
|
||||
<div id="orca-profiles-list" style="margin-bottom:8px;font-size:12px;color:var(--txt2)"></div>
|
||||
<button class="btn btn-sm" id="btn-orca-profiles-import" onclick="openProfileImport()"
|
||||
style="background:var(--raised);color:var(--txt)">
|
||||
⬆ <span id="lbl-orca-profiles-import">Profile importieren</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-title"><span>🎯</span> <span id="lbl-filament-mapping">Filament-Profil-Mapping (pro Slot)</span></div>
|
||||
<div style="font-size:11px;color:var(--txt2);margin-bottom:8px" id="filament-mapping-hint">
|
||||
Festes Orca-Profil pro AMS-Slot. Beim Slicer-Sync sendet die Bridge dieses Profil statt „Generic".
|
||||
</div>
|
||||
<div id="filament-mapping-list"></div>
|
||||
<button class="btn btn-sm" style="background:var(--accent);color:#fff;margin-top:8px" onclick="saveFilamentMapping()"><span id="lbl-filament-mapping-save">Mapping speichern</span></button>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-title"><span>👁</span> <span id="lbl-visible-vendors">Sichtbare Hersteller (Profil-Dropdown)</span></div>
|
||||
<div style="font-size:11px;color:var(--txt2);margin-bottom:8px" id="visible-vendors-hint">
|
||||
Nur diese Hersteller erscheinen im Slot-Profil-Dropdown. Nichts ausgewählt = alle anzeigen. „Generic" und eigene Profile sind immer sichtbar.
|
||||
</div>
|
||||
<input type="text" id="vendor-filter-search" placeholder="Hersteller suchen…" oninput="renderVendorChecklist()"
|
||||
style="width:100%;padding:6px 10px;margin-bottom:8px;background:var(--raised);border:1px solid var(--border);border-radius:6px;color:var(--txt);font-size:12px">
|
||||
<div id="visible-vendors-list" style="max-height:260px;overflow-y:auto;border:1px solid var(--border);border-radius:6px;padding:8px"></div>
|
||||
<button class="btn btn-sm" style="background:var(--accent);color:#fff;margin-top:8px" onclick="saveVisibleVendors()"><span id="lbl-visible-vendors-save">Auswahl speichern</span></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- System -->
|
||||
<div class="set-group" id="setgrp-system">
|
||||
<div class="card">
|
||||
<div class="card-title"><span>⚙</span> <span id="modal-sec-version">Version</span></div>
|
||||
<div class="update-row">
|
||||
<span id="s-version-label" style="font-size:13px;color:var(--txt)">–</span>
|
||||
<button class="btn btn-sm" style="background:var(--raised);color:var(--txt)" onclick="checkUpdate()" id="btn-update-check">🔄 <span id="lbl-update-check">Auf Updates prüfen</span></button>
|
||||
</div>
|
||||
<div class="update-status" id="update-status" style="margin-top:6px"></div>
|
||||
<button class="btn btn-sm btn-accent" id="btn-update-apply" style="display:none;margin-top:8px" onclick="applyUpdate()">
|
||||
<span id="lbl-update-apply">Jetzt installieren</span>
|
||||
</button>
|
||||
<div id="update-changelog" style="display:none;margin-top:10px;background:var(--raised);border-radius:6px;padding:10px;font-size:11px;font-family:var(--mono);color:var(--txt2);white-space:pre-wrap;max-height:180px;overflow-y:auto;line-height:1.6"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="modal-save" onclick="saveSettings()" id="btn-save-settings" style="margin-top:14px">Speichern & Neustart</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
@ -528,6 +589,7 @@
|
||||
<button class="bnav-btn" onclick="showPanel('printers');loadPrinterTab()" id="bnb-printers"><span class="bnav-icon">🖨</span>Drucker</button>
|
||||
<button class="bnav-btn" onclick="showPanel('store');loadStore()" id="bnb-store"><span class="bnav-icon">🗂</span>Browser</button>
|
||||
<button class="bnav-btn" onclick="showPanel('console');clearLogBadge()" id="bnb-console"><span class="bnav-icon">≡</span>Log<span id="log-badge-bot" style="display:none;margin-left:3px;background:var(--err);color:#fff;border-radius:10px;font-size:10px;padding:1px 4px;font-weight:700"></span></button>
|
||||
<button class="bnav-btn" onclick="showPanel('settings')" id="bnb-settings"><span class="bnav-icon">⚙</span>Setup</button>
|
||||
</nav>
|
||||
|
||||
|
||||
|
||||
@ -212,6 +212,24 @@ canvas.tchart{width:100%;height:60px;display:block;border-radius:6px;background:
|
||||
.panel{display:none}
|
||||
.panel.active{display:block}
|
||||
|
||||
/* ── SETTINGS (Master-Detail) ── */
|
||||
.settings-wrap{display:grid;grid-template-columns:200px 1fr;gap:16px;align-items:start}
|
||||
.settings-cats{display:flex;flex-direction:column;gap:4px;position:sticky;top:12px}
|
||||
.set-cat{display:flex;align-items:center;gap:8px;padding:10px 12px;border-radius:8px;
|
||||
border:1px solid transparent;background:var(--raised);color:var(--txt2);cursor:pointer;
|
||||
font-size:13px;text-align:left;transition:background .15s,color .15s}
|
||||
.set-cat:hover{color:var(--txt)}
|
||||
.set-cat.active{background:var(--accent);color:#fff;border-color:var(--accent)}
|
||||
.set-group{display:none}
|
||||
.set-group.active{display:block}
|
||||
.set-group .card{margin-bottom:14px}
|
||||
@media(max-width:768px){
|
||||
.settings-wrap{grid-template-columns:1fr}
|
||||
.settings-cats{flex-direction:row;flex-wrap:wrap;position:static;overflow-x:auto}
|
||||
.set-cat{flex:1;min-width:auto;justify-content:center;padding:8px 6px;font-size:12px}
|
||||
.set-cat .nav-text{display:inline}
|
||||
}
|
||||
|
||||
/* ── FILE BROWSER UPLOAD ZONE ── */
|
||||
#store-upload-zone{
|
||||
display:flex;flex-direction:column;align-items:center;justify-content:center;
|
||||
|
||||
Reference in New Issue
Block a user