Tokens (light + dark, neumorphism, 54/54 WCAG AA contrast pairs), the self-hosted JetBrains Mono fonts, and interactive mockups of every screen across three iterations. Iteration 3 is final: phone, tablet and desktop tiers, device preview, and "empty spool weight" terminology. The build scripts regenerate the screens byte-identically from source. Implementation is tracked in Maestro feature UI Refresh - V3.0 (224b72a1-8d84-4970-97ea-5dafa4d8ea3b). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1483 lines
259 KiB
HTML
1483 lines
259 KiB
HTML
<!doctype html>
|
||
<html lang="en" data-theme="dark">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<title>Extrudex Devices</title>
|
||
<meta name="description" content="Every screen in phone and tablet frames.">
|
||
<script>
|
||
(function () { var t = null; try { t = localStorage.getItem('extrudex-v3-theme'); } catch (e) {}
|
||
var q = /[?&]theme=(light|dark)/.exec(location.search); if (q) t = q[1];
|
||
if (t !== 'light' && t !== 'dark') t = window.matchMedia && matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark';
|
||
document.documentElement.setAttribute('data-theme', t); })();
|
||
</script>
|
||
<style>
|
||
/*
|
||
* CubeCraftLabs Design System v3 (proposal): Soft UI / Neumorphism
|
||
*
|
||
* Built on Design System v2.0 (http://pages.ccl/design-v2.0/, verified
|
||
* identical to Extrudex web/src/theme.css at dev 321ce0d on 2026-09-18).
|
||
*
|
||
* Structure
|
||
* 1. Font faces JetBrains Mono, self-hosted woff2, no CDN
|
||
* 2. v2.0 base scale kept verbatim, so v2 references still resolve
|
||
* 3. v3 theme tokens dark (default, as in v2.0) and light (new)
|
||
* 4. Neumorphism surfaces, raised/inset shadow pairs, intensity, radius
|
||
* 5. State tokens focus, pressed, disabled, error
|
||
* 6. Reference components used by the mockups
|
||
*
|
||
* Accessibility rules this file enforces:
|
||
* - Shadows are decoration. Every control also has a border at 3:1 or more
|
||
* against its surface (WCAG 1.4.11), so it stays visible with shadows off.
|
||
* - Focus, pressed, disabled and error each change something besides shadow
|
||
* direction: the outline, the fill plus a check glyph, a dashed border, or
|
||
* a red border plus an icon plus a message.
|
||
* - The contrast table is in design-system-v3.md.
|
||
*
|
||
* Theme selection: dark is the default because v2.0 is dark. Light applies
|
||
* when the OS prefers it and nothing is forced, or with data-theme="light" on
|
||
* <html>. data-theme="dark" forces dark.
|
||
*/
|
||
|
||
/* ── 1. Font faces ──────────────────────────────────────────────────────── */
|
||
/* The same files @fontsource/jetbrains-mono 5.3.0 ships (the app's dependency). */
|
||
@font-face { font-family: 'JetBrains Mono'; font-style: normal; font-display: swap; font-weight: 400; src: url('data:font/woff2;base64,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') format('woff2'); }
|
||
@font-face { font-family: 'JetBrains Mono'; font-style: normal; font-display: swap; font-weight: 500; src: url('data:font/woff2;base64,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') format('woff2'); }
|
||
@font-face { font-family: 'JetBrains Mono'; font-style: normal; font-display: swap; font-weight: 700; src: url('data:font/woff2;base64,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') format('woff2'); }
|
||
@font-face { font-family: 'JetBrains Mono'; font-style: normal; font-display: swap; font-weight: 800; src: url('data:font/woff2;base64,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') format('woff2'); }
|
||
|
||
/* ── 2. v2.0 base scale (unchanged) ─────────────────────────────────────── */
|
||
:root {
|
||
--ccl-charcoal: #2c2c30;
|
||
--ccl-charcoal-2: #36363a;
|
||
--ccl-charcoal-3: #1b1b1e;
|
||
--ccl-gold: #8f7ae0;
|
||
--ccl-gold-dim: #7a63cc;
|
||
--ccl-text: #f4f4f6;
|
||
--ccl-text-muted: #b2b2ba;
|
||
--ccl-border: #3d3d44;
|
||
--ccl-border-2: #4e4e57;
|
||
--ccl-green: #a9d88b;
|
||
--ccl-red: #f08b93;
|
||
--ccl-white: #ffffff;
|
||
--ccl-ink: #2a2431;
|
||
|
||
--ccl-grey: var(--ccl-charcoal);
|
||
--ccl-grey-2: var(--ccl-charcoal-2);
|
||
--ccl-grey-3: var(--ccl-charcoal-3);
|
||
--ccl-purple: var(--ccl-gold);
|
||
--ccl-purple-dim: var(--ccl-gold-dim);
|
||
--ccl-purple-wash: rgba(143, 122, 224, 0.12);
|
||
|
||
/* v3 additions to the base scale: tints derived from the v2.0 hues */
|
||
--ccl-gold-light: #a99af0; /* accent lifted to pass 4.5:1 as text on charcoal */
|
||
--ccl-violet-deep: #5b43b8; /* accent darkened to pass 4.5:1 on the light surface */
|
||
--ccl-charcoal-inset:#232327; /* between charcoal and charcoal-3: dark wells */
|
||
--ccl-amber: #e8c170; /* new: warning (v2.0 had none) */
|
||
--ccl-blue: #8fc3f0; /* new: informational / provenance */
|
||
--ccl-mist: #e4e2ea; /* light page: charcoal's hue, inverted */
|
||
--ccl-mist-2: #f1f0f5; /* light high surface */
|
||
--ccl-mist-3: #d9d6e1; /* light inset */
|
||
|
||
/* Typography, spacing and motion: unchanged from v2.0 */
|
||
--font-mono: 'JetBrains Mono', ui-monospace, 'SF Mono', Menlo, Consolas, monospace;
|
||
--font-body: var(--font-mono);
|
||
--font-display: var(--font-mono);
|
||
--fw-regular: 400; --fw-medium: 500; --fw-bold: 700; --fw-display: 800;
|
||
--fs-caption: 11px; --fs-label: 12px; --fs-body: 14px; --fs-body-lg: 15px;
|
||
--fs-h3: 18px; --fs-h2: 22px; --fs-h1: 28px; --fs-display: 40px;
|
||
--lh-tight: 1.15; --lh-snug: 1.35; --lh-body: 1.7;
|
||
--tracking-wordmark: 0.02em; --tracking-caps: 0.08em; --tracking-caps-wide: 0.14em;
|
||
--space-1: 4px; --space-2: 8px; --space-3: 12px; --space-4: 16px; --space-5: 20px;
|
||
--space-6: 24px; --space-8: 32px; --space-10: 40px; --space-14: 56px; --space-16: 64px;
|
||
--ease-out: cubic-bezier(0.2, 0, 0, 1);
|
||
--dur-fast: 100ms; --dur-base: 160ms;
|
||
--dur-press: 60ms; /* v3: pressing reacts faster than hover */
|
||
--scrollbar-width: 6px;
|
||
|
||
/* ── 4a. Radius scale (v3; replaces v2.0's "0 is the rule") ── */
|
||
--radius-none: 0px; /* tables, code, grid lines: square stays available */
|
||
--radius-xs: 4px; /* chips, swatches, inline code */
|
||
--radius-sm: 8px; /* inputs, small buttons */
|
||
--radius-md: 12px; /* buttons, list rows */
|
||
--radius-lg: 18px; /* cards, panels */
|
||
--radius-xl: 26px; /* dialogs, the app shell */
|
||
--radius-pill: 999px; /* toggles, progress tracks, badges */
|
||
|
||
/* ── 4b. Shadow geometry: distance + blur per intensity level ── */
|
||
--nm-d-1: 2px; --nm-b-1: 5px; /* level 1: chips, small controls */
|
||
--nm-d-2: 4px; --nm-b-2: 10px; /* level 2: buttons, rows */
|
||
--nm-d-3: 8px; --nm-b-3: 18px; /* level 3: cards, panels */
|
||
--nm-d-4: 14px; --nm-b-4: 30px; /* level 4: dialogs */
|
||
|
||
/* Focus ring geometry: identical in both themes */
|
||
--focus-width: 2px;
|
||
--focus-offset: 3px;
|
||
--border-control-width: 1px;
|
||
--border-error-width: 2px;
|
||
}
|
||
|
||
/* ── 3. Theme tokens: DARK (default) ───────────────────────────────────── */
|
||
:root,
|
||
:root[data-theme="dark"] {
|
||
color-scheme: dark;
|
||
|
||
/* Surfaces */
|
||
--surface-page: var(--ccl-charcoal); /* #2c2c30 */
|
||
--surface-raised: var(--ccl-charcoal); /* v3: same as page; the lift comes from the shadow */
|
||
--surface-high: var(--ccl-charcoal-2); /* #36363a dialogs, menus */
|
||
--surface-inset: var(--ccl-charcoal-inset); /* #232327 inputs, wells, progress tracks */
|
||
--surface-code: var(--ccl-charcoal-3);
|
||
|
||
/* Text */
|
||
--text-primary: var(--ccl-text); /* 12.66:1 on page */
|
||
--text-secondary: var(--ccl-text-muted); /* 6.60:1 on page */
|
||
--text-accent: var(--ccl-gold-light); /* 5.67:1 on page */
|
||
--text-on-accent: var(--ccl-ink); /* on --accent-fill */
|
||
--text-on-gold: var(--ccl-ink);
|
||
|
||
/* Accent */
|
||
--accent: var(--ccl-gold); /* brand hue, for large marks only */
|
||
--accent-hover: var(--ccl-gold-dim);
|
||
--accent-fill: var(--ccl-gold-light); /* primary button fill; ink on it: 6.14:1 */
|
||
--accent-fill-press: #9785e6;
|
||
--accent-wash: rgba(169, 154, 240, 0.10);
|
||
|
||
/* Status: the v2.0 hues plus two new ones */
|
||
--status-success: var(--ccl-green);
|
||
--status-error: var(--ccl-red);
|
||
--status-warning: var(--ccl-amber);
|
||
--status-info: var(--ccl-blue);
|
||
--wash-success: rgba(169, 216, 139, 0.10);
|
||
--wash-error: rgba(240, 139, 147, 0.10);
|
||
--wash-warning: rgba(232, 193, 112, 0.10);
|
||
--wash-info: rgba(143, 195, 240, 0.10);
|
||
|
||
/* Borders */
|
||
--border-hairline: var(--ccl-border); /* decorative card edges */
|
||
--border-strong: var(--ccl-border-2);
|
||
--border-control: #85858f; /* 1.4.11: 3.81:1 page, 4.29:1 inset, 3.29:1 high */
|
||
--focus-ring: var(--ccl-gold-light); /* 5.67:1 on page */
|
||
|
||
/* ── 4c. Neumorphism shadow pairs, DARK ──
|
||
Dark soft UI fails when the "dark" shadow is only a darker charcoal:
|
||
nothing on #2c2c30 is visibly darker than #1b1b1e. Four rules make it
|
||
work here:
|
||
a. The dark half is true black at high alpha, not a darker gray.
|
||
b. The light half is white at 4–7% alpha. It reads as sheen, not glow.
|
||
c. Raised surfaces add a 1px top rim light (inset, white 6%). The rim
|
||
stays visible even where the drop shadow merges into the background.
|
||
d. Every control has a 3:1 border anyway, so the shadow never carries
|
||
structure on its own. */
|
||
--nm-shadow-dark: rgba(0, 0, 0, 0.62);
|
||
--nm-shadow-light: rgba(255, 255, 255, 0.055);
|
||
--nm-rim: inset 0 1px 0 rgba(255, 255, 255, 0.06);
|
||
--nm-inset-dark: rgba(0, 0, 0, 0.55);
|
||
--nm-inset-light: rgba(255, 255, 255, 0.045);
|
||
--nm-raised-gradient: linear-gradient(145deg, #313136, #29292d);
|
||
}
|
||
|
||
/* ── 3. Theme tokens: LIGHT (new in v3) ────────────────────────────────── */
|
||
@media (prefers-color-scheme: light) {
|
||
:root:not([data-theme="dark"]) {
|
||
color-scheme: light;
|
||
--surface-page: var(--ccl-mist);
|
||
--surface-raised: var(--ccl-mist);
|
||
--surface-high: var(--ccl-mist-2);
|
||
--surface-inset: var(--ccl-mist-3);
|
||
--surface-code: #d2cfdb;
|
||
--text-primary: var(--ccl-ink);
|
||
--text-secondary: #55505f;
|
||
--text-accent: var(--ccl-violet-deep);
|
||
--text-on-accent: var(--ccl-white);
|
||
--text-on-gold: var(--ccl-white);
|
||
--accent: var(--ccl-gold);
|
||
--accent-hover: var(--ccl-gold-dim);
|
||
--accent-fill: var(--ccl-violet-deep);
|
||
--accent-fill-press: #4d37a3;
|
||
--accent-wash: rgba(91, 67, 184, 0.10);
|
||
--status-success: #2c6619;
|
||
--status-error: #a8202f;
|
||
--status-warning: #7c5100;
|
||
--status-info: #1d5e94;
|
||
--wash-success: rgba(44, 102, 25, 0.10);
|
||
--wash-error: rgba(168, 32, 47, 0.10);
|
||
--wash-warning: rgba(124, 81, 0, 0.10);
|
||
--wash-info: rgba(29, 94, 148, 0.10);
|
||
--border-hairline: #cfccd8;
|
||
--border-strong: #b9b5c6;
|
||
--border-control: #736e86;
|
||
--focus-ring: var(--ccl-violet-deep);
|
||
--nm-shadow-dark: rgba(88, 80, 120, 0.30);
|
||
--nm-shadow-light: rgba(255, 255, 255, 0.95);
|
||
--nm-rim: inset 0 1px 0 rgba(255, 255, 255, 0.7);
|
||
--nm-inset-dark: rgba(88, 80, 120, 0.28);
|
||
--nm-inset-light: rgba(255, 255, 255, 0.9);
|
||
--nm-raised-gradient: linear-gradient(145deg, #eceaf1, #dcd9e3);
|
||
}
|
||
}
|
||
:root[data-theme="light"] {
|
||
color-scheme: light;
|
||
--surface-page: var(--ccl-mist);
|
||
--surface-raised: var(--ccl-mist);
|
||
--surface-high: var(--ccl-mist-2);
|
||
--surface-inset: var(--ccl-mist-3);
|
||
--surface-code: #d2cfdb;
|
||
--text-primary: var(--ccl-ink);
|
||
--text-secondary: #55505f;
|
||
--text-accent: var(--ccl-violet-deep);
|
||
--text-on-accent: var(--ccl-white);
|
||
--text-on-gold: var(--ccl-white);
|
||
--accent: var(--ccl-gold);
|
||
--accent-hover: var(--ccl-gold-dim);
|
||
--accent-fill: var(--ccl-violet-deep);
|
||
--accent-fill-press: #4d37a3;
|
||
--accent-wash: rgba(91, 67, 184, 0.10);
|
||
--status-success: #2c6619;
|
||
--status-error: #a8202f;
|
||
--status-warning: #7c5100;
|
||
--status-info: #1d5e94;
|
||
--wash-success: rgba(44, 102, 25, 0.10);
|
||
--wash-error: rgba(168, 32, 47, 0.10);
|
||
--wash-warning: rgba(124, 81, 0, 0.10);
|
||
--wash-info: rgba(29, 94, 148, 0.10);
|
||
--border-hairline: #cfccd8;
|
||
--border-strong: #b9b5c6;
|
||
--border-control: #736e86;
|
||
--focus-ring: var(--ccl-violet-deep);
|
||
--nm-shadow-dark: rgba(88, 80, 120, 0.30);
|
||
--nm-shadow-light: rgba(255, 255, 255, 0.95);
|
||
--nm-rim: inset 0 1px 0 rgba(255, 255, 255, 0.7);
|
||
--nm-inset-dark: rgba(88, 80, 120, 0.28);
|
||
--nm-inset-light: rgba(255, 255, 255, 0.9);
|
||
--nm-raised-gradient: linear-gradient(145deg, #eceaf1, #dcd9e3);
|
||
}
|
||
|
||
/* ── 4d. Composed shadows: theme-agnostic, built from the pairs above ── */
|
||
:root {
|
||
--nm-raised-1: calc(var(--nm-d-1) * -1) calc(var(--nm-d-1) * -1) var(--nm-b-1) var(--nm-shadow-light),
|
||
var(--nm-d-1) var(--nm-d-1) var(--nm-b-1) var(--nm-shadow-dark), var(--nm-rim);
|
||
--nm-raised-2: calc(var(--nm-d-2) * -1) calc(var(--nm-d-2) * -1) var(--nm-b-2) var(--nm-shadow-light),
|
||
var(--nm-d-2) var(--nm-d-2) var(--nm-b-2) var(--nm-shadow-dark), var(--nm-rim);
|
||
--nm-raised-3: calc(var(--nm-d-3) * -1) calc(var(--nm-d-3) * -1) var(--nm-b-3) var(--nm-shadow-light),
|
||
var(--nm-d-3) var(--nm-d-3) var(--nm-b-3) var(--nm-shadow-dark), var(--nm-rim);
|
||
--nm-raised-4: calc(var(--nm-d-4) * -1) calc(var(--nm-d-4) * -1) var(--nm-b-4) var(--nm-shadow-light),
|
||
var(--nm-d-4) var(--nm-d-4) var(--nm-b-4) var(--nm-shadow-dark), var(--nm-rim);
|
||
--nm-inset-1: inset var(--nm-d-1) var(--nm-d-1) var(--nm-b-1) var(--nm-inset-dark),
|
||
inset calc(var(--nm-d-1) * -1) calc(var(--nm-d-1) * -1) var(--nm-b-1) var(--nm-inset-light);
|
||
--nm-inset-2: inset var(--nm-d-2) var(--nm-d-2) var(--nm-b-2) var(--nm-inset-dark),
|
||
inset calc(var(--nm-d-2) * -1) calc(var(--nm-d-2) * -1) var(--nm-b-2) var(--nm-inset-light);
|
||
--nm-flat: none;
|
||
/* v2.0 names kept so old rules still resolve */
|
||
--shadow-none: none;
|
||
--shadow-raised: var(--nm-raised-2);
|
||
/* Borders, v2.0 names */
|
||
--border-width: 1px;
|
||
--border: var(--border-width) solid var(--border-hairline);
|
||
--border-strong-rule: var(--border-width) solid var(--border-strong);
|
||
--border-accent: 1.5px solid var(--accent);
|
||
--scrollbar-thumb: var(--border-strong);
|
||
--scrollbar-thumb-hover: var(--accent);
|
||
}
|
||
|
||
/* ── 6. Reference components ────────────────────────────────────────────── */
|
||
*, *::before, *::after { box-sizing: border-box; }
|
||
html { background: var(--surface-page); }
|
||
body {
|
||
margin: 0;
|
||
background: var(--surface-page);
|
||
color: var(--text-primary);
|
||
font-family: var(--font-body);
|
||
font-size: var(--fs-body);
|
||
line-height: var(--lh-body);
|
||
-webkit-font-smoothing: antialiased;
|
||
}
|
||
@media (prefers-reduced-motion: reduce) { * { transition: none !important; } }
|
||
|
||
/* Surfaces */
|
||
.nm-raised { background: var(--nm-raised-gradient); box-shadow: var(--nm-raised-3); border: 1px solid var(--border-hairline); border-radius: var(--radius-lg); }
|
||
.nm-raised-sm { background: var(--surface-raised); box-shadow: var(--nm-raised-2); border: 1px solid var(--border-hairline); border-radius: var(--radius-md); }
|
||
.nm-inset { background: var(--surface-inset); box-shadow: var(--nm-inset-2); border: 1px solid var(--border-hairline); border-radius: var(--radius-md); }
|
||
.nm-dialog { background: var(--surface-high); box-shadow: var(--nm-raised-4); border: 1px solid var(--border-strong); border-radius: var(--radius-xl); }
|
||
|
||
/* Buttons
|
||
* rest raised-2 + 1px control border
|
||
* hover border takes the accent color
|
||
* pressed inset shadow + accent wash fill + accent border (+ ✓ on toggles)
|
||
* focus 2px solid outline, offset 3px, in the focus color
|
||
* disabled no shadow, dashed border, secondary text, not-allowed cursor
|
||
*/
|
||
.btn {
|
||
font: var(--fw-medium) var(--fs-body)/1 var(--font-body);
|
||
color: var(--text-primary);
|
||
background: var(--surface-raised);
|
||
border: var(--border-control-width) solid var(--border-control);
|
||
border-radius: var(--radius-md);
|
||
padding: 10px 16px;
|
||
min-height: 40px;
|
||
box-shadow: var(--nm-raised-2);
|
||
cursor: pointer;
|
||
display: inline-flex; align-items: center; gap: var(--space-2);
|
||
transition: box-shadow var(--dur-base) var(--ease-out), border-color var(--dur-fast), background var(--dur-fast);
|
||
}
|
||
.btn:hover { border-color: var(--text-accent); }
|
||
.btn:active, .btn[aria-pressed="true"], .btn.is-pressed {
|
||
box-shadow: var(--nm-inset-1);
|
||
background: var(--accent-wash);
|
||
border-color: var(--text-accent);
|
||
transition-duration: var(--dur-press);
|
||
}
|
||
.btn[aria-pressed="true"]::before, .btn.is-pressed::before { content: "✓"; font-weight: var(--fw-bold); color: var(--text-accent); }
|
||
.btn:focus-visible, .btn.is-focused, .input:focus-visible, .input.is-focused, .select:focus-visible, .select.is-focused {
|
||
outline: var(--focus-width) solid var(--focus-ring);
|
||
outline-offset: var(--focus-offset);
|
||
}
|
||
.btn:disabled, .btn.is-disabled {
|
||
box-shadow: none;
|
||
border-style: dashed;
|
||
color: var(--text-secondary);
|
||
background: transparent;
|
||
cursor: not-allowed;
|
||
}
|
||
.btn-primary { background: var(--accent-fill); color: var(--text-on-accent); border-color: var(--accent-fill); font-weight: var(--fw-bold); }
|
||
.btn-primary:hover { border-color: var(--text-primary); }
|
||
.btn-primary:active, .btn-primary.is-pressed { background: var(--accent-fill-press); color: var(--text-on-accent); border-color: var(--text-primary); }
|
||
.btn-primary.is-pressed::before { color: var(--text-on-accent); }
|
||
.btn-danger { color: var(--status-error); border-color: var(--status-error); }
|
||
.btn-sm { min-height: 32px; padding: 6px 12px; font-size: var(--fs-label); border-radius: var(--radius-sm); box-shadow: var(--nm-raised-1); }
|
||
.btn-icon { width: 40px; padding: 0; justify-content: center; }
|
||
|
||
/* Inputs: always inset, always bordered */
|
||
.input, .select {
|
||
font: var(--fw-regular) var(--fs-body)/1.4 var(--font-body);
|
||
color: var(--text-primary);
|
||
background: var(--surface-inset);
|
||
border: var(--border-control-width) solid var(--border-control);
|
||
border-radius: var(--radius-sm);
|
||
box-shadow: var(--nm-inset-1);
|
||
padding: 9px 12px;
|
||
min-height: 40px;
|
||
width: 100%;
|
||
}
|
||
.input::placeholder { color: var(--text-secondary); }
|
||
.input:disabled, .input.is-disabled, .select:disabled {
|
||
box-shadow: none; border-style: dashed; color: var(--text-secondary); background: transparent;
|
||
}
|
||
/* Error: 2px red border + icon in the label + message. Never color alone. */
|
||
.field.has-error .input, .field.has-error .select, .input.is-error {
|
||
border: var(--border-error-width) solid var(--status-error);
|
||
padding: 8px 11px;
|
||
}
|
||
.field-error {
|
||
display: flex; gap: var(--space-2); align-items: flex-start;
|
||
color: var(--status-error); font-size: var(--fs-label); line-height: var(--lh-snug); margin-top: var(--space-1);
|
||
}
|
||
.field-error::before { content: "⚠"; font-weight: var(--fw-bold); }
|
||
|
||
/* Chips / badges: text + wash + border + glyph */
|
||
.chip {
|
||
display: inline-flex; align-items: center; gap: 6px;
|
||
font-size: var(--fs-caption); font-weight: var(--fw-bold);
|
||
letter-spacing: var(--tracking-caps); text-transform: uppercase;
|
||
padding: 2px 9px; border-radius: var(--radius-pill);
|
||
border: 1px solid currentColor; line-height: 1.6;
|
||
white-space: nowrap;
|
||
}
|
||
.chip-ok { color: var(--status-success); background: var(--wash-success); }
|
||
.chip-err { color: var(--status-error); background: var(--wash-error); }
|
||
.chip-warn { color: var(--status-warning); background: var(--wash-warning); }
|
||
.chip-info { color: var(--status-info); background: var(--wash-info); }
|
||
.chip-accent { color: var(--text-accent); background: var(--accent-wash); }
|
||
.chip-neutral { color: var(--text-secondary); background: transparent; }
|
||
.chip-dashed { border-style: dashed; }
|
||
|
||
/* Progress track (inset) + fill (raised) */
|
||
.track { height: 10px; border-radius: var(--radius-pill); background: var(--surface-inset); box-shadow: var(--nm-inset-1); border: 1px solid var(--border-hairline); overflow: hidden; }
|
||
.track > span { display: block; height: 100%; border-radius: var(--radius-pill); background: var(--text-accent); }
|
||
.track.is-low > span { background: var(--status-error); }
|
||
|
||
/* Swatch: 1–3 colors, a hairline so white/black filament shows on either theme */
|
||
.swatch { display: inline-flex; width: 26px; height: 26px; border-radius: 50%; overflow: hidden; border: 1px solid var(--border-control); box-shadow: var(--nm-raised-1); flex: none; }
|
||
.swatch > i { flex: 1; }
|
||
.swatch-sm { width: 18px; height: 18px; }
|
||
|
||
/* Text helpers */
|
||
.muted { color: var(--text-secondary); }
|
||
.accent { color: var(--text-accent); }
|
||
.caps { text-transform: uppercase; letter-spacing: var(--tracking-caps-wide); font-size: var(--fs-caption); font-weight: var(--fw-bold); }
|
||
.num { font-variant-numeric: tabular-nums; }
|
||
|
||
/* ── Mockup app shell (shared by all four screens) ── */
|
||
.shell { display: grid; grid-template-columns: 232px minmax(0, 1fr); gap: var(--space-6); max-width: 1440px; margin: 0 auto; padding: var(--space-6); min-height: 100vh; }
|
||
.rail { position: sticky; top: var(--space-6); align-self: start; padding: var(--space-5); display: flex; flex-direction: column; gap: var(--space-5); }
|
||
.wordmark { font-weight: var(--fw-display); font-size: var(--fs-h3); letter-spacing: var(--tracking-wordmark); display: flex; align-items: center; gap: var(--space-2); }
|
||
.wordmark-mark { width: 30px; height: 30px; border-radius: 10px; background: var(--accent-fill); color: var(--text-on-accent); display: grid; place-items: center; font-size: 15px; box-shadow: var(--nm-raised-1); border: 1px solid var(--border-control); }
|
||
.nav { display: flex; flex-direction: column; gap: var(--space-2); }
|
||
.nav a { display: flex; align-items: center; gap: var(--space-3); padding: 10px 12px; border-radius: var(--radius-md); color: var(--text-primary); text-decoration: none; border: 1px solid transparent; font-weight: var(--fw-medium); }
|
||
.nav a:hover { border-color: var(--border-control); }
|
||
.nav a:focus-visible { outline: var(--focus-width) solid var(--focus-ring); outline-offset: var(--focus-offset); }
|
||
.nav a[aria-current="page"] { background: var(--surface-inset); box-shadow: var(--nm-inset-1); border-color: var(--text-accent); color: var(--text-accent); font-weight: var(--fw-bold); }
|
||
.nav svg { width: 18px; height: 18px; flex: none; }
|
||
.nav .nav-new { margin-left: auto; }
|
||
.rail-foot { display: flex; flex-direction: column; gap: var(--space-3); border-top: 1px solid var(--border-hairline); padding-top: var(--space-4); }
|
||
.seg { display: inline-flex; padding: 4px; gap: 4px; border-radius: var(--radius-pill); background: var(--surface-inset); box-shadow: var(--nm-inset-1); border: 1px solid var(--border-control); }
|
||
.seg button { font: var(--fw-medium) var(--fs-label)/1 var(--font-body); color: var(--text-secondary); background: transparent; border: 1px solid transparent; border-radius: var(--radius-pill); padding: 7px 12px; cursor: pointer; display: inline-flex; gap: 6px; align-items: center; }
|
||
.seg button[aria-pressed="true"] { background: var(--surface-raised); color: var(--text-primary); box-shadow: var(--nm-raised-1); border-color: var(--border-control); font-weight: var(--fw-bold); }
|
||
.seg button[aria-pressed="true"]::before { content: "✓"; color: var(--text-accent); }
|
||
.seg button:focus-visible { outline: var(--focus-width) solid var(--focus-ring); outline-offset: 2px; }
|
||
.who { font-size: var(--fs-label); }
|
||
|
||
main { min-width: 0; display: flex; flex-direction: column; gap: var(--space-6); padding-bottom: var(--space-16); }
|
||
.proposal { display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-4); align-items: center; font-size: var(--fs-label); color: var(--text-secondary); padding: var(--space-3) var(--space-4); }
|
||
.page-head { display: flex; flex-wrap: wrap; gap: var(--space-4); align-items: flex-end; justify-content: space-between; }
|
||
.page-head h1 { margin: 0; font-size: var(--fs-h1); line-height: var(--lh-tight); font-weight: var(--fw-display); }
|
||
.page-head p { margin: var(--space-1) 0 0; }
|
||
.actions { display: flex; flex-wrap: wrap; gap: var(--space-3); align-items: center; }
|
||
h2.section { font-size: var(--fs-label); text-transform: uppercase; letter-spacing: var(--tracking-caps-wide); color: var(--text-secondary); margin: 0 0 var(--space-3); font-weight: var(--fw-bold); display: flex; align-items: center; gap: var(--space-2); }
|
||
.card { padding: var(--space-5); }
|
||
.card h3 { margin: 0; font-size: var(--fs-body-lg); }
|
||
.grid { display: grid; gap: var(--space-5); }
|
||
.stack { display: flex; flex-direction: column; gap: var(--space-3); }
|
||
.row { display: flex; gap: var(--space-3); align-items: center; flex-wrap: wrap; }
|
||
.spacer { flex: 1; }
|
||
a { color: var(--text-accent); }
|
||
a:focus-visible { outline: var(--focus-width) solid var(--focus-ring); outline-offset: 2px; border-radius: 4px; }
|
||
small, .small { font-size: var(--fs-label); }
|
||
.tiny { font-size: var(--fs-caption); }
|
||
code { font-family: var(--font-mono); font-size: 0.92em; background: var(--surface-inset); padding: 1px 5px; border-radius: var(--radius-xs); border: 1px solid var(--border-hairline); }
|
||
.kbd-note { font-size: var(--fs-caption); color: var(--text-secondary); }
|
||
|
||
/* Data tables: square, numbers right-aligned, inside a soft card */
|
||
.table-wrap { overflow-x: auto; margin: 0 calc(var(--space-5) * -1); padding: 0 var(--space-5); }
|
||
table.tbl { width: 100%; border-collapse: collapse; border-radius: var(--radius-none); font-size: 13px; }
|
||
.tbl th { text-align: left; font-size: var(--fs-caption); text-transform: uppercase; letter-spacing: var(--tracking-caps); color: var(--text-secondary); font-weight: var(--fw-bold); padding: 10px 10px; border-bottom: 1px solid var(--border-strong); white-space: nowrap; }
|
||
.tbl td { padding: 11px 10px; border-bottom: 1px solid var(--border-hairline); vertical-align: middle; }
|
||
.tbl tr:last-child td { border-bottom: 0; }
|
||
.tbl .r { text-align: right; font-variant-numeric: tabular-nums; }
|
||
.tbl tbody tr:hover td { background: var(--accent-wash); }
|
||
|
||
/* Provenance marker used across screens */
|
||
.src { display: inline-flex; gap: 4px; align-items: center; font-size: var(--fs-caption); font-weight: var(--fw-bold); letter-spacing: var(--tracking-caps); text-transform: uppercase; }
|
||
.dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; flex: none; }
|
||
|
||
/* Stat tiles */
|
||
.stats { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--space-5); }
|
||
.stat { padding: var(--space-5); display: flex; flex-direction: column; gap: var(--space-1); }
|
||
.stat-value { font-size: 30px; font-weight: var(--fw-display); line-height: var(--lh-tight); font-variant-numeric: tabular-nums; }
|
||
.stat-label { font-size: var(--fs-label); color: var(--text-secondary); text-transform: uppercase; letter-spacing: var(--tracking-caps); font-weight: var(--fw-bold); }
|
||
.stat-foot { font-size: var(--fs-label); color: var(--text-secondary); }
|
||
.stat.warn .stat-value { color: var(--status-warning); }
|
||
|
||
/* Modal backdrop for dialogs */
|
||
.scrim { position: fixed; inset: 0; background: rgba(10, 10, 14, 0.55); }
|
||
:root[data-theme="light"] .scrim { background: rgba(60, 55, 80, 0.35); }
|
||
|
||
/* Responsive */
|
||
@media (max-width: 1100px) { .stats { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
|
||
@media (max-width: 860px) {
|
||
.shell { grid-template-columns: 1fr; padding: var(--space-4); gap: var(--space-4); }
|
||
.rail { position: static; flex-direction: row; flex-wrap: wrap; align-items: center; padding: var(--space-3) var(--space-4); gap: var(--space-3); }
|
||
.nav { flex-direction: row; flex-wrap: wrap; gap: var(--space-1); }
|
||
.nav a { padding: 8px 10px; }
|
||
.nav a span.lbl { display: none; }
|
||
.nav a[aria-current="page"] span.lbl { display: inline; }
|
||
.nav .nav-new { display: none; }
|
||
.rail-foot { border: 0; padding: 0; flex-direction: row; margin-left: auto; }
|
||
.who { display: none; }
|
||
}
|
||
@media (max-width: 520px) {
|
||
.stats { grid-template-columns: 1fr 1fr; gap: var(--space-3); }
|
||
.stat { padding: var(--space-4); }
|
||
.stat-value { font-size: 22px; }
|
||
.page-head h1 { font-size: var(--fs-h2); }
|
||
}
|
||
|
||
a.btn { text-decoration: none; }
|
||
.th-note { display: block; margin-top: 4px; }
|
||
|
||
/* ── Design System v3.1 components (iteration 2) ─────────────────────────
|
||
* Built only from design-system-v3.css tokens. No new colors. */
|
||
|
||
/* Shell */
|
||
.shell { display: grid; grid-template-columns: 224px minmax(0, 1fr); gap: var(--space-6); max-width: 1480px; margin: 0 auto; padding: var(--space-5) var(--space-6); min-height: 100vh; }
|
||
.rail { position: sticky; top: var(--space-5); align-self: start; padding: var(--space-4); display: flex; flex-direction: column; gap: var(--space-4); max-height: calc(100vh - 40px); }
|
||
.wordmark { font-weight: var(--fw-display); font-size: var(--fs-h3); display: flex; align-items: center; gap: var(--space-2); padding: 4px 6px; }
|
||
.wordmark-mark { width: 30px; height: 30px; border-radius: 10px; background: var(--accent-fill); color: var(--text-on-accent); display: grid; place-items: center; font-size: 15px; border: 1px solid var(--border-control); box-shadow: var(--nm-raised-1); }
|
||
.nav-group { display: flex; flex-direction: column; gap: 4px; }
|
||
.nav-label { font-size: var(--fs-caption); text-transform: uppercase; letter-spacing: var(--tracking-caps-wide); color: var(--text-secondary); font-weight: var(--fw-bold); padding: 8px 12px 2px; }
|
||
.nav a { display: flex; align-items: center; gap: var(--space-3); padding: 9px 12px; border-radius: var(--radius-md); color: var(--text-primary); text-decoration: none; border: 1px solid transparent; font-weight: var(--fw-medium); position: relative; }
|
||
.nav a:hover { border-color: var(--border-control); }
|
||
.nav a:focus-visible { outline: var(--focus-width) solid var(--focus-ring); outline-offset: 2px; }
|
||
.nav a[aria-current="page"] { background: var(--surface-inset); box-shadow: var(--nm-inset-1); border-color: var(--text-accent); color: var(--text-accent); font-weight: var(--fw-bold); }
|
||
.nav svg { width: 18px; height: 18px; flex: none; }
|
||
.nav .count { margin-left: auto; min-width: 22px; height: 20px; padding: 0 6px; border-radius: var(--radius-pill); font-size: var(--fs-caption); font-weight: var(--fw-bold); display: inline-grid; place-items: center; border: 1px solid currentColor; }
|
||
.count-warn { color: var(--status-warning); background: var(--wash-warning); }
|
||
.count-err { color: var(--status-error); background: var(--wash-error); }
|
||
.rail-foot { margin-top: auto; display: flex; flex-direction: column; gap: var(--space-3); border-top: 1px solid var(--border-hairline); padding-top: var(--space-3); }
|
||
|
||
/* Top bar: search, freshness, context */
|
||
.topbar { display: flex; align-items: center; gap: var(--space-4); flex-wrap: wrap; }
|
||
.search-trigger { flex: 1; min-width: 220px; max-width: 560px; display: flex; align-items: center; gap: var(--space-3); padding: 9px 14px; border-radius: var(--radius-pill); background: var(--surface-inset); box-shadow: var(--nm-inset-1); border: 1px solid var(--border-control); color: var(--text-secondary); font: inherit; cursor: pointer; text-align: left; }
|
||
.search-trigger:hover { border-color: var(--text-accent); }
|
||
.search-trigger:focus-visible { outline: var(--focus-width) solid var(--focus-ring); outline-offset: var(--focus-offset); }
|
||
.search-trigger svg { width: 16px; height: 16px; }
|
||
.kbd { font: var(--fw-bold) 10px/1 var(--font-mono); padding: 3px 6px; border-radius: 5px; border: 1px solid var(--border-control); color: var(--text-secondary); background: var(--surface-raised); box-shadow: 0 1px 0 var(--border-strong); }
|
||
.fresh { display: inline-flex; align-items: center; gap: 8px; font-size: var(--fs-label); color: var(--text-secondary); }
|
||
.fresh .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--status-success); box-shadow: 0 0 0 3px var(--wash-success); }
|
||
.fresh.is-stale .dot { background: var(--status-warning); box-shadow: 0 0 0 3px var(--wash-warning); }
|
||
.fresh.is-stale { color: var(--status-warning); }
|
||
|
||
/* Breadcrumb */
|
||
.crumbs { display: flex; gap: 6px; align-items: center; font-size: var(--fs-label); color: var(--text-secondary); }
|
||
.crumbs a { color: var(--text-secondary); text-decoration: none; }
|
||
.crumbs a:hover { color: var(--text-accent); text-decoration: underline; }
|
||
|
||
/* Page header: title, one-line summary, ONE primary action, overflow for the rest */
|
||
.page-head { display: flex; flex-wrap: wrap; gap: var(--space-4); align-items: flex-end; justify-content: space-between; }
|
||
.page-head h1 { margin: 0; font-size: var(--fs-h1); line-height: var(--lh-tight); font-weight: var(--fw-display); }
|
||
.page-head .sum { margin: 6px 0 0; color: var(--text-secondary); font-size: 13px; }
|
||
.page-head .sum b { color: var(--text-primary); }
|
||
|
||
/* Overflow / row menu */
|
||
.menu-wrap { position: relative; display: inline-block; }
|
||
.menu { position: absolute; right: 0; top: calc(100% + 6px); z-index: 30; min-width: 220px; padding: 6px; display: none; flex-direction: column; gap: 2px; background: var(--surface-high); border: 1px solid var(--border-strong); border-radius: var(--radius-md); box-shadow: var(--nm-raised-3); }
|
||
.menu.is-open { display: flex; }
|
||
.menu button, .menu a { all: unset; box-sizing: border-box; display: flex; gap: 10px; align-items: center; padding: 8px 10px; border-radius: var(--radius-sm); font-size: 13px; cursor: pointer; color: var(--text-primary); }
|
||
.menu button:hover, .menu a:hover, .menu button:focus-visible, .menu a:focus-visible { background: var(--accent-wash); outline: 1px solid var(--text-accent); }
|
||
.menu .danger { color: var(--status-error); }
|
||
.menu hr { border: 0; border-top: 1px solid var(--border-hairline); margin: 4px 0; width: 100%; }
|
||
.menu .hint { margin-left: auto; font-size: var(--fs-caption); color: var(--text-secondary); }
|
||
|
||
/* Tabs (segmented, with counts) */
|
||
.tabs { display: flex; gap: 4px; padding: 4px; border-radius: var(--radius-pill); background: var(--surface-inset); box-shadow: var(--nm-inset-1); border: 1px solid var(--border-control); width: max-content; max-width: 100%; overflow-x: auto; }
|
||
.tabs button { font: var(--fw-medium) 13px/1 var(--font-body); color: var(--text-secondary); background: transparent; border: 1px solid transparent; border-radius: var(--radius-pill); padding: 8px 14px; cursor: pointer; display: inline-flex; gap: 8px; align-items: center; white-space: nowrap; }
|
||
.tabs button[aria-selected="true"] { background: var(--surface-raised); color: var(--text-primary); font-weight: var(--fw-bold); box-shadow: var(--nm-raised-1); border-color: var(--border-control); }
|
||
.tabs button:focus-visible { outline: var(--focus-width) solid var(--focus-ring); outline-offset: 2px; }
|
||
.tabs .n { font-size: var(--fs-caption); padding: 1px 7px; border-radius: var(--radius-pill); border: 1px solid currentColor; }
|
||
.tabs .n.warn { color: var(--status-warning); }
|
||
|
||
/* Filter chips (removable) */
|
||
.fchips { display: flex; gap: var(--space-2); flex-wrap: wrap; align-items: center; }
|
||
.fchip { display: inline-flex; gap: 6px; align-items: center; padding: 4px 6px 4px 10px; border-radius: var(--radius-pill); border: 1px solid var(--text-accent); background: var(--accent-wash); color: var(--text-accent); font-size: var(--fs-label); font-weight: var(--fw-bold); }
|
||
.fchip button { all: unset; cursor: pointer; width: 18px; height: 18px; display: grid; place-items: center; border-radius: 50%; }
|
||
.fchip button:hover, .fchip button:focus-visible { background: var(--text-accent); color: var(--text-on-accent); }
|
||
.fadd { font-size: var(--fs-label); }
|
||
|
||
/* Quiet provenance: icon + lowercase text, never a chip */
|
||
.prov { display: inline-flex; gap: 4px; align-items: center; font-size: var(--fs-caption); color: var(--text-secondary); white-space: nowrap; }
|
||
.prov i { font-style: normal; width: 14px; text-align: center; }
|
||
.prov.warn { color: var(--status-warning); font-weight: var(--fw-bold); }
|
||
.prov.info { color: var(--status-info); }
|
||
|
||
/* Status: small filled label + glyph. Reserved for state, not provenance */
|
||
.st { display: inline-flex; gap: 6px; align-items: center; font-size: var(--fs-label); font-weight: var(--fw-bold); white-space: nowrap; }
|
||
.st::before { content: ""; width: 8px; height: 8px; border-radius: 50%; background: currentColor; flex: none; }
|
||
.st.run { color: var(--text-accent); } .st.ok { color: var(--status-success); } .st.err { color: var(--status-error); } .st.warn { color: var(--status-warning); } .st.idle { color: var(--text-secondary); }
|
||
.st.warn::before { border-radius: 2px; transform: rotate(45deg); }
|
||
.st.err::before { border-radius: 1px; }
|
||
.st.idle::before { background: transparent; border: 2px solid currentColor; width: 6px; height: 6px; }
|
||
|
||
/* Data tables (v3.1): quieter, row = one line where possible */
|
||
.tbl { width: 100%; border-collapse: collapse; font-size: 13px; }
|
||
.tbl th { text-align: left; font-size: var(--fs-caption); text-transform: uppercase; letter-spacing: var(--tracking-caps); color: var(--text-secondary); font-weight: var(--fw-bold); padding: 10px 12px; border-bottom: 1px solid var(--border-strong); white-space: nowrap; position: sticky; top: 0; background: var(--surface-raised); z-index: 1; }
|
||
.tbl th button.sort { all: unset; cursor: pointer; display: inline-flex; gap: 4px; align-items: center; }
|
||
.tbl th button.sort:focus-visible { outline: var(--focus-width) solid var(--focus-ring); outline-offset: 2px; border-radius: 3px; }
|
||
.tbl th[aria-sort="ascending"] button.sort::after { content: "▲"; font-size: 9px; color: var(--text-accent); }
|
||
.tbl th[aria-sort="descending"] button.sort::after { content: "▼"; font-size: 9px; color: var(--text-accent); }
|
||
.tbl th[aria-sort="none"] button.sort::after { content: "↕"; font-size: 10px; opacity: .6; }
|
||
.tbl td { padding: 10px 12px; border-bottom: 1px solid var(--border-hairline); vertical-align: middle; }
|
||
.tbl tr:last-child td { border-bottom: 0; }
|
||
.tbl .r { text-align: right; font-variant-numeric: tabular-nums; }
|
||
.tbl tbody tr.clickable { cursor: pointer; }
|
||
.tbl tbody tr.clickable:hover td { background: var(--accent-wash); }
|
||
.tbl tbody tr.is-selected td { background: var(--accent-wash); box-shadow: inset 0 1px 0 var(--text-accent), inset 0 -1px 0 var(--text-accent); }
|
||
.tbl tbody tr.is-selected td:first-child { box-shadow: inset 3px 0 0 var(--text-accent), inset 0 1px 0 var(--text-accent), inset 0 -1px 0 var(--text-accent); }
|
||
.tbl tbody tr:focus-visible { outline: var(--focus-width) solid var(--focus-ring); outline-offset: -2px; }
|
||
.sub { display: block; font-size: var(--fs-caption); color: var(--text-secondary); font-weight: var(--fw-regular); line-height: var(--lh-snug); }
|
||
.strong { font-weight: var(--fw-bold); }
|
||
.num { font-variant-numeric: tabular-nums; }
|
||
.table-card { padding: 0; overflow: hidden; }
|
||
.table-scroll { overflow: auto; max-height: 70vh; }
|
||
.table-foot { display: flex; justify-content: space-between; align-items: center; gap: var(--space-3); padding: var(--space-3) var(--space-4); border-top: 1px solid var(--border-hairline); flex-wrap: wrap; font-size: var(--fs-label); }
|
||
|
||
/* Meter: remaining bar with the 200 g threshold tick */
|
||
.meter { position: relative; height: 8px; border-radius: var(--radius-pill); background: var(--surface-inset); box-shadow: var(--nm-inset-1); border: 1px solid var(--border-hairline); overflow: visible; min-width: 90px; }
|
||
.meter > span { display: block; height: 100%; border-radius: var(--radius-pill); background: var(--text-accent); }
|
||
.meter.is-low > span { background: var(--status-warning); }
|
||
.meter .tick { position: absolute; top: -3px; bottom: -3px; width: 2px; background: var(--text-secondary); border-radius: 1px; }
|
||
|
||
/* Drawer (right sheet; full-screen on phones) */
|
||
.drawer-scrim { position: fixed; inset: 0; background: rgba(10, 10, 14, .5); opacity: 0; pointer-events: none; transition: opacity var(--dur-base) var(--ease-out); z-index: 40; }
|
||
:root[data-theme="light"] .drawer-scrim { background: rgba(60, 55, 80, .28); }
|
||
.drawer { position: fixed; top: 12px; right: 12px; bottom: 12px; width: min(520px, calc(100vw - 24px)); z-index: 41; transform: translateX(calc(100% + 24px)); transition: transform 220ms var(--ease-out); display: flex; flex-direction: column; overflow: hidden; }
|
||
body.drawer-open .drawer { transform: none; }
|
||
body.drawer-open .drawer-scrim { opacity: 1; pointer-events: auto; }
|
||
.drawer-head { padding: var(--space-5) var(--space-5) var(--space-3); display: flex; gap: var(--space-3); align-items: flex-start; border-bottom: 1px solid var(--border-hairline); }
|
||
.drawer-head h2 { margin: 0; font-size: var(--fs-h3); line-height: var(--lh-snug); }
|
||
.drawer-tabs { padding: var(--space-3) var(--space-5) 0; }
|
||
.drawer-body { flex: 1; overflow: auto; padding: var(--space-5); display: flex; flex-direction: column; gap: var(--space-5); }
|
||
.drawer-foot { padding: var(--space-4) var(--space-5); border-top: 1px solid var(--border-hairline); display: flex; gap: var(--space-3); justify-content: flex-end; align-items: center; flex-wrap: wrap; }
|
||
.drawer-nav { display: flex; gap: 4px; }
|
||
@media (prefers-reduced-motion: reduce) { .drawer, .drawer-scrim { transition: none; } }
|
||
|
||
/* Key-value list */
|
||
.kv { display: grid; grid-template-columns: minmax(110px, auto) 1fr; gap: 8px var(--space-4); margin: 0; font-size: 13px; }
|
||
.kv dt { color: var(--text-secondary); }
|
||
.kv dd { margin: 0; font-variant-numeric: tabular-nums; }
|
||
.panel { padding: var(--space-4); }
|
||
.panel h3 { margin: 0 0 var(--space-3); font-size: var(--fs-label); text-transform: uppercase; letter-spacing: var(--tracking-caps-wide); color: var(--text-secondary); }
|
||
|
||
/* Command palette */
|
||
.pal-scrim { position: fixed; inset: 0; background: rgba(10, 10, 14, .55); z-index: 60; display: none; align-items: flex-start; justify-content: center; padding-top: 12vh; }
|
||
:root[data-theme="light"] .pal-scrim { background: rgba(60, 55, 80, .3); }
|
||
.pal-scrim.is-open { display: flex; }
|
||
.pal { width: min(640px, calc(100vw - 32px)); max-height: 70vh; display: flex; flex-direction: column; overflow: hidden; }
|
||
.pal-input { display: flex; gap: var(--space-3); align-items: center; padding: var(--space-4); border-bottom: 1px solid var(--border-hairline); }
|
||
.pal-input input { all: unset; flex: 1; font: var(--fw-medium) 16px/1.4 var(--font-body); color: var(--text-primary); }
|
||
.pal-input input::placeholder { color: var(--text-secondary); }
|
||
.pal-list { overflow: auto; padding: 6px; }
|
||
.pal-sec { font-size: var(--fs-caption); text-transform: uppercase; letter-spacing: var(--tracking-caps-wide); color: var(--text-secondary); font-weight: var(--fw-bold); padding: 10px 10px 4px; }
|
||
.pal-item { display: flex; gap: var(--space-3); align-items: center; padding: 9px 10px; border-radius: var(--radius-sm); cursor: pointer; font-size: 13px; border: 1px solid transparent; }
|
||
.pal-item[aria-selected="true"] { background: var(--accent-wash); border-color: var(--text-accent); }
|
||
.pal-item .meta { margin-left: auto; font-size: var(--fs-caption); color: var(--text-secondary); white-space: nowrap; }
|
||
.pal-foot { padding: 8px var(--space-4); border-top: 1px solid var(--border-hairline); display: flex; gap: var(--space-4); font-size: var(--fs-caption); color: var(--text-secondary); flex-wrap: wrap; }
|
||
|
||
/* Toasts */
|
||
.toasts { position: fixed; bottom: 20px; left: 50%; transform: translateX(-50%); z-index: 70; display: flex; flex-direction: column; gap: 8px; align-items: center; pointer-events: none; }
|
||
.toast { pointer-events: auto; display: flex; gap: var(--space-3); align-items: center; padding: 10px 12px 10px 16px; background: var(--surface-high); border: 1px solid var(--border-strong); border-left: 4px solid var(--status-success); border-radius: var(--radius-md); box-shadow: var(--nm-raised-3); font-size: 13px; max-width: min(560px, calc(100vw - 32px)); }
|
||
.toast.warn { border-left-color: var(--status-warning); }
|
||
|
||
/* Attention list (dashboard) */
|
||
.attn-list { display: flex; flex-direction: column; gap: var(--space-3); }
|
||
.attn { display: grid; grid-template-columns: 44px minmax(0, 1fr) auto; gap: var(--space-4); align-items: center; padding: var(--space-4); border-radius: var(--radius-md); background: var(--surface-raised); border: 1px solid var(--border-hairline); box-shadow: var(--nm-raised-2); }
|
||
.attn .ico { width: 44px; height: 44px; border-radius: 12px; display: grid; place-items: center; font-size: 20px; font-weight: var(--fw-bold); background: var(--surface-inset); box-shadow: var(--nm-inset-1); border: 1px solid var(--border-hairline); }
|
||
.attn.warn .ico { color: var(--status-warning); border-color: var(--status-warning); }
|
||
.attn.err .ico { color: var(--status-error); border-color: var(--status-error); }
|
||
.attn.info .ico { color: var(--status-info); }
|
||
.attn h3 { margin: 0; font-size: 14px; }
|
||
.attn p { margin: 2px 0 0; font-size: var(--fs-label); color: var(--text-secondary); }
|
||
.attn-done { padding: var(--space-5); text-align: center; color: var(--text-secondary); }
|
||
|
||
/* Skeleton + empty + error states */
|
||
.skel { border-radius: 6px; background: linear-gradient(90deg, var(--surface-inset) 0%, var(--border-hairline) 50%, var(--surface-inset) 100%); background-size: 200% 100%; animation: skel 1.4s linear infinite; height: 12px; }
|
||
@keyframes skel { to { background-position: -200% 0; } }
|
||
@media (prefers-reduced-motion: reduce) { .skel { animation: none; } }
|
||
.empty { padding: var(--space-8) var(--space-5); text-align: center; display: flex; flex-direction: column; gap: var(--space-3); align-items: center; }
|
||
.empty h3 { margin: 0; font-size: 15px; }
|
||
.empty p { margin: 0; color: var(--text-secondary); font-size: 13px; max-width: 420px; }
|
||
.banner { display: flex; gap: var(--space-3); align-items: center; padding: var(--space-3) var(--space-4); border-radius: var(--radius-md); font-size: 13px; border: 1px solid var(--status-warning); background: var(--wash-warning); }
|
||
.banner.err { border-color: var(--status-error); background: var(--wash-error); }
|
||
|
||
/* Slot grid (printers + move flow) */
|
||
.slotgrid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 8px; }
|
||
.slotgrid.one { grid-template-columns: minmax(0, 1fr); max-width: 25%; }
|
||
.slot { all: unset; box-sizing: border-box; cursor: pointer; display: flex; flex-direction: column; gap: 6px; align-items: flex-start; padding: 10px; min-height: 92px; border-radius: var(--radius-md); border: 1px solid var(--border-control); background: var(--surface-raised); box-shadow: var(--nm-raised-1); font-size: var(--fs-caption); position: relative; min-width: 0; }
|
||
.slot:hover { border-color: var(--text-accent); }
|
||
.slot:focus-visible { outline: var(--focus-width) solid var(--focus-ring); outline-offset: 2px; }
|
||
.slot .sn { color: var(--text-secondary); font-weight: var(--fw-bold); }
|
||
.slot .nm { font-weight: var(--fw-bold); font-size: 12px; line-height: var(--lh-snug); overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
|
||
.slot .g { margin-top: auto; font-variant-numeric: tabular-nums; font-size: 12px; }
|
||
.slot.empty { border-style: dashed; box-shadow: none; background: transparent; color: var(--text-secondary); justify-content: center; align-items: center; text-align: center; }
|
||
.slot.is-low { border-color: var(--status-warning); }
|
||
.slot[aria-pressed="true"] { background: var(--accent-wash); border: 2px solid var(--text-accent); box-shadow: var(--nm-inset-1); }
|
||
.slot[aria-pressed="true"]::after { content: "✓"; position: absolute; top: 6px; right: 8px; color: var(--text-accent); font-weight: var(--fw-bold); font-size: 13px; }
|
||
.slot[disabled] { cursor: not-allowed; opacity: .75; }
|
||
|
||
/* Mockup pill (replaces the iteration-1 banner) */
|
||
.mock { position: fixed; left: 16px; bottom: 16px; z-index: 35; }
|
||
.mock > button { font: var(--fw-bold) var(--fs-caption)/1 var(--font-body); text-transform: uppercase; letter-spacing: var(--tracking-caps); padding: 8px 12px; border-radius: var(--radius-pill); border: 1px solid var(--text-accent); background: var(--surface-high); color: var(--text-accent); box-shadow: var(--nm-raised-2); cursor: pointer; }
|
||
.mock .menu { left: 0; right: auto; top: auto; bottom: calc(100% + 8px); width: 330px; padding: var(--space-4); gap: var(--space-2); font-size: var(--fs-label); }
|
||
.mock .menu p { margin: 0; line-height: var(--lh-snug); }
|
||
|
||
/* Mobile bottom tab bar + FAB */
|
||
.tabbar { display: none; }
|
||
.fab { display: none; }
|
||
@media (max-width: 699px) {
|
||
.shell { grid-template-columns: minmax(0, 1fr); padding: var(--space-3) var(--space-4) 96px; gap: var(--space-4); }
|
||
.rail { display: none; }
|
||
.tabbar { display: grid; grid-template-columns: repeat(4, 1fr); position: fixed; left: 8px; right: 8px; bottom: 8px; z-index: 36; padding: 6px; border-radius: var(--radius-xl); }
|
||
.tabbar a { display: flex; flex-direction: column; align-items: center; gap: 2px; padding: 6px 0; border-radius: var(--radius-lg); color: var(--text-secondary); text-decoration: none; font-size: 11px; font-weight: var(--fw-medium); position: relative; border: 1px solid transparent; }
|
||
.tabbar a[aria-current="page"] { color: var(--text-accent); background: var(--surface-inset); box-shadow: var(--nm-inset-1); border-color: var(--text-accent); font-weight: var(--fw-bold); }
|
||
.tabbar svg { width: 20px; height: 20px; }
|
||
.tabbar .count { position: absolute; top: 2px; right: calc(50% - 22px); min-width: 16px; height: 16px; font-size: 9px; padding: 0 4px; border-radius: 99px; display: grid; place-items: center; border: 1px solid currentColor; }
|
||
.fab { display: grid; place-items: center; position: fixed; right: 16px; bottom: 88px; z-index: 36; width: 56px; height: 56px; border-radius: 18px; font-size: 26px; padding: 0; }
|
||
.mock { bottom: 88px; }
|
||
.topbar .fresh span.long { display: none; }
|
||
.page-head .primary-desktop { display: none; }
|
||
.drawer { top: 0; right: 0; bottom: 0; width: 100vw; border-radius: 0; }
|
||
.slotgrid.one { max-width: 50%; }
|
||
}
|
||
@media (max-width: 520px) { .slotgrid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
|
||
|
||
/* Form grid + small utilities */
|
||
.fr2 { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 12px 16px; }
|
||
.nowrap { white-space: nowrap; }
|
||
.drawer-body a { color: var(--text-accent); }
|
||
.linkish { all: unset; cursor: pointer; color: var(--text-accent); text-decoration: underline; }
|
||
.linkish:focus-visible { outline: var(--focus-width) solid var(--focus-ring); outline-offset: 2px; }
|
||
.tile-link { text-decoration: none; color: inherit; }
|
||
.tile-link:hover { border-color: var(--text-accent); }
|
||
.tile-link:focus-visible { outline: var(--focus-width) solid var(--focus-ring); outline-offset: var(--focus-offset); }
|
||
.is-flash { animation: flash 1.6s var(--ease-out) 1; }
|
||
@keyframes flash { 0%, 40% { outline: 3px solid var(--focus-ring); outline-offset: 4px; } 100% { outline: 3px solid transparent; outline-offset: 4px; } }
|
||
@media (prefers-reduced-motion: reduce) { .is-flash { animation: none; outline: 3px solid var(--focus-ring); } }
|
||
#page { display: flex; flex-direction: column; gap: var(--space-6); min-width: 0; }
|
||
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
|
||
#drawer-title:focus { outline: none; }
|
||
#drawer-title:focus-visible { outline: none; text-decoration: underline; text-decoration-color: var(--focus-ring); text-underline-offset: 4px; }
|
||
.short { display: none; }
|
||
@media (max-width: 699px) {
|
||
.search-trigger .long, .search-trigger .kbd.long, .mock .long { display: none; }
|
||
.search-trigger .short { display: inline; }
|
||
.search-trigger { min-width: 0; }
|
||
.remcell .top { flex-wrap: nowrap; white-space: nowrap; }
|
||
.remcell .top .sub { display: none !important; }
|
||
}
|
||
.pal-input svg { width: 18px; height: 18px; flex: none; color: var(--text-secondary); }
|
||
|
||
/* ── Tablet tier (700–1180 px): compact rail, side-sheet panel ───────────── */
|
||
@media (min-width: 700px) and (max-width: 1180px) {
|
||
.shell { grid-template-columns: 88px minmax(0, 1fr); gap: var(--space-5); padding: var(--space-4) var(--space-5); }
|
||
.rail { padding: var(--space-3) 8px; gap: var(--space-3); align-items: stretch; }
|
||
.wordmark { justify-content: center; font-size: 0; padding: 4px 0; }
|
||
.wordmark-mark { width: 40px; height: 40px; font-size: 18px; }
|
||
.nav-label { font-size: 9px; padding: 6px 0 0; text-align: center; letter-spacing: .06em; }
|
||
.nav a { flex-direction: column; gap: 4px; padding: 10px 2px 8px; font-size: 11px; text-align: center; justify-content: center; }
|
||
.nav a svg { width: 22px; height: 22px; }
|
||
.nav a > span:not(.count) { line-height: 1.1; }
|
||
.nav a .sub { display: none; }
|
||
.nav .count { position: absolute; top: 4px; right: 10px; margin: 0; min-width: 18px; height: 18px; font-size: 10px; padding: 0 4px; }
|
||
.rail-foot .seg, .rail-foot .small { display: none; }
|
||
.rail-foot { border-top: 0; padding-top: 0; }
|
||
.drawer { width: min(480px, calc(100vw - 96px)); }
|
||
.hide-t { display: none !important; }
|
||
.page-head h1 { font-size: var(--fs-h2); }
|
||
}
|
||
/* Tablet touch targets: at least 44 px for primary interactive rows */
|
||
@media (pointer: coarse) {
|
||
.btn-sm { min-height: 40px; }
|
||
.tbl td { padding-top: 12px; padding-bottom: 12px; }
|
||
.menu button, .menu a { padding: 12px 12px; }
|
||
.kbd { display: none; }
|
||
}
|
||
.more-sheet { position: fixed; left: 8px; right: 8px; bottom: 84px; top: auto; z-index: 37; padding: 8px; }
|
||
.more-sheet a svg, .more-sheet button svg { width: 18px; height: 18px; }
|
||
.findit { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; padding: 12px 14px; border-radius: var(--radius-md); border: 2px solid var(--text-accent); background: var(--accent-wash); }
|
||
.findit b { font-size: 17px; }
|
||
.findit .sub { width: 100%; text-transform: uppercase; letter-spacing: var(--tracking-caps-wide); font-weight: 700; }
|
||
details.chg { margin-top: 8px; }
|
||
details.chg summary { cursor: pointer; font-size: var(--fs-label); color: var(--text-accent); font-weight: 700; }
|
||
.holder { padding: var(--space-3) var(--space-4); border-radius: var(--radius-md); background: var(--surface-inset); box-shadow: var(--nm-inset-1); border: 1px solid var(--border-hairline); display: flex; flex-direction: column; gap: 8px; }
|
||
.holder-h { display: flex; gap: var(--space-3); align-items: center; font-size: var(--fs-label); flex-wrap: wrap; }
|
||
.tabbar { grid-template-columns: repeat(5, 1fr) !important; }
|
||
|
||
|
||
.dv-bar { display: flex; flex-wrap: wrap; gap: var(--space-3); align-items: center; }
|
||
.dv-row { display: flex; gap: var(--space-6); align-items: flex-start; flex-wrap: wrap; }
|
||
.dev { display: flex; flex-direction: column; gap: 10px; align-items: flex-start; }
|
||
.dev h2 { margin: 0; font-size: var(--fs-label); text-transform: uppercase; letter-spacing: var(--tracking-caps-wide); color: var(--text-secondary); }
|
||
.bezel { border-radius: 36px; padding: 14px; background: linear-gradient(145deg, #3a3a40, #1b1b1e); box-shadow: var(--nm-raised-3); border: 1px solid var(--border-control); }
|
||
.bezel.tab { border-radius: 28px; padding: 18px; }
|
||
.screen { overflow: hidden; border-radius: 24px; background: var(--surface-page); position: relative; }
|
||
.bezel.tab .screen { border-radius: 12px; }
|
||
.screen iframe { border: 0; transform-origin: 0 0; position: absolute; left: 0; top: 0; }
|
||
.dv-list { display: flex; gap: 6px; flex-wrap: wrap; }
|
||
.dv-list button { font: var(--fw-medium) 12px/1 var(--font-body); padding: 8px 12px; border-radius: var(--radius-pill); border: 1px solid var(--border-control); background: var(--surface-raised); color: var(--text-primary); cursor: pointer; }
|
||
.dv-list button[aria-pressed="true"] { background: var(--accent-wash); border-color: var(--text-accent); color: var(--text-accent); font-weight: 700; }
|
||
.dv-list button[aria-pressed="true"]::before { content: "✓ "; }
|
||
.dv-list button:focus-visible { outline: var(--focus-width) solid var(--focus-ring); outline-offset: 2px; }
|
||
.bp { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 240px), 1fr)); gap: var(--space-3); }
|
||
.bp div { padding: var(--space-4); font-size: 13px; }
|
||
.bp b { display: block; margin-bottom: 4px; }
|
||
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<script>
|
||
/* Sample data for the iteration-2 mockups. One model shared by every page, so a
|
||
* spool, print or printer is the same object wherever it appears. Values are
|
||
* based on the real inventory (import/filament_review.tsv, seed.sql spool
|
||
* types) and the 2026-08-12 production canary. Nothing here is live. */
|
||
var NOW = new Date('2026-09-18T09:41:07');
|
||
|
||
var CANARY = { spools: 105, brands: 18, kg: 95.72, value: 1721.18 };
|
||
|
||
/* tsrc: where this spool's tare came from.
|
||
* 'spool' weighed for this reel (tare_origin=spool)
|
||
* 'manufacturer' | 'community' | 'estimated' via the chosen spool type
|
||
* null no tare known */
|
||
var SPOOLS = [
|
||
{ id: 40, c: ['#1f3fbf'], color: 'Klein Blue', brand: 'Sunlu', speed: null, mat: 'PLA', type: '+ 2.0', fin: 'Basic', rem: 64, tot: 250, tare: 60, tsrc: 'community', stype: 'Sunlu — 250 g mini', remSrc: 'typed', kg: 25.20, cont: null, loc: 'Shelf B2', label: true, v: 3 },
|
||
{ id: 41, c: ['#b87333'], color: 'Copper', brand: 'Sunlu', speed: null, mat: 'PLA', type: '+', fin: 'Silk', rem: 97, tot: 1000, tare: 140, tsrc: 'community', stype: 'Sunlu — 1 kg plastic', remSrc: 'typed', kg: 19.99, cont: 'p1-ams1', label: true, v: 7 },
|
||
{ id: 42, c: ['#141414'], color: 'Black', brand: 'Elegoo', speed: 'Rapid', mat: 'PETG', type: null, fin: 'Basic', rem: 142, tot: 1000, tare: 154, tsrc: 'manufacturer', stype: 'Elegoo — 1 kg cardboard', remSrc: 'typed', kg: 11.74, cont: 'p2-ams1', label: true, v: 9 },
|
||
{ id: 43, c: ['#ece8df'], color: 'Rock White', brand: 'Overture', speed: null, mat: 'PLA', type: null, fin: 'Marble', rem: 171, tot: 1000, tare: 155, tsrc: 'community', stype: 'Overture — cardboard, PLA lines', remSrc: 'typed', kg: 17.99, cont: null, loc: 'Shelf C', label: true, v: 4 },
|
||
{ id: 44, c: ['#161616', '#c0232f'], color: 'Black Red', brand: 'TECSONAR', speed: null, mat: 'PLA', type: null, fin: 'Matte', rem: 188, tot: 1000, tare: null, tsrc: null, stype: null, remSrc: 'typed', kg: 15.99, cont: null, loc: 'Shelf C', label: false, v: 2 },
|
||
{ id: 45, c: ['#101010'], color: 'Black', brand: 'JAYO', speed: 'Rapid', mat: 'PETG', type: null, fin: 'Matte', rem: 655, tot: 1000, tare: null, tsrc: null, stype: null, remSrc: 'typed', kg: 12.72, cont: 'p1-ams1', label: true, v: 5 },
|
||
{ id: 46, c: ['#8a8f7a', '#e9e4d8'], color: 'Marble', brand: 'iSANMATE', speed: null, mat: 'ASA', type: null, fin: 'Marble', rem: 430, tot: 1000, tare: 180, tsrc: 'estimated', stype: 'Generic — 1 kg spool (assumed)', remSrc: 'weighed', kg: 25.99, cont: null, loc: 'Dry box 1', label: true, v: 6 },
|
||
{ id: 47, c: ['#1b1b1b'], color: 'Black', brand: 'Sunlu', speed: null, mat: 'PETG', type: 'CF', fin: 'Matte', rem: 812, tot: 1000, tare: 262, tsrc: 'spool', stype: 'Sunlu — 1 kg plastic', remSrc: 'weighed', weighedAt: '18 Sep 09:39', kg: 22.79, cont: 'p3-lite', label: true, v: 5 },
|
||
{ id: 48, c: ['#b8864b'], color: 'Natural Wood', brand: 'Sunlu', speed: null, mat: 'PLA', type: null, fin: 'Wood', rem: 1000, tot: 1000, tare: 35, tsrc: 'estimated', stype: 'Refill — bare cardboard core', remSrc: 'typed', kg: 16.50, cont: null, loc: 'Shelf A3', label: false, v: 1 },
|
||
{ id: 49, c: ['#a3402c'], color: 'Brick Red', brand: 'Sunlu', speed: 'High Speed', mat: 'PLA', type: null, fin: 'Marble', rem: 1000, tot: 1000, tare: 140, tsrc: 'community', stype: 'Sunlu — 1 kg plastic', remSrc: 'typed', kg: 17.50, cont: null, loc: 'Shelf A2', label: true, v: 1 },
|
||
{ id: 50, c: ['#e8453c', '#f2c230', '#2a8fd6'], color: 'Gradient Rainbow', brand: 'AMOLEN', speed: null, mat: 'PETG', type: null, fin: 'Gradient', rem: 1000, tot: 1000, tare: 180, tsrc: 'estimated', stype: 'Generic — 1 kg spool (assumed)', remSrc: 'typed', kg: 23.99, cont: null, loc: 'Shelf D', label: false, v: 1 },
|
||
{ id: 51, c: ['#1d4fa8'], color: 'Blue', brand: 'Elegoo', speed: 'Rapid', mat: 'PETG', type: null, fin: 'Basic', rem: 1000, tot: 1000, tare: 154, tsrc: 'manufacturer', stype: 'Elegoo — 1 kg cardboard', remSrc: 'typed', kg: 17.99, cont: 'p2-ams1', label: true, v: 2 },
|
||
{ id: 52, c: ['#141414'], color: 'Black', brand: 'Sunlu', speed: null, mat: 'PLA', type: '+ 2.0', fin: 'Basic', rem: 873, tot: 1000, tare: 140, tsrc: 'community', stype: 'Sunlu — 1 kg plastic', remSrc: 'typed', kg: 11.47, cont: 'p1-ams1', label: true, v: 11 },
|
||
{ id: 53, c: ['#b9bcc0'], color: 'Mist Gray', brand: 'Overture', speed: null, mat: 'PLA', type: null, fin: 'Marble', rem: 920, tot: 1000, tare: 155, tsrc: 'community', stype: 'Overture — cardboard, PLA lines', remSrc: 'typed', kg: 16.99, cont: 'p1-ams1', label: true, v: 3 },
|
||
{ id: 54, c: ['#f4f4f0'], color: 'White', brand: 'Elegoo', speed: null, mat: 'PLA', type: null, fin: 'Matte', rem: 569, tot: 1000, tare: 154, tsrc: 'manufacturer', stype: 'Elegoo — 1 kg cardboard', remSrc: 'typed', kg: 14.99, cont: 'p2-ams1', label: true, v: 6 },
|
||
{ id: 55, c: ['#e8453c', '#f2c230', '#2a8fd6'], color: 'Red Yellow Blue', brand: 'AMOLEN', speed: null, mat: 'PLA', type: null, fin: 'Matte', rem: 532, tot: 1000, tare: 180, tsrc: 'estimated', stype: 'Generic — 1 kg spool (assumed)', remSrc: 'typed', kg: 19.99, cont: 'p3-lite', label: true, v: 4 },
|
||
{ id: 56, c: ['#6e1a2a'], color: 'Transparent Wine Red', brand: 'Creality', speed: null, mat: 'PETG', type: null, fin: 'Translucent', rem: 1000, tot: 1000, tare: null, tsrc: null, stype: null, remSrc: 'typed', kg: 17.99, cont: null, loc: 'Shelf D', label: false, v: 1 }
|
||
];
|
||
|
||
var PRINTERS = [
|
||
{ id: 1, name: 'P1S - Prints Charming', model: 'P1S', ip: '10.60.1.242', serial: '01P09A••••••4821', state: 'RUNNING', connected: true, lifecycle: 'ready', stale: false, age: 2, session: 3, camera: 'snapshot', job: 'Gridfinity_bins_3x2', openPrint: 212,
|
||
containers: [{ id: 'p1-ams1', kind: 'ams', label: 'AMS 1', cap: 4 }, { id: 'p1-ext', kind: 'external', label: 'External', cap: 1 }] },
|
||
{ id: 2, name: 'H2C - Constipation Orion', model: 'H2C', ip: '10.60.1.202', serial: '31B••••••••0093', state: 'FINISH', connected: true, lifecycle: 'ready', stale: false, age: 4, session: 2, camera: 'snapshot', job: '', openPrint: null,
|
||
containers: [{ id: 'p2-ams1', kind: 'ams', label: 'AMS 1', cap: 4 }, { id: 'p2-ext', kind: 'external', label: 'External', cap: 1 }] },
|
||
{ id: 3, name: 'A1 - Jordyns Layer Slayer', model: 'A1', ip: '10.60.1.x (sample)', serial: '039••••••••7710', state: 'RUNNING', connected: false, lifecycle: 'reconnecting', stale: true, age: 366, session: 14, camera: 'snapshot', job: 'Coaster_set', openPrint: 213,
|
||
containers: [{ id: 'p3-lite', kind: 'ams_lite', label: 'AMS Lite', cap: 4 }, { id: 'p3-ext', kind: 'external', label: 'External', cap: 1 }] }
|
||
];
|
||
|
||
/* weight source: typed | sliced | recalled | unknown | null (not known yet)
|
||
* gaps: null = before counting, 0 = asked and none */
|
||
var PRINTS = [
|
||
{ id: 213, job: 'Coaster_set', key: '/coasters/plate_1.gcode', printer: 3, status: 'running', start: '2026-09-18T08:55', end: null, dur: null, gaps: 0, grams: null, wsrc: null, cost: null, spool: null, use: 'personal', open: true },
|
||
{ id: 212, job: 'Gridfinity_bins_3x2', key: '/gridfinity/bins_3x2.gcode', printer: 1, status: 'running', start: '2026-09-18T08:37', end: null, dur: null, gaps: 0, grams: null, wsrc: null, cost: null, spool: null, use: 'business' },
|
||
{ id: 211, job: 'Benchy_x4', key: '/benchy-batch/plate_1.gcode', printer: 2, status: 'finished', start: '2026-09-18T02:15', end: '2026-09-18T05:27', dur: 11520, gaps: 1, grams: 46, wsrc: 'sliced', cost: 0.54, spool: 42, use: 'business' },
|
||
{ id: 210, job: 'Sign_CubeCraft', key: '/signs/cubecraft.gcode', printer: 2, status: 'finished', start: '2026-09-17T21:48', end: '2026-09-17T23:53', dur: 7500, gaps: 0, grams: 64, wsrc: 'sliced', cost: 0.61, spool: null, use: 'business',
|
||
filaments: [{ slot: 0, spool: 54, grams: 41, hex: '#f4f4f0', cost: 0.61 }, { slot: 1, spool: null, suggested: 51, grams: 23, hex: '#1d4fa8', cost: null }] },
|
||
{ id: 209, job: 'Cable_clips', key: '/misc/cable_clips.gcode', printer: 3, status: 'unknown', start: '2026-09-17T19:30', end: '2026-09-17T21:02', dur: null, gaps: 1, grams: 12, wsrc: 'typed', cost: 0.27, spool: 47, use: 'personal' },
|
||
{ id: 208, job: 'Lid_v3', key: '/enclosure/lid_v3.gcode', printer: 1, status: 'failed', start: '2026-09-17T16:02', end: '2026-09-17T16:49', dur: 2820, gaps: 0, grams: 118, wsrc: 'typed', cost: 1.35, spool: 52, use: 'business' },
|
||
{ id: 207, job: 'Planter_small', key: '/garden/planter_small.gcode', printer: 3, status: 'finished', start: '2026-09-16T10:20', end: '2026-09-16T16:00', dur: 20400, gaps: 0, grams: 212, wsrc: 'recalled', cost: 4.24, spool: 55, use: 'personal' },
|
||
{ id: 206, job: 'Bracket_M3', key: '/brackets/m3.gcode', printer: 1, status: 'finished', start: '2026-09-16T08:05', end: '2026-09-16T09:17', dur: 4320, gaps: 0, grams: null, wsrc: null, cost: null, spool: null, use: null },
|
||
{ id: 205, job: 'Filament_clip', key: '/misc/filament_clip.gcode', printer: 1, status: 'finished', start: '2026-09-15T14:40', end: '2026-09-15T15:18', dur: 2280, gaps: 0, grams: 7, wsrc: 'sliced', cost: null, spool: null, suggested: 41, use: 'personal' },
|
||
{ id: 201, job: 'Test_cube', key: '/calibration/test_cube.gcode', printer: 1, status: 'finished', start: '2026-08-07T14:11', end: '2026-08-07T14:42', dur: 1860, gaps: null, grams: 9, wsrc: 'unknown', cost: 0.10, spool: 52, use: null, unreconciled: true }
|
||
];
|
||
|
||
var LOW = 200;
|
||
|
||
/* ── Iteration 3 additions ───────────────────────────────────────────── */
|
||
/* Lookup lists (Lists screen). `used` = spools referencing the value in the
|
||
* real 105-spool inventory where known from import/filament_review.tsv. Brand
|
||
* tares are the last-resort guess the form labels as such. */
|
||
var LOOKUPS = {
|
||
brands: [['Sunlu', 35, null], ['Elegoo', 27, null], ['Overture', 7, null], ['TECSONAR', 6, null], ['Polymaker', 4, null], ['Inland', 4, null], ['ZIRO', 3, null], ['iSANMATE', 3, null], ['Sovol', 2, null], ['JAYO', 2, null], ['GratKit', 2, null], ['Creality', 2, null], ['AMOLEN', 2, null], ['TINMORRY', 1, null], ['Kingroon', 1, null], ['Geeetech', 1, null], ['Anycubic', 1, null], ['3DimsFila', 1, null], ['Prusament', 0, null], ['eSun', 0, null], ['Hatchbox', 0, null], ['Bambu Lab', 0, null]],
|
||
materials: [['PLA', 55], ['PETG', 34], ['ASA', 10], ['TPU', 3], ['PA6', 2], ['ABS', 1]],
|
||
types: [['Standard', 0], ['CF', 3], ['GF', 4], ['+', 6], ['+ 2.0', 14], ['HF', 0], ['95A', 3]],
|
||
speeds: [['Standard', 0], ['Rapid', 18], ['High Speed', 9]],
|
||
finishes: [['Basic', 44], ['Matte', 17], ['Silk', 6], ['Sparkle', 3], ['Translucent', 4], ['Metallic', 0], ['Wood', 5], ['Marble', 12], ['Rock', 1], ['Gradient', 9], ['Glow', 0]],
|
||
spool_types: [['Generic — 1 kg spool (assumed)', 33, 180, 'estimated'], ['Sunlu — 1 kg plastic', 23, 140, 'community'], ['Elegoo — 1 kg cardboard', 20, 154, 'manufacturer'], ['Sunlu — 250 g mini', 8, 60, 'community'], ['Overture — cardboard, PLA lines', 6, 155, 'community'], ['Refill — bare cardboard core', 4, 35, 'estimated'], ['Inland — PLA Basics cardboard', 4, 143, 'community'], ['Polymaker — 0.5 kg cardboard', 3, 190, 'manufacturer'], ['Polymaker — 1 kg cardboard', 1, 140, 'manufacturer'], ['Overture — cardboard, PETG / ASA', 1, 132, 'community'], ['Bambu Lab — reusable', 0, 250, 'manufacturer'], ['Bambu Lab — cardboard', 0, 196, 'estimated'], ['Hatchbox — 1 kg', 0, 225, 'manufacturer'], ['Creality — Hyper cardboard', 0, 176, 'community']]
|
||
};
|
||
|
||
/* Holder ids as the database numbers them (used on holder labels, /c/:id). */
|
||
var HOLDER_IDS = { 'p1-ams1': 1, 'p1-ext': 2, 'p2-ams1': 3, 'p2-ext': 4, 'p3-lite': 5, 'p3-ext': 6 };
|
||
|
||
/* Access (humans come from Authentik; Extrudex shows its local projection). */
|
||
var PEOPLE = [
|
||
{ id: 1, name: 'Joshua King', email: 'owner@cubecraft (sample)', role: 'admin', lastSeen: '2026-09-18T09:40', sessions: [{ created: '2026-09-18T07:02', lastSeen: '2026-09-18T09:40', idleExp: '2026-09-18T17:40', maxExp: '2026-09-25T07:02', revoked: null, you: true }, { created: '2026-09-15T20:11', lastSeen: '2026-09-17T22:03', idleExp: '2026-09-18T06:03', maxExp: '2026-09-22T20:11', revoked: null }] },
|
||
{ id: 2, name: 'Jordyn', email: 'jordyn@cubecraft (sample)', role: 'operator', lastSeen: '2026-09-17T19:28', sessions: [{ created: '2026-09-17T18:50', lastSeen: '2026-09-17T19:28', idleExp: '2026-09-18T03:28', maxExp: '2026-09-24T18:50', revoked: null }] },
|
||
{ id: 3, name: 'Guest tablet', email: null, role: 'reader', lastSeen: '2026-09-09T12:00', sessions: [] }
|
||
];
|
||
var MACHINES = [
|
||
{ id: 10, name: 'hermes-gateway', role: 'operator', tokens: [{ id: 21, name: 'gateway 2026-09', created: '2026-09-01T10:00', lastUsed: '2026-09-18T09:39', revoked: null }, { id: 17, name: 'gateway 2026-08', created: '2026-08-03T10:00', lastUsed: '2026-08-31T23:58', revoked: '2026-09-01T10:02' }] },
|
||
{ id: 11, name: 'uptime-kuma', role: 'reader', tokens: [{ id: 22, name: 'health probe', created: '2026-09-05T14:30', lastUsed: '2026-09-18T09:41', revoked: null }] }
|
||
];
|
||
var AUDIT = [
|
||
{ id: 9031, who: 'Joshua King', action: 'spool.weighed', type: 'spool', rid: 47, ctx: '860 g → 812 g · tare 262 g (spool)', at: '2026-09-18T09:39' },
|
||
{ id: 9030, who: 'hermes-gateway', action: 'print.spool_set', type: 'print', rid: 211, ctx: 'spool #42 · 46 g deducted at $11.74/kg', at: '2026-09-18T05:28' },
|
||
{ id: 9029, who: 'Jordyn', action: 'spool.moved', type: 'spool', rid: 55, ctx: 'Shelf D → A1 · AMS Lite', at: '2026-09-17T19:26' },
|
||
{ id: 9028, who: 'Jordyn', action: 'print.use_type_set', type: 'print', rid: 209, ctx: 'personal', at: '2026-09-17T19:25' },
|
||
{ id: 9027, who: 'Joshua King', action: 'lookup.updated', type: 'spool_type', rid: 4, ctx: 'Sunlu — 250 g mini · tare 60 g · community', at: '2026-09-17T08:12' },
|
||
{ id: 9026, who: 'Joshua King', action: 'printer.updated', type: 'printer', rid: 3, ctx: 'ip changed · access code unchanged', at: '2026-09-16T21:40' },
|
||
{ id: 9025, who: 'Joshua King', action: 'session.revoked', type: 'principal', rid: 3, ctx: '1 local session', at: '2026-09-09T12:05' }
|
||
];
|
||
var WEBHOOK = { enabled: true, schedule: 'Mondays 08:00 America/New_York', threshold: 200, target: 'configured (URL kept in a root-only file, never shown)',
|
||
events: [{ period: '2026-W38', observed: '2026-09-14T08:00', state: 'delivered', attempts: 1, spools: 4, delivered: '2026-09-14T08:00' }, { period: '2026-W37', observed: '2026-09-07T08:00', state: 'delivered', attempts: 3, spools: 3, delivered: '2026-09-07T08:21', lastError: 'HTTP 502 from receiver (retried)' }, { period: '2026-W36', observed: '2026-08-31T08:00', state: 'no_low', attempts: 0, spools: 0 }] };
|
||
|
||
/* Iteration-2 shared behaviour: shell, navigation badges, command palette,
|
||
* drawers (spool / print / load), toasts, freshness and demo states.
|
||
* Plain browser JS, no dependencies, works offline. */
|
||
(function () {
|
||
'use strict';
|
||
|
||
/* ── helpers ─────────────────────────────────────────────── */
|
||
var $ = function (s, r) { return (r || document).querySelector(s); };
|
||
var $$ = function (s, r) { return Array.prototype.slice.call((r || document).querySelectorAll(s)); };
|
||
function esc(s) { return String(s == null ? '' : s).replace(/[&<>"]/g, function (c) { return { '&': '&', '<': '<', '>': '>', '"': '"' }[c]; }); }
|
||
function g(n) { return n == null ? '—' : Math.round(n).toLocaleString('en-US') + ' g'; }
|
||
function kg(n) { return (n / 1000).toFixed(2) + ' kg'; }
|
||
function usd(n) { return n == null ? '—' : '$' + n.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 }); }
|
||
function dur(s) { if (s == null) return '—'; if (s < 60) return '<1m'; var h = Math.floor(s / 3600), m = Math.floor(s % 3600 / 60); return h ? h + 'h ' + String(m).padStart(2, '0') + 'm' : m + 'm'; }
|
||
function when(iso) {
|
||
if (!iso) return '—';
|
||
var d = new Date(iso), t = d.toLocaleTimeString('en-GB', { hour: '2-digit', minute: '2-digit' });
|
||
var days = Math.round((new Date(NOW.toDateString()) - new Date(d.toDateString())) / 864e5);
|
||
if (days === 0) return 'Today ' + t;
|
||
if (days === 1) return 'Yesterday ' + t;
|
||
return d.getDate() + ' ' + d.toLocaleString('en-GB', { month: 'short' }) + ' ' + t;
|
||
}
|
||
function ago(sec) { if (sec < 60) return sec + 's ago'; var m = Math.floor(sec / 60); if (m < 60) return m + 'm ago'; var h = Math.floor(m / 60); return h < 24 ? h + 'h ago' : Math.floor(h / 24) + 'd ago'; }
|
||
function since(iso) { return Math.max(0, Math.round((NOW - new Date(iso)) / 1000)); }
|
||
|
||
var byId = function (arr, id) { for (var i = 0; i < arr.length; i++) if (arr[i].id === id) return arr[i]; return null; };
|
||
function spool(id) { return byId(SPOOLS, id); }
|
||
function printer(id) { return byId(PRINTERS, id); }
|
||
function print(id) { return byId(PRINTS, id); }
|
||
function container(cid) {
|
||
for (var i = 0; i < PRINTERS.length; i++) for (var j = 0; j < PRINTERS[i].containers.length; j++)
|
||
if (PRINTERS[i].containers[j].id === cid) return { printer: PRINTERS[i], c: PRINTERS[i].containers[j] };
|
||
return null;
|
||
}
|
||
function contents(cid) { return SPOOLS.filter(function (s) { return s.cont === cid; }); }
|
||
function fullName(s) { return [s.brand, s.speed, s.mat, s.type, s.fin].filter(Boolean).join(' '); }
|
||
function short(s) { return s.brand + ' ' + s.color; }
|
||
function where(s) {
|
||
if (!s.cont) return { loaded: false, text: s.loc || 'Storage', short: s.loc || 'Storage' };
|
||
var c = container(s.cont);
|
||
return { loaded: true, text: c.printer.name + ' · ' + c.c.label, short: c.printer.model + ' · ' + c.c.label, printer: c.printer };
|
||
}
|
||
/* Unknown, or an estimated figure nobody weighed: the weigh-in would rest on a guess. */
|
||
function unverified(s) { return s.tare == null || s.tsrc === 'estimated'; }
|
||
function gross(s) { return s.tare == null ? null : s.tare + s.rem; }
|
||
function value(s) { return s.rem / 1000 * s.kg; }
|
||
function low(s) { return s.rem < LOW; }
|
||
function sw(colors, cls) { return '<span class="swatch ' + (cls || '') + '" aria-hidden="true">' + colors.map(function (h) { return '<i style="background:' + h + '"></i>'; }).join('') + '</span>'; }
|
||
function meter(s) {
|
||
var pct = Math.max(0, Math.min(100, s.rem / s.tot * 100)), tick = Math.min(100, LOW / s.tot * 100);
|
||
return '<div class="meter' + (low(s) ? ' is-low' : '') + '" role="meter" aria-valuemin="0" aria-valuemax="' + s.tot + '" aria-valuenow="' + s.rem + '" aria-label="' + esc(s.color) + ' remaining"><span style="width:' + pct + '%"></span><i class="tick" style="left:' + tick + '%" title="200 g low-stock line"></i></div>';
|
||
}
|
||
|
||
/* provenance: quiet icon + words */
|
||
var TSRC = { spool: ['⚖', 'weighed bare', ''], manufacturer: ['◆', 'manufacturer', ''], community: ['◇', 'community', ''], estimated: ['≈', 'estimated', 'warn'] };
|
||
function tareProv(s) {
|
||
if (s.tare == null) return '<span class="prov warn"><i>?</i>empty spool weight unknown</span>';
|
||
var t = TSRC[s.tsrc]; return '<span class="prov ' + t[2] + '" title="' + esc(s.tsrc === 'spool' ? 'This reel was weighed empty' : 'From spool type: ' + s.stype) + '"><i>' + t[0] + '</i>' + t[1] + '</span>';
|
||
}
|
||
function remProv(s) { return s.remSrc === 'weighed' ? '<span class="prov"><i>⚖</i>weighed' + (s.weighedAt ? ' ' + esc(s.weighedAt) : '') + '</span>' : '<span class="prov"><i>✎</i>typed</span>'; }
|
||
var WSRC = { typed: ['✎', 'typed', '', 'A measurement someone entered. Survives an identity correction.'], sliced: ['▦', 'sliced', 'info', "Read from this run's 3MF over FTPS."], recalled: ['⎘', 'recalled', 'info', 'Copied from the weight remembered for this gcode path. No longer written.'], unknown: ['?', 'source unknown', 'warn', 'Written before the source was recorded. Neither correction path will move it.'] };
|
||
function weightProv(p) { if (!p.wsrc) return ''; var w = WSRC[p.wsrc]; return '<span class="prov ' + w[2] + '" title="' + esc(w[3]) + '"><i>' + w[0] + '</i>' + w[1] + '</span>'; }
|
||
function durCell(p) {
|
||
if (p.status === 'running') return '<span class="num">' + dur(since(p.start)) + '</span><span class="sub">so far</span>';
|
||
if (p.status === 'unknown') return '<span class="muted" title="Nobody saw this print end. The only measurable figure is the outage, which is not how long anything printed.">no duration</span><span class="sub">end not seen</span>';
|
||
if (p.gaps) return '<span class="num strong" style="color:var(--status-warning)" title="The connection dropped ' + (p.gaps === 1 ? 'once' : p.gaps + ' times') + ' during this print. That time is inside the figure and nobody watched it, so it is an upper bound.">≤ ' + dur(p.dur) + '</span><span class="sub prov warn">upper bound · ' + p.gaps + ' gap' + (p.gaps > 1 ? 's' : '') + '</span>';
|
||
if (p.gaps == null) return '<span class="num">' + dur(p.dur) + '</span><span class="sub" title="Predates gap counting: nobody asked whether the connection dropped.">gaps not counted</span>';
|
||
return '<span class="num">' + dur(p.dur) + '</span>';
|
||
}
|
||
var PST = { running: ['run', 'Running'], finished: ['ok', 'Finished'], failed: ['err', 'Failed'], unknown: ['warn', 'Unknown'] };
|
||
function printSt(p) { var s = PST[p.status]; return '<span class="st ' + s[0] + '">' + s[1] + '</span>'; }
|
||
function printerSt(pr) {
|
||
if (!pr.connected) return '<span class="st warn">' + (pr.lifecycle === 'reconnecting' ? 'Reconnecting' : 'Offline') + '</span>';
|
||
if (pr.state === 'RUNNING') return '<span class="st run">Printing</span>';
|
||
if (pr.state === 'PAUSE') return '<span class="st warn">Paused</span>';
|
||
if (pr.state === 'FAILED') return '<span class="st err">Failed</span>';
|
||
return '<span class="st idle">Idle</span>';
|
||
}
|
||
/* what a print still needs; empty while running */
|
||
function needs(p) {
|
||
if (p.status === 'running') return [];
|
||
var n = [];
|
||
if (p.grams == null) n.push({ k: 'weight', t: 'filament weight' });
|
||
if (p.filaments) p.filaments.forEach(function (f) { if (f.spool == null) n.push({ k: 'spool', t: 'spool for color ' + (f.slot + 1), f: f }); });
|
||
else if (p.spool == null) n.push({ k: 'spool', t: 'spool' });
|
||
return n;
|
||
}
|
||
function printsNeeding() { return PRINTS.filter(function (p) { return needs(p).length && !p.archived; }); }
|
||
function spoolLinks(p) {
|
||
if (p.filaments) return p.filaments.map(function (f) { var s = f.spool && spool(f.spool); return s ? '<a href="#spool=' + s.id + '" data-spool="' + s.id + '">' + sw(s.c, 'swatch-sm') + ' ' + esc(short(s)) + '</a>' : '<span class="prov warn"><i>!</i>color ' + (f.slot + 1) + ' unattributed</span>'; }).join('<br>');
|
||
var s = p.spool && spool(p.spool);
|
||
return s ? '<a href="#spool=' + s.id + '" data-spool="' + s.id + '" class="nowrap">' + sw(s.c, 'swatch-sm') + ' ' + esc(short(s)) + '</a>' : (p.status === 'running' ? '<span class="muted">—</span>' : '<span class="prov warn"><i>!</i>no spool</span>');
|
||
}
|
||
|
||
/* ── shell ───────────────────────────────────────────────── */
|
||
var ICON = {
|
||
dashboard: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><rect x="3" y="3" width="7" height="9" rx="2"/><rect x="14" y="3" width="7" height="5" rx="2"/><rect x="14" y="12" width="7" height="9" rx="2"/><rect x="3" y="16" width="7" height="5" rx="2"/></svg>',
|
||
filament: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><circle cx="12" cy="12" r="9"/><circle cx="12" cy="12" r="3"/><path d="M12 3v6M12 15v6"/></svg>',
|
||
prints: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M5 20h14M7 16l3-9h4l3 9M9 12h6"/></svg>',
|
||
printers: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><rect x="4" y="3" width="16" height="18" rx="2"/><path d="M8 17h8M9 7h6v5H9z"/></svg>',
|
||
lists: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M9 6h11M9 12h11M9 18h11M4 6h.01M4 12h.01M4 18h.01"/></svg>',
|
||
labels: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M3 12V4h8l10 10-8 8z"/><circle cx="7.5" cy="7.5" r="1.5"/></svg>',
|
||
access: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><circle cx="9" cy="8" r="4"/><path d="M2 21a7 7 0 0 1 14 0M17 11l2 2 4-4"/></svg>',
|
||
system: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M4 6h16M4 12h10M4 18h7"/><circle cx="18" cy="16" r="3"/></svg>',
|
||
more: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><circle cx="5" cy="12" r="1.6"/><circle cx="12" cy="12" r="1.6"/><circle cx="19" cy="12" r="1.6"/></svg>',
|
||
search: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><circle cx="11" cy="11" r="7"/><path d="m20 20-4-4"/></svg>',
|
||
sun: '<svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4"/></svg>'
|
||
};
|
||
var MANAGE = [['lists', 'Lists', '05-lists.html', 0], ['labels', 'Labels', '06-labels.html', 0], ['access', 'Access', '07-access.html', 1], ['system', 'System', '08-system.html', 1]];
|
||
var PAGES = [['dashboard', 'Dashboard', '01-dashboard.html'], ['filament', 'Filament', '02-filament.html'], ['prints', 'Prints', '03-prints.html'], ['printers', 'Printers', '04-printers.html']];
|
||
function badges() {
|
||
var need = printsNeeding().length, down = PRINTERS.filter(function (p) { return !p.connected; }).length;
|
||
var unv = SPOOLS.filter(unverified), unk = unv.filter(function (s) { return s.tare == null; }).length;
|
||
return {
|
||
filament: unv.length ? '<span class="count count-warn" title="' + unv.length + ' spools with an unverified empty spool weight: ' + unk + ' unknown, ' + (unv.length - unk) + ' estimated">' + unv.length + '</span>' : '',
|
||
prints: need ? '<span class="count count-warn" title="' + need + ' prints need a spool or weight">' + need + '</span>' : '',
|
||
printers: down ? '<span class="count count-err" title="' + down + ' printer not connected">' + down + '</span>' : ''
|
||
};
|
||
}
|
||
function shell(page) {
|
||
var b = badges();
|
||
var nav = PAGES.map(function (p) { return '<a href="' + p[2] + '"' + (p[0] === page ? ' aria-current="page"' : '') + '>' + ICON[p[0]] + '<span>' + p[1] + '</span>' + (b[p[0]] || '') + '</a>'; }).join('');
|
||
var unl = SPOOLS.filter(function (s) { return !s.label; }).length;
|
||
var rail = '<nav class="rail nm-raised" aria-label="Main">' +
|
||
'<a class="wordmark" href="01-dashboard.html" style="text-decoration:none;color:inherit"><span class="wordmark-mark" aria-hidden="true">X</span>Extrudex</a>' +
|
||
'<div class="nav nav-group">' + nav + '</div>' +
|
||
'<div class="nav nav-group"><span class="nav-label">Manage</span>' +
|
||
MANAGE.map(function (m) { return '<a href="' + m[2] + '"' + (m[0] === page ? ' aria-current="page"' : '') + '>' + ICON[m[0]] + '<span>' + m[1] + '</span>' + (m[0] === 'labels' ? '<span class="count" style="color:var(--text-secondary)" title="' + unl + ' spools without a printed label">' + unl + '</span>' : m[3] ? '<span class="sub" style="margin-left:auto">admin</span>' : '') + '</a>'; }).join('') + '</div>' +
|
||
'<div class="rail-foot"><div class="seg" role="group" aria-label="Color theme"><button type="button" data-set-theme="light">Light</button><button type="button" data-set-theme="dark">Dark</button></div>' +
|
||
'<span class="small muted">Signed in · operator</span></div></nav>';
|
||
var inManage = MANAGE.some(function (m) { return m[0] === page; });
|
||
var tabbar = '<nav class="tabbar nm-raised" aria-label="Main">' + PAGES.map(function (p) { return '<a href="' + p[2] + '"' + (p[0] === page ? ' aria-current="page"' : '') + '>' + ICON[p[0]] + (b[p[0]] || '') + '<span>' + p[1] + '</span></a>'; }).join('') +
|
||
'<a href="#" data-menu="moremenu" aria-haspopup="menu" aria-expanded="false"' + (inManage ? ' aria-current="page"' : '') + '>' + ICON.more + '<span>More</span></a></nav>' +
|
||
'<div class="menu more-sheet nm-dialog" id="moremenu" role="menu" aria-label="More">' + MANAGE.map(function (m) { return '<a role="menuitem" href="' + m[2] + '">' + ICON[m[0]] + ' ' + m[1] + (m[3] ? '<span class="hint">admin</span>' : '') + '</a>'; }).join('') + '<hr><button role="menuitem" type="button" data-toggle-theme>' + ICON.sun + ' Switch light / dark</button></div>';
|
||
var top = '<div class="topbar"><button class="search-trigger" type="button" data-palette>' + ICON.search + '<span style="flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis"><span class="long">Search spools, prints, printers…</span><span class="short">Search…</span></span><span class="kbd long">⌘K</span></button>' +
|
||
'<span class="fresh" id="fresh" role="status"><span class="dot" aria-hidden="true"></span><span id="fresh-t">Live · updated 3s ago</span></span>' +
|
||
'<button class="btn btn-icon btn-sm" type="button" data-toggle-theme aria-label="Switch color theme">' + ICON.sun + '</button></div>';
|
||
var mock = '<div class="mock"><button type="button" data-menu="mockmenu" aria-expanded="false">Mockup<span class="long"> · v3 iteration 3</span></button><div class="menu" id="mockmenu" role="dialog" aria-label="About this mockup">' +
|
||
'<p><b>Sample data</b>, based on the real inventory. Nothing is wired to the API.</p>' +
|
||
'<p><span class="prov info"><i>◈</i>feature</span> fields exist only on the printer-connections branch. <span class="prov warn"><i>⚠</i>API gap</span> means stored but not served.</p><hr>' +
|
||
'<button type="button" data-demo="loading">Replay first load (skeletons)</button>' +
|
||
'<button type="button" data-demo="offline">Simulate polling failure</button>' +
|
||
'<button type="button" data-demo="online">Back to live</button><hr>' +
|
||
'<a href="index.html">Flow map of all screens →</a></div></div>';
|
||
var drawer = '<div class="drawer-scrim" data-close-drawer></div><aside class="drawer nm-dialog" id="drawer" role="dialog" aria-modal="true" aria-labelledby="drawer-title" hidden></aside>';
|
||
var pal = '<div class="pal-scrim" id="pal" role="presentation"><div class="pal nm-dialog" role="dialog" aria-modal="true" aria-label="Search and commands"><div class="pal-input">' + ICON.search +
|
||
'<input id="pal-q" role="combobox" aria-expanded="true" aria-controls="pal-list" aria-autocomplete="list" placeholder="Type a spool, #id, job, printer or action…" autocomplete="off"><span class="kbd">esc</span></div>' +
|
||
'<div class="pal-list" id="pal-list" role="listbox"></div><div class="pal-foot"><span><span class="kbd">↑</span> <span class="kbd">↓</span> move</span><span><span class="kbd">↵</span> open</span><span><span class="kbd">/</span> or <span class="kbd">⌘K</span> anywhere</span></div></div></div>';
|
||
return { rail: rail, tabbar: tabbar, top: top, rest: mock + drawer + pal + '<div class="toasts" id="toasts" aria-live="polite"></div>' };
|
||
}
|
||
|
||
/* ── theme ───────────────────────────────────────────────── */
|
||
function setTheme(v) {
|
||
document.documentElement.setAttribute('data-theme', v);
|
||
$$('[data-set-theme]').forEach(function (b) { b.setAttribute('aria-pressed', String(b.getAttribute('data-set-theme') === v)); });
|
||
try { localStorage.setItem('extrudex-v3-theme', v); } catch (e) {}
|
||
}
|
||
|
||
/* ── toasts ──────────────────────────────────────────────── */
|
||
function updateBadges() {
|
||
var b = badges();
|
||
$$('.nav a, .tabbar a').forEach(function (a) {
|
||
var key = { '02-filament.html': 'filament', '03-prints.html': 'prints', '04-printers.html': 'printers' }[a.getAttribute('href')]; if (!key) return;
|
||
var old = a.querySelector('.count'); if (old) old.parentNode.removeChild(old);
|
||
if (b[key]) a.querySelector('span').insertAdjacentHTML(a.closest('.tabbar') ? 'beforebegin' : 'afterend', b[key]);
|
||
});
|
||
}
|
||
function toast(html, kind) {
|
||
setTimeout(updateBadges, 0);
|
||
var t = document.createElement('div'); t.className = 'toast ' + (kind || ''); t.setAttribute('role', 'status');
|
||
t.innerHTML = '<span>' + html + '</span><button class="btn btn-sm btn-icon" type="button" aria-label="Dismiss">✕</button>';
|
||
$('#toasts').appendChild(t);
|
||
var kill = function () { if (t.parentNode) t.parentNode.removeChild(t); };
|
||
t.querySelector('button').onclick = kill; setTimeout(kill, 6000);
|
||
}
|
||
|
||
/* ── drawer ──────────────────────────────────────────────── */
|
||
var D = { kind: null, id: null, tab: 'overview', state: 'normal', list: null, lastFocus: null, dirty: false };
|
||
function openDrawer(kind, id, tab, push) {
|
||
D.kind = kind; D.id = id; D.tab = tab || 'overview'; D.state = 'normal'; D.dirty = false;
|
||
if (!document.body.classList.contains('drawer-open')) D.lastFocus = document.activeElement;
|
||
renderDrawer();
|
||
var el = $('#drawer'); el.hidden = false;
|
||
requestAnimationFrame(function () { document.body.classList.add('drawer-open'); var f = el.querySelector('[autofocus]') || el.querySelector('h2'); if (f) { f.setAttribute('tabindex', f.tagName === 'H2' ? '-1' : f.getAttribute('tabindex')); f.focus(); } });
|
||
var h = '#' + kind + '=' + id + (D.tab !== 'overview' ? '&tab=' + D.tab : '');
|
||
if (push !== false && location.hash !== h) history.pushState({ d: 1 }, '', h);
|
||
$$('[data-row-spool],[data-row-print]').forEach(function (r) { r.classList.toggle('is-selected', (kind === 'spool' && +r.getAttribute('data-row-spool') === id) || (kind === 'print' && +r.getAttribute('data-row-print') === id)); });
|
||
}
|
||
function closeDrawer(push) {
|
||
if (D.dirty && !confirmDiscard()) return;
|
||
document.body.classList.remove('drawer-open'); D.kind = null; D.session = false; D.brand = null;
|
||
setTimeout(function () { if (!D.kind) $('#drawer').hidden = true; }, 240);
|
||
if (push !== false && location.hash) history.pushState({}, '', location.pathname + location.search);
|
||
$$('.is-selected').forEach(function (r) { r.classList.remove('is-selected'); });
|
||
if (D.lastFocus && D.lastFocus.focus) D.lastFocus.focus();
|
||
}
|
||
function confirmDiscard() {
|
||
var bar = $('#discard-bar'); if (bar) { bar.hidden = false; bar.querySelector('button').focus(); return false; }
|
||
return true;
|
||
}
|
||
function renderDrawer() {
|
||
var el = $('#drawer');
|
||
if (D.kind === 'spool') el.innerHTML = spoolDrawer(spool(D.id));
|
||
else if (D.kind === 'new') el.innerHTML = editDrawer(null);
|
||
else if (D.kind === 'print') el.innerHTML = printDrawer(print(D.id));
|
||
else if (D.kind === 'load') el.innerHTML = loadDrawer(D.id);
|
||
else if (D.kind === 'pedit') el.innerHTML = peditDrawer(D.id);
|
||
else if (D.kind === 'holders') el.innerHTML = holdersDrawer(D.id);
|
||
else if (D.kind === 'premove') el.innerHTML = premoveDrawer(D.id);
|
||
if (window.PAGE && PAGE.onDrawer) PAGE.onDrawer();
|
||
}
|
||
function head(icon, title, sub, extra) {
|
||
var list = D.list || [], i = list.indexOf(D.id);
|
||
var nav = (D.kind === 'spool' && i >= 0 && list.length > 1) ? '<div class="drawer-nav"><button class="btn btn-sm btn-icon" type="button" data-step="-1" aria-label="Previous spool in list" ' + (i === 0 ? 'disabled' : '') + '>↑</button><button class="btn btn-sm btn-icon" type="button" data-step="1" aria-label="Next spool in list" ' + (i === list.length - 1 ? 'disabled' : '') + '>↓</button></div>' : '';
|
||
return '<div class="drawer-head">' + icon + '<div style="flex:1;min-width:0"><h2 id="drawer-title">' + title + '</h2><span class="sub">' + sub + '</span>' + (extra || '') + '</div>' + nav + '<button class="btn btn-sm btn-icon" type="button" data-close-drawer aria-label="Close panel (Esc)">✕</button></div>';
|
||
}
|
||
function tabs(list) {
|
||
return '<div class="drawer-tabs"><div class="tabs" role="tablist" aria-label="Spool panel">' + list.map(function (t) { return '<button type="button" role="tab" data-tab="' + t[0] + '" aria-selected="' + (D.tab === t[0]) + '">' + t[1] + '</button>'; }).join('') + '</div></div>';
|
||
}
|
||
|
||
function spoolDrawer(s) {
|
||
var w = where(s);
|
||
var h = head(sw(s.c, '" style="width:44px;height:44px'), esc(s.color) + ' <span class="muted" style="font-weight:400">#' + s.id + '</span>', esc(fullName(s)) + ' · ' + esc(w.text));
|
||
var t = tabs([['overview', 'Overview'], ['weigh', 'Weigh'], ['move', w.loaded ? 'Move' : 'Load / move'], ['edit', 'Edit']]);
|
||
var body = '', foot = '';
|
||
if (D.tab === 'overview') {
|
||
var used = PRINTS.filter(function (p) { return p.spool === s.id || (p.filaments || []).some(function (f) { return f.spool === s.id; }); });
|
||
var gr = gross(s);
|
||
body = '<div class="nm-inset panel"><div class="row" style="align-items:baseline"><span style="font-size:34px;font-weight:800;line-height:1" class="num' + (low(s) ? '" style="color:var(--status-warning)' : '') + '">' + Math.round(s.rem).toLocaleString('en-US') + '<span style="font-size:15px"> g</span></span><span class="muted">of ' + g(s.tot) + ' net</span><span class="spacer"></span>' + remProv(s) + '</div>' +
|
||
'<div style="margin-top:12px">' + meter(s) + '</div>' + (low(s) ? '<p class="small" style="margin:10px 0 0;color:var(--status-warning)"><b>⚠ Below the 200 g line.</b> Reorder or finish it on a small job.</p>' : '') + '</div>' +
|
||
'<div class="panel nm-raised-sm"><h3>On the scale</h3>' + (gr == null
|
||
? '<p class="small" style="margin:0">Gross is <b>unknown</b> because no empty spool weight is known. It is never treated as 0.</p><div class="row" style="margin-top:12px"><button class="btn btn-sm" type="button" data-tab="weigh">Set empty spool weight</button></div>'
|
||
: '<dl class="kv"><dt>Empty spool</dt><dd>' + g(s.tare) + ' ' + tareProv(s) + '</dd><dt>+ Filament (net)</dt><dd>' + g(s.rem) + '</dd><dt class="strong" style="color:var(--text-primary)">= Gross, on a scale</dt><dd class="strong">' + g(gr) + '</dd></dl>') + '</div>' +
|
||
'<dl class="kv"><dt>Location</dt><dd>' + (w.loaded ? '<a href="04-printers.html#printer=' + w.printer.id + '">' + esc(w.text) + '</a>' : esc(w.text)) + ' · <button class="btn btn-sm" type="button" data-tab="move" style="min-height:26px;padding:2px 8px">Move</button></dd>' +
|
||
'<dt>Value left</dt><dd>' + usd(value(s)) + ' <span class="sub" style="display:inline">at ' + usd(s.kg) + '/kg</span></dd>' +
|
||
'<dt>Spool type</dt><dd>' + (s.stype ? esc(s.stype) : '<span class="muted">none chosen</span>') + '</dd>' +
|
||
'<dt>Label</dt><dd>' + (s.label ? 'printed' : '<span class="prov warn"><i>!</i>not printed</span> <button class="btn btn-sm" type="button" data-fake="Label PDF generated for #' + s.id + '" style="min-height:26px;padding:2px 8px">Print label</button>') + '</dd>' +
|
||
'<dt>Record</dt><dd class="muted">version ' + s.v + '</dd></dl>' +
|
||
'<div><h3 class="section" style="margin-bottom:8px">Prints from this spool <span class="muted">· ' + used.length + '</span></h3>' + (used.length ? '<div class="stack" style="gap:6px">' + used.map(function (p) { var f = (p.filaments || []).filter(function (f) { return f.spool === s.id; })[0]; return '<a class="row nm-raised-sm" style="padding:8px 12px;text-decoration:none;color:inherit;gap:10px" href="#print=' + p.id + '" data-print="' + p.id + '">' + printSt(p) + '<span class="strong" style="flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis">' + esc(p.job) + '</span><span class="num">' + g(f ? f.grams : p.grams) + '</span><span class="sub" style="display:inline">' + when(p.start) + '</span></a>'; }).join('') + '</div>' : '<p class="small muted" style="margin:0">No prints drew from this spool yet.</p>') + '</div>' +
|
||
'<p class="small muted" style="margin:0"><span class="prov warn"><i>⚠</i>API gap</span> Location history is stored in <code>spool_location_event</code> but there is no endpoint to read it.</p>';
|
||
foot = '<button class="btn btn-danger btn-sm" type="button" data-delete="' + s.id + '" style="margin-right:auto">Delete…</button><button class="btn" type="button" data-tab="edit">Edit</button><button class="btn btn-primary" type="button" data-tab="weigh" ' + (s.tare == null ? 'title="Needs an empty spool weight first"' : '') + '>Weigh</button>';
|
||
}
|
||
if (D.tab === 'weigh') {
|
||
if (s.tare == null) {
|
||
body = '<div class="banner"><b>?</b><span>This spool has <b>no empty spool weight</b>, so a scale reading can\'t be turned into filament. Set one first. It is never assumed to be 0.</span></div>' +
|
||
'<label class="field stack" style="gap:6px"><span class="strong small">Pick a spool type</span><select class="select" autofocus><option>Choose…</option><option>Generic — 1 kg spool (assumed) · 180 g · estimated</option><option>Creality — Hyper cardboard · 176 g · community</option><option>Creality — legacy plastic · 138 g · community</option></select><span class="sub">Fastest. The figure\'s trust level is shown next to it.</span></label>' +
|
||
'<div class="row"><hr style="flex:1;border:0;border-top:1px solid var(--border-hairline)"><span class="sub">or, more accurate</span><hr style="flex:1;border:0;border-top:1px solid var(--border-hairline)"></div>' +
|
||
'<label class="field stack" style="gap:6px"><span class="strong small">Weigh the empty spool</span><input class="input num" inputmode="numeric" placeholder="e.g. 210 g"><span class="sub">Only possible once the spool is used up, or from an identical empty.</span></label>';
|
||
foot = '<button class="btn" type="button" data-tab="overview">Cancel</button><button class="btn btn-primary" type="button" data-fake="Empty spool weight saved for #' + s.id + '. Weigh-in unlocked.">Save empty spool weight</button>';
|
||
} else {
|
||
var wl = where(s), li = D.list ? D.list.indexOf(s.id) : -1;
|
||
body = (D.session && li >= 0 ? '<div class="row small" style="gap:10px"><span class="st run">Weigh session</span><span class="muted">spool ' + (li + 1) + ' of ' + D.list.length + ' · lightest first</span></div>' : '') +
|
||
'<div class="findit"><span class="sub">Find it</span><b>' + esc(wl.text) + '</b>' + sw(s.c, 'swatch-sm') + '<span class="muted small">' + esc(s.color) + ' · #' + s.id + '</span></div>' +
|
||
'<div class="panel nm-inset"><label class="stack" style="gap:8px"><span class="strong">Whole spool on the scale</span><input class="input num" id="scale" inputmode="numeric" autofocus placeholder="full reads ' + g(s.tare + s.tot) + '" style="font-size:22px;min-height:56px"><span class="sub">Remaining = reading − ' + g(s.tare) + ' empty spool (' + tareProv(s) + ')</span></label></div>' +
|
||
'<div class="panel nm-raised-sm" id="weigh-out" aria-live="polite"><h3>Result</h3><div class="row"><span class="muted">Now</span><b class="num">' + g(s.rem) + '</b><span class="muted">→</span><b class="num" id="w-after">—</b><span class="spacer"></span><span id="w-diff" class="num"></span></div><p class="small muted" id="w-note" style="margin:8px 0 0">Type the reading and the result appears here before you save.</p></div>';
|
||
foot = '<button class="btn" type="button" data-tab="overview">Cancel</button><button class="btn btn-primary" type="button" id="w-save" disabled>Save weigh-in</button>' + (D.list && D.list.indexOf(s.id) < D.list.length - 1 ? '<button class="btn" type="button" id="w-next" disabled>Save & weigh next ↓</button>' : '');
|
||
}
|
||
}
|
||
if (D.tab === 'move') {
|
||
var dests = PRINTERS.map(function (pr) {
|
||
return '<div class="stack" style="gap:8px"><div class="row"><b class="small">' + esc(pr.name) + '</b>' + (pr.connected ? '' : '<span class="prov warn"><i>⚡</i>disconnected (loading still recorded)</span>') + '</div><div class="row" style="gap:8px;align-items:stretch">' +
|
||
pr.containers.map(function (c) { var n = contents(c.id).length, full = n >= c.cap && s.cont !== c.id, here = s.cont === c.id; return '<button type="button" class="slot" style="min-height:64px;flex:1" data-dest="' + c.id + '" ' + (full ? 'disabled title="Full: unload a spool first"' : '') + ' aria-pressed="' + here + '"><span class="sn">' + esc(c.label) + '</span><span class="g">' + n + ' / ' + c.cap + (here ? ' · here now' : full ? ' · full' : '') + '</span></button>'; }).join('') + '</div></div>';
|
||
}).join('');
|
||
body = '<p class="small muted" style="margin:0">Where is the spool going? Full holders are disabled. Extrudex records which holder a spool is in, not its tray position.</p>' +
|
||
'<div class="stack" style="gap:8px"><b class="small">Storage</b><div class="row" style="gap:8px;align-items:stretch"><button type="button" class="slot" style="min-height:64px;flex:1" data-dest="storage" aria-pressed="' + !s.cont + '"><span class="sn">Shelf / storage</span><span class="g">' + (s.cont ? 'unload' : 'here now') + '</span></button></div>' +
|
||
'<label class="stack" style="gap:6px"><span class="sub">Where in storage (optional)</span><input class="input" value="' + esc(s.loc || '') + '" placeholder="Shelf A2, Dry box 1…"></label></div>' + dests;
|
||
foot = '<button class="btn" type="button" data-tab="overview">Cancel</button><button class="btn btn-primary" type="button" id="m-save" disabled>Move spool</button>';
|
||
}
|
||
if (D.tab === 'edit') { return editDrawer(s); }
|
||
return h + t + '<div class="drawer-body">' + body + '</div><div class="drawer-foot">' + foot + '</div>';
|
||
}
|
||
|
||
function editDrawer(s) {
|
||
var isNew = !s; s = s || { id: null, c: ['#888888'], color: '', brand: 'Sunlu', speed: null, mat: 'PLA', type: null, fin: 'Basic', rem: 1000, tot: 1000, tare: 140, tsrc: 'community', stype: 'Sunlu — 1 kg plastic', kg: 0, loc: '', v: null };
|
||
var st = D.state;
|
||
var h = isNew ? head('<span class="swatch" style="width:44px;height:44px"><i style="background:#888"></i></span>', 'Add spool', 'New spools carry no version') : head(sw(s.c, '" style="width:44px;height:44px'), 'Edit ' + esc(s.color) + ' <span class="muted" style="font-weight:400">#' + s.id + '</span>', 'Opened 09:32 at version ' + (st === 'conflict' ? s.v - 1 : s.v));
|
||
var t = isNew ? '' : tabs([['overview', 'Overview'], ['weigh', 'Weigh'], ['move', where(s).loaded ? 'Move' : 'Load / move'], ['edit', 'Edit']]);
|
||
function sel(label, val, req, opts) { return '<label class="stack" style="gap:6px"><span class="small strong">' + label + ' ' + (req ? '<span class="sub" style="display:inline;color:var(--text-accent)">required</span>' : '<span class="sub" style="display:inline">optional</span>') + '</span><select class="select"><option>' + (val || 'None') + '</option>' + (opts || '') + '</select></label>'; }
|
||
var demo = '<div class="row small" style="gap:8px"><span class="muted">Demo state</span><div class="tabs" role="group" aria-label="Demo state">' + [['normal', 'Normal'], ['errors', '400 errors'], ['conflict', '409 conflict']].map(function (x) { return '<button type="button" data-state="' + x[0] + '" aria-selected="' + (st === x[0]) + '">' + x[1] + '</button>'; }).join('') + '</div></div>';
|
||
var conflict = st === 'conflict' ? '<div class="nm-inset" role="alert" style="padding:16px;border:2px solid var(--status-warning);background:var(--wash-warning);border-radius:var(--radius-lg)"><b style="color:var(--status-warning)">⚠ This spool changed since you opened it. Nothing was saved.</b><p class="small" style="margin:8px 0">Someone weighed it at 09:39 (version ' + (s.v - 1) + ' → ' + s.v + '). Saving would have put the old remaining weight back.</p>' +
|
||
'<table class="tbl" style="font-size:12px"><thead><tr><th>Field</th><th class="r">You opened</th><th class="r">Now</th><th class="r">Your edit</th></tr></thead><tbody><tr><td>Remaining</td><td class="r">860 g</td><td class="r strong" style="color:var(--status-warning)">' + g(s.rem) + ' ⚖</td><td class="r muted">—</td></tr><tr><td>Location</td><td class="r">Shelf A3</td><td class="r">Shelf A3</td><td class="r strong accent">' + esc(where(s).short) + '</td></tr></tbody></table>' +
|
||
'<div class="row" style="margin-top:12px"><button class="btn btn-primary btn-sm" type="button" data-fake="Reloaded version ' + s.v + ' and reapplied your location change. Review, then save.">Reload & reapply my change</button><button class="btn btn-sm" type="button" data-state="normal">Reload, discard mine</button></div></div>' : '';
|
||
var errsum = st === 'errors' ? '<div role="alert" style="padding:14px 16px;border:2px solid var(--status-error);background:var(--wash-error);border-radius:var(--radius-lg)"><b style="color:var(--status-error)">⚠ 2 fields need fixing. Nothing was saved.</b><ul class="small" style="margin:6px 0 0;padding-left:18px"><li><a href="#e-rem">Remaining is more than the spool holds</a></li><li><a href="#e-name">Color name is empty</a></li></ul></div>' : '';
|
||
var name = '<div class="nm-inset" style="padding:12px 14px;border-radius:var(--radius-md)"><span class="sub">Reads as</span><div class="strong" style="font-size:17px">' + esc(fullName(s)) + '</div><span class="sub">brand · <i>speed</i> · material · <i>type</i> · finish (italic = optional)</span></div>';
|
||
var body = demo + conflict + errsum + name +
|
||
'<div class="fr2">' + sel('Brand', s.brand, 1) + sel('Print speed', s.speed, 0, '<option>Rapid</option><option>High Speed</option>') + sel('Material', s.mat, 1) + sel('Filament type', s.type, 0, '<option>CF</option><option>+ 2.0</option>') + sel('Finish', s.fin, 1) + '</div>' +
|
||
'<div class="fr2"><div class="stack' + (st === 'errors' ? ' field has-error' : '') + '" id="e-name" style="gap:6px"><label class="small strong" for="f-name">Color name <span class="sub" style="display:inline;color:var(--text-accent)">required</span></label><input class="input" id="f-name" value="' + (st === 'errors' ? '' : esc(s.color)) + '" ' + (st === 'errors' ? 'aria-invalid="true" aria-describedby="e-name-m"' : '') + ' placeholder="e.g. Charcoal">' + (st === 'errors' ? '<p class="field-error" id="e-name-m" style="margin:0">Give the color a name, even "Unknown". <span class="sub" style="display:inline">server: <code>color_name is required</code></span></p>' : '') + '</div>' +
|
||
'<label class="stack" style="gap:6px"><span class="small strong">Colors <span class="sub" style="display:inline">1–3</span></span><span class="row" style="gap:8px">' + sw(s.c) + '<input class="input" value="' + s.c[0] + '" style="max-width:110px"><button class="btn btn-sm" type="button">+ color</button></span></label></div>' +
|
||
'<div class="fr2"><label class="stack" style="gap:6px"><span class="small strong">Total <span class="sub" style="display:inline">net, as sold</span></span><input class="input num" value="' + g(s.tot) + '"></label>' +
|
||
'<div class="stack' + (st === 'errors' ? ' field has-error' : '') + '" id="e-rem" style="gap:6px"><label class="small strong" for="f-rem">Remaining <span class="sub" style="display:inline">net</span></label><input class="input num" id="f-rem" value="' + (st === 'errors' ? '1,040 g' : g(s.rem)) + '" ' + (st === 'errors' ? 'aria-invalid="true" aria-describedby="e-rem-m"' : '') + '>' + (st === 'errors' ? '<p class="field-error" id="e-rem-m" style="margin:0">1,040 g is more than this spool holds (' + g(s.tot) + '). <span class="sub" style="display:inline">server: <code>remaining_grams cannot exceed total_grams</code></span></p>' : '<span class="sub">To measure it, use the <b>Weigh</b> tab instead.</span>') + '</div></div>' +
|
||
'<div class="fr2"><label class="stack" style="gap:6px"><span class="small strong">Spool type <span class="sub" style="display:inline">supplies the empty spool weight</span></span><select class="select"><option>' + esc(s.stype || 'None chosen') + '</option></select></label>' +
|
||
'<label class="stack" style="gap:6px"><span class="small strong">Empty spool weight</span><input class="input num" value="' + (s.tare == null ? '' : g(s.tare)) + '" placeholder="unknown"><span class="sub">' + (s.tare == null ? '<span class="prov warn"><i>?</i>unknown, never 0</span>' : tareProv(s)) + '</span></label></div>' +
|
||
'<div class="fr2"><label class="stack" style="gap:6px"><span class="small strong">Cost per kg</span><input class="input num" value="' + usd(s.kg) + '"></label><label class="stack" style="gap:6px"><span class="small strong">Storage location <span class="sub" style="display:inline">optional</span></span><input class="input" value="' + esc(s.loc || '') + '"></label></div>' +
|
||
'<div class="banner" id="discard-bar" hidden><span style="flex:1">Discard unsaved changes?</span><button class="btn btn-sm" type="button" data-discard>Discard</button><button class="btn btn-sm" type="button" data-keep>Keep editing</button></div>';
|
||
var foot = (st === 'conflict' ? '<span class="small muted" style="margin-right:auto">Save is off until you choose a version.</span>' : '') + '<button class="btn" type="button" data-' + (isNew ? 'close-drawer' : 'tab="overview"') + '>Cancel</button><button class="btn btn-primary" type="button" ' + (st === 'conflict' ? 'disabled' : 'data-save') + '>' + (isNew ? 'Add spool' : 'Save changes') + '</button>';
|
||
return h + t + '<div class="drawer-body">' + body + '</div><div class="drawer-foot">' + foot + '</div>';
|
||
}
|
||
|
||
function printDrawer(p) {
|
||
var pr = printer(p.printer), n = needs(p);
|
||
var h = head('<span class="wordmark-mark" style="width:44px;height:44px;background:var(--surface-inset);color:var(--text-primary)">' + ICON.prints + '</span>', esc(p.job), printSt(p) + ' · ' + esc(pr.name) + ' · print #' + p.id);
|
||
var fix = '';
|
||
if (n.length) {
|
||
fix = '<div class="nm-inset panel" style="border:1px solid var(--status-warning)"><h3 style="color:var(--status-warning)">Needs ' + n.map(function (x) { return x.t; }).join(' + ') + '</h3>' + fixers(p) + '</div>';
|
||
}
|
||
var body = fix + '<dl class="kv"><dt>Printer</dt><dd><a href="04-printers.html#printer=' + pr.id + '">' + esc(pr.name) + '</a></dd><dt>Started</dt><dd>' + when(p.start) + '</dd><dt>Ended</dt><dd>' + (p.end ? when(p.end) : p.status === 'running' ? '<span class="muted">still running</span>' : '—') + '</dd>' +
|
||
'<dt>Duration</dt><dd>' + durCell(p) + '</dd>' +
|
||
'<dt>Job key</dt><dd><code>' + esc(p.key) + '</code><span class="sub">The identity. Two jobs can share a name, never a path.</span></dd>' +
|
||
'<dt>Filament</dt><dd>' + (p.grams == null ? '<span class="muted">not known yet</span>' : '<b class="num">' + g(p.grams) + '</b> ' + weightProv(p) + (p.status === 'failed' ? '<span class="sub">Failed prints record the full job weight. Correct by hand if needed.</span>' : '')) + '</dd>' +
|
||
'<dt>Spool</dt><dd>' + spoolLinks(p) + changeSpool(p) + '</dd><dt>Cost</dt><dd>' + (p.cost == null ? '<span class="muted">pending: needs weight and spool</span>' : usd(p.cost) + (p.filaments && p.filaments.some(function (f) { return f.spool == null; }) ? ' <span class="sub" style="display:inline">so far</span>' : '')) + '</dd>' +
|
||
'<dt>Use</dt><dd><div class="tabs" role="group" aria-label="Use type"><button type="button" aria-selected="' + (p.use === 'business') + '">Business</button><button type="button" aria-selected="' + (p.use === 'personal') + '">Personal</button></div>' + (p.use == null ? '<span class="sub">Not recorded</span>' : '') + '</dd></dl>' +
|
||
(p.filaments ? '<div><h3 class="section">Colors</h3><table class="tbl"><thead><tr><th>#</th><th>Color</th><th>Spool</th><th class="r">Grams</th><th class="r">Cost</th></tr></thead><tbody>' + p.filaments.map(function (f) { var s = f.spool && spool(f.spool); return '<tr><td>' + (f.slot + 1) + '</td><td><span class="swatch swatch-sm"><i style="background:' + f.hex + '"></i></span> <code>' + f.hex + '</code></td><td>' + (s ? '<a href="#spool=' + s.id + '" data-spool="' + s.id + '">' + esc(short(s)) + '</a>' : '<span class="prov warn"><i>!</i>unattributed</span>') + '</td><td class="r">' + g(f.grams) + '</td><td class="r">' + usd(f.cost) + '</td></tr>'; }).join('') + '</tbody></table></div>' : '');
|
||
var foot = p.status === 'running' ? '<span class="small muted">Name and weight are captured when the print ends.</span>' : '<button class="btn btn-sm" type="button" data-fake="Archived ' + esc(p.job) + '. Accounting unchanged." style="margin-right:auto">Archive</button><button class="btn" type="button" data-close-drawer>Done</button>';
|
||
return h + '<div class="drawer-body">' + body + '</div><div class="drawer-foot">' + foot + '</div>';
|
||
}
|
||
function changeSpool(p) {
|
||
var attributed = p.filaments ? p.filaments.filter(function (f) { return f.spool; }) : (p.spool ? [{ spool: p.spool, grams: p.grams, slot: null }] : []);
|
||
if (!attributed.length || p.status === 'running') return '';
|
||
if (p.unreconciled) return '<div class="banner err" role="alert" style="margin-top:8px;font-size:12px"><span><b>Can\'t move this one yet.</b> It was attributed to spool #' + p.spool + ' before Extrudex tracked deductions, so nothing records how much came off. Check that spool\'s remaining grams, then re-attribute this print to <b>the same spool</b> to reconcile it. After that it can move.</span></div>';
|
||
return attributed.map(function (f) {
|
||
var from = spool(f.spool);
|
||
return '<details class="chg"><summary>Change spool' + (f.slot != null ? ' for color ' + (f.slot + 1) : '') + '</summary><div class="stack" style="gap:8px;margin-top:8px"><select class="select" style="min-height:36px" aria-label="New spool">' + SPOOLS.filter(function (x) { return x.id !== f.spool; }).slice(0, 10).map(function (x) { return '<option>' + g(x.rem) + ' · ' + esc(short(x)) + ' · ' + x.mat + ' · #' + x.id + '</option>'; }).join('') + '</select>' +
|
||
'<span class="sub">' + (f.grams != null ? '<b>' + g(f.grams) + ' goes back</b> to ' + esc(short(from)) + ' #' + from.id + ' and comes off the spool you pick. Cost is re-rated at that spool\'s $/kg.' : 'No weight yet, so nothing moves until one is entered.') + '</span>' +
|
||
'<button class="btn btn-sm" type="button" data-fake="Moved ' + esc(p.job) + (f.grams != null ? ': ' + g(f.grams) + ' returned to ' + esc(short(from)) : '') + '">Move to this spool</button></div></details>';
|
||
}).join('');
|
||
}
|
||
function fixers(p) {
|
||
var out = '';
|
||
if (p.grams == null) out += '<label class="stack" style="gap:6px;margin-bottom:12px"><span class="small strong">Filament used</span><span class="row" style="gap:8px"><input class="input num" style="max-width:140px" placeholder="grams" inputmode="numeric" autofocus><button class="btn btn-sm" type="button" data-fake="Saved ' + esc(p.job) + ' weight · remembered for ' + esc(p.key) + '">Save weight</button></span><span class="sub">From the slicer. Remembered for <code>' + esc(p.key) + '</code> so later runs fill in by themselves.</span></label>';
|
||
var slots = p.filaments ? p.filaments.filter(function (f) { return f.spool == null; }) : (p.spool == null ? [{ suggested: p.suggested, slot: null }] : []);
|
||
slots.forEach(function (f) {
|
||
var sug = f.suggested && spool(f.suggested);
|
||
out += '<div class="stack" style="gap:6px;margin-bottom:8px"><span class="small strong">Spool' + (f.slot != null ? ' for color ' + (f.slot + 1) + ' <span class="swatch swatch-sm"><i style="background:' + f.hex + '"></i></span>' : '') + '</span>' +
|
||
(sug ? '<div class="row nm-raised-sm" style="padding:8px 12px;gap:10px">' + sw(sug.c, 'swatch-sm') + '<span style="flex:1"><b>' + esc(short(sug)) + '</b><span class="sub">Suggested: same color, loaded in ' + esc(where(sug).short) + '. Not charged until you confirm.</span></span><button class="btn btn-sm btn-primary" type="button" data-fake="Attributed to ' + esc(short(sug)) + ' · ' + g(f.grams || p.grams) + ' deducted">Use this</button></div>' : '') +
|
||
'<select class="select"><option>' + (sug ? 'Or pick another spool…' : 'Pick a spool…') + '</option>' + SPOOLS.slice(0, 8).map(function (s) { return '<option>' + g(s.rem) + ' · ' + esc(short(s)) + ' · ' + esc(s.mat) + '</option>'; }).join('') + '</select></div>';
|
||
});
|
||
return out;
|
||
}
|
||
var MODELS = { bambu: ['A1', 'A1 mini', 'P1P', 'P1S', 'X1 Carbon', 'X1E', 'H2D', 'H2C', 'H2S'], elegoo: ['Centauri', 'Centauri Carbon', 'OrangeStorm Giga'] };
|
||
function peditDrawer(id) {
|
||
var pr = id === 'new' ? null : printer(+id), st = D.state;
|
||
var h = head('<span class="wordmark-mark" style="width:44px;height:44px;background:var(--surface-inset);color:var(--text-primary)">' + ICON.printers + '</span>', pr ? 'Edit ' + esc(pr.name) : 'Add printer', pr ? 'Connection details · admin' : 'Extrudex connects read-only; nothing is sent to the printer');
|
||
var brand = pr ? 'bambu' : (D.brand || 'bambu');
|
||
function f(label, hint, input, err) { return '<div class="stack' + (err ? ' field has-error' : '') + '" style="gap:6px"><label class="small strong">' + label + (hint ? ' <span class="sub" style="display:inline">' + hint + '</span>' : '') + '</label>' + input + (err ? '<p class="field-error" style="margin:0">' + err + '</p>' : '') + '</div>'; }
|
||
var body = '<div class="row small" style="gap:8px"><span class="muted">Demo state</span><div class="tabs" role="group" aria-label="Demo state">' + [['normal', 'Normal'], ['errors', '400 errors']].map(function (x) { return '<button type="button" data-state="' + x[0] + '" aria-selected="' + (st === x[0]) + '">' + x[1] + '</button>'; }).join('') + '</div></div>' +
|
||
'<div class="fr2">' + f('Brand', '', '<select class="select" id="pe-brand"><option value="bambu"' + (brand === 'bambu' ? ' selected' : '') + '>Bambu Lab</option><option value="elegoo"' + (brand === 'elegoo' ? ' selected' : '') + '>Elegoo</option></select>') +
|
||
f('Model', '', '<select class="select">' + MODELS[brand].map(function (m) { return '<option' + (pr && pr.model === m ? ' selected' : '') + '>' + m + '</option>'; }).join('') + '</select>') + '</div>' +
|
||
f('Name', 'shown everywhere; must be unique', '<input class="input" value="' + (pr ? esc(pr.name) : '') + '" placeholder="X1C — Bench">', st === 'errors' && !pr ? 'A printer called “P1S - Prints Charming” already exists. Names must be unique.' : '') +
|
||
'<div class="fr2">' + f('IP address', 'same network as Extrudex', '<input class="input" value="' + (st === 'errors' ? '10.60.1.300' : pr ? esc(pr.ip) : '') + '" placeholder="10.60.1.10" inputmode="decimal">', st === 'errors' ? '10.60.1.300 isn\'t a valid IPv4 address.' : '') +
|
||
f('Serial', 'on the printer\'s label', '<input class="input" value="' + (pr ? esc(pr.serial) : '') + '">') + '</div>' +
|
||
(brand === 'bambu' ? f('Access code', pr ? 'leave blank to keep the stored code' : 'from the printer screen: Settings → LAN', '<input class="input" type="password" placeholder="' + (pr ? 'unchanged' : '8 characters') + '" autocomplete="off">') + '<p class="sub" style="margin:0">Stored, never shown again. Read-only MQTT needs only IP, serial and access code, no Developer Mode.</p>'
|
||
: '<p class="small muted" style="margin:0">Elegoo Centauri uses unauthenticated SDCP, so there is no access code.</p>') +
|
||
(pr ? '' : '<div class="nm-inset panel small"><b>After you add it</b><ul style="margin:6px 0 0;padding-left:18px"><li>An <b>External</b> holder is created automatically.</li><li>Add its AMS units under <b>Manage holders</b>.</li><li>Capture starts on the first status report.</li></ul></div>');
|
||
return h + '<div class="drawer-body">' + body + '</div><div class="drawer-foot"><button class="btn" type="button" data-close-drawer>Cancel</button><button class="btn btn-primary" type="button" ' + (st === 'errors' ? 'disabled' : 'data-fake="' + (pr ? 'Saved ' + esc(pr.name) + '. The monitor reconnects with the new details.' : 'Added printer. Waiting for its first status report.') + '"') + '>' + (pr ? 'Save' : 'Add printer') + '</button></div>';
|
||
}
|
||
function holdersDrawer(id) {
|
||
var pr = printer(+id);
|
||
var h = head('<span class="wordmark-mark" style="width:44px;height:44px;background:var(--surface-inset);color:var(--text-primary)">' + ICON.printers + '</span>', 'Holders · ' + esc(pr.model), esc(pr.name) + ' · where filament can be loaded');
|
||
var body = pr.containers.map(function (c) {
|
||
var n = contents(c.id).length, ext = c.kind === 'external';
|
||
return '<div class="holder"><div class="holder-h"><b>' + esc(c.label) + '</b><span class="muted">' + { ams: 'AMS', ams_lite: 'AMS Lite', external: 'External' }[c.kind] + ' · holds ' + c.cap + ' · #' + HOLDER_IDS[c.id] + '</span><span class="spacer"></span><span class="small">' + n + ' loaded</span></div>' +
|
||
'<div class="row" style="gap:8px"><button class="btn btn-sm" type="button" data-fake="Holder label PDF #' + HOLDER_IDS[c.id] + ' generated">Print label</button>' +
|
||
(ext ? '<span class="sub">Every printer keeps exactly one external holder.</span>' : n ? '<button class="btn btn-sm" type="button" disabled title="Unload its spools first">Remove</button><span class="sub">Unload its ' + n + ' spool' + (n > 1 ? 's' : '') + ' first.</span>' : '<button class="btn btn-sm btn-danger" type="button" data-fake="Removed ' + esc(c.label) + '">Remove</button>') + '</div></div>';
|
||
}).join('') +
|
||
'<div class="panel nm-raised-sm stack" style="gap:10px"><h3 style="margin:0">Add an AMS unit</h3><div class="fr2"><label class="stack" style="gap:6px"><span class="small strong">Type</span><select class="select"><option>AMS (4 spools)</option><option>AMS Lite (4 spools)</option></select></label><label class="stack" style="gap:6px"><span class="small strong">Label</span><input class="input" value="AMS 2"><span class="sub">Unique on this printer</span></label></div><button class="btn btn-sm" type="button" data-fake="Added AMS 2 to ' + esc(pr.model) + '" style="align-self:flex-start">Add holder</button></div>' +
|
||
'<p class="small muted" style="margin:0">Capacity comes from the type, so a holder can\'t disagree with its hardware. Tray positions aren\'t recorded.</p>';
|
||
return h + '<div class="drawer-body">' + body + '</div><div class="drawer-foot"><button class="btn" type="button" data-close-drawer>Done</button></div>';
|
||
}
|
||
function premoveDrawer(id) {
|
||
var pr = printer(+id), loaded = pr.containers.reduce(function (a, c) { return a + contents(c.id).length; }, 0), open = pr.openPrint && print(pr.openPrint);
|
||
var h = head('<span class="wordmark-mark" style="width:44px;height:44px;background:var(--wash-error);color:var(--status-error);border-color:var(--status-error)">!</span>', 'Remove ' + esc(pr.name) + '?', esc(pr.model) + ' · ' + esc(pr.ip));
|
||
var body = '<ul class="small" style="margin:0;padding-left:18px;display:flex;flex-direction:column;gap:6px"><li>Capture stops for this printer.</li><li><b>Print history is kept</b>, including cost records.</li>' + (open ? '<li><b>' + esc(open.job) + '</b> is open, so it will be <b>closed as unknown</b> with no duration, because nobody will watch it end.</li>' : '') + '</ul>' +
|
||
(loaded ? '<div class="banner err" role="alert"><span><b>Can\'t remove yet:</b> ' + loaded + ' spool' + (loaded > 1 ? 's are' : ' is') + ' still loaded in its holders. Unload them first, so they don\'t lose their place. <a href="04-printers.html#printer=' + pr.id + '" data-close-drawer>Show holders</a></span></div>' : '') +
|
||
'<label class="stack" style="gap:6px"><span class="small strong">Type <i>' + esc(pr.name) + '</i> to confirm</span><input class="input" id="rm-name" autocomplete="off" ' + (loaded ? 'disabled' : '') + '></label>';
|
||
return h + '<div class="drawer-body">' + body + '</div><div class="drawer-foot"><button class="btn" type="button" data-close-drawer autofocus>Keep printer</button><button class="btn btn-danger" type="button" id="rm-go" disabled data-name="' + esc(pr.name) + '">Remove printer</button></div>';
|
||
}
|
||
function loadDrawer(cid) {
|
||
var c = container(cid), inIt = contents(cid);
|
||
var h = head('<span class="wordmark-mark" style="width:44px;height:44px;background:var(--surface-inset);color:var(--text-primary)">' + ICON.printers + '</span>', 'Load into ' + esc(c.c.label), esc(c.printer.name) + ' · ' + inIt.length + ' of ' + c.c.cap + ' in use');
|
||
var cands = SPOOLS.filter(function (s) { return !s.cont; });
|
||
var body = '<label class="stack" style="gap:6px"><span class="small strong">Find a spool in storage</span><input class="input" id="load-q" placeholder="Brand, color, material…" autofocus></label><div class="stack" style="gap:6px" id="load-list">' +
|
||
cands.map(function (s) { return '<button type="button" class="slot" style="min-height:0;flex-direction:row;align-items:center;gap:12px;width:100%" data-load="' + s.id + '" aria-pressed="false" data-hay="' + esc((fullName(s) + ' ' + s.color + ' ' + (s.loc || '')).toLowerCase()) + '">' + sw(s.c) + '<span style="flex:1;min-width:0"><span class="nm" style="display:block">' + esc(s.color) + ' · ' + esc(s.brand) + '</span><span class="sub">' + esc(fullName(s)) + ' · ' + esc(s.loc || 'Storage') + '</span></span><span class="g num' + (low(s) ? '" style="color:var(--status-warning)' : '') + '">' + g(s.rem) + '</span></button>'; }).join('') + '</div>' +
|
||
'<p class="small muted" style="margin:0">Scanning the holder\'s QR label does the same thing from a phone (existing <code>/c/:id</code> scan session).</p>';
|
||
return h + '<div class="drawer-body">' + body + '</div><div class="drawer-foot"><button class="btn" type="button" data-close-drawer>Cancel</button><button class="btn btn-primary" type="button" id="l-save" disabled>Load spool</button></div>';
|
||
}
|
||
|
||
/* ── palette ─────────────────────────────────────────────── */
|
||
var P = { items: [], sel: 0 };
|
||
function palItems(q) {
|
||
q = q.replace(/^\s+/, '').toLowerCase(); if (!/^(load|attribute)\s/.test(q)) q = q.trim(); else q = q.replace(/\s+$/, function (m) { return /\s(into|to)\s+$/.test(q) ? ' ' : ''; });
|
||
var acts = [
|
||
{ sec: 'Actions', ico: '+', t: 'Add spool', m: 'new', run: function () { openDrawer('new', 0, 'edit'); } },
|
||
{ sec: 'Actions', ico: '⚖', t: 'Weigh a spool…', m: 'type its name', run: function () { $('#pal-q').value = 'weigh '; palRender(); }, keep: 1 },
|
||
{ sec: 'Actions', ico: '⇥', t: 'Load a spool into a holder…', m: 'load ‹spool› into ‹holder›', run: function () { $('#pal-q').value = 'load '; palRender(); }, keep: 1 },
|
||
{ sec: 'Actions', ico: '⇄', t: 'Attribute a print to a spool…', m: 'attribute ‹print› to ‹spool›', run: function () { $('#pal-q').value = 'attribute '; palRender(); }, keep: 1 },
|
||
{ sec: 'Actions', ico: '!', t: 'Prints needing attention', m: printsNeeding().length + ' open', href: '03-prints.html?tab=needs' },
|
||
{ sec: 'Actions', ico: '◐', t: 'Switch color theme', m: 'light / dark', run: function () { setTheme(document.documentElement.getAttribute('data-theme') === 'dark' ? 'light' : 'dark'); } }
|
||
].concat(PAGES.map(function (p) { return { sec: 'Go to', ico: '→', t: p[1], m: 'page', href: p[2] }; }));
|
||
var lm = /^load(?:\s|$)/.test(q) && /^load\s*(.*?)(?:\s+into\b\s*(.*))?$/.exec(q), am = /^attribute(?:\s|$)/.test(q) && /^attribute\s*(.*?)(?:\s+to\b\s*(.*))?$/.exec(q);
|
||
if (lm) return loadCmd(lm[1] || '', lm[2]);
|
||
if (am) return attrCmd(am[1] || '', am[2]);
|
||
var weigh = /^weigh\s*/.test(q); if (weigh) q = q.replace(/^weigh\s*/, '');
|
||
var sp = SPOOLS.map(function (s) { return { sec: weigh ? 'Weigh which spool?' : 'Spools', html: sw(s.c, 'swatch-sm'), t: s.color + ' · ' + fullName(s), m: '#' + s.id + ' · ' + g(s.rem) + ' · ' + where(s).short, hay: ('#' + s.id + ' ' + s.id + ' ' + fullName(s) + ' ' + s.color + ' ' + where(s).text).toLowerCase(), run: function () { go('spool', s.id, weigh ? 'weigh' : 'overview'); } }; });
|
||
var pr = PRINTS.map(function (p) { return { sec: 'Prints', ico: '▤', t: p.job, m: PST[p.status][1] + ' · ' + when(p.start), hay: (p.job + ' ' + p.key + ' ' + printer(p.printer).name).toLowerCase(), run: function () { go('print', p.id); } }; });
|
||
var pt = PRINTERS.map(function (p) { return { sec: 'Printers', ico: '▣', t: p.name, m: p.connected ? (p.state === 'RUNNING' ? 'printing' : 'idle') : 'reconnecting', hay: p.name.toLowerCase(), href: '04-printers.html#printer=' + p.id }; });
|
||
if (weigh) return sp.filter(function (i) { return !q || i.hay.indexOf(q) >= 0; }).slice(0, 12);
|
||
if (!q) return acts.concat(sp.filter(function (i, k) { return k < 3; }).map(function (i) { i.sec = 'Recent spools'; return i; }));
|
||
var match = function (i) { return (i.hay || i.t.toLowerCase()).indexOf(q) >= 0; };
|
||
return acts.filter(match).concat(sp.filter(match).slice(0, 8), pr.filter(match).slice(0, 5), pt.filter(match));
|
||
}
|
||
function holders() { var out = []; PRINTERS.forEach(function (pr) { pr.containers.forEach(function (c) { out.push({ id: c.id, pr: pr, c: c, n: contents(c.id).length, hay: (pr.name + ' ' + pr.model + ' ' + c.label + ' ' + c.kind).toLowerCase() }); }); }); return out; }
|
||
function spoolHay(s) { return ('#' + s.id + ' ' + s.id + ' ' + fullName(s) + ' ' + s.color + ' ' + where(s).text).toLowerCase(); }
|
||
function loadCmd(sq, hq) {
|
||
var sp = SPOOLS.filter(function (s) { return !sq || spoolHay(s).indexOf(sq) >= 0; }).slice(0, 6);
|
||
if (hq == null) return sp.map(function (s) { return { sec: 'Load which spool? (then type “ into …”)', html: sw(s.c, 'swatch-sm'), t: s.color + ' · ' + fullName(s), m: '#' + s.id + ' · ' + where(s).short, keep: 1, run: function () { $('#pal-q').value = 'load #' + s.id + ' into '; palRender(); } }; });
|
||
var s1 = sp[0]; if (!s1) return [];
|
||
return holders().filter(function (h) { return !hq || h.hay.indexOf(hq) >= 0; }).map(function (h) {
|
||
var full = h.n >= h.c.cap && s1.cont !== h.id;
|
||
return { sec: 'Load ' + s1.color + ' · ' + s1.brand + ' (#' + s1.id + ') into…', ico: full ? '✕' : '⇥', t: h.pr.model + ' · ' + h.c.label, m: full ? 'full · ' + h.n + '/' + h.c.cap : h.n + '/' + h.c.cap + ' in use', run: function () { if (full) return toast(h.c.label + ' on ' + h.pr.model + ' is full. Unload a spool first.', 'warn'); s1.cont = h.id; s1.v++; toast('Loaded <b>' + esc(s1.color + ' · ' + s1.brand) + '</b> into ' + esc(h.pr.model + ' · ' + h.c.label)); if (PAGE.refresh) PAGE.refresh(); } };
|
||
});
|
||
}
|
||
function attrCmd(pq, sq) {
|
||
var pr = printsNeeding().filter(function (p) { return !pq || (p.job + ' ' + p.key).toLowerCase().indexOf(pq) >= 0; });
|
||
if (sq == null) return pr.map(function (p) { return { sec: 'Attribute which print? (then type “ to …”)', ico: '▤', t: p.job, m: needs(p).map(function (n) { return n.t; }).join(', '), keep: 1, run: function () { $('#pal-q').value = 'attribute ' + p.job.toLowerCase() + ' to '; palRender(); } }; });
|
||
var p1 = pr[0]; if (!p1) return [];
|
||
return SPOOLS.filter(function (s) { return !sq || spoolHay(s).indexOf(sq) >= 0; }).slice(0, 8).map(function (s) {
|
||
return { sec: 'Attribute ' + p1.job + ' to…', html: sw(s.c, 'swatch-sm'), t: s.color + ' · ' + fullName(s), m: '#' + s.id + ' · ' + g(s.rem), run: function () { if (p1.filaments) p1.filaments.forEach(function (f) { if (f.spool == null) f.spool = s.id; }); else p1.spool = s.id; toast('Attributed <b>' + esc(p1.job) + '</b> to ' + esc(short(s)) + (p1.grams != null ? ' · ' + g(p1.grams) + ' deducted' : '')); if (PAGE.refresh) PAGE.refresh(); } };
|
||
});
|
||
}
|
||
function go(kind, id, tab) {
|
||
var pageFor = { spool: null, print: null };
|
||
if (window.PAGE && PAGE.name) openDrawer(kind, id, tab);
|
||
}
|
||
function palRender() {
|
||
P.items = palItems($('#pal-q').value); if (P.sel >= P.items.length) P.sel = 0;
|
||
var last = '', html = '';
|
||
P.items.forEach(function (it, i) {
|
||
if (it.sec !== last) { html += '<div class="pal-sec">' + esc(it.sec) + '</div>'; last = it.sec; }
|
||
html += '<div class="pal-item" role="option" id="pi' + i + '" data-i="' + i + '" aria-selected="' + (i === P.sel) + '">' + (it.html || '<span style="width:18px;text-align:center" aria-hidden="true">' + it.ico + '</span>') + '<span>' + esc(it.t) + '</span><span class="meta">' + esc(it.m || '') + '</span></div>';
|
||
});
|
||
$('#pal-list').innerHTML = html || '<div class="empty"><h3>No matches</h3><p>Try a color, brand, <code>#47</code>, a job name or a printer.</p></div>';
|
||
$('#pal-q').setAttribute('aria-activedescendant', P.items.length ? 'pi' + P.sel : '');
|
||
var cur = $('#pi' + P.sel); if (cur) cur.scrollIntoView({ block: 'nearest' });
|
||
}
|
||
function palOpen(prefill) { $('#pal').classList.add('is-open'); $('#pal-q').value = prefill || ''; P.sel = 0; palRender(); $('#pal-q').focus(); }
|
||
function palClose() { $('#pal').classList.remove('is-open'); }
|
||
function palRun(i) { var it = P.items[i]; if (!it) return; if (!it.keep) palClose(); if (it.href) location.href = it.href; else it.run(); }
|
||
|
||
/* ── freshness + demos ───────────────────────────────────── */
|
||
var fresh = { n: 3, stale: false };
|
||
function tick() {
|
||
fresh.n = fresh.stale ? fresh.n + 1 : (fresh.n >= 10 ? 0 : fresh.n + 1);
|
||
var f = $('#fresh'); if (!f) return;
|
||
f.classList.toggle('is-stale', fresh.stale);
|
||
$('#fresh-t').innerHTML = fresh.stale ? 'Not updating · data from 09:41:07 <span class="long">· retrying</span>' : 'Live <span class="long">· updated ' + fresh.n + 's ago</span>';
|
||
}
|
||
function demo(kind) {
|
||
var main = $('#page');
|
||
if (kind === 'loading') {
|
||
var keep = main.innerHTML;
|
||
main.innerHTML = '<div class="stack" style="gap:24px" aria-busy="true" aria-label="Loading"><div class="skel" style="width:40%;height:28px"></div><div class="stats">' + [1, 2, 3, 4].map(function () { return '<div class="stat nm-raised"><div class="skel" style="width:50%"></div><div class="skel" style="width:70%;height:24px;margin-top:10px"></div></div>'; }).join('') + '</div><div class="nm-raised card stack">' + [1, 2, 3, 4, 5].map(function () { return '<div class="skel" style="height:18px"></div>'; }).join('') + '</div></div>';
|
||
setTimeout(function () { main.innerHTML = keep; if (PAGE.after) PAGE.after(); }, 1600);
|
||
}
|
||
if (kind === 'offline') { fresh.stale = true; fresh.n = 0; tick(); var b = $('#offline-banner'); if (!b) { b = document.createElement('div'); b.id = 'offline-banner'; b.className = 'banner'; b.setAttribute('role', 'alert'); b.innerHTML = '<b>⚠</b><span style="flex:1">Can\'t reach Extrudex. You are looking at data from <b>09:41:07</b>, and it is not updating. Retrying every 10 s. Saving is paused.</span><button class="btn btn-sm" type="button" data-demo="online">Retry now</button>'; main.parentNode.insertBefore(b, main); } }
|
||
if (kind === 'online') { fresh.stale = false; fresh.n = 0; tick(); var ob = $('#offline-banner'); if (ob) ob.parentNode.removeChild(ob); }
|
||
}
|
||
|
||
/* ── wiring ──────────────────────────────────────────────── */
|
||
function parseHash() {
|
||
var m = /^#(spool|print|load|pedit|holders|premove)=([\w-]+)(?:&tab=(\w+))?(?:&state=(\w+))?/.exec(location.hash);
|
||
if (m) { var id = (m[1] === 'load' || m[2] === 'new') ? m[2] : +m[2]; openDrawer(m[1], id, m[3], false); if (m[4]) { D.state = m[4]; renderDrawer(); } }
|
||
else if (D.kind) closeDrawer(false);
|
||
}
|
||
function init(page, render) {
|
||
var s = shell(page.name);
|
||
document.body.insertAdjacentHTML('afterbegin', '<a class="btn btn-sm" href="#page" style="position:absolute;left:-9999px;top:8px" onfocus="this.style.left=\'8px\'" onblur="this.style.left=\'-9999px\'">Skip to content</a><div class="shell">' + s.rail + '<main id="main">' + s.top + '<div id="page"></div></main></div>' + s.tabbar + s.rest);
|
||
window.PAGE = page;
|
||
$('#page').innerHTML = render();
|
||
if (page.after) page.after();
|
||
setTheme(document.documentElement.getAttribute('data-theme'));
|
||
setInterval(tick, 1000);
|
||
parseHash();
|
||
var pq = /[?&]palette=([^&]*)/.exec(location.search); if (pq) palOpen(decodeURIComponent(pq[1]).replace(/\+/g, ' '));
|
||
}
|
||
|
||
document.addEventListener('click', function (e) {
|
||
var t = e.target, a;
|
||
if ((a = t.closest('[data-set-theme]'))) return setTheme(a.getAttribute('data-set-theme'));
|
||
if (t.closest('[data-toggle-theme]')) return setTheme(document.documentElement.getAttribute('data-theme') === 'dark' ? 'light' : 'dark');
|
||
if (t.closest('[data-palette]')) return palOpen();
|
||
if (t.id === 'pal') return palClose();
|
||
if ((a = t.closest('.pal-item'))) return palRun(+a.getAttribute('data-i'));
|
||
if ((a = t.closest('[data-menu]'))) { e.preventDefault(); var m = $('#' + a.getAttribute('data-menu')), open = !m.classList.contains('is-open'); $$('.menu.is-open').forEach(function (x) { x.classList.remove('is-open'); }); m.classList.toggle('is-open', open); a.setAttribute('aria-expanded', String(open)); if (open) { var f = m.querySelector('button,a'); if (f) f.focus(); } e.stopPropagation(); return; }
|
||
if (!t.closest('.menu')) $$('.menu.is-open').forEach(function (x) { x.classList.remove('is-open'); });
|
||
if ((a = t.closest('[data-demo]'))) { $$('.menu.is-open').forEach(function (x) { x.classList.remove('is-open'); }); return demo(a.getAttribute('data-demo')); }
|
||
if ((a = t.closest('[data-soon]'))) { e.preventDefault(); return toast('<b>' + a.getAttribute('data-soon') + '</b> keeps its current screen on dev. It isn\'t redesigned in this iteration.'); }
|
||
if ((a = t.closest('[data-spool]'))) { e.preventDefault(); D.list = null; return openDrawer('spool', +a.getAttribute('data-spool'), a.getAttribute('data-tab-open') || 'overview'); }
|
||
if ((a = t.closest('[data-print]'))) { e.preventDefault(); return openDrawer('print', +a.getAttribute('data-print')); }
|
||
if ((a = t.closest('[data-load-into]'))) { e.preventDefault(); return openDrawer('load', a.getAttribute('data-load-into')); }
|
||
if ((a = t.closest('[data-pedit]'))) { e.preventDefault(); var pv = a.getAttribute('data-pedit'); return openDrawer('pedit', pv === 'new' ? 'new' : +pv); }
|
||
if ((a = t.closest('[data-holders]'))) { e.preventDefault(); return openDrawer('holders', +a.getAttribute('data-holders')); }
|
||
if ((a = t.closest('[data-premove]'))) { e.preventDefault(); return openDrawer('premove', +a.getAttribute('data-premove')); }
|
||
if (t.id === 'rm-go') { closeDrawer(); return toast('Removed <b>' + esc(t.getAttribute('data-name')) + '</b>. Its print history is kept.'); }
|
||
if ((a = t.closest('[data-add-spool]'))) { e.preventDefault(); return openDrawer('new', 0, 'edit'); }
|
||
if (t.closest('[data-close-drawer]')) return closeDrawer();
|
||
if ((a = t.closest('#drawer [data-tab]'))) { D.tab = a.getAttribute('data-tab'); D.state = 'normal'; renderDrawer(); history.replaceState({ d: 1 }, '', '#' + D.kind + '=' + D.id + (D.tab !== 'overview' ? '&tab=' + D.tab : '')); var f = $('#drawer [autofocus]'); if (f) f.focus(); return; }
|
||
if ((a = t.closest('[data-state]'))) { D.state = a.getAttribute('data-state'); return renderDrawer(); }
|
||
if ((a = t.closest('[data-step]'))) { var i = D.list.indexOf(D.id) + +a.getAttribute('data-step'); if (D.list[i] != null) openDrawer('spool', D.list[i], D.tab, false); history.replaceState({ d: 1 }, '', '#spool=' + D.id + (D.tab !== 'overview' ? '&tab=' + D.tab : '')); return; }
|
||
if ((a = t.closest('[data-dest]'))) { $$('[data-dest]').forEach(function (b) { b.setAttribute('aria-pressed', String(b === a)); }); $('#m-save').disabled = false; var dc = a.getAttribute('data-dest'), cc = dc !== 'storage' && container(dc); $('#m-save').textContent = cc ? 'Load into ' + cc.printer.model + ' · ' + cc.c.label : 'Move to storage'; return; }
|
||
if ((a = t.closest('[data-load]'))) { $$('[data-load]').forEach(function (b) { b.setAttribute('aria-pressed', String(b === a)); }); var ls = spool(+a.getAttribute('data-load')); $('#l-save').disabled = false; $('#l-save').textContent = 'Load ' + ls.color + ' · ' + ls.brand; $('#l-save').setAttribute('data-sid', ls.id); return; }
|
||
if (t.id === 'l-save') { var sid = +t.getAttribute('data-sid'), ss = spool(sid), cc2 = container(D.id); ss.cont = D.id; closeDrawer(); toast('Loaded <b>' + esc(ss.color + ' · ' + ss.brand) + '</b> into ' + esc(cc2.printer.model + ' · ' + cc2.c.label) + ' <a href="#spool=' + sid + '" data-spool="' + sid + '">View</a>'); if (PAGE.refresh) PAGE.refresh(); return; }
|
||
if (t.id === 'm-save') { var dest = $('[data-dest][aria-pressed="true"]').getAttribute('data-dest'), sp = spool(D.id); sp.cont = dest === 'storage' ? null : dest; sp.v++; D.tab = 'overview'; renderDrawer(); toast('Moved <b>' + esc(sp.color) + '</b> to ' + esc(where(sp).text)); if (PAGE.refresh) PAGE.refresh(); return; }
|
||
if (t.id === 'w-save' || t.id === 'w-next') { var ws = spool(D.id), before = ws.rem, nv = Math.round(+$('#scale').value.replace(/[^\d.]/g, '') - ws.tare); ws.rem = nv; ws.remSrc = 'weighed'; ws.weighedAt = '18 Sep 09:41'; ws.v++; toast('Weighed <b>' + esc(ws.color + ' · ' + ws.brand) + '</b>: ' + g(before) + ' → ' + g(nv)); if (PAGE.refresh) PAGE.refresh(); if (t.id === 'w-next') { var nx = D.list[D.list.indexOf(D.id) + 1]; openDrawer('spool', nx, 'weigh', false); history.replaceState({ d: 1 }, '', '#spool=' + nx + '&tab=weigh'); } else { D.tab = 'overview'; renderDrawer(); } return; }
|
||
if (t.closest('[data-save]')) { var sv = D.kind === 'new'; closeDrawer(); D.dirty = false; return toast(sv ? 'Added spool <b>#57</b>. <a href="#">Print its label</a>' : 'Saved changes'); }
|
||
if (t.closest('[data-discard]')) { D.dirty = false; return closeDrawer(); }
|
||
if (t.closest('[data-keep]')) { $('#discard-bar').hidden = true; return; }
|
||
if ((a = t.closest('[data-fake]'))) { toast(a.getAttribute('data-fake')); return; }
|
||
if ((a = t.closest('[data-delete]'))) { var ds = spool(+a.getAttribute('data-delete')); $('#drawer .drawer-foot').innerHTML = '<div class="stack" style="gap:8px;width:100%"><b>Delete ' + esc(ds.color + ' · ' + fullName(ds)) + '?</b><span class="small">Its print history stays. Prints keep their grams and cost and only lose the link to this spool. This can\'t be undone.</span><div class="row" style="justify-content:flex-end"><button class="btn" type="button" data-tab="overview" autofocus>Keep spool</button><button class="btn btn-danger" type="button" data-fake="Spool #' + ds.id + ' deleted. 0 prints lost their cost.">Delete spool</button></div></div>'; $('#drawer .drawer-foot [autofocus]').focus(); return; }
|
||
});
|
||
document.addEventListener('input', function (e) {
|
||
if (e.target.id === 'pal-q') { P.sel = 0; return palRender(); }
|
||
if (e.target.closest('#drawer .drawer-body') && D.tab === 'edit') D.dirty = true;
|
||
if (e.target.id === 'scale') {
|
||
var s = spool(D.id), v = +e.target.value.replace(/[^\d.]/g, ''), r = Math.round(v - s.tare);
|
||
var ok = v > 0 && v >= s.tare && r <= s.tot + 1;
|
||
$('#w-after').textContent = v ? g(r) : '—';
|
||
$('#w-diff').innerHTML = v && ok ? '<span class="prov">' + (r - s.rem > 0 ? '+' : '') + (r - s.rem).toLocaleString('en-US') + ' g</span>' : '';
|
||
$('#w-note').innerHTML = !v ? 'Type the reading and the result appears here before you save.' : v < s.tare ? '<span style="color:var(--status-error)">⚠ ' + g(v) + ' is less than the ' + g(s.tare) + ' empty spool. One of them is wrong. Nothing will be saved.</span>' : r > s.tot + 1 ? '<span style="color:var(--status-error)">⚠ That leaves ' + g(r) + ', more than the spool holds full (' + g(s.tot) + '). Check the total or the empty spool weight.</span>' : 'Saved with the empty spool weight it used, so correcting that weight later re-bases this reading.';
|
||
$('#w-save').disabled = !ok; if ($('#w-next')) $('#w-next').disabled = !ok;
|
||
}
|
||
if (e.target.id === 'rm-name') { $('#rm-go').disabled = e.target.value !== $('#rm-go').getAttribute('data-name'); }
|
||
if (e.target.id === 'load-q') { var q = e.target.value.toLowerCase(); $$('[data-load]').forEach(function (b) { b.hidden = q && b.getAttribute('data-hay').indexOf(q) < 0; }); }
|
||
});
|
||
document.addEventListener('change', function (e) { if (e.target.id === 'pe-brand') { D.brand = e.target.value; renderDrawer(); } });
|
||
document.addEventListener('keydown', function (e) {
|
||
var typing = /INPUT|SELECT|TEXTAREA/.test(document.activeElement.tagName);
|
||
if ((e.key === 'k' && (e.metaKey || e.ctrlKey)) || (e.key === '/' && !typing)) { e.preventDefault(); return palOpen(); }
|
||
if ($('#pal').classList.contains('is-open')) {
|
||
if (e.key === 'Escape') { e.preventDefault(); return palClose(); }
|
||
if (e.key === 'ArrowDown') { e.preventDefault(); P.sel = Math.min(P.items.length - 1, P.sel + 1); return palRender(); }
|
||
if (e.key === 'ArrowUp') { e.preventDefault(); P.sel = Math.max(0, P.sel - 1); return palRender(); }
|
||
if (e.key === 'Enter') { e.preventDefault(); return palRun(P.sel); }
|
||
return;
|
||
}
|
||
if (e.key === 'Escape') { if ($('.menu.is-open')) { $$('.menu.is-open').forEach(function (x) { x.classList.remove('is-open'); }); return; } if (D.kind) return closeDrawer(); }
|
||
if (D.kind === 'spool' && !typing && D.list && (e.key === 'j' || e.key === 'k')) { var b = $('[data-step="' + (e.key === 'j' ? 1 : -1) + '"]'); if (b && !b.disabled) b.click(); }
|
||
if (e.key === 'Enter' && document.activeElement.matches('tr[data-row-spool],tr[data-row-print]')) document.activeElement.click();
|
||
if (D.kind && e.key === 'Tab') { var dr = $('#drawer'), f = $$('button:not([disabled]),a[href],input,select,[tabindex="0"]', dr).filter(function (x) { return x.offsetParent; }); if (!f.length) return; if (e.shiftKey && document.activeElement === f[0]) { e.preventDefault(); f[f.length - 1].focus(); } else if (!e.shiftKey && document.activeElement === f[f.length - 1]) { e.preventDefault(); f[0].focus(); } }
|
||
});
|
||
window.addEventListener('popstate', parseHash);
|
||
|
||
window.X = { unverified: unverified, holderIds: HOLDER_IDS, init: init, esc: esc, g: g, kg: kg, usd: usd, dur: dur, when: when, ago: ago, since: since, spool: spool, printer: printer, print: print, container: container, contents: contents, fullName: fullName, short: short, where: where, gross: gross, value: value, low: low, sw: sw, meter: meter, tareProv: tareProv, remProv: remProv, weightProv: weightProv, durCell: durCell, printSt: printSt, printerSt: printerSt, needs: needs, printsNeeding: printsNeeding, spoolLinks: spoolLinks, openDrawer: openDrawer, D: D, toast: toast, ICON: ICON };
|
||
})();
|
||
|
||
</script>
|
||
<script>
|
||
|
||
(function () {
|
||
var SCREENS = [['01-dashboard.html', 'Dashboard'], ['02-filament.html', 'Filament'], ['02-filament.html?f=low#spool=41&tab=weigh', 'Weigh panel'], ['03-prints.html', 'Prints'], ['04-printers.html', 'Printers'], ['05-lists.html', 'Lists'], ['06-labels.html', 'Labels'], ['07-access.html', 'Access'], ['08-system.html', 'System'], ['09-scan-holder.html', 'Scan: holder'], ['10-scan-spool.html?s=44', 'Scan: spool'], ['02-filament.html?palette=weigh', 'Palette']];
|
||
var DEV = [['Phone', 390, 844, 0.62, ''], ['Tablet portrait', 820, 1180, 0.46, 'tab'], ['Tablet landscape', 1180, 820, 0.5, 'tab']];
|
||
var q = new URLSearchParams(location.search);
|
||
var S = { i: +(q.get('i') || 0), theme: q.get('t') || 'dark' };
|
||
function url(u) { return u.replace(/(\?[^#]*)?(#.*)?$/, function (m, qs, h) { return (qs ? qs + '&' : '?') + 'theme=' + S.theme + (h || ''); }); }
|
||
function frames() {
|
||
return DEV.map(function (d) {
|
||
var w = Math.round(d[1] * d[3]), h = Math.round(d[2] * d[3]);
|
||
return '<div class="dev"><h2>' + d[0] + ' · ' + d[1] + ' × ' + d[2] + '</h2><div class="bezel ' + d[4] + '"><div class="screen" style="width:' + w + 'px;height:' + h + 'px"><iframe title="' + SCREENS[S.i][1] + ' on ' + d[0] + '" src="' + url(SCREENS[S.i][0]) + '" width="' + d[1] + '" height="' + d[2] + '" style="transform:scale(' + d[3] + ')"></iframe></div></div><a class="small" href="' + url(SCREENS[S.i][0]) + '" target="_blank" rel="noopener">Open full size ↗</a></div>';
|
||
}).join('');
|
||
}
|
||
function render() {
|
||
return '<header class="page-head"><div><h1>Devices</h1><p class="sum">Every screen on a phone and on a tablet, portrait and landscape. The frames are live, so you can click inside them.</p></div>' +
|
||
'<div class="tabs" role="group" aria-label="Preview theme"><button type="button" data-t="dark" aria-selected="' + (S.theme === 'dark') + '">Dark</button><button type="button" data-t="light" aria-selected="' + (S.theme === 'light') + '">Light</button></div></header>' +
|
||
'<div class="dv-list" role="group" aria-label="Screen">' + SCREENS.map(function (s, i) { return '<button type="button" data-i="' + i + '" aria-pressed="' + (i === S.i) + '">' + s[1] + '</button>'; }).join('') + '</div>' +
|
||
'<div class="dv-row">' + frames() + '</div>' +
|
||
'<section class="bp" aria-label="Breakpoints"><div class="nm-inset"><b>Phone · under 700 px</b>Bottom tab bar (4 + More) with badges, a floating + for the page\'s main action, panels as full-screen sheets, tables drop to 2–3 columns, 44 px touch targets.</div>' +
|
||
'<div class="nm-inset"><b>Tablet · 700–1180 px</b>Compact icon rail with labels and badges, panels as a 480 px side sheet over the page, low-priority columns hidden, no keyboard hints on touch.</div>' +
|
||
'<div class="nm-inset"><b>Desktop · over 1180 px</b>Full rail with the Manage group, a 520 px panel, all columns, keyboard shortcuts shown.</div></section>';
|
||
}
|
||
function redraw() { document.getElementById('page').innerHTML = render(); history.replaceState(null, '', location.pathname + '?i=' + S.i + '&t=' + S.theme); }
|
||
document.addEventListener('click', function (e) {
|
||
var a = e.target.closest('[data-i]'); if (a) { S.i = +a.getAttribute('data-i'); return redraw(); }
|
||
a = e.target.closest('[data-t]'); if (a) { S.theme = a.getAttribute('data-t'); return redraw(); }
|
||
});
|
||
X.init({ name: 'devices' }, render);
|
||
})();
|
||
|
||
</script>
|
||
</body>
|
||
</html>
|