561 lines
39 KiB
HTML
561 lines
39 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="de" data-theme="dark">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||
<title>KX-Bridge</title>
|
||
<link rel="stylesheet" href="/kx/ui/style.css">
|
||
<body>
|
||
|
||
<div id="conn-error-banner" style="display:none;background:#c0392b;color:#fff;padding:10px 18px;font-size:14px;text-align:center;position:sticky;top:0;z-index:999;"></div>
|
||
<div id="file-ready-banner" style="display:none;background:#1a6e3c;color:#fff;padding:10px 18px;font-size:14px;text-align:center;position:sticky;top:0;z-index:998;display:none;align-items:center;justify-content:center;gap:12px;flex-wrap:wrap">
|
||
<span>📄 <span id="file-ready-name"></span></span>
|
||
<button id="file-ready-btn" onclick="startReadyFile()"
|
||
style="padding:5px 16px;background:#fff;color:#1a6e3c;border:none;border-radius:6px;font-weight:700;cursor:pointer;font-size:13px"></button>
|
||
<button id="file-slots-btn" onclick="startReadyFileWithSlots()"
|
||
style="padding:5px 16px;background:rgba(255,255,255,0.15);color:#fff;border:1px solid rgba(255,255,255,0.5);border-radius:6px;font-weight:700;cursor:pointer;font-size:13px"></button>
|
||
<button id="file-cancel-btn" onclick="cancelReadyFile()"
|
||
style="padding:5px 16px;background:rgba(255,255,255,0.15);color:#fff;border:1px solid rgba(255,255,255,0.5);border-radius:6px;font-weight:700;cursor:pointer;font-size:13px"></button>
|
||
</div>
|
||
|
||
<header>
|
||
<div class="logo">⬡ KX-Bridge</div>
|
||
<div style="flex:1"></div>
|
||
<div id="printer-dropdown-wrap" style="display:none;position:relative">
|
||
<button id="printer-dropdown-btn" onclick="togglePrinterDropdown()" style="background:var(--raised);border:1px solid var(--border);border-radius:6px;padding:4px 10px;color:var(--txt);cursor:pointer;font-size:13px;display:flex;align-items:center;gap:6px">
|
||
<span id="h-pname">Anycubic Kobra X</span><span style="opacity:.5">▾</span>
|
||
</button>
|
||
<div id="printer-dropdown-menu" style="display:none;position:absolute;top:calc(100% + 4px);right:0;background:var(--card);border:1px solid var(--border);border-radius:8px;min-width:200px;z-index:200;box-shadow:0 4px 16px #0006;overflow:hidden">
|
||
</div>
|
||
</div>
|
||
<div id="h-pname-single" class="hname">Anycubic Kobra X</div>
|
||
<span id="h-version" style="font-size:11px;opacity:.5;margin-left:6px"></span>
|
||
<div class="hbadge" id="h-badge"><span class="dot"></span><span id="h-state">Standby</span></div>
|
||
<button class="theme-btn" onclick="toggleTheme()">☀ / ☾</button>
|
||
<button class="theme-btn" onclick="toggleLang()" id="lang-btn">EN</button>
|
||
<button class="theme-btn" onclick="openSettings()" 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>
|
||
|
||
<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>
|
||
|
||
<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>
|
||
|
||
<!-- ═══ AMS SLOT EDIT DIALOG ═══ -->
|
||
<div class="modal-overlay" id="slot-edit-modal" onclick="if(event.target===this)closeSlotEdit()">
|
||
<div class="modal-box" style="max-width:340px">
|
||
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:16px">
|
||
<span class="modal-title" id="slot-edit-title"></span>
|
||
<button onclick="closeSlotEdit()" style="background:none;border:none;color:var(--txt2);font-size:20px;cursor:pointer;line-height:1">✕</button>
|
||
</div>
|
||
<div style="display:flex;align-items:center;gap:16px;margin-bottom:20px">
|
||
<div id="slot-edit-preview" style="width:56px;height:56px;border-radius:50%;border:3px solid rgba(255,255,255,.2);flex-shrink:0"></div>
|
||
<div style="flex:1">
|
||
<div style="font-size:11px;color:var(--txt2);margin-bottom:4px" id="lbl-slot-color"></div>
|
||
<input type="color" id="slot-edit-color"
|
||
oninput="document.getElementById('slot-edit-preview').style.background=this.value"
|
||
style="width:100%;height:36px;border:1px solid var(--border);border-radius:6px;background:var(--raised);cursor:pointer;padding:2px">
|
||
</div>
|
||
</div>
|
||
<div style="margin-bottom:20px">
|
||
<div style="font-size:11px;color:var(--txt2);margin-bottom:6px" id="lbl-slot-material"></div>
|
||
<div style="display:flex;flex-wrap:wrap;gap:6px" id="slot-mat-btns">
|
||
</div>
|
||
<input type="text" id="slot-edit-mat"
|
||
oninput="highlightMatBtn(this.value)"
|
||
style="margin-top:8px;width:100%;padding:6px 10px;background:var(--raised);border:1px solid var(--border);border-radius:6px;color:var(--txt);font-size:13px;box-sizing:border-box">
|
||
</div>
|
||
<button class="btn" id="btn-slot-edit-feed" style="width:100%;margin-bottom:8px" onclick="slotEditFeed()"></button>
|
||
<button class="modal-save" id="btn-slot-edit-save" onclick="saveSlotEdit()"></button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="layout">
|
||
<nav class="sidebar">
|
||
<button class="nav-btn active" onclick="showPanel('dashboard')" id="nb-dashboard">
|
||
<span class="nav-icon">⊞</span><span class="nav-text">Dashboard</span></button>
|
||
<button class="nav-btn" onclick="showPanel('printers');loadPrinterTab()" id="nb-printers">
|
||
<span class="nav-icon">🖨</span><span class="nav-text">Drucker</span></button>
|
||
<button class="nav-btn" onclick="showPanel('store');loadStore()" id="nb-store">
|
||
<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>
|
||
</nav>
|
||
|
||
<main>
|
||
<!-- ═══ DASHBOARD ═══ -->
|
||
<div class="panel active" id="panel-dashboard">
|
||
<div class="grid">
|
||
<!-- Kamera -->
|
||
<div class="card" style="grid-column:1/-1">
|
||
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:10px">
|
||
<div class="card-title" style="margin-bottom:0"><span>📷</span> <span id="d-card-cam">Kamera</span></div>
|
||
<div style="display:flex;align-items:center;gap:10px">
|
||
<span id="d-lbl-light" style="font-size:12px;color:var(--txt2)">💡 Licht</span>
|
||
<label class="toggle">
|
||
<input type="checkbox" id="d-light-toggle" onchange="setLight()">
|
||
<span class="toggle-track"></span>
|
||
<span class="toggle-thumb"></span>
|
||
</label>
|
||
</div>
|
||
</div>
|
||
<div class="cam-wrap" id="cam-wrap">
|
||
<div class="cam-placeholder" id="cam-placeholder"><span id="cam-placeholder-txt">📷 Kamera nicht gestartet</span></div>
|
||
<div class="cam-spinner" id="cam-spinner"></div>
|
||
<img id="cam-img" style="display:none;width:100%;height:auto" alt="Kamera">
|
||
<div class="cam-overlay" id="cam-overlay" style="display:none">
|
||
<div style="font-size:12px;color:#fff" id="cam-fname"></div>
|
||
</div>
|
||
<button class="cam-toggle" onclick="toggleCam()" id="cam-toggle-btn">▶ Kamera</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Fortschritt -->
|
||
<div class="card" style="grid-column:1/-1">
|
||
<div class="card-title"><span>◉</span> <span id="d-card-progress">Fortschritt</span></div>
|
||
<img id="d-thumbnail" src="" alt="" style="display:none;width:100%;max-height:160px;object-fit:contain;border-radius:8px;background:#111;margin-bottom:10px">
|
||
<div class="pct-big"><span id="d-pct">0</span><small>%</small></div>
|
||
<div style="display:flex;align-items:center;gap:10px;margin:8px 0">
|
||
<div class="progress-bar" style="flex:1;margin:0"><div class="progress-fill" id="d-pbar" style="width:0%"></div></div>
|
||
<div class="time-block" style="padding:6px 10px;min-width:72px;text-align:center;flex-shrink:0">
|
||
<div class="time-label" id="d-lbl-layers"></div>
|
||
<div class="time-val" style="font-size:16px" id="d-layers">–</div>
|
||
</div>
|
||
</div>
|
||
<div class="time-grid">
|
||
<div class="time-block">
|
||
<div class="time-label" id="d-lbl-elapsed"></div>
|
||
<div class="time-val" id="d-elapsed">–</div>
|
||
</div>
|
||
<div class="time-block" id="d-slicer-row" style="display:none">
|
||
<div class="time-label" id="d-slicer-label"></div>
|
||
<div class="time-val" id="d-slicer-time">–</div>
|
||
</div>
|
||
<div class="time-block" style="color:var(--acc)">
|
||
<div class="time-label" id="d-lbl-remain"></div>
|
||
<div class="time-val" id="d-remain" style="color:var(--acc)">–</div>
|
||
</div>
|
||
</div>
|
||
<div class="fname" id="d-fname" title="" style="margin-top:6px">–</div>
|
||
<div class="ctrl-btns" id="d-ctrl-btns" style="margin-top:12px">
|
||
<button class="btn btn-pause btn-sm" id="d-btn-pause" onclick="printAction('pause')">⏸ Pause</button>
|
||
<button class="btn btn-resume btn-sm" id="d-btn-resume" onclick="printAction('resume')">▶ Weiter</button>
|
||
<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>
|
||
</div>
|
||
|
||
<!-- Temperatursteuerung + Verlauf -->
|
||
<div class="card" style="grid-column:1/-1">
|
||
<div class="card-title"><span>⊙</span> <span id="d-card-temps">Temperaturen</span></div>
|
||
<div class="temp-card-inner">
|
||
<div class="temp-block">
|
||
<div class="temp-label">Nozzle</div>
|
||
<div class="temp-row">
|
||
<div class="temp-val" id="d-nt">–</div>
|
||
<div class="temp-unit">°C</div>
|
||
</div>
|
||
<div class="temp-target">→ <span id="d-nt-t">0</span>°C</div>
|
||
<div class="progress-bar" style="margin:8px 0 0">
|
||
<div class="progress-fill" id="d-ntbar" style="width:0%;background:linear-gradient(90deg,var(--accent2),#ffb020)"></div>
|
||
</div>
|
||
<div class="temp-edit" style="margin-top:10px">
|
||
<input type="number" class="temp-input" id="p-nozzle-inp" placeholder="Ziel" min="0" max="300" style="flex:1">
|
||
<button class="btn btn-sm btn-accent" onclick="setNozzle()"><span class="lbl-set">Set</span></button>
|
||
<button class="btn btn-sm" style="background:var(--raised);color:var(--txt)" onclick="document.getElementById('p-nozzle-inp').value=0;setNozzle()"><span class="lbl-off">Aus</span></button>
|
||
</div>
|
||
</div>
|
||
<div class="temp-block">
|
||
<div class="temp-label" id="d-lbl-bed">Bett</div>
|
||
<div class="temp-row">
|
||
<div class="temp-val" id="d-bt">–</div>
|
||
<div class="temp-unit">°C</div>
|
||
</div>
|
||
<div class="temp-target">→ <span id="d-bt-t">0</span>°C</div>
|
||
<div class="progress-bar" style="margin:8px 0 0">
|
||
<div class="progress-fill" id="d-btbar" style="width:0%;background:linear-gradient(90deg,#ff6b35,var(--warn))"></div>
|
||
</div>
|
||
<div class="temp-edit" style="margin-top:10px">
|
||
<input type="number" class="temp-input" id="p-bed-inp" placeholder="Ziel" min="0" max="120" style="flex:1">
|
||
<button class="btn btn-sm btn-accent" onclick="setBed()"><span class="lbl-set">Set</span></button>
|
||
<button class="btn btn-sm" style="background:var(--raised);color:var(--txt)" onclick="document.getElementById('p-bed-inp').value=0;setBed()"><span class="lbl-off">Aus</span></button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div style="margin-top:14px">
|
||
<div style="font-size:10px;color:var(--txt2);margin-bottom:4px" id="d-chart-label">Verlauf (letzte 60 Messungen)</div>
|
||
<canvas id="d-chart" width="800" height="120" style="width:100%;height:120px;background:var(--raised);border-radius:8px"></canvas>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Achsensteuerung -->
|
||
<div class="card">
|
||
<div class="card-title"><span>✛</span> <span id="ptitle-motion-xy">XY-Achsen</span></div>
|
||
<div class="joypad">
|
||
<div></div>
|
||
<button class="joy" onclick="move(1,1,getStep())" title="Y+">▲</button>
|
||
<div></div>
|
||
<button class="joy" onclick="move(0,-1,getStep())" title="X−">◀</button>
|
||
<button class="joy home" onclick="homeAll()" title="Home All">⌂</button>
|
||
<button class="joy" onclick="move(0,1,getStep())" title="X+">▶</button>
|
||
<div></div>
|
||
<button class="joy" onclick="move(1,-1,getStep())" title="Y−">▼</button>
|
||
<div></div>
|
||
</div>
|
||
<div class="step-btns">
|
||
<button class="step-btn" onclick="setStep(this,0.1)">0.1</button>
|
||
<button class="step-btn active" onclick="setStep(this,1)">1</button>
|
||
<button class="step-btn" onclick="setStep(this,5)">5</button>
|
||
<button class="step-btn" onclick="setStep(this,10)">10 mm</button>
|
||
</div>
|
||
<div class="home-btns">
|
||
<button class="btn btn-sm" style="background:var(--raised);color:var(--txt)" onclick="homeZ()"><span class="lbl-home-z">Home Z</span></button>
|
||
<button class="btn btn-sm" style="background:var(--raised);color:var(--txt)" onclick="homeXY()"><span class="lbl-home-xy">Home XY</span></button>
|
||
<button class="btn btn-sm btn-accent" onclick="homeAll()"><span class="lbl-home-all">Home All</span></button>
|
||
<button class="btn btn-sm" style="background:var(--raised);color:var(--txt)" onclick="disableMotors()"><span class="lbl-disable-motors">Motors Off</span></button>
|
||
</div>
|
||
</div>
|
||
<div class="card">
|
||
<div class="card-title"><span>↕</span> <span id="ptitle-motion-z">Z-Achse</span></div>
|
||
<div class="joypad" style="grid-template-columns:52px;grid-template-rows:repeat(2,52px)">
|
||
<button class="joy" onclick="move(2,1,getStep())" title="Z+">▲</button>
|
||
<button class="joy" onclick="move(2,-1,getStep())" title="Z−">▼</button>
|
||
</div>
|
||
<div style="text-align:center;margin-top:8px;font-size:12px;color:var(--txt2)"><span class="lbl-step">Schrittweite:</span> <span id="step-display">1</span> mm</div>
|
||
</div>
|
||
|
||
<!-- Print Speed -->
|
||
<div class="card">
|
||
<div class="card-title"><span>🏎</span> <span id="d-card-speed">Druckgeschwindigkeit</span></div>
|
||
<div style="display:flex;gap:8px;margin-top:4px">
|
||
<button class="spd-btn" id="d-spd-1" onclick="setSpeed(1)">
|
||
<span class="spd-icon">🐢</span>
|
||
<span id="d-spd-lbl-1">Leise</span>
|
||
</button>
|
||
<button class="spd-btn spd-active" id="d-spd-2" onclick="setSpeed(2)">
|
||
<span class="spd-icon">⚡</span>
|
||
<span id="d-spd-lbl-2">Normal</span>
|
||
</button>
|
||
<button class="spd-btn" id="d-spd-3" onclick="setSpeed(3)">
|
||
<span class="spd-icon">🚀</span>
|
||
<span id="d-spd-lbl-3">Sport</span>
|
||
</button>
|
||
</div>
|
||
<div class="spd-bar" style="margin-top:12px">
|
||
<div class="spd-bar-fill" id="d-spd-bar" style="width:50%"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Lüfter -->
|
||
<div class="card">
|
||
<div class="card-title"><span>🌀</span> <span id="d-card-lightfan">Lüfter</span></div>
|
||
<div class="slider-row">
|
||
<input type="range" class="slider" min="0" max="100" value="0" id="d-fan" oninput="document.getElementById('d-fan-val').textContent=this.value" onchange="setFan()">
|
||
<span class="slider-val" id="d-fan-val">0</span>
|
||
</div>
|
||
<div style="margin-top:12px;display:flex;gap:8px;flex-wrap:wrap">
|
||
<button class="btn btn-sm" style="background:var(--raised);color:var(--txt)" onclick="quickFan(0)">Aus</button>
|
||
<button class="btn btn-sm" style="background:var(--raised);color:var(--txt)" onclick="quickFan(25)">25%</button>
|
||
<button class="btn btn-sm" style="background:var(--raised);color:var(--txt)" onclick="quickFan(50)">50%</button>
|
||
<button class="btn btn-sm" style="background:var(--raised);color:var(--txt)" onclick="quickFan(75)">75%</button>
|
||
<button class="btn btn-sm btn-accent" onclick="quickFan(100)">100%</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="d-ace-dry-wrap" style="display:none">
|
||
<div id="d-ace-dry-grid" style="display:contents"></div>
|
||
</div>
|
||
|
||
<!-- AMS -->
|
||
<div class="card" style="grid-column:1/-1" id="d-ams-card">
|
||
<div class="card-title"><span>◫</span> <span id="d-card-ams">Filament</span></div>
|
||
<div class="ams-slots" id="ams-slots">
|
||
<div style="grid-column:1/-1;text-align:center;color:var(--txt2);padding:20px" id="ams-no-data">Keine AMS-Daten empfangen</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ═══ CONSOLE ═══ -->
|
||
<!-- ═══ DRUCKER ═══ -->
|
||
<div class="panel" id="panel-printers">
|
||
<div class="card">
|
||
<div class="card-title" style="display:flex;justify-content:space-between;align-items:center;margin-bottom:12px">
|
||
<span id="printers-panel-title">🖨 Drucker</span>
|
||
<div style="display:flex;gap:8px">
|
||
<button onclick="openAddPrinterDialog()" style="font-size:12px;padding:4px 12px;background:var(--accent);border:none;border-radius:6px;color:#fff;cursor:pointer;font-weight:600">+ <span id="add-printer-btn-label">Drucker hinzufügen</span></button>
|
||
<button onclick="loadPrinterTab()" style="font-size:12px;padding:4px 12px;background:var(--raised);border:1px solid var(--border);border-radius:6px;color:var(--txt2);cursor:pointer">↻</button>
|
||
</div>
|
||
</div>
|
||
<div id="printers-grid" style="display:grid;grid-template-columns:repeat(auto-fill,minmax(260px,1fr));gap:14px"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ═══ GCODE STORE ═══ -->
|
||
<div class="panel" id="panel-store">
|
||
<div class="card">
|
||
<div class="card-title" style="display:flex;justify-content:space-between;align-items:center;margin-bottom:10px">
|
||
<span id="store-panel-title">🗂 Datei-Browser</span>
|
||
<button id="store-refresh-btn" onclick="loadStore()" style="font-size:12px;padding:4px 12px;background:var(--raised);border:1px solid var(--border);border-radius:6px;color:var(--txt2);cursor:pointer">↻ Aktualisieren</button>
|
||
</div>
|
||
<div style="display:flex;gap:8px;margin-bottom:12px;flex-wrap:wrap">
|
||
<input id="store-search" type="text" placeholder="🔍 Suche…" oninput="renderStore()"
|
||
style="flex:1;min-width:140px;padding:6px 10px;background:var(--raised);border:1px solid var(--border);border-radius:6px;color:var(--txt);font-size:13px">
|
||
<select id="store-filter" onchange="renderStore()"
|
||
style="padding:6px 8px;background:var(--raised);border:1px solid var(--border);border-radius:6px;color:var(--txt);font-size:13px">
|
||
<option value="all" id="sf-all">Alle</option>
|
||
<option value="completed" id="sf-ok">✓ Erfolgreich</option>
|
||
<option value="failed" id="sf-err">✗ Fehler</option>
|
||
<option value="never" id="sf-new">Neu</option>
|
||
</select>
|
||
<select id="store-sort" onchange="renderStore()"
|
||
style="padding:6px 8px;background:var(--raised);border:1px solid var(--border);border-radius:6px;color:var(--txt);font-size:13px">
|
||
<option value="date_desc" id="ss-date">↓ Datum</option>
|
||
<option value="name_asc" id="ss-name">A–Z Name</option>
|
||
<option value="duration_asc" id="ss-dur">⏱ Druckzeit</option>
|
||
</select>
|
||
</div>
|
||
<div id="store-empty" style="display:none;color:var(--txt2);text-align:center;padding:40px 0;font-size:14px">
|
||
</div>
|
||
<div id="store-grid" style="display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:14px"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="panel" id="panel-console">
|
||
<div class="card">
|
||
<div class="card-title" style="display:flex;justify-content:space-between;align-items:center">
|
||
<span><span>≡</span> <span id="ptitle-console">Ereignis-Log</span></span>
|
||
<a id="btn-log-dl" href="/api/log/download" download="kx-bridge.log"
|
||
style="font-size:12px;padding:4px 10px;background:var(--raised);border-radius:6px;color:var(--txt2);text-decoration:none">⬇ Download</a>
|
||
</div>
|
||
<div style="display:flex;gap:6px;margin-bottom:6px;flex-wrap:wrap;align-items:center">
|
||
<input id="log-filter" type="text" placeholder="Filter…"
|
||
oninput="renderLog()"
|
||
style="flex:1;min-width:120px;padding:5px 10px;background:var(--raised);border:1px solid var(--border);border-radius:6px;color:var(--txt);font-size:12px;font-family:var(--mono)">
|
||
<button id="btn-autoscroll" onclick="toggleAutoScroll()"
|
||
style="font-size:12px;padding:5px 10px;border-radius:6px;border:1px solid var(--border);background:var(--accent);color:#fff;cursor:pointer;white-space:nowrap">⬇ Auto</button>
|
||
<button onclick="consoleLogs=[];renderLog()"
|
||
style="font-size:12px;padding:5px 10px;border-radius:6px;border:1px solid var(--border);background:var(--raised);color:var(--txt2);cursor:pointer">✕ Clear</button>
|
||
</div>
|
||
<div style="display:flex;gap:5px;margin-bottom:8px;flex-wrap:wrap">
|
||
<span style="font-size:11px;color:var(--txt2);align-self:center;margin-right:2px">Dir:</span>
|
||
<button class="log-dir-btn active" id="logdir-all" onclick="setLogDir('all')" style="font-size:11px;padding:3px 9px;border-radius:5px;border:1px solid var(--border);cursor:pointer"></button>
|
||
<button class="log-dir-btn" id="logdir-rx" onclick="setLogDir('rx')" style="font-size:11px;padding:3px 9px;border-radius:5px;border:1px solid var(--border);cursor:pointer">RX</button>
|
||
<button class="log-dir-btn" id="logdir-tx" onclick="setLogDir('tx')" style="font-size:11px;padding:3px 9px;border-radius:5px;border:1px solid var(--border);cursor:pointer">TX</button>
|
||
<span style="font-size:11px;color:var(--txt2);align-self:center;margin-left:6px;margin-right:2px">Topic:</span>
|
||
<button class="log-topic-btn" data-topic="multiColorBox" onclick="setLogTopic('multiColorBox')" style="font-size:11px;padding:3px 9px;border-radius:5px;border:1px solid var(--border);cursor:pointer">AMS</button>
|
||
<button class="log-topic-btn" data-topic="print" onclick="setLogTopic('print')" style="font-size:11px;padding:3px 9px;border-radius:5px;border:1px solid var(--border);cursor:pointer">print</button>
|
||
<button class="log-topic-btn" data-topic="info" onclick="setLogTopic('info')" style="font-size:11px;padding:3px 9px;border-radius:5px;border:1px solid var(--border);cursor:pointer">info</button>
|
||
<button class="log-topic-btn" data-topic="status" onclick="setLogTopic('status')" style="font-size:11px;padding:3px 9px;border-radius:5px;border:1px solid var(--border);cursor:pointer">status</button>
|
||
</div>
|
||
<div class="console" id="console-log" style="height:calc(100vh - 260px);min-height:160px" onscroll="onLogScroll()"></div>
|
||
</div>
|
||
</div>
|
||
</main>
|
||
</div>
|
||
|
||
<nav class="bottom-nav">
|
||
<button class="bnav-btn active" onclick="showPanel('dashboard')" id="bnb-dashboard"><span class="bnav-icon">⊞</span>Dashboard</button>
|
||
<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>
|
||
</nav>
|
||
|
||
|
||
<!-- Filament-Slot-Dialog -->
|
||
<div class="modal-overlay" id="filament-dialog" onclick="if(event.target===this)closeFilamentDialog()">
|
||
<div class="modal-box" style="max-width:380px;width:100%">
|
||
<div class="modal-header" style="margin-bottom:14px">
|
||
<span class="modal-title" id="fd-title" style="font-size:14px;word-break:break-all"></span>
|
||
<button onclick="closeFilamentDialog()" style="background:none;border:none;font-size:18px;cursor:pointer;color:var(--txt2)">✕</button>
|
||
</div>
|
||
<p id="fd-slots-hint" style="font-size:12px;color:var(--txt2);margin-bottom:10px">GCode-Kanal → AMS-Slot zuweisen:</p>
|
||
<div id="fd-slots" style="display:flex;flex-direction:column;gap:8px;margin-bottom:16px"></div>
|
||
<div id="fd-objects-section" style="display:none;margin-bottom:16px">
|
||
<p id="fd-objects-hint" style="font-size:12px;color:var(--txt2);margin-bottom:8px">Objekte überspringen (optional):</p>
|
||
<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="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>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<!-- Drucker-hinzufügen-Dialog -->
|
||
<div class="modal-overlay" id="add-printer-dialog" onclick="if(event.target===this)closeAddPrinterDialog()">
|
||
<div class="modal-box" style="max-width:380px;width:100%">
|
||
<div class="modal-header" style="margin-bottom:14px">
|
||
<span class="modal-title" id="apd-title">Drucker hinzufügen</span>
|
||
<button onclick="closeAddPrinterDialog()" style="background:none;border:none;font-size:18px;cursor:pointer;color:var(--txt2)">✕</button>
|
||
</div>
|
||
<label id="apd-lbl-ip" style="display:block;font-size:12px;color:var(--txt2);margin-bottom:4px">Drucker-IP</label>
|
||
<input type="text" id="apd-ip" placeholder="192.168.1.100" style="width:100%;box-sizing:border-box;padding:8px;background:var(--raised);border:1px solid var(--border);border-radius:6px;color:var(--txt);margin-bottom:10px">
|
||
<label id="apd-lbl-name" style="display:block;font-size:12px;color:var(--txt2);margin-bottom:4px">Name (optional)</label>
|
||
<input type="text" id="apd-name" placeholder="z.B. Kobra X Wohnzimmer" style="width:100%;box-sizing:border-box;padding:8px;background:var(--raised);border:1px solid var(--border);border-radius:6px;color:var(--txt);margin-bottom:6px">
|
||
<div id="apd-status" style="font-size:12px;margin:8px 0;min-height:16px;color:var(--txt2)"></div>
|
||
<div style="display:flex;gap:8px;justify-content:flex-end">
|
||
<button onclick="closeAddPrinterDialog()" style="padding:8px 16px;background:var(--raised);border:1px solid var(--border);border-radius:8px;color:var(--txt);cursor:pointer">Abbrechen</button>
|
||
<button id="apd-confirm" onclick="confirmAddPrinter()" style="padding:8px 18px;background:var(--accent);color:#fff;border:none;border-radius:8px;cursor:pointer;font-weight:600">Hinzufügen</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<!-- Mid-Print Skip-Dialog -->
|
||
<div class="modal-overlay" id="skip-dialog" onclick="if(event.target===this)closeSkipDialog()">
|
||
<div class="modal-box" style="max-width:420px;width:100%">
|
||
<div class="modal-header" style="margin-bottom:14px">
|
||
<span class="modal-title" id="skip-title">✂ Objekte überspringen</span>
|
||
<button onclick="closeSkipDialog()" style="background:none;border:none;font-size:18px;cursor:pointer;color:var(--txt2)">✕</button>
|
||
</div>
|
||
<p id="skip-hint" style="font-size:12px;color:var(--txt2);margin-bottom:10px">Objekte abwählen, die nicht weiter gedruckt werden sollen:</p>
|
||
<div id="skip-svg" style="display:none;background:var(--raised);border:1px solid var(--border);border-radius:8px;padding:6px;margin-bottom:10px;text-align:center"></div>
|
||
<div id="skip-list" style="display:flex;flex-direction:column;gap:6px;max-height:200px;overflow-y:auto;margin-bottom:12px"></div>
|
||
<div id="skip-status" style="font-size:12px;color:var(--txt2);min-height:16px;margin-bottom:8px"></div>
|
||
<div style="display:flex;gap:8px;justify-content:flex-end">
|
||
<button onclick="closeSkipDialog()" style="padding:8px 16px;background:var(--raised);border:1px solid var(--border);border-radius:8px;color:var(--txt);cursor:pointer">Abbrechen</button>
|
||
<button id="skip-confirm" onclick="confirmSkip()" style="padding:8px 18px;background:var(--accent);color:#fff;border:none;border-radius:8px;cursor:pointer;font-weight:600">Überspringen</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ACE Dryer Temp/Time Settings Dialog -->
|
||
<div class="modal-overlay" id="ace-dry-dialog" onclick="if(event.target===this)closeAceDryDialog()">
|
||
<div class="modal-box" style="max-width:560px;width:100%">
|
||
<div class="modal-header" style="margin-bottom:10px">
|
||
<span class="modal-title" id="ace-dry-dialog-title">Dryer Temp/Time Settings</span>
|
||
<button onclick="closeAceDryDialog()" style="background:none;border:none;font-size:18px;cursor:pointer;color:var(--txt2)">✕</button>
|
||
</div>
|
||
<div style="display:flex;align-items:center;gap:12px;margin-bottom:8px">
|
||
<label id="ace-dry-dialog-temp-label" style="min-width:190px;font-size:12px;color:var(--txt)">Temperature (30-80°C)</label>
|
||
<input id="ace-dry-dialog-temp" type="number" min="30" max="80" step="1"
|
||
oninput="aceDryDialogInputsChanged()"
|
||
style="width:130px;padding:8px;background:var(--raised);border:1px solid var(--border);border-radius:6px;color:var(--txt);text-align:center" value="45">
|
||
</div>
|
||
<div style="display:flex;align-items:center;gap:12px;margin-bottom:16px">
|
||
<label id="ace-dry-dialog-time-label" style="min-width:190px;font-size:12px;color:var(--txt)">Rem. Time (h:m:s)</label>
|
||
<div style="display:flex;align-items:center;gap:8px">
|
||
<input id="ace-dry-dialog-h" type="number" min="0" max="24" step="1" value="4"
|
||
oninput="aceDryDialogInputsChanged()"
|
||
style="width:70px;padding:8px;background:var(--raised);border:1px solid var(--border);border-radius:6px;color:var(--txt);text-align:center">
|
||
<span style="color:var(--txt2)">:</span>
|
||
<input id="ace-dry-dialog-m" type="number" min="0" max="59" step="1" value="0"
|
||
oninput="aceDryDialogInputsChanged()"
|
||
style="width:70px;padding:8px;background:var(--raised);border:1px solid var(--border);border-radius:6px;color:var(--txt);text-align:center">
|
||
<span style="color:var(--txt2)">:</span>
|
||
<input id="ace-dry-dialog-s" type="number" min="0" max="59" step="1" value="0"
|
||
oninput="aceDryDialogInputsChanged()"
|
||
style="width:70px;padding:8px;background:var(--raised);border:1px solid var(--border);border-radius:6px;color:var(--txt);text-align:center">
|
||
</div>
|
||
</div>
|
||
<div style="display:grid;grid-template-columns:repeat(3,1fr);gap:8px;margin-bottom:8px">
|
||
<button class="ace-dry-preset-btn" data-preset="pla" onclick="aceDryDialogPreset('pla')" style="padding:8px;border-radius:8px;border:1px solid var(--border);background:var(--raised);color:var(--txt2);cursor:pointer">PLA</button>
|
||
<button class="ace-dry-preset-btn" data-preset="pla_plus" onclick="aceDryDialogPreset('pla_plus')" style="padding:8px;border-radius:8px;border:1px solid var(--border);background:var(--raised);color:var(--txt2);cursor:pointer">PLA+</button>
|
||
<button class="ace-dry-preset-btn" data-preset="petg" onclick="aceDryDialogPreset('petg')" style="padding:8px;border-radius:8px;border:1px solid var(--border);background:var(--raised);color:var(--txt2);cursor:pointer">PETG</button>
|
||
<button class="ace-dry-preset-btn" data-preset="tpu" onclick="aceDryDialogPreset('tpu')" style="padding:8px;border-radius:8px;border:1px solid var(--border);background:var(--raised);color:var(--txt2);cursor:pointer">TPU</button>
|
||
<button class="ace-dry-preset-btn" data-preset="abs_asa" onclick="aceDryDialogPreset('abs_asa')" style="padding:8px;border-radius:8px;border:1px solid var(--border);background:var(--raised);color:var(--txt2);cursor:pointer">ABS / ASA</button>
|
||
<button class="ace-dry-preset-btn" data-preset="pa_pc" onclick="aceDryDialogPreset('pa_pc')" style="padding:8px;border-radius:8px;border:1px solid var(--border);background:var(--raised);color:var(--txt2);cursor:pointer">PA / PC</button>
|
||
<button class="ace-dry-preset-btn" data-preset="custom_1" onclick="aceDryDialogPreset('custom_1')" style="padding:8px;border-radius:8px;border:1px solid var(--border);background:var(--raised);color:var(--txt2);cursor:pointer">Custom 1</button>
|
||
<button class="ace-dry-preset-btn" data-preset="custom_2" onclick="aceDryDialogPreset('custom_2')" style="padding:8px;border-radius:8px;border:1px solid var(--border);background:var(--raised);color:var(--txt2);cursor:pointer">Custom 2</button>
|
||
<button class="ace-dry-preset-btn" data-preset="custom_3" onclick="aceDryDialogPreset('custom_3')" style="padding:8px;border-radius:8px;border:1px solid var(--border);background:var(--raised);color:var(--txt2);cursor:pointer">Custom 3</button>
|
||
</div>
|
||
<div id="ace-dry-dialog-custom-name-row" style="display:none;align-items:center;gap:12px;margin-bottom:14px">
|
||
<label id="ace-dry-dialog-custom-name-label" style="min-width:190px;font-size:12px;color:var(--txt)">Custom Name</label>
|
||
<input id="ace-dry-dialog-custom-name" type="text" maxlength="32" oninput="aceDryDialogInputsChanged()"
|
||
style="width:220px;padding:8px;background:var(--raised);border:1px solid var(--border);border-radius:6px;color:var(--txt)">
|
||
</div>
|
||
<div style="display:flex;justify-content:flex-end;gap:8px">
|
||
<button id="ace-dry-dialog-reset-default" onclick="resetAceDryPresetToDefault()" style="display:none;padding:8px 14px;background:var(--raised);border:1px solid var(--border);border-radius:8px;color:var(--txt);cursor:pointer">Reset to Default</button>
|
||
<button id="ace-dry-dialog-save-preset" onclick="saveAceDryPresetAndRestart()" style="display:none;padding:8px 14px;background:var(--warn);border:1px solid transparent;border-radius:8px;color:#fff;cursor:pointer">Save & Restart</button>
|
||
<button id="ace-dry-dialog-cancel" onclick="closeAceDryDialog()" style="padding:8px 14px;background:var(--raised);border:1px solid var(--border);border-radius:8px;color:var(--txt);cursor:pointer">Cancel</button>
|
||
<button id="ace-dry-dialog-confirm" onclick="confirmAceDryDialog()" style="padding:8px 16px;background:var(--accent);color:#fff;border:none;border-radius:8px;cursor:pointer;font-weight:600">Confirm</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<script src="/kx/ui/app.js"></script></head>
|
||
<footer style="text-align:center;padding:12px;font-size:11px;color:var(--txt2);border-top:1px solid var(--border);margin-top:auto">
|
||
© ViewIT 2026
|
||
</footer>
|
||
</body>
|
||
</html>
|