mirror of
https://github.com/mineracks/seedhammer-v1-companion.git
synced 2026-06-26 22:01:05 +10:00
emulator: redesign chassis to match real v1 device layout
Layout was a generic dev-tool affordance (small canvas on top, square button grid below). Real device is landscape orientation with the LCD recessed in the middle, ONE disc joystick on the left side, and THREE round side keys stacked on the right. before: after: +----------------+ +---------------------------+ | [canvas] | | (\) +-------+ o | | | | | | LCD | o | | [ U ] | | (/) | | o | | [L][C][R] | | +-------+ | | [ D ] | +---------------------------+ | | orange anodised chassis | [K1][K2][K3] | +----------------+ The chassis is a CSS-only render — orange gradient with edge highlight + inset shadow stack so it reads as machined aluminium. The disc joystick is a single circular metal-look element with 5 overlapping square-ish hit zones (top/bottom/left/right/centre); arrow glyphs hint at the directions but the look is one cohesive control rather than five separate buttons. The three side keys are round metal-look domes with press states. Footer copy refreshed: no more "Phase 2 scaffolding" caveat — the real upstream GUI is running. Notes the camera and engraver are stubbed. Responsive breakpoint at 600px shrinks chassis + controls so the emulator stays usable on phones. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
9675c05ff1
commit
174f1bfc05
@ -1,84 +1,167 @@
|
||||
/* SeedHammer v1 emulator-specific styles, layered on top of the
|
||||
composer's app.css for the shared shell/card/glass primitives. */
|
||||
/* SeedHammer v1 emulator — chassis styling. Mirrors the physical
|
||||
device: orange anodised aluminium body in landscape orientation,
|
||||
one disc joystick on the left, recessed LCD in the centre, three
|
||||
round side keys on the right. */
|
||||
|
||||
.emu-device-card { padding: 24px; }
|
||||
|
||||
.emu-device {
|
||||
.emu-device-card {
|
||||
padding: 24px;
|
||||
display: flex;
|
||||
gap: 32px;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.emu-lcd-frame {
|
||||
background: #0a0a0a;
|
||||
border-radius: 18px;
|
||||
padding: 18px;
|
||||
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35), inset 0 0 0 2px #2a2a2a;
|
||||
/* Outer chassis — orange anodised look, with subtle gradient + edge
|
||||
highlight so it reads as machined metal rather than flat colour. */
|
||||
.emu-chassis {
|
||||
--chassis-orange: #ff7a1f;
|
||||
--chassis-edge: #c45a0f;
|
||||
--chassis-corner-radius: 36px;
|
||||
|
||||
display: grid;
|
||||
grid-template-columns: 130px 1fr 70px;
|
||||
align-items: center;
|
||||
gap: 18px;
|
||||
padding: 22px 28px;
|
||||
|
||||
background:
|
||||
linear-gradient(180deg, #ff8a30 0%, var(--chassis-orange) 50%, #ed6b14 100%);
|
||||
border-radius: var(--chassis-corner-radius);
|
||||
box-shadow:
|
||||
0 16px 40px rgba(0, 0, 0, 0.35),
|
||||
inset 0 0 0 1.5px rgba(255, 255, 255, 0.18),
|
||||
inset 0 2px 0 rgba(255, 255, 255, 0.32),
|
||||
inset 0 -2px 0 rgba(0, 0, 0, 0.22);
|
||||
max-width: 720px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* ─── LCD recess ─────────────────────────────────────────────────── */
|
||||
|
||||
.emu-lcd-bezel {
|
||||
background:
|
||||
linear-gradient(180deg, #1a1a1a 0%, #0a0a0a 100%);
|
||||
border-radius: 14px;
|
||||
padding: 14px;
|
||||
box-shadow:
|
||||
inset 0 2px 4px rgba(0, 0, 0, 0.7),
|
||||
inset 0 -1px 0 rgba(255, 255, 255, 0.08);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.emu-lcd-screen {
|
||||
background: #000;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
aspect-ratio: 1 / 1;
|
||||
max-width: 320px;
|
||||
max-height: 320px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
#lcd {
|
||||
display: block;
|
||||
width: 320px;
|
||||
height: 320px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
image-rendering: pixelated;
|
||||
image-rendering: crisp-edges;
|
||||
background: #000;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.emu-controls {
|
||||
display: grid;
|
||||
grid-template-columns: auto auto;
|
||||
gap: 28px;
|
||||
align-items: center;
|
||||
}
|
||||
/* ─── Disc joystick (left) ───────────────────────────────────────── */
|
||||
|
||||
/* The disc itself: a round metal-look button. Five overlapping
|
||||
square-ish hit zones inside cover the 5 joystick directions. The
|
||||
visible disc is a circle; we hide the .disc-zone rectangles
|
||||
underneath so users see one cohesive control with arrow hints. */
|
||||
.emu-joystick {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 56px);
|
||||
grid-template-rows: repeat(3, 56px);
|
||||
gap: 4px;
|
||||
position: relative;
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
border-radius: 50%;
|
||||
background:
|
||||
radial-gradient(circle at 35% 30%, #f8f4ee 0%, #c8c2b6 65%, #8a857a 100%);
|
||||
box-shadow:
|
||||
inset 0 -3px 6px rgba(0, 0, 0, 0.25),
|
||||
inset 0 3px 3px rgba(255, 255, 255, 0.6),
|
||||
0 6px 16px rgba(0, 0, 0, 0.45);
|
||||
justify-self: center;
|
||||
}
|
||||
.emu-joystick .jb.up { grid-column: 2; grid-row: 1; }
|
||||
.emu-joystick .jb.left { grid-column: 1; grid-row: 2; }
|
||||
.emu-joystick .jb.center { grid-column: 2; grid-row: 2; }
|
||||
.emu-joystick .jb.right { grid-column: 3; grid-row: 2; }
|
||||
.emu-joystick .jb.down { grid-column: 2; grid-row: 3; }
|
||||
.emu-disc-zone {
|
||||
position: absolute;
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: rgba(60, 50, 35, 0.7);
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
user-select: none;
|
||||
transition: background 0.08s;
|
||||
}
|
||||
.emu-disc-zone:hover {
|
||||
color: rgba(40, 30, 15, 1);
|
||||
}
|
||||
.emu-disc-zone.pressed {
|
||||
background: rgba(0, 0, 0, 0.18);
|
||||
}
|
||||
.disc-up { top: 0; left: 33%; width: 34%; height: 34%; border-radius: 50% 50% 8px 8px; }
|
||||
.disc-down { bottom: 0; left: 33%; width: 34%; height: 34%; border-radius: 8px 8px 50% 50%; }
|
||||
.disc-left { top: 33%; left: 0; width: 34%; height: 34%; border-radius: 50% 8px 8px 50%; }
|
||||
.disc-right { top: 33%; right: 0; width: 34%; height: 34%; border-radius: 8px 50% 50% 8px; }
|
||||
.disc-center {
|
||||
top: 30%; left: 30%; width: 40%; height: 40%;
|
||||
border-radius: 50%;
|
||||
background: radial-gradient(circle at 35% 30%, #ffffff 0%, #d4cfc4 60%, #8a857a 100%);
|
||||
box-shadow:
|
||||
inset 0 -2px 4px rgba(0, 0, 0, 0.3),
|
||||
inset 0 2px 2px rgba(255, 255, 255, 0.6);
|
||||
}
|
||||
.disc-center.pressed {
|
||||
background: radial-gradient(circle at 35% 30%, #d8d2c5 0%, #b0a99b 60%, #6f6a5d 100%);
|
||||
}
|
||||
|
||||
/* ─── Side keys (right) ──────────────────────────────────────────── */
|
||||
|
||||
.emu-keys {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.emu-btn {
|
||||
background: var(--surface-strong);
|
||||
border: 1px solid var(--hairline);
|
||||
border-radius: 12px;
|
||||
color: var(--text);
|
||||
font: inherit;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
font-size: 18px;
|
||||
user-select: none;
|
||||
transition: transform 0.05s, background 0.1s;
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.emu-btn:active,
|
||||
.emu-btn.pressed {
|
||||
background: var(--accent);
|
||||
color: var(--accent-text);
|
||||
transform: scale(0.94);
|
||||
box-shadow: 0 2px 8px rgb(255 136 0 / 0.4);
|
||||
.emu-key {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 50%;
|
||||
border: none;
|
||||
font: inherit;
|
||||
font-weight: 700;
|
||||
font-size: 18px;
|
||||
color: rgba(60, 50, 35, 0.85);
|
||||
background:
|
||||
radial-gradient(circle at 35% 30%, #fafafa 0%, #cdc8bd 70%, #8a857a 100%);
|
||||
box-shadow:
|
||||
0 4px 10px rgba(0, 0, 0, 0.35),
|
||||
inset 0 -2px 4px rgba(0, 0, 0, 0.22),
|
||||
inset 0 2px 2px rgba(255, 255, 255, 0.6);
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
transition: transform 0.05s;
|
||||
}
|
||||
.emu-key:active,
|
||||
.emu-key.pressed {
|
||||
transform: translateY(1px);
|
||||
background:
|
||||
radial-gradient(circle at 35% 30%, #d4cfc4 0%, #a8a297 70%, #6f6a5d 100%);
|
||||
box-shadow:
|
||||
0 2px 4px rgba(0, 0, 0, 0.35),
|
||||
inset 0 -2px 4px rgba(0, 0, 0, 0.22),
|
||||
inset 0 2px 2px rgba(255, 255, 255, 0.4);
|
||||
}
|
||||
|
||||
.emu-btn.key {
|
||||
width: 64px;
|
||||
height: 56px;
|
||||
font-size: 14px;
|
||||
}
|
||||
/* ─── Keymap reference (unchanged) ───────────────────────────────── */
|
||||
|
||||
.emu-keymap {
|
||||
width: 100%;
|
||||
@ -102,3 +185,15 @@
|
||||
font-size: 12px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/* ─── Responsive ─────────────────────────────────────────────────── */
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.emu-chassis {
|
||||
grid-template-columns: 90px 1fr 56px;
|
||||
gap: 12px;
|
||||
padding: 16px 18px;
|
||||
}
|
||||
.emu-joystick { width: 90px; height: 90px; }
|
||||
.emu-key { width: 38px; height: 38px; font-size: 15px; }
|
||||
}
|
||||
|
||||
@ -22,24 +22,35 @@
|
||||
|
||||
<main>
|
||||
<section class="card glass emu-device-card" aria-label="Device">
|
||||
<div class="emu-device">
|
||||
<div class="emu-lcd-frame">
|
||||
<canvas id="lcd" width="240" height="240" aria-label="v1 LCD"></canvas>
|
||||
<!-- The chassis. Orange anodised body, landscape. Left: disc joystick.
|
||||
Centre: recessed LCD. Right: 3 stacked round keys. Mirrors the
|
||||
physical SeedHammer v1 device layout. -->
|
||||
<div class="emu-chassis" role="group" aria-label="SeedHammer v1 device">
|
||||
<!-- Left side: disc joystick. The disc is one big round element
|
||||
with five overlapping hit zones (top/bottom/left/right/centre).
|
||||
Each zone triggers a different gui.Button. -->
|
||||
<div class="emu-joystick">
|
||||
<button class="emu-disc-zone disc-up" data-btn="0" aria-label="Joystick up">▲</button>
|
||||
<button class="emu-disc-zone disc-left" data-btn="2" aria-label="Joystick left">◀</button>
|
||||
<button class="emu-disc-zone disc-center" data-btn="4" aria-label="Joystick press"></button>
|
||||
<button class="emu-disc-zone disc-right" data-btn="3" aria-label="Joystick right">▶</button>
|
||||
<button class="emu-disc-zone disc-down" data-btn="1" aria-label="Joystick down">▼</button>
|
||||
</div>
|
||||
<div class="emu-controls">
|
||||
<div class="emu-joystick" role="group" aria-label="Joystick">
|
||||
<button class="emu-btn jb up" data-btn="0" aria-label="Up">▲</button>
|
||||
<button class="emu-btn jb left" data-btn="2" aria-label="Left">◀</button>
|
||||
<button class="emu-btn jb center" data-btn="4" aria-label="Center">●</button>
|
||||
<button class="emu-btn jb right" data-btn="3" aria-label="Right">▶</button>
|
||||
<button class="emu-btn jb down" data-btn="1" aria-label="Down">▼</button>
|
||||
</div>
|
||||
<div class="emu-keys" role="group" aria-label="Side keys">
|
||||
<button class="emu-btn key" data-btn="5" aria-label="Button 1">K1</button>
|
||||
<button class="emu-btn key" data-btn="6" aria-label="Button 2">K2</button>
|
||||
<button class="emu-btn key" data-btn="7" aria-label="Button 3">K3</button>
|
||||
|
||||
<!-- Centre: LCD recess. Native 240×240 canvas, displayed bigger
|
||||
with image-rendering:pixelated so the upscale stays crisp. -->
|
||||
<div class="emu-lcd-bezel">
|
||||
<div class="emu-lcd-screen">
|
||||
<canvas id="lcd" width="240" height="240" aria-label="v1 LCD"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Right side: 3 round side keys, stacked. -->
|
||||
<div class="emu-keys">
|
||||
<button class="emu-key" data-btn="5" aria-label="Button 1 (back)">1</button>
|
||||
<button class="emu-key" data-btn="6" aria-label="Button 2 (secondary)">2</button>
|
||||
<button class="emu-key" data-btn="7" aria-label="Button 3 (confirm)">3</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@ -64,12 +75,11 @@
|
||||
|
||||
<footer class="card glass footer-card">
|
||||
<p>
|
||||
Phase 2 scaffolding stage — the GUI itself isn't lifted yet, but the
|
||||
LCD canvas, button input layer, and platform.Platform contract are
|
||||
all in place. The boot screen below shows the 240×240 frame rendered
|
||||
by the Go runtime; press any mapped key (or click any button) and
|
||||
the Go side echoes the event to the browser console.
|
||||
See <a href="https://github.com/mineracks/seedhammer-v1-companion">the repo</a>.
|
||||
Running the upstream SeedHammer v1 firmware GUI in your browser via
|
||||
WebAssembly. The camera/QR-scan path is stubbed for now (the SeedSigner
|
||||
sim handoff lands in Phase 2.5). The engraver path is null — you can
|
||||
navigate, set up multisig, and prepare designs, but the browser can't
|
||||
punch metal. See <a href="https://github.com/mineracks/seedhammer-v1-companion">the repo</a>.
|
||||
</p>
|
||||
</footer>
|
||||
</main>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user