- docs/mocks/v09_full_ux.html — high-fidelity 1920x1080 mock showing the proposed Control / Program / Console / Settings tab layout with the V09 flat slate jog/macro palette and an underline ribbon header tab style. - plans/2026-04-30_ux_redesign.md — phased implementation plan to port index.pug + control-view.pug to the new shell while keeping hash routing and existing settings/admin views intact.
901 lines
53 KiB
HTML
901 lines
53 KiB
HTML
<!doctype html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="utf-8" />
|
||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||
<title>Onefinity · V09 · Full UX</title>
|
||
<script src="https://cdn.tailwindcss.com"></script>
|
||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6.5.2/css/all.min.css" />
|
||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&family=JetBrains+Mono:wght@500;700&display=swap" rel="stylesheet">
|
||
<style>
|
||
*{box-sizing:border-box}
|
||
html,body{margin:0;font-family:'Inter',system-ui,sans-serif;background:#0f172a;color:#e5e7eb}
|
||
.mono{font-family:'JetBrains Mono',monospace}
|
||
|
||
/* ---------- HOST CHROME ---------- */
|
||
.host{min-height:100vh;display:flex;flex-direction:column;background:radial-gradient(circle at 30% 0%,#374151,#0f172a 60%);}
|
||
.topbar{display:flex;align-items:center;gap:.6rem;flex-wrap:wrap;padding:.7rem 1rem;background:rgba(255,255,255,.04);border-bottom:1px solid rgba(255,255,255,.08);position:sticky;top:0;z-index:50;backdrop-filter:blur(10px);}
|
||
.topbar .brand{display:flex;align-items:center;gap:.5rem;font-weight:800;color:#fff}
|
||
.stripe-logo-sm{background:repeating-linear-gradient(135deg,#a7c7a3 0 6px,transparent 6px 14px);width:26px;height:26px;border-radius:6px}
|
||
.pill{padding:.3rem .65rem;border-radius:9999px;font-size:.75rem;font-weight:700;background:rgba(255,255,255,.08);color:#cbd5e1}
|
||
.seg-host{display:inline-flex;background:rgba(255,255,255,.05);border-radius:9999px;padding:3px;gap:3px}
|
||
.seg-host button{padding:.4rem .85rem;border-radius:9999px;font-size:.78rem;font-weight:700;color:#cbd5e1}
|
||
.seg-host button.on{background:#fde047;color:#0f172a}
|
||
.toggle{display:inline-flex;align-items:center;gap:.4rem;padding:.4rem .7rem;border-radius:8px;background:rgba(255,255,255,.08);font-size:.75rem;font-weight:600;color:#e5e7eb;cursor:pointer}
|
||
.toggle.on{background:#22c55e;color:#0b1220}
|
||
|
||
.stage{flex:1;display:flex;align-items:flex-start;justify-content:center;padding:1rem;overflow:auto}
|
||
.scaler-viewport{position:relative;flex:0 0 auto}
|
||
.scaler{position:absolute;top:0;left:0;width:1920px;height:auto;transform-origin:top left;transition:transform .2s}
|
||
|
||
/* ---------- KIOSK (1920x1080) ---------- */
|
||
.kiosk{
|
||
width:1920px;height:1080px;overflow:hidden;border-radius:14px;position:relative;
|
||
box-shadow:0 30px 60px rgba(0,0,0,.5);
|
||
display:flex;flex-direction:column;
|
||
background:#ffffff;color:#0f172a;
|
||
}
|
||
|
||
/* Header */
|
||
.head{
|
||
flex:0 0 96px;height:96px;
|
||
display:flex;align-items:center;gap:18px;
|
||
padding:0 24px;background:#ffffff;border-bottom:1px solid #e5e7eb;
|
||
}
|
||
.brand-blk{display:flex;align-items:center;gap:14px}
|
||
.menu-btn{width:54px;height:54px;border-radius:12px;background:#f1f5f9;border:1px solid #e2e8f0;color:#0f172a;display:inline-flex;align-items:center;justify-content:center;font-size:1.1rem}
|
||
.menu-btn:hover{background:#e2e8f0}
|
||
.brand-logo{width:42px;height:42px;border-radius:8px;background:repeating-linear-gradient(135deg,#a7c7a3 0 6px,transparent 6px 14px)}
|
||
.brand-name{font-weight:900;font-size:22px;letter-spacing:-.01em}
|
||
|
||
/* Underline-ribbon tab style (V02) */
|
||
.kiosk-tabs{display:inline-flex;gap:0;margin-right:auto;padding-left:18px;align-items:stretch;height:96px}
|
||
.ktab{
|
||
position:relative;
|
||
height:96px;padding:0 26px;
|
||
background:transparent;border:none;border-radius:0;
|
||
color:#475569;font-size:1.05rem;font-weight:700;
|
||
display:inline-flex;align-items:center;gap:.55rem;cursor:pointer;
|
||
transition:color .15s;
|
||
}
|
||
.ktab i{font-size:1.1rem;color:#94a3b8;transition:color .15s}
|
||
.ktab:hover{color:#0f172a}
|
||
.ktab:hover i{color:#475569}
|
||
.ktab.active{color:#0f172a}
|
||
.ktab.active i{color:#0f172a}
|
||
.ktab.active::after{
|
||
content:"";position:absolute;left:14px;right:14px;bottom:0;
|
||
height:5px;background:#fde047;border-radius:5px 5px 0 0;
|
||
}
|
||
.ktab .ktab-badge{background:#fee2e2;color:#991b1b;font-size:.7rem;padding:3px 8px;border-radius:9999px;font-weight:800;line-height:1}
|
||
.ktab.active .ktab-badge{background:#fde047;color:#0f172a}
|
||
|
||
.sys-btn{display:inline-flex;align-items:center;gap:.55rem;height:54px;padding:0 1.1rem;border-radius:14px;background:#f1f5f9;border:1px solid #e2e8f0;color:#0f172a;font-size:.9rem;font-weight:600}
|
||
.sys-btn .pip{width:9px;height:9px;border-radius:9999px;background:#22c55e}
|
||
.state-badge{display:inline-flex;align-items:center;gap:.6rem;height:54px;padding:0 1.1rem;border-radius:14px;background:#dcfce7;color:#166534;font-weight:800;font-size:1rem;letter-spacing:.04em}
|
||
.state-badge .dot{width:10px;height:10px;border-radius:9999px;background:currentColor;position:relative}
|
||
.state-badge .dot::after{content:"";position:absolute;inset:-3px;border-radius:9999px;border:2px solid currentColor;opacity:.5;animation:pls 1.6s ease-out infinite}
|
||
@keyframes pls{0%{transform:scale(.7);opacity:.6}100%{transform:scale(2.2);opacity:0}}
|
||
|
||
.estop{
|
||
width:88px;height:88px;background:#dc2626;color:#fff;font-weight:900;
|
||
clip-path:polygon(30% 0,70% 0,100% 30%,100% 70%,70% 100%,30% 100%,0 70%,0 30%);
|
||
display:flex;align-items:center;justify-content:center;
|
||
border:3px solid #fff;box-shadow:0 0 0 3px #b91c1c, 0 8px 20px rgba(220,38,38,.35);font-size:1rem;letter-spacing:.05em
|
||
}
|
||
|
||
/* Body */
|
||
.body{flex:1;display:flex;flex-direction:column;background:#f1f5f9;min-height:0}
|
||
.panel{display:none;flex:1;min-height:0;flex-direction:column;padding:18px;gap:14px}
|
||
.panel.active{display:flex}
|
||
|
||
/* ----------------------- V09 jog/macro palette ----------------------- */
|
||
/* Flat soft slate, no shadow */
|
||
:root{
|
||
--jog-bg:#3f4b63;
|
||
--jog-hover:#4a5777;
|
||
--jog-dir-bg:#5b6885;
|
||
--jog-dir-hover:#6a779a;
|
||
--jog-ghost-bg:#8c97ad;
|
||
--jog-ghost-hover:#9ba6bb;
|
||
--jog-ink:#fff;
|
||
--jog-ghost-ink:#0f172a;
|
||
}
|
||
|
||
/* JOG */
|
||
.jog-card{background:#fff;border:1px solid #e5e7eb;border-radius:18px;display:flex;flex-direction:column;padding:18px;min-height:0}
|
||
.jog-head{display:flex;align-items:center;justify-content:space-between;margin-bottom:14px}
|
||
.jog-title{font-size:18px;font-weight:700;color:#0f172a}
|
||
.jog-title .step{color:#0ea5e9;font-family:'JetBrains Mono',monospace}
|
||
.step-seg{display:inline-flex;background:#f1f5f9;border:1px solid #e2e8f0;border-radius:14px;padding:4px}
|
||
.step-seg button{height:48px;min-width:64px;padding:0 1rem;border-radius:11px;font-size:1rem;font-weight:800;color:#475569;cursor:pointer}
|
||
.step-seg button.active{background:#0f172a;color:#fde047}
|
||
.jog-grid{display:grid;grid-template-columns:repeat(4,1fr);grid-template-rows:repeat(4,1fr);gap:10px;flex:1;min-height:0}
|
||
.jbtn{
|
||
border-radius:16px;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:4px;
|
||
user-select:none;-webkit-tap-highlight-color:transparent;cursor:pointer;
|
||
font-weight:700;font-size:1.05rem;border:none;
|
||
transition:transform .06s, background .15s;
|
||
background:var(--jog-bg);color:var(--jog-ink);
|
||
}
|
||
.jbtn:hover{background:var(--jog-hover)}
|
||
.jbtn:active{transform:scale(.97)}
|
||
.jbtn .ico{font-size:1.6rem}
|
||
.jbtn .lbl{font-size:.8rem;color:inherit;opacity:.85;font-weight:600}
|
||
.jbtn.dir{background:var(--jog-dir-bg)} .jbtn.dir:hover{background:var(--jog-dir-hover)}
|
||
.jbtn.ghost{background:var(--jog-ghost-bg);color:var(--jog-ghost-ink)} .jbtn.ghost:hover{background:var(--jog-ghost-hover)}
|
||
|
||
/* DRO + STATUS */
|
||
.control-grid{display:grid;grid-template-columns:720px 1fr;gap:18px;flex:1;min-height:0}
|
||
.right-col{display:grid;grid-template-rows:1fr 158px;gap:18px;min-height:0}
|
||
.dro-card{background:#fff;border:1px solid #e5e7eb;border-radius:18px;overflow:hidden;display:flex;flex-direction:column}
|
||
.dro-head{display:grid;grid-template-columns:84px 1.4fr 1fr 1fr 170px 170px 280px;column-gap:.75rem;align-items:center;padding:14px 22px;background:#f8fafc;border-bottom:1px solid #e5e7eb;font-size:.78rem;font-weight:800;text-transform:uppercase;letter-spacing:.1em;color:#94a3b8}
|
||
.dro-row{display:grid;grid-template-columns:84px 1.4fr 1fr 1fr 170px 170px 280px;column-gap:.75rem;align-items:center;padding:14px 22px;border-bottom:1px solid #f1f5f9;flex:1;min-height:0}
|
||
.dro-row:last-child{border-bottom:none}
|
||
.dro-axis{font-weight:900;font-size:46px;line-height:1}
|
||
.dro-pos{font-family:'JetBrains Mono',monospace;font-size:36px;font-weight:800}
|
||
.dro-pos .u{font-size:14px;color:#94a3b8;font-weight:500;margin-left:6px}
|
||
.dro-sec{font-family:'JetBrains Mono',monospace;font-size:18px;color:#64748b;font-weight:600}
|
||
.axis-x{color:#dc2626} .axis-y{color:#16a34a} .axis-z{color:#2563eb} .axis-w{color:#7c3aed}
|
||
|
||
.chip{display:inline-flex;align-items:center;gap:.4rem;padding:.4rem .7rem;border-radius:9999px;font-size:.78rem;font-weight:700}
|
||
.chip-green{background:#dcfce7;color:#166534}
|
||
.chip-amber{background:#fef3c7;color:#92400e}
|
||
.chip-red{background:#fee2e2;color:#991b1b}
|
||
.chip-slate{background:#e2e8f0;color:#334155}
|
||
.chip-blue{background:#dbeafe;color:#1e40af}
|
||
|
||
.icon-btn{
|
||
width:72px;height:72px;border-radius:14px;cursor:pointer;
|
||
display:inline-flex;align-items:center;justify-content:center;
|
||
color:#334155;background:#f1f5f9;border:1px solid #e2e8f0;
|
||
font-size:1.45rem
|
||
}
|
||
.icon-btn:hover{background:#e2e8f0}
|
||
.actions-cell{display:flex;justify-content:flex-end;gap:10px}
|
||
.z-highlight{background:rgba(254,243,199,.4)}
|
||
|
||
.status-strip{display:grid;grid-template-columns:repeat(4,1fr);gap:18px;min-height:0}
|
||
.stat-card{background:#fff;border:1px solid #e5e7eb;border-radius:18px;padding:18px 22px;display:flex;flex-direction:column;justify-content:center}
|
||
.stat-label{font-size:11px;font-weight:800;text-transform:uppercase;letter-spacing:.14em;color:#94a3b8}
|
||
.stat-val{font-family:'JetBrains Mono',monospace;font-size:30px;font-weight:800;margin-top:6px}
|
||
.stat-val.ok{color:#166534}
|
||
.stat-sub{font-size:13px;color:#64748b;margin-top:2px}
|
||
|
||
/* MACROS */
|
||
.macro-row{display:grid;grid-template-columns:repeat(8,1fr);gap:12px;flex:0 0 auto}
|
||
.macro-btn{
|
||
height:84px;border-radius:14px;border:none;cursor:pointer;
|
||
color:#fff;background:#3f4b63;
|
||
font-weight:800;font-size:1rem;
|
||
display:flex;align-items:center;justify-content:center;gap:.6rem;
|
||
transition:transform .06s, background .15s
|
||
}
|
||
.macro-btn:hover{background:#4a5777}
|
||
.macro-btn:active{transform:translateY(2px)}
|
||
.macro-btn .mnum{display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;border-radius:8px;background:#fde047;color:#0f172a;font-size:.85rem;font-weight:900}
|
||
.macro-btn .micon{font-size:1.1rem;opacity:.75}
|
||
|
||
/* =============================================================
|
||
PROGRAM PANEL
|
||
============================================================= */
|
||
.program-card{background:#fff;border:1px solid #e5e7eb;border-radius:18px;display:flex;flex-direction:column;flex:1;min-height:0;overflow:hidden}
|
||
.ptab-bar{display:flex;align-items:center;gap:6px;border-bottom:1px solid #e5e7eb;flex:0 0 auto;background:#fff;padding:0 18px}
|
||
.ptab{height:60px;padding:0 22px;font-weight:700;color:#64748b;border-bottom:3px solid transparent;font-size:1rem;display:inline-flex;align-items:center;gap:.5rem;cursor:pointer}
|
||
.ptab:hover{color:#0f172a}
|
||
.ptab.active{color:#0f172a;border-bottom-color:#0f172a}
|
||
.ptab .ptab-badge{background:#fde047;color:#0f172a;font-size:.7rem;padding:2px 7px;border-radius:9999px;font-weight:900}
|
||
|
||
.action-bar{display:flex;align-items:center;gap:12px;padding:18px;flex-wrap:wrap;border-bottom:1px solid #f1f5f9}
|
||
.action-btn{height:84px;padding:0 24px;border-radius:14px;background:#3f4b63;color:#fff;border:none;cursor:pointer;display:inline-flex;flex-direction:column;align-items:center;justify-content:center;gap:4px;font-weight:800;font-size:.9rem;letter-spacing:.04em;transition:background .15s}
|
||
.action-btn:hover{background:#4a5777}
|
||
.action-btn .ico{font-size:1.4rem}
|
||
.action-btn.run{background:#16a34a}
|
||
.action-btn.run:hover{background:#15803d}
|
||
.action-btn.stop{background:#0f172a}
|
||
.action-btn.stop:hover{background:#1e293b}
|
||
.action-btn.danger{background:#fee2e2;color:#7f1d1d}
|
||
.action-btn.danger:hover{background:#fecaca}
|
||
.action-btn.danger .ico{color:#dc2626}
|
||
|
||
.file-bar{display:flex;align-items:center;gap:10px;padding:14px 18px;flex-wrap:wrap;border-bottom:1px solid #f1f5f9}
|
||
.file-btn{height:54px;padding:0 18px;border-radius:12px;background:#f1f5f9;border:1px solid #e2e8f0;font-weight:700;color:#0f172a;font-size:.9rem;display:inline-flex;align-items:center;gap:.5rem;cursor:pointer}
|
||
.file-btn:hover{background:#e2e8f0}
|
||
.file-select{height:54px;padding:0 16px;border-radius:12px;background:#fff;border:1px solid #e2e8f0;font-weight:600;color:#0f172a;font-size:.9rem;display:inline-flex;align-items:center;gap:.5rem;cursor:pointer}
|
||
.file-select .caret{color:#94a3b8;margin-left:.5rem}
|
||
.file-select.primary{background:#fff;border:2px solid #0ea5e9;flex:1;min-width:300px}
|
||
|
||
.program-body{flex:1;display:grid;grid-template-columns:1fr 600px;min-height:0}
|
||
.gcode{font-family:'JetBrains Mono',monospace;font-size:14px;line-height:1.6;background:#fafafa;border-right:1px solid #f1f5f9;padding:14px 0;overflow:auto;color:#1e293b}
|
||
.gline{display:grid;grid-template-columns:60px 1fr;gap:14px;padding:1px 18px 1px 0}
|
||
.gline:nth-child(odd){background:#f4f4f5}
|
||
.gline .gn{color:#f59e0b;text-align:right;font-weight:700}
|
||
.gline.cur{background:#dbeafe !important}
|
||
.gline.cur .gn{color:#1e40af}
|
||
.gcomment{color:#64748b}
|
||
.gword{color:#0f172a}
|
||
.gnum{color:#16a34a}
|
||
|
||
.viewer{display:flex;flex-direction:column;min-height:0}
|
||
.viewer-3d{flex:1;background:#0b1220;position:relative;overflow:hidden;display:flex;align-items:center;justify-content:center}
|
||
.viewer-tools{display:flex;gap:8px;padding:14px;border-top:1px solid #f1f5f9;background:#fff;flex-wrap:wrap}
|
||
.vtool{height:60px;width:60px;border-radius:12px;background:#f1f5f9;border:1px solid #e2e8f0;color:#475569;display:inline-flex;align-items:center;justify-content:center;font-size:1.2rem;cursor:pointer}
|
||
.vtool:hover{background:#e2e8f0}
|
||
.vtool.on{background:#0f172a;color:#fff;border-color:#0f172a}
|
||
.vinfo{padding:14px 18px;background:#fff;font-size:13px;color:#64748b;border-top:1px solid #f1f5f9;display:flex;justify-content:space-between;align-items:center}
|
||
.vinfo .ext{color:#0f172a;font-weight:600}
|
||
|
||
/* =============================================================
|
||
MESSAGES PANEL
|
||
============================================================= */
|
||
.messages{display:none;flex-direction:column;flex:1;min-height:0;padding:18px;gap:12px;overflow:auto}
|
||
.messages.active{display:flex}
|
||
.msg{background:#fff;border:1px solid #e5e7eb;border-radius:14px;padding:18px 22px;display:grid;grid-template-columns:54px 1fr auto;gap:18px;align-items:flex-start}
|
||
.msg .mi{width:54px;height:54px;border-radius:12px;display:inline-flex;align-items:center;justify-content:center;font-size:1.4rem}
|
||
.msg.error{border-left:6px solid #dc2626}
|
||
.msg.error .mi{background:#fee2e2;color:#991b1b}
|
||
.msg.warn{border-left:6px solid #f59e0b}
|
||
.msg.warn .mi{background:#fef3c7;color:#92400e}
|
||
.msg.info{border-left:6px solid #0ea5e9}
|
||
.msg.info .mi{background:#dbeafe;color:#1e40af}
|
||
.msg.ok{border-left:6px solid #16a34a}
|
||
.msg.ok .mi{background:#dcfce7;color:#166534}
|
||
.msg .mtitle{font-weight:800;font-size:1.05rem;color:#0f172a}
|
||
.msg .mtime{font-size:.8rem;color:#94a3b8;margin-top:2px}
|
||
.msg .mbody{margin-top:6px;color:#475569;font-size:.95rem;line-height:1.5}
|
||
.msg .mbody .mono{background:#f1f5f9;padding:2px 6px;border-radius:4px;font-size:.85rem}
|
||
.msg .mactions{display:flex;gap:8px}
|
||
.mbtn{height:48px;padding:0 16px;border-radius:10px;background:#f1f5f9;border:1px solid #e2e8f0;font-weight:700;color:#0f172a;font-size:.85rem;cursor:pointer}
|
||
.mbtn:hover{background:#e2e8f0}
|
||
.mbtn.primary{background:#0f172a;color:#fff;border-color:#0f172a}
|
||
.mbtn.primary:hover{background:#1e293b}
|
||
|
||
/* =============================================================
|
||
INDICATORS PANEL
|
||
============================================================= */
|
||
.indicators{display:none;flex:1;min-height:0;padding:18px;gap:14px;overflow:auto;grid-template-columns:repeat(4,1fr);grid-auto-rows:min-content}
|
||
.indicators.active{display:grid}
|
||
.ind{background:#fff;border:1px solid #e5e7eb;border-radius:14px;padding:16px 18px;display:flex;flex-direction:column;gap:6px}
|
||
.ind-label{font-size:.8rem;font-weight:800;text-transform:uppercase;letter-spacing:.1em;color:#94a3b8}
|
||
.ind-val{font-family:'JetBrains Mono',monospace;font-size:1.6rem;font-weight:800;color:#0f172a}
|
||
.ind-state{display:inline-flex;align-items:center;gap:.4rem;font-size:.8rem;font-weight:700;color:#475569}
|
||
.ind-state .dot{width:10px;height:10px;border-radius:9999px}
|
||
.ind .progress{height:8px;background:#f1f5f9;border-radius:9999px;overflow:hidden;margin-top:4px}
|
||
.ind .progress > div{height:100%;background:#0ea5e9}
|
||
.ind.full{grid-column:span 2}
|
||
|
||
/* =============================================================
|
||
MDI PANEL
|
||
============================================================= */
|
||
.mdi{display:none;flex-direction:column;flex:1;min-height:0;padding:18px;gap:14px}
|
||
.mdi.active{display:flex}
|
||
.mdi-input{
|
||
background:#0b1220;color:#86efac;border:1px solid #1e293b;border-radius:14px;
|
||
padding:22px 24px;font-family:'JetBrains Mono',monospace;font-size:1.4rem;font-weight:600;
|
||
display:flex;align-items:center;gap:.6rem;
|
||
}
|
||
.mdi-input .prompt{color:#475569}
|
||
.mdi-input .cursor{display:inline-block;width:14px;height:1.4rem;background:#86efac;animation:blink 1s steps(2,end) infinite;vertical-align:middle}
|
||
@keyframes blink{50%{opacity:0}}
|
||
.mdi-keys{display:grid;grid-template-columns:repeat(8,1fr);gap:8px;flex:0 0 auto}
|
||
.mkey{height:64px;border-radius:12px;background:#fff;border:1px solid #e2e8f0;font-weight:800;font-size:1.05rem;color:#0f172a;cursor:pointer;font-family:'JetBrains Mono',monospace}
|
||
.mkey:hover{background:#f1f5f9}
|
||
.mkey.send{background:#16a34a;color:#fff;border-color:#15803d;grid-column:span 2;font-family:'Inter',sans-serif;font-size:.95rem;letter-spacing:.04em}
|
||
.mkey.send:hover{background:#15803d}
|
||
.mkey.clear{background:#fee2e2;color:#7f1d1d;border-color:#fca5a5;font-family:'Inter',sans-serif;font-size:.95rem;letter-spacing:.04em}
|
||
.mdi-history{flex:1;background:#fff;border:1px solid #e5e7eb;border-radius:14px;padding:14px 18px;overflow:auto;font-family:'JetBrains Mono',monospace;font-size:.95rem}
|
||
.mdi-history .h-row{display:grid;grid-template-columns:80px 1fr auto;gap:14px;padding:6px 0;border-bottom:1px solid #f1f5f9;align-items:center}
|
||
.mdi-history .h-time{color:#94a3b8;font-size:.8rem}
|
||
.mdi-history .h-cmd{color:#0f172a;font-weight:700}
|
||
.mdi-history .h-status{color:#16a34a;font-weight:700;font-size:.8rem}
|
||
.mdi-history .h-status.err{color:#dc2626}
|
||
|
||
/* =============================================================
|
||
SETTINGS PANEL
|
||
============================================================= */
|
||
.settings{display:none;flex:1;min-height:0;padding:18px;gap:14px;overflow:auto;grid-template-columns:280px 1fr}
|
||
.settings.active{display:grid}
|
||
.set-side{background:#fff;border:1px solid #e5e7eb;border-radius:14px;padding:10px;display:flex;flex-direction:column;gap:4px;height:fit-content}
|
||
.set-item{height:56px;padding:0 16px;border-radius:10px;display:flex;align-items:center;gap:.6rem;color:#475569;font-weight:700;cursor:pointer}
|
||
.set-item:hover{background:#f1f5f9}
|
||
.set-item.active{background:#0f172a;color:#fff}
|
||
.set-content{display:flex;flex-direction:column;gap:14px}
|
||
.set-card{background:#fff;border:1px solid #e5e7eb;border-radius:14px;padding:22px}
|
||
.set-title{font-weight:800;font-size:1.1rem;color:#0f172a;margin-bottom:14px}
|
||
.set-row{display:grid;grid-template-columns:280px 1fr auto;gap:14px;align-items:center;padding:14px 0;border-bottom:1px solid #f1f5f9}
|
||
.set-row:last-child{border-bottom:none}
|
||
.set-row .label{font-weight:700;color:#0f172a;font-size:.95rem}
|
||
.set-row .desc{color:#64748b;font-size:.85rem;margin-top:2px}
|
||
.set-row .val{font-family:'JetBrains Mono',monospace;color:#475569}
|
||
.set-input{height:48px;padding:0 14px;border-radius:10px;border:1px solid #e2e8f0;background:#fff;font-family:'JetBrains Mono',monospace;font-size:.95rem;color:#0f172a;min-width:200px}
|
||
.set-toggle{width:54px;height:30px;border-radius:9999px;background:#cbd5e1;position:relative;cursor:pointer;transition:background .15s}
|
||
.set-toggle::after{content:"";position:absolute;left:3px;top:3px;width:24px;height:24px;border-radius:9999px;background:#fff;transition:transform .15s}
|
||
.set-toggle.on{background:#16a34a}
|
||
.set-toggle.on::after{transform:translateX(24px)}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="host">
|
||
|
||
<div class="topbar">
|
||
<div class="brand">
|
||
<div class="stripe-logo-sm"></div>
|
||
ONEFINITY · V09 · Full UX preview
|
||
</div>
|
||
<span class="pill">Click the inner tabs to navigate</span>
|
||
<div style="margin-left:auto"></div>
|
||
<button id="oneToOne" class="toggle">1:1</button>
|
||
<button id="fitBtn" class="toggle on">Fit</button>
|
||
<span id="scaleInfo" class="pill mono">100%</span>
|
||
</div>
|
||
|
||
<div class="stage" id="stage">
|
||
<div class="scaler-viewport" id="viewport">
|
||
<div class="scaler" id="scaler">
|
||
|
||
<!-- ============= KIOSK ============= -->
|
||
<div class="kiosk">
|
||
<header class="head">
|
||
<div class="brand-blk">
|
||
<div class="brand-logo"></div>
|
||
<div class="brand-name">ONEFINITY</div>
|
||
</div>
|
||
<div class="kiosk-tabs">
|
||
<button class="ktab active" data-target="control"><i class="fa-solid fa-gamepad"></i> Control</button>
|
||
<button class="ktab" data-target="program"><i class="fa-solid fa-list-ol"></i> Program</button>
|
||
<button class="ktab" data-target="console"><i class="fa-solid fa-terminal"></i> Console <span class="ktab-badge">2</span></button>
|
||
<button class="ktab" data-target="settings"><i class="fa-solid fa-sliders"></i> Settings</button>
|
||
</div>
|
||
<button class="sys-btn"><span class="pip"></span> All systems · view <i class="fa-solid fa-chevron-down" style="font-size:10px;opacity:.6"></i></button>
|
||
<span class="state-badge"><span class="dot"></span> READY</span>
|
||
<button class="estop">STOP</button>
|
||
</header>
|
||
|
||
<div class="body">
|
||
|
||
<!-- ============= CONTROL ============= -->
|
||
<div class="panel active" data-panel="control">
|
||
<div class="control-grid">
|
||
<!-- jog -->
|
||
<div class="jog-card">
|
||
<div class="jog-head">
|
||
<div class="jog-title">Jog · step <span class="step">10mm</span></div>
|
||
<div class="step-seg">
|
||
<button>0.1</button><button>1</button><button class="active">10</button><button>100</button>
|
||
</div>
|
||
</div>
|
||
<div class="jog-grid">
|
||
<button class="jbtn dir"><i class="fa-solid fa-arrow-up ico" style="transform:rotate(-45deg)"></i></button>
|
||
<button class="jbtn">Y+</button>
|
||
<button class="jbtn dir"><i class="fa-solid fa-arrow-up ico" style="transform:rotate(45deg)"></i></button>
|
||
<button class="jbtn">Z+</button>
|
||
<button class="jbtn">X−</button>
|
||
<button class="jbtn ghost"><span class="lbl">XY</span><span style="font-size:1rem;font-weight:700">Origin</span></button>
|
||
<button class="jbtn">X+</button>
|
||
<button class="jbtn ghost"><span class="lbl">Z</span><span style="font-size:1rem;font-weight:700">Origin</span></button>
|
||
<button class="jbtn dir"><i class="fa-solid fa-arrow-down ico" style="transform:rotate(45deg)"></i></button>
|
||
<button class="jbtn">Y−</button>
|
||
<button class="jbtn dir"><i class="fa-solid fa-arrow-down ico" style="transform:rotate(-45deg)"></i></button>
|
||
<button class="jbtn">Z−</button>
|
||
<button class="jbtn"><i class="fa-solid fa-arrow-down ico"></i><span class="lbl">W−</span></button>
|
||
<button class="jbtn ghost"><span class="lbl">W</span><span style="font-size:1rem;font-weight:700">Origin</span></button>
|
||
<button class="jbtn"><i class="fa-solid fa-arrow-up ico"></i><span class="lbl">W+</span></button>
|
||
<button class="jbtn"><i class="fa-solid fa-house ico"></i><span class="lbl">Home</span></button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- DRO + status -->
|
||
<div class="right-col">
|
||
<div class="dro-card">
|
||
<div class="dro-head">
|
||
<div>Axis</div><div>Position</div><div>Absolute</div><div>Offset</div><div>State</div><div>Toolpath</div><div style="text-align:right">Actions</div>
|
||
</div>
|
||
<div class="dro-row">
|
||
<div class="dro-axis axis-x">X</div>
|
||
<div class="dro-pos">0.000<span class="u">mm</span></div>
|
||
<div class="dro-sec">0.000</div>
|
||
<div class="dro-sec">0.000</div>
|
||
<div><span class="chip chip-amber"><i class="fa-solid fa-question"></i> Unhomed</span></div>
|
||
<div><span class="chip chip-green"><i class="fa-solid fa-check"></i> OK</span></div>
|
||
<div class="actions-cell">
|
||
<button class="icon-btn"><i class="fa-solid fa-gear"></i></button>
|
||
<button class="icon-btn"><i class="fa-solid fa-location-dot"></i></button>
|
||
<button class="icon-btn"><i class="fa-solid fa-house"></i></button>
|
||
</div>
|
||
</div>
|
||
<div class="dro-row">
|
||
<div class="dro-axis axis-y">Y</div>
|
||
<div class="dro-pos">0.000<span class="u">mm</span></div>
|
||
<div class="dro-sec">0.000</div>
|
||
<div class="dro-sec">0.000</div>
|
||
<div><span class="chip chip-amber"><i class="fa-solid fa-question"></i> Unhomed</span></div>
|
||
<div><span class="chip chip-green"><i class="fa-solid fa-check"></i> OK</span></div>
|
||
<div class="actions-cell">
|
||
<button class="icon-btn"><i class="fa-solid fa-gear"></i></button>
|
||
<button class="icon-btn"><i class="fa-solid fa-location-dot"></i></button>
|
||
<button class="icon-btn"><i class="fa-solid fa-house"></i></button>
|
||
</div>
|
||
</div>
|
||
<div class="dro-row z-highlight">
|
||
<div class="dro-axis axis-z">Z</div>
|
||
<div class="dro-pos">0.000<span class="u">mm</span></div>
|
||
<div class="dro-sec">0.000</div>
|
||
<div class="dro-sec">0.000</div>
|
||
<div><span class="chip chip-amber"><i class="fa-solid fa-question"></i> Unhomed</span></div>
|
||
<div><span class="chip chip-amber"><i class="fa-solid fa-triangle-exclamation"></i> Over</span></div>
|
||
<div class="actions-cell">
|
||
<button class="icon-btn"><i class="fa-solid fa-gear"></i></button>
|
||
<button class="icon-btn"><i class="fa-solid fa-location-dot"></i></button>
|
||
<button class="icon-btn"><i class="fa-solid fa-house"></i></button>
|
||
</div>
|
||
</div>
|
||
<div class="dro-row">
|
||
<div class="dro-axis axis-w">W</div>
|
||
<div class="dro-pos">0.000<span class="u">mm</span></div>
|
||
<div class="dro-sec">0.000</div>
|
||
<div class="dro-sec" style="opacity:.4">—</div>
|
||
<div><span class="chip chip-amber"><i class="fa-solid fa-question"></i> Unhomed</span></div>
|
||
<div><span class="chip chip-green"><i class="fa-solid fa-check"></i> OK</span></div>
|
||
<div class="actions-cell">
|
||
<button class="icon-btn"><i class="fa-solid fa-location-dot"></i></button>
|
||
<button class="icon-btn"><i class="fa-solid fa-house"></i></button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="status-strip">
|
||
<div class="stat-card"><div class="stat-label">State</div><div class="stat-val ok">READY</div><div class="stat-sub">No alerts</div></div>
|
||
<div class="stat-card"><div class="stat-label">Velocity / Feed</div><div class="stat-val">0 · 0</div><div class="stat-sub">m/min · mm/min</div></div>
|
||
<div class="stat-card"><div class="stat-label">Spindle</div><div class="stat-val">0 (0)</div><div class="stat-sub">RPM (commanded / actual)</div></div>
|
||
<div class="stat-card"><div class="stat-label">Job</div><div class="stat-val">0 / 1,785</div><div class="stat-sub">Line · 19:07 remaining</div></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- macros -->
|
||
<div class="macro-row">
|
||
<button class="macro-btn"><span class="mnum">1</span><i class="fa-solid fa-circle-play micon"></i> Macro 1</button>
|
||
<button class="macro-btn"><span class="mnum">2</span><i class="fa-solid fa-circle-play micon"></i> Macro 2</button>
|
||
<button class="macro-btn"><span class="mnum">3</span><i class="fa-solid fa-circle-play micon"></i> Macro 3</button>
|
||
<button class="macro-btn"><span class="mnum">4</span><i class="fa-solid fa-circle-play micon"></i> Macro 4</button>
|
||
<button class="macro-btn"><span class="mnum">5</span><i class="fa-solid fa-circle-play micon"></i> Macro 5</button>
|
||
<button class="macro-btn"><span class="mnum">6</span><i class="fa-solid fa-circle-play micon"></i> Macro 6</button>
|
||
<button class="macro-btn"><span class="mnum">7</span><i class="fa-solid fa-circle-play micon"></i> Macro 7</button>
|
||
<button class="macro-btn"><span class="mnum">8</span><i class="fa-solid fa-circle-play micon"></i> Macro 8</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ============= PROGRAM ============= -->
|
||
<div class="panel" data-panel="program" style="padding:0;gap:0">
|
||
<div class="program-card" style="margin:18px;border-radius:18px">
|
||
<!-- Auto sub-panel -->
|
||
<div class="auto-sub" data-sub="auto" style="display:flex;flex-direction:column;flex:1;min-height:0">
|
||
<div class="action-bar">
|
||
<button class="action-btn run"><i class="fa-solid fa-play ico"></i><span>RUN</span></button>
|
||
<button class="action-btn stop"><i class="fa-solid fa-stop ico"></i><span>STOP</span></button>
|
||
<button class="action-btn"><i class="fa-solid fa-folder-arrow-up ico"></i><span>UPLOAD FOLDER</span></button>
|
||
<button class="action-btn"><i class="fa-solid fa-file-arrow-up ico"></i><span>UPLOAD FILE</span></button>
|
||
<button class="action-btn"><i class="fa-solid fa-file-arrow-down ico"></i><span>DOWNLOAD FILE</span></button>
|
||
<button class="action-btn danger"><i class="fa-solid fa-trash ico"></i><span>DELETE</span></button>
|
||
</div>
|
||
|
||
<div class="file-bar">
|
||
<button class="file-btn"><i class="fa-solid fa-folder-plus"></i> Create Folder</button>
|
||
<button class="file-btn"><i class="fa-solid fa-folder-minus"></i> Delete Folder</button>
|
||
<span class="file-select"><i class="fa-solid fa-folder-open" style="color:#64748b"></i> Default folder <i class="fa-solid fa-chevron-down caret"></i></span>
|
||
<span class="file-select primary"><i class="fa-solid fa-file-code" style="color:#0ea5e9"></i> thin-rough.nc <i class="fa-solid fa-chevron-down caret" style="margin-left:auto"></i></span>
|
||
<span class="file-select"><i class="fa-solid fa-arrow-down-wide-short" style="color:#64748b"></i> By Upload Date <i class="fa-solid fa-chevron-down caret"></i></span>
|
||
</div>
|
||
|
||
<div class="program-body">
|
||
<div class="gcode" id="gcode-list"></div>
|
||
<div class="viewer">
|
||
<div class="viewer-3d">
|
||
<svg viewBox="0 0 400 220" style="width:100%;height:100%">
|
||
<defs>
|
||
<pattern id="gridv" width="20" height="20" patternUnits="userSpaceOnUse">
|
||
<path d="M 20 0 L 0 0 0 20" fill="none" stroke="#1e293b" stroke-width="1"/>
|
||
</pattern>
|
||
</defs>
|
||
<rect width="400" height="220" fill="url(#gridv)"/>
|
||
<rect x="40" y="80" width="320" height="60" stroke="#475569" stroke-width="1" fill="none" stroke-dasharray="3 3"/>
|
||
<text x="40" y="74" fill="#64748b" font-size="9" font-family="monospace">Stock: 250 × 25 × 16 mm</text>
|
||
<!-- toolpath -->
|
||
<path d="M40,110 L360,110 M40,100 L360,100 M40,120 L360,120 M40,90 L360,90 M40,130 L360,130" stroke="#22c55e" stroke-width="1.4" fill="none" opacity=".8"/>
|
||
<path d="M40,110 L40,80 L60,80 L60,110 M80,110 L80,80 L100,80 L100,110 M120,110 L120,80 L140,80 L140,110" stroke="#ef4444" stroke-width="1.4" fill="none" opacity=".8"/>
|
||
<circle cx="40" cy="110" r="3" fill="#22c55e"/>
|
||
<circle cx="360" cy="110" r="3" fill="#ef4444"/>
|
||
<text x="46" y="108" fill="#22c55e" font-size="8" font-family="monospace">START</text>
|
||
<text x="332" y="108" fill="#ef4444" font-size="8" font-family="monospace">END</text>
|
||
<!-- axes gizmo -->
|
||
<g transform="translate(28,196)">
|
||
<line x1="0" y1="0" x2="22" y2="0" stroke="#ef4444" stroke-width="2"/>
|
||
<line x1="0" y1="0" x2="0" y2="-22" stroke="#3b82f6" stroke-width="2"/>
|
||
<line x1="0" y1="0" x2="-12" y2="12" stroke="#22c55e" stroke-width="2"/>
|
||
<text x="24" y="4" fill="#ef4444" font-size="9" font-family="monospace">X</text>
|
||
<text x="-4" y="-26" fill="#3b82f6" font-size="9" font-family="monospace">Z</text>
|
||
<text x="-22" y="22" fill="#22c55e" font-size="9" font-family="monospace">Y</text>
|
||
</g>
|
||
</svg>
|
||
</div>
|
||
<div class="viewer-tools">
|
||
<button class="vtool" title="Fit"><i class="fa-solid fa-expand"></i></button>
|
||
<button class="vtool on" title="Tool"><i class="fa-solid fa-screwdriver-wrench"></i></button>
|
||
<button class="vtool" title="Stock"><i class="fa-solid fa-cube"></i></button>
|
||
<button class="vtool" title="Origin"><i class="fa-solid fa-up-right-and-down-left-from-center"></i></button>
|
||
<button class="vtool" title="Top"><i class="fa-solid fa-square"></i></button>
|
||
<button class="vtool" title="Front"><i class="fa-solid fa-square-full"></i></button>
|
||
<button class="vtool" title="Iso"><i class="fa-solid fa-cubes"></i></button>
|
||
</div>
|
||
<div class="vinfo">
|
||
<span><span class="ext">thin-rough.nc</span> · 1,785 lines · 12.4 KB</span>
|
||
<span class="mono">est. 19:07</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ============= CONSOLE ============= -->
|
||
<div class="panel" data-panel="console" style="padding:0;gap:0">
|
||
<div class="program-card" style="margin:18px;border-radius:18px">
|
||
|
||
<div class="ptab-bar">
|
||
<button class="ptab active" data-ptab="mdi"><i class="fa-solid fa-keyboard"></i> MDI</button>
|
||
<button class="ptab" data-ptab="messages"><i class="fa-solid fa-comment-dots"></i> Messages <span class="ptab-badge">2</span></button>
|
||
<button class="ptab" data-ptab="indicators"><i class="fa-solid fa-bell"></i> Indicators</button>
|
||
</div>
|
||
|
||
<!-- MDI sub-panel -->
|
||
<div class="mdi active" data-sub="mdi">
|
||
<div class="mdi-input">
|
||
<span class="prompt">G></span>
|
||
<span class="mono">G0 X100 Y50 F2000</span>
|
||
<span class="cursor"></span>
|
||
</div>
|
||
<div class="mdi-keys">
|
||
<button class="mkey">G0</button>
|
||
<button class="mkey">G1</button>
|
||
<button class="mkey">G2</button>
|
||
<button class="mkey">G3</button>
|
||
<button class="mkey">G28</button>
|
||
<button class="mkey">G92</button>
|
||
<button class="mkey">M3</button>
|
||
<button class="mkey">M5</button>
|
||
<button class="mkey">X</button>
|
||
<button class="mkey">Y</button>
|
||
<button class="mkey">Z</button>
|
||
<button class="mkey">W</button>
|
||
<button class="mkey">F</button>
|
||
<button class="mkey">S</button>
|
||
<button class="mkey clear">CLEAR</button>
|
||
<button class="mkey send">SEND ↵</button>
|
||
</div>
|
||
<div class="mdi-history">
|
||
<div class="h-row"><span class="h-time">19:42:11</span><span class="h-cmd">G21</span><span class="h-status">✓ ok</span></div>
|
||
<div class="h-row"><span class="h-time">19:42:14</span><span class="h-cmd">G90</span><span class="h-status">✓ ok</span></div>
|
||
<div class="h-row"><span class="h-time">19:43:02</span><span class="h-cmd">G0 Y12.800</span><span class="h-status">✓ ok</span></div>
|
||
<div class="h-row"><span class="h-time">19:43:08</span><span class="h-cmd">G0 Z19.040</span><span class="h-status">✓ ok</span></div>
|
||
<div class="h-row"><span class="h-time">19:43:30</span><span class="h-cmd">G1 Z-20 F800</span><span class="h-status err">✗ blocked: Z over travel</span></div>
|
||
<div class="h-row"><span class="h-time">19:44:01</span><span class="h-cmd">G0 Z5</span><span class="h-status">✓ ok</span></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Messages sub-panel -->
|
||
<div class="messages" data-sub="messages">
|
||
<div class="msg warn">
|
||
<div class="mi"><i class="fa-solid fa-triangle-exclamation"></i></div>
|
||
<div>
|
||
<div style="display:flex;align-items:baseline;gap:.6rem">
|
||
<div class="mtitle">Z toolpath exceeds soft-limit</div>
|
||
<div class="mtime">2 min ago · sticky</div>
|
||
</div>
|
||
<div class="mbody">Loaded program reaches <span class="mono">Z = -16.500</span>. Configured soft-limit is <span class="mono">Z = -15.000</span>. Adjust the Z origin or set a deeper soft-limit before running.</div>
|
||
</div>
|
||
<div class="mactions">
|
||
<button class="mbtn">Open settings</button>
|
||
<button class="mbtn primary">Acknowledge</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="msg info">
|
||
<div class="mi"><i class="fa-solid fa-circle-info"></i></div>
|
||
<div>
|
||
<div style="display:flex;align-items:baseline;gap:.6rem">
|
||
<div class="mtitle">Camera offline</div>
|
||
<div class="mtime">12 min ago</div>
|
||
</div>
|
||
<div class="mbody">Camera at <span class="mono">10.1.10.55:8554</span> did not respond on last poll. Live preview disabled.</div>
|
||
</div>
|
||
<div class="mactions">
|
||
<button class="mbtn">Retry</button>
|
||
<button class="mbtn">Dismiss</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="msg ok">
|
||
<div class="mi"><i class="fa-solid fa-check"></i></div>
|
||
<div>
|
||
<div style="display:flex;align-items:baseline;gap:.6rem">
|
||
<div class="mtitle">File uploaded · thin-rough.nc</div>
|
||
<div class="mtime">21 min ago</div>
|
||
</div>
|
||
<div class="mbody">1,785 lines · 12.4 KB · checksum verified.</div>
|
||
</div>
|
||
<div class="mactions">
|
||
<button class="mbtn">Open</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="msg error">
|
||
<div class="mi"><i class="fa-solid fa-circle-xmark"></i></div>
|
||
<div>
|
||
<div style="display:flex;align-items:baseline;gap:.6rem">
|
||
<div class="mtitle">WiFi: not connected</div>
|
||
<div class="mtime">1 h ago</div>
|
||
</div>
|
||
<div class="mbody">Falling back to wired ethernet. SSID <span class="mono">workshop-2g</span> last seen 53 min ago.</div>
|
||
</div>
|
||
<div class="mactions">
|
||
<button class="mbtn">Network…</button>
|
||
<button class="mbtn">Mute</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Indicators sub-panel -->
|
||
<div class="indicators" data-sub="indicators">
|
||
<div class="ind">
|
||
<div class="ind-label">Spindle Load</div>
|
||
<div class="ind-val">0 %</div>
|
||
<div class="ind-state"><span class="dot" style="background:#16a34a"></span> idle</div>
|
||
<div class="progress"><div style="width:0%"></div></div>
|
||
</div>
|
||
<div class="ind">
|
||
<div class="ind-label">Spindle Temp</div>
|
||
<div class="ind-val">24 °C</div>
|
||
<div class="ind-state"><span class="dot" style="background:#16a34a"></span> nominal</div>
|
||
<div class="progress"><div style="width:24%"></div></div>
|
||
</div>
|
||
<div class="ind">
|
||
<div class="ind-label">Driver Voltage</div>
|
||
<div class="ind-val">48.1 V</div>
|
||
<div class="ind-state"><span class="dot" style="background:#16a34a"></span> ok</div>
|
||
</div>
|
||
<div class="ind">
|
||
<div class="ind-label">Coolant</div>
|
||
<div class="ind-val">OFF</div>
|
||
<div class="ind-state"><span class="dot" style="background:#94a3b8"></span> standby</div>
|
||
</div>
|
||
|
||
<div class="ind">
|
||
<div class="ind-label">Limit X</div>
|
||
<div class="ind-val" style="color:#16a34a">CLEAR</div>
|
||
<div class="ind-state"><span class="dot" style="background:#16a34a"></span> ok</div>
|
||
</div>
|
||
<div class="ind">
|
||
<div class="ind-label">Limit Y</div>
|
||
<div class="ind-val" style="color:#16a34a">CLEAR</div>
|
||
<div class="ind-state"><span class="dot" style="background:#16a34a"></span> ok</div>
|
||
</div>
|
||
<div class="ind">
|
||
<div class="ind-label">Limit Z</div>
|
||
<div class="ind-val" style="color:#dc2626">BLOCKED</div>
|
||
<div class="ind-state"><span class="dot" style="background:#dc2626"></span> over-travel</div>
|
||
</div>
|
||
<div class="ind">
|
||
<div class="ind-label">Probe</div>
|
||
<div class="ind-val">OPEN</div>
|
||
<div class="ind-state"><span class="dot" style="background:#94a3b8"></span> not contacted</div>
|
||
</div>
|
||
|
||
<div class="ind">
|
||
<div class="ind-label">E-Stop</div>
|
||
<div class="ind-val" style="color:#16a34a">RELEASED</div>
|
||
<div class="ind-state"><span class="dot" style="background:#16a34a"></span> safe</div>
|
||
</div>
|
||
<div class="ind">
|
||
<div class="ind-label">Door</div>
|
||
<div class="ind-val">CLOSED</div>
|
||
<div class="ind-state"><span class="dot" style="background:#16a34a"></span> ok</div>
|
||
</div>
|
||
<div class="ind">
|
||
<div class="ind-label">Air Pressure</div>
|
||
<div class="ind-val">6.2 bar</div>
|
||
<div class="ind-state"><span class="dot" style="background:#16a34a"></span> ok</div>
|
||
<div class="progress"><div style="width:62%"></div></div>
|
||
</div>
|
||
<div class="ind">
|
||
<div class="ind-label">Vacuum</div>
|
||
<div class="ind-val">−0.81 bar</div>
|
||
<div class="ind-state"><span class="dot" style="background:#16a34a"></span> hold</div>
|
||
<div class="progress"><div style="width:81%"></div></div>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ============= SETTINGS ============= -->
|
||
<div class="panel" data-panel="settings" style="padding:0;gap:0">
|
||
<div class="settings active" style="padding:18px">
|
||
<div class="set-side">
|
||
<div class="set-item active"><i class="fa-solid fa-display"></i> Display & Units</div>
|
||
<div class="set-item"><i class="fa-solid fa-arrows-up-down-left-right"></i> Motion</div>
|
||
<div class="set-item"><i class="fa-solid fa-bolt"></i> Spindle</div>
|
||
<div class="set-item"><i class="fa-solid fa-shield-halved"></i> Safety / Soft-limits</div>
|
||
<div class="set-item"><i class="fa-solid fa-network-wired"></i> Network</div>
|
||
<div class="set-item"><i class="fa-solid fa-video"></i> Camera</div>
|
||
<div class="set-item"><i class="fa-solid fa-keyboard"></i> Macros</div>
|
||
<div class="set-item"><i class="fa-solid fa-circle-info"></i> About</div>
|
||
</div>
|
||
<div class="set-content">
|
||
<div class="set-card">
|
||
<div class="set-title">Display & Units</div>
|
||
<div class="set-row">
|
||
<div>
|
||
<div class="label">Display Units</div>
|
||
<div class="desc">Position, feed and dimensions throughout the UI.</div>
|
||
</div>
|
||
<div><div class="step-seg" style="display:inline-flex"><button class="active">METRIC</button><button>IMPERIAL</button></div></div>
|
||
<div></div>
|
||
</div>
|
||
<div class="set-row">
|
||
<div>
|
||
<div class="label">Decimal places</div>
|
||
<div class="desc">Position readout precision.</div>
|
||
</div>
|
||
<div><input class="set-input" value="3" /></div>
|
||
<div class="val">0–4</div>
|
||
</div>
|
||
<div class="set-row">
|
||
<div>
|
||
<div class="label">Pulse-dot animation</div>
|
||
<div class="desc">Animate status badges (ready, idle, alarm).</div>
|
||
</div>
|
||
<div><div class="set-toggle on"></div></div>
|
||
<div></div>
|
||
</div>
|
||
<div class="set-row">
|
||
<div>
|
||
<div class="label">Theme</div>
|
||
<div class="desc">Pick a tile finish.</div>
|
||
</div>
|
||
<div><span class="file-select"><i class="fa-solid fa-palette" style="color:#64748b"></i> V09 · Flat soft slate <i class="fa-solid fa-chevron-down caret"></i></span></div>
|
||
<div></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="set-card">
|
||
<div class="set-title">Network</div>
|
||
<div class="set-row">
|
||
<div>
|
||
<div class="label">IP Address</div>
|
||
<div class="desc">Wired ethernet, DHCP.</div>
|
||
</div>
|
||
<div><span class="mono" style="font-size:1.05rem;font-weight:700">10.1.10.55</span></div>
|
||
<div><button class="mbtn">Edit</button></div>
|
||
</div>
|
||
<div class="set-row">
|
||
<div>
|
||
<div class="label">WiFi</div>
|
||
<div class="desc">Wireless network connection.</div>
|
||
</div>
|
||
<div><span class="chip chip-red"><i class="fa-solid fa-wifi"></i> Not connected</span></div>
|
||
<div><button class="mbtn primary">Configure</button></div>
|
||
</div>
|
||
<div class="set-row">
|
||
<div>
|
||
<div class="label">Hostname</div>
|
||
<div class="desc">Used in mDNS / Bonjour discovery.</div>
|
||
</div>
|
||
<div><input class="set-input" value="onefinity-shop.local" style="width:300px" /></div>
|
||
<div></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<script>
|
||
// ----- Build G-code list -----
|
||
const gcodeLines = [
|
||
[1,'G21','word'],[2,'; X = along blank, Z = tool entry from top, Y fixed','c'],
|
||
[3,'; Y fixed to blank center: 12.800','c'],[4,'; nominal rapid: 3200.0 mm/min','c'],
|
||
[5,'; stock top Z: -0.960','c'],[6,'; deepest allowed cut Z: -16.500','c'],
|
||
[7,'G21','word'],[8,'G90','word'],[9,'G0 Y12.800','word'],
|
||
[10,'G0 Z19.040','word'],[11,'; rough pass 1 radius=18.540','c'],
|
||
[12,'G0 X0.000','word'],[13,'G1 Z-0.710 F800.000','word cur'],
|
||
[14,'G1 Z-0.960 F200.000','word'],[15,'G4 P0.250','word'],
|
||
[16,'G1 X249.500 F200.000','word'],[17,'G4 P0.250','word'],
|
||
[18,'G0 Z19.040','word'],[19,'; rough pass 2 radius=17.540','c'],
|
||
[20,'G0 X0.000','word'],[21,'G1 Z-1.710 F800.000','word'],
|
||
[22,'G1 Z-1.960 F200.000','word'],[23,'G4 P0.250','word'],
|
||
[24,'G1 X249.500 F200.000','word'],[25,'G4 P0.250','word'],
|
||
[26,'G0 Z19.040','word'],[27,'; rough pass 3 radius=16.540','c'],
|
||
[28,'G0 X0.000','word'],[29,'G1 Z-2.710 F800.000','word'],
|
||
[30,'G1 Z-2.960 F200.000','word'],[31,'G4 P0.250','word'],
|
||
[32,'G1 X249.500 F200.000','word'],[33,'G4 P0.250','word'],
|
||
[34,'G0 Z19.040','word'],[35,'; rough pass 4 radius=15.540','c'],
|
||
];
|
||
document.getElementById('gcode-list').innerHTML = gcodeLines.map(([n,t,cls])=>{
|
||
const isComment = cls.includes('c');
|
||
const isCur = cls.includes('cur');
|
||
const cls2 = 'gline' + (isCur?' cur':'');
|
||
const inner = isComment ? `<span class="gcomment">${t}</span>` : `<span class="gword">${t}</span>`;
|
||
return `<div class="${cls2}"><span class="gn">${n}</span><span>${inner}</span></div>`;
|
||
}).join('');
|
||
|
||
// ----- Top tab switching (Control / Program / Settings) -----
|
||
document.querySelectorAll('.ktab').forEach(b=>{
|
||
b.addEventListener('click', ()=>{
|
||
const target = b.dataset.target;
|
||
document.querySelectorAll('.ktab').forEach(x=>x.classList.remove('active'));
|
||
b.classList.add('active');
|
||
document.querySelectorAll('.panel').forEach(p=>p.classList.remove('active'));
|
||
document.querySelector(`.panel[data-panel="${target}"]`).classList.add('active');
|
||
applyScale();
|
||
});
|
||
});
|
||
|
||
// ----- Console sub-tab switching (MDI / Messages / Indicators) -----
|
||
function showSub(name){
|
||
document.querySelectorAll('.ptab').forEach(x=>x.classList.toggle('active', x.dataset.ptab===name));
|
||
document.querySelectorAll('[data-sub]').forEach(s=>{
|
||
const on = s.dataset.sub===name;
|
||
if(s.classList.contains('messages') || s.classList.contains('indicators') || s.classList.contains('mdi')){
|
||
s.classList.toggle('active', on);
|
||
}
|
||
});
|
||
}
|
||
document.querySelectorAll('.ptab').forEach(b=>{
|
||
b.addEventListener('click', ()=>{ showSub(b.dataset.ptab); });
|
||
});
|
||
// Default Console sub: MDI active
|
||
document.querySelectorAll('.messages[data-sub], .indicators[data-sub]').forEach(s=>s.classList.remove('active'));
|
||
|
||
// ----- Scaling -----
|
||
const stage = document.getElementById('stage');
|
||
const scaler = document.getElementById('scaler');
|
||
const viewport = document.getElementById('viewport');
|
||
const fitBtn = document.getElementById('fitBtn');
|
||
const oneToOne = document.getElementById('oneToOne');
|
||
const scaleInfo = document.getElementById('scaleInfo');
|
||
let mode = 'fit';
|
||
function activeKioskHeight(){
|
||
const m = document.querySelector('.kiosk');
|
||
return m ? Math.max(1080, m.offsetHeight) : 1080;
|
||
}
|
||
function applyScale(){
|
||
let s;
|
||
if(mode==='1:1'){
|
||
s = 1; scaleInfo.textContent = '100% · 1920px wide';
|
||
} else {
|
||
const sw = stage.clientWidth - 32;
|
||
s = Math.min(sw/1920, 1);
|
||
scaleInfo.textContent = Math.round(s*100) + '% · 1920px wide';
|
||
}
|
||
const h = activeKioskHeight();
|
||
scaler.style.transform = `scale(${s})`;
|
||
viewport.style.width = (1920 * s) + 'px';
|
||
viewport.style.height = (h * s) + 'px';
|
||
}
|
||
window.addEventListener('resize', applyScale);
|
||
fitBtn.addEventListener('click', ()=>{ mode='fit'; fitBtn.classList.add('on'); oneToOne.classList.remove('on'); applyScale(); });
|
||
oneToOne.addEventListener('click', ()=>{ mode='1:1'; oneToOne.classList.add('on'); fitBtn.classList.remove('on'); applyScale(); });
|
||
applyScale();
|
||
</script>
|
||
</body>
|
||
</html>
|