PFD/cockpit polish + KAP140 autopilot + UI refinements
- PFD: full-screen 2D attitude, G1000 yellow+magenta chevron symbology, rAF 60fps horizon smoothing, translucent tapes, slimmer softkey bar, header fixes - Collapsible macOS-dark sidebar (Inter), VFR six-pack + engine cluster + tach - KAP140 autopilot on the analog page; GMC-710 AFCS tab - FMS rebuilt as an X-Plane-style CDU; PWA; settings panel (knob mode) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -34,7 +34,7 @@ const MFD_MENU = {
|
||||
// autopilot_state bitfield (best-effort; tweak per aircraft)
|
||||
const AP_BITS = { fd: 1 << 0, hdg: 1 << 1, vs: 1 << 4, flc: 1 << 6, nav: 1 << 8, apr: 1 << 9, vnav: 1 << 11, altHold: 1 << 14, bc: 1 << 18 };
|
||||
|
||||
export default function Bezel({ variant = 'pfd', xp, svt3d, onToggleSvt, inset, onSetInset, insetMode, onInsetMode, nrst, onToggleNrst, tmr, onToggleTmr, onDirect, onProc, mapMode, onMapMode, children }) {
|
||||
export default function Bezel({ variant = 'pfd', xp, svt3d, onToggleSvt, inset, onSetInset, insetMode, onInsetMode, nrst, onToggleNrst, tmr, onToggleTmr, onDirect, onProc, mapMode, onMapMode, knobMode = 'arrows', children }) {
|
||||
const u = variant === 'mfd' ? 'mfd' : 'pfd'; // command prefix
|
||||
const fire = (suffix) => xp && xp.command(`${u}_${suffix}`);
|
||||
const [page, setPage] = useState('root'); // softkey menu page
|
||||
@@ -105,12 +105,12 @@ export default function Bezel({ variant = 'pfd', xp, svt3d, onToggleSvt, inset,
|
||||
return (
|
||||
<div className="bezel">
|
||||
<div className="bezel-knobs left">
|
||||
<Knob label="NAV" sub="VOL · PUSH ID" fire={fire}
|
||||
<Knob label="NAV" sub="VOL · PUSH ID" fire={fire} mode={knobMode}
|
||||
outer={['nav_outer_up', 'nav_outer_down']} inner={['nav_inner_up', 'nav_inner_down']} push="nav12" />
|
||||
<Knob label="HDG" sub="PUSH HDG SYNC" fire={fire}
|
||||
<Knob label="HDG" sub="PUSH HDG SYNC" fire={fire} mode={knobMode}
|
||||
outer={['hdg_up', 'hdg_down']} push="hdg_sync" />
|
||||
{variant === 'mfd' && xp && <APController xp={xp} />}
|
||||
<Knob label="ALT" sub="" big fire={fire}
|
||||
<Knob label="ALT" sub="" big fire={fire} mode={knobMode}
|
||||
outer={['alt_outer_up', 'alt_outer_down']} inner={['alt_inner_up', 'alt_inner_down']} />
|
||||
</div>
|
||||
|
||||
@@ -133,18 +133,18 @@ export default function Bezel({ variant = 'pfd', xp, svt3d, onToggleSvt, inset,
|
||||
</div>
|
||||
|
||||
<div className="bezel-knobs right">
|
||||
<Knob label="COM" sub="VOL · PUSH SQ" fire={fire}
|
||||
<Knob label="COM" sub="VOL · PUSH SQ" fire={fire} mode={knobMode}
|
||||
outer={['com_outer_up', 'com_outer_down']} inner={['com_inner_up', 'com_inner_down']} push="com12" />
|
||||
<Knob label="CRS / BARO" sub="PUSH CRS CTR" fire={fire}
|
||||
<Knob label="CRS / BARO" sub="PUSH CRS CTR" fire={fire} mode={knobMode}
|
||||
outer={['crs_up', 'crs_down']} inner={['baro_up', 'baro_down']} push="crs_sync" />
|
||||
<Knob label="RANGE" sub="PUSH PAN" joy fire={fire}
|
||||
<Knob label="RANGE" sub="PUSH PAN" joy fire={fire} mode={knobMode}
|
||||
outer={['range_up', 'range_down']} push="pan_push" pan />
|
||||
<div className="bezel-grid">
|
||||
<BtnG fire={fire} cmd="direct" onClick={onDirect}>D→</BtnG><BtnG fire={fire} cmd="menu">MENU</BtnG>
|
||||
<BtnG fire={fire} cmd="fpl">FPL</BtnG><BtnG fire={fire} cmd="proc" onClick={onProc}>PROC</BtnG>
|
||||
<BtnG fire={fire} cmd="clr">CLR</BtnG><BtnG fire={fire} cmd="ent">ENT</BtnG>
|
||||
<BtnG fire={fire} mode={knobMode} cmd="direct" onClick={onDirect}>D→</BtnG><BtnG fire={fire} mode={knobMode} cmd="menu">MENU</BtnG>
|
||||
<BtnG fire={fire} mode={knobMode} cmd="fpl">FPL</BtnG><BtnG fire={fire} mode={knobMode} cmd="proc" onClick={onProc}>PROC</BtnG>
|
||||
<BtnG fire={fire} mode={knobMode} cmd="clr">CLR</BtnG><BtnG fire={fire} mode={knobMode} cmd="ent">ENT</BtnG>
|
||||
</div>
|
||||
<Knob label="FMS" sub="PUSH CRSR" big fire={fire}
|
||||
<Knob label="FMS" sub="PUSH CRSR" big fire={fire} mode={knobMode}
|
||||
outer={['fms_outer_up', 'fms_outer_down']} inner={['fms_inner_up', 'fms_inner_down']} push="cursor" />
|
||||
</div>
|
||||
</div>
|
||||
@@ -186,30 +186,43 @@ function APController({ xp }) {
|
||||
// the mouse wheel; the inner ring via the top/bottom arrows (˄ ˅) and shift+wheel.
|
||||
// Clicking the knob centre fires the push action (PUSH …). The RANGE knob also
|
||||
// pans with a directional cross.
|
||||
function Knob({ label, sub, outer, inner, push, big, joy, pan, fire }) {
|
||||
function Knob({ label, sub, outer, inner, push, big, joy, pan, fire, mode = 'arrows' }) {
|
||||
const onWheel = (e) => {
|
||||
if (!outer) return;
|
||||
e.preventDefault();
|
||||
const set = (e.shiftKey && inner) ? inner : outer;
|
||||
fire(e.deltaY < 0 ? set[0] : set[1]);
|
||||
};
|
||||
const zoneClick = (e) => {
|
||||
const r = e.currentTarget.getBoundingClientRect();
|
||||
const dx = e.clientX - (r.left + r.width / 2);
|
||||
const dy = e.clientY - (r.top + r.height / 2);
|
||||
const rel = Math.hypot(dx, dy) / (r.width / 2);
|
||||
if (rel < 0.42 && push) { fire(push); return; } // centre → PUSH
|
||||
if (Math.abs(dy) >= Math.abs(dx)) { if (outer) fire(dy < 0 ? outer[0] : outer[1]); }
|
||||
else if (inner) fire(dx > 0 ? inner[0] : inner[1]);
|
||||
else if (outer) fire(dx > 0 ? outer[0] : outer[1]);
|
||||
};
|
||||
const zones = mode === 'zones';
|
||||
return (
|
||||
<div className={`knob-wrap ${big ? 'big' : ''}`}>
|
||||
<span className="knob-lbl">{label}</span>
|
||||
<div className="knob-cluster">
|
||||
{inner && <button className="knob-arrow top" onClick={() => fire(inner[0])}>˄</button>}
|
||||
{outer && <button className="knob-arrow left" onClick={() => fire(outer[1])}>‹</button>}
|
||||
<div className={`knob-cluster ${zones ? 'zones' : ''}`}>
|
||||
{/* arrows mode (touch-friendly): visible ˄‹›˅ buttons. zones mode: click
|
||||
the knob face itself (top/bottom = outer, left/right = inner). */}
|
||||
{!zones && inner && <button className="knob-arrow top" onClick={() => fire(inner[0])}>˄</button>}
|
||||
{!zones && outer && <button className="knob-arrow left" onClick={() => fire(outer[1])}>‹</button>}
|
||||
<button
|
||||
className={`knob outer ${joy ? 'joy' : ''}`}
|
||||
onWheel={onWheel}
|
||||
onClick={() => push && fire(push)}
|
||||
title={push ? 'PUSH' : ''}
|
||||
onClick={zones ? zoneClick : (() => push && fire(push))}
|
||||
title={zones ? `${outer ? 'oben/unten' : ''}${inner ? ' · links/rechts (fein)' : ''}${push ? ' · Mitte: PUSH' : ''}` : (push ? 'PUSH' : '')}
|
||||
>
|
||||
<span className="knob inner" />
|
||||
{joy && <div className="joy-cross">+</div>}
|
||||
</button>
|
||||
{outer && <button className="knob-arrow right" onClick={() => fire(outer[0])}>›</button>}
|
||||
{inner && <button className="knob-arrow bottom" onClick={() => fire(inner[1])}>˅</button>}
|
||||
{!zones && outer && <button className="knob-arrow right" onClick={() => fire(outer[0])}>›</button>}
|
||||
{!zones && inner && <button className="knob-arrow bottom" onClick={() => fire(inner[1])}>˅</button>}
|
||||
</div>
|
||||
{pan && (
|
||||
<div className="pan-pad">
|
||||
|
||||
Reference in New Issue
Block a user