feat(dashboard): wire all rail buttons + add Ghost Murmur view
Previously the Inspector and Witness rail buttons did nothing useful. The Ghost Murmur research spec from docs/research/quantum-sensing/16-ghost-murmur-ruview-spec.md had no in-dashboard surface at all. Both addressed. ## nv-rail - Inspector button → view='inspector', pins inspector to Signal tab - Witness button → view='witness', pins inspector to Witness tab - New Ghost Murmur button (ghost-shaped svg) → view='ghost-murmur' - All 5 nav buttons + Settings now functional ## nv-app - View union extended: scene | apps | inspector | witness | ghost-murmur - Main area swaps between <nv-scene>, <nv-app-store>, <nv-ghost-murmur> - nv-inspector receives a `pinTab` prop forcing Signal/Witness tab when the user clicks the corresponding rail button ## nv-ghost-murmur (new view) - Full research view summarising the publicly-reported April 2026 CIA NV-diamond heartbeat program and RuView's 3-tier mesh equivalent - Sections: news context, physics reality check, RuView mapping table, $165 build BoM + honest performance, privacy/ethics/legal, refs - Links out to the spec doc, public gist, issue #437, Sci Am article - Content sourced verbatim from the on-disk research spec ## nv-inspector pinTab - Implements willUpdate() so parent-driven tab pin happens within the same render pass, fixing a Lit "update after update" warning Validated end-to-end with `npx agent-browser` against the live GitHub Pages deploy at https://ruvnet.github.io/RuView/nvsim/ — all 5 rail buttons work, Ghost Murmur view renders 7 sections / 9 cards / 4 outbound links, witness verification still passes. Co-Authored-By: claude-flow <ruv@ruv.net>
This commit is contained in:
parent
79826b9a4d
commit
4483a88b22
|
|
@ -17,8 +17,9 @@ import './nv-palette';
|
|||
import './nv-debug-hud';
|
||||
import './nv-settings-drawer';
|
||||
import './nv-onboarding';
|
||||
import './nv-ghost-murmur';
|
||||
|
||||
export type View = 'scene' | 'apps' | 'settings';
|
||||
export type View = 'scene' | 'apps' | 'inspector' | 'witness' | 'ghost-murmur';
|
||||
|
||||
@customElement('nv-app')
|
||||
export class NvApp extends LitElement {
|
||||
|
|
@ -78,9 +79,16 @@ export class NvApp extends LitElement {
|
|||
<nv-topbar></nv-topbar>
|
||||
<nv-sidebar></nv-sidebar>
|
||||
<div class="main">
|
||||
${this.view === 'apps' ? html`<nv-app-store></nv-app-store>` : html`<nv-scene></nv-scene>`}
|
||||
${this.view === 'apps'
|
||||
? html`<nv-app-store></nv-app-store>`
|
||||
: this.view === 'ghost-murmur'
|
||||
? html`<nv-ghost-murmur></nv-ghost-murmur>`
|
||||
: html`<nv-scene></nv-scene>`}
|
||||
</div>
|
||||
<nv-inspector></nv-inspector>
|
||||
<nv-inspector
|
||||
.pinTab=${this.view === 'inspector' ? 'signal'
|
||||
: this.view === 'witness' ? 'witness' : null}>
|
||||
</nv-inspector>
|
||||
<nv-console></nv-console>
|
||||
</div>
|
||||
<nv-toast></nv-toast>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,339 @@
|
|||
/* Ghost Murmur — research view.
|
||||
*
|
||||
* Walks through the publicly-reported April 2026 CIA program and maps
|
||||
* the physically-defensible parts onto RuView's three-tier heartbeat
|
||||
* mesh. Source: docs/research/quantum-sensing/16-ghost-murmur-ruview-spec.md
|
||||
*
|
||||
* This view is reference material, not an operational mode. It exists
|
||||
* so practitioners (and journalists) can audit the physics-vs-press
|
||||
* gap in the open. ADR-092 §14b.
|
||||
*/
|
||||
|
||||
import { LitElement, html, css } from 'lit';
|
||||
import { customElement } from 'lit/decorators.js';
|
||||
|
||||
@customElement('nv-ghost-murmur')
|
||||
export class NvGhostMurmur extends LitElement {
|
||||
static styles = css`
|
||||
:host {
|
||||
display: block;
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
background: radial-gradient(ellipse at 50% 30%, var(--bg-2) 0%, var(--bg-0) 70%);
|
||||
padding: 24px 28px 60px;
|
||||
}
|
||||
h1 {
|
||||
margin: 0 0 4px;
|
||||
font-size: 22px;
|
||||
letter-spacing: -0.02em;
|
||||
color: var(--ink);
|
||||
}
|
||||
.subtitle {
|
||||
color: var(--ink-3);
|
||||
font-size: 13px;
|
||||
margin-bottom: 22px;
|
||||
}
|
||||
.links {
|
||||
display: flex; flex-wrap: wrap; gap: 6px;
|
||||
margin-bottom: 22px;
|
||||
}
|
||||
.links a {
|
||||
padding: 5px 10px;
|
||||
background: var(--bg-2);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 999px;
|
||||
font-size: 11.5px;
|
||||
font-family: var(--mono);
|
||||
color: var(--accent-2);
|
||||
text-decoration: none;
|
||||
}
|
||||
.links a:hover { border-color: var(--accent-2); }
|
||||
h2 {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.06em;
|
||||
text-transform: uppercase;
|
||||
color: var(--ink-3);
|
||||
margin: 28px 0 10px;
|
||||
}
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
||||
gap: 12px;
|
||||
}
|
||||
.card {
|
||||
background: var(--bg-2);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: var(--radius);
|
||||
padding: 14px;
|
||||
}
|
||||
.card h3 {
|
||||
margin: 0 0 8px;
|
||||
font-size: 13.5px; font-weight: 600;
|
||||
color: var(--ink);
|
||||
}
|
||||
.card p {
|
||||
font-size: 12.5px; color: var(--ink-2);
|
||||
margin: 0 0 8px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
.card p:last-child { margin-bottom: 0; }
|
||||
.stat {
|
||||
display: inline-flex; align-items: baseline; gap: 6px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
.stat .v {
|
||||
font-family: var(--mono); font-size: 16px; font-weight: 600;
|
||||
color: var(--accent);
|
||||
}
|
||||
.stat .l {
|
||||
font-size: 10px; color: var(--ink-3);
|
||||
text-transform: uppercase; letter-spacing: 0.04em;
|
||||
}
|
||||
table {
|
||||
width: 100%; border-collapse: collapse;
|
||||
font-size: 12.5px;
|
||||
}
|
||||
th, td {
|
||||
padding: 8px 10px;
|
||||
text-align: left;
|
||||
border-bottom: 1px solid var(--line);
|
||||
}
|
||||
th {
|
||||
color: var(--ink-3);
|
||||
font-weight: 600;
|
||||
font-size: 11px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.06em;
|
||||
}
|
||||
td.amber { color: var(--accent); font-family: var(--mono); }
|
||||
td.cyan { color: var(--accent-2); font-family: var(--mono); }
|
||||
td.bad { color: var(--bad); font-family: var(--mono); }
|
||||
.pill {
|
||||
display: inline-block;
|
||||
padding: 1px 6px;
|
||||
border-radius: 4px;
|
||||
font-family: var(--mono);
|
||||
font-size: 10px;
|
||||
border: 1px solid var(--line);
|
||||
}
|
||||
.pill.ok { color: var(--ok); border-color: oklch(0.78 0.14 145 / 0.4); }
|
||||
.pill.skeptical { color: var(--bad); border-color: oklch(0.65 0.22 25 / 0.4); }
|
||||
.pill.partial { color: var(--warn); border-color: oklch(0.7 0.18 35 / 0.4); }
|
||||
.architecture {
|
||||
font-family: var(--mono);
|
||||
font-size: 11px;
|
||||
color: var(--ink-2);
|
||||
background: var(--bg-3);
|
||||
padding: 16px;
|
||||
border-radius: var(--radius-sm);
|
||||
border: 1px solid var(--line);
|
||||
white-space: pre;
|
||||
overflow-x: auto;
|
||||
line-height: 1.4;
|
||||
}
|
||||
.ethics {
|
||||
background: linear-gradient(180deg, var(--bg-2) 0%, oklch(0.65 0.22 25 / 0.04) 100%);
|
||||
border: 1px solid oklch(0.65 0.22 25 / 0.25);
|
||||
border-radius: var(--radius);
|
||||
padding: 16px;
|
||||
}
|
||||
.ethics h3 { color: var(--bad); margin-top: 0; }
|
||||
.ethics ul { padding-left: 18px; margin: 8px 0; }
|
||||
.ethics li { font-size: 12.5px; color: var(--ink-2); margin-bottom: 4px; }
|
||||
`;
|
||||
|
||||
override render() {
|
||||
return html`
|
||||
<h1>Ghost Murmur — open-source reality check</h1>
|
||||
<div class="subtitle">
|
||||
The physics-vs-press audit for the publicly-reported April 2026
|
||||
CIA NV-diamond heartbeat detector, and how RuView's existing
|
||||
stack maps onto an honest, civilian version of the same idea.
|
||||
</div>
|
||||
|
||||
<div class="links">
|
||||
<a href="https://github.com/ruvnet/RuView/blob/feat/nvsim-pipeline-simulator/docs/research/quantum-sensing/16-ghost-murmur-ruview-spec.md" target="_blank" rel="noopener">
|
||||
📄 Full spec (583 lines)
|
||||
</a>
|
||||
<a href="https://gist.github.com/ruvnet/e44d0c3f0ad10d9c4933a196a16d405c" target="_blank" rel="noopener">
|
||||
✦ Public gist
|
||||
</a>
|
||||
<a href="https://github.com/ruvnet/RuView/issues/437" target="_blank" rel="noopener">
|
||||
# Issue #437
|
||||
</a>
|
||||
<a href="https://www.scientificamerican.com/article/what-is-the-quantum-ghost-murmur-purportedly-used-in-iran-scientists/" target="_blank" rel="noopener">
|
||||
↗ Scientific American
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<h2>What the press reported</h2>
|
||||
<div class="grid">
|
||||
<div class="card">
|
||||
<h3>The story</h3>
|
||||
<p>3 Apr 2026: USAF F-15E pilot "Dude 44 Bravo" goes down in southern Iran during the regional exchange and evades for ~2 days.</p>
|
||||
<p>President Trump publicly suggests detection from <b>40 miles away</b> on a mountainside at night; CIA Director Ratcliffe says "invisible to the enemy, but not to the CIA."</p>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h3>The named tech</h3>
|
||||
<p><b>"Ghost Murmur"</b> — Lockheed Skunk Works system using NV defects in synthetic diamond + AI to extract a heartbeat from environmental noise.</p>
|
||||
<p>Outlets: <i>Newsweek, Scientific American, Military.com, WION, Open The Magazine, Yahoo, Calcalist</i> + HN thread #47679241.</p>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h3>What physicists said</h3>
|
||||
<p>Wikswo (Vanderbilt), Orzel (Union College), Roth (Oakland) — all pushing back hard.</p>
|
||||
<p>"At 1 km, the heartbeat field drops to ~10⁻¹² of its 10 cm value." MCG-only at multi-mile range is <span class="pill skeptical">not consistent with published physics</span>.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>Physics reality check</h2>
|
||||
<div class="card" style="padding: 6px 14px;">
|
||||
<table>
|
||||
<thead>
|
||||
<tr><th>Distance</th><th>Cardiac MCG (peak QRS)</th><th>vs Earth field (~50 µT)</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr><td>10 cm</td><td class="amber">50 pT</td><td>10⁹× weaker</td></tr>
|
||||
<tr><td>1 m</td><td class="amber">50 fT</td><td>10¹²× weaker</td></tr>
|
||||
<tr><td>10 m</td><td class="cyan">50 aT</td><td>10¹⁵× weaker</td></tr>
|
||||
<tr><td>1 km</td><td class="bad">5 × 10⁻²³ T</td><td>10²⁷× weaker</td></tr>
|
||||
<tr><td>40 mi (65 km)</td><td class="bad">~10⁻²⁸ T</td><td>10³³× weaker</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p style="font-size: 12px; color: var(--ink-3); margin: 10px 0 0; line-height: 1.5;">
|
||||
Best published NV-ensemble lab record: <b>0.9 pT/√Hz</b> [Wolf 2015].
|
||||
Best SQUID in a shielded room: <b>~1 fT/√Hz</b>. To detect a single heartbeat at 10 m
|
||||
you'd need ~2 billion× more sensitivity than any published ensemble has ever shown,
|
||||
in a magnetically silent environment. <i>40 miles is press-release physics.</i>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<h2>RuView's three-tier mesh — what is actually buildable</h2>
|
||||
<div class="architecture"> ┌──────────────────────────┐
|
||||
│ Tier 3 — NV-diamond │ Range: 0.1–2 m (lab)
|
||||
│ magnetometer ring │ Status: nvsim simulator only
|
||||
│ (close-confirm) │ Hardware: $$$ (≥$8k DNV-B1)
|
||||
└──────────┬───────────────┘
|
||||
│
|
||||
┌──────────┴───────────────┐
|
||||
│ Tier 2 — 60 GHz FMCW │ Range: 1–10 m HR/BR
|
||||
│ mmWave radar mesh │ Status: shipping (ADR-021)
|
||||
│ (vital signs, posture) │ Hardware: $15 (MR60BHA2 + ESP32-C6)
|
||||
└──────────┬───────────────┘
|
||||
│
|
||||
┌──────────┴───────────────┐
|
||||
│ Tier 1 — WiFi CSI mesh │ Range: 10–30 m through-wall
|
||||
│ (presence, breathing, │ Status: shipping (ADR-014, ADR-029)
|
||||
│ pose, intention) │ Hardware: $9 (ESP32-S3 8MB)
|
||||
└──────────┬───────────────┘
|
||||
│
|
||||
▼
|
||||
┌────────────────────────────────┐
|
||||
│ RuvSense multistatic fusion │
|
||||
│ + cross-viewpoint attention │
|
||||
│ + AETHER re-ID embeddings │
|
||||
│ + Cramer-Rao gating │
|
||||
└────────────────────────────────┘</div>
|
||||
|
||||
<h2>Press claim → RuView equivalent</h2>
|
||||
<div class="card" style="padding: 6px 14px;">
|
||||
<table>
|
||||
<thead>
|
||||
<tr><th>Press claim</th><th>RuView equivalent today</th><th>Crate / ADR</th><th>Honest range</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>NV-diamond magnetometry</td>
|
||||
<td>Deterministic NV pipeline simulator</td>
|
||||
<td><code>nvsim</code> · ADR-089</td>
|
||||
<td>Simulator only</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>"AI strips environmental noise"</td>
|
||||
<td>RuvSense multistatic fusion + AETHER</td>
|
||||
<td>signal/ruvsense/ · ADR-029</td>
|
||||
<td>Mature</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Heartbeat at distance</td>
|
||||
<td>60 GHz FMCW HR/BR + WiFi CSI breathing</td>
|
||||
<td>vitals · ADR-021</td>
|
||||
<td><span class="pill ok">1–5 m HR · 10–30 m presence</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Long-range localisation</td>
|
||||
<td>Multistatic time-of-flight + CRLB</td>
|
||||
<td>ruvector/viewpoint/</td>
|
||||
<td>Limited by node spacing</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><i>40-mile single-heartbeat detection</i></td>
|
||||
<td><i>Not feasible at any tier</i></td>
|
||||
<td>—</td>
|
||||
<td><span class="pill skeptical">Press-release physics</span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<h2>Build today on $165</h2>
|
||||
<div class="grid">
|
||||
<div class="card">
|
||||
<h3>Bill of materials</h3>
|
||||
<p style="font-family: var(--mono); font-size: 11.5px; line-height: 1.7; color: var(--ink-2);">
|
||||
3 × ESP32-S3 8 MB ($9 ea)<br>
|
||||
3 × PoE injector + cat6 ($6 ea)<br>
|
||||
1 × ESP32-C6 + Seeed MR60BHA2 ($15)<br>
|
||||
1 × Raspberry Pi 5 8 GB ($80)<br>
|
||||
1 × unmanaged GbE switch ($25)
|
||||
</p>
|
||||
<p><b>Total: $165</b></p>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h3>Honest performance</h3>
|
||||
<span class="stat"><span class="v">95%</span><span class="l">TPR (LOS, 0–15 m)</span></span><br><br>
|
||||
<span class="stat"><span class="v">±2 bpm</span><span class="l">HR (LOS 0–3 m)</span></span><br><br>
|
||||
<span class="stat"><span class="v">±1 br/min</span><span class="l">BR (any mode)</span></span><br><br>
|
||||
<span class="stat"><span class="v">~10 cm</span><span class="l">pose error</span></span><br><br>
|
||||
<span class="stat"><span class="v">80–150 ms</span><span class="l">end-to-end latency</span></span>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h3>Determinism</h3>
|
||||
<p>Same <code style="font-family: var(--mono); color: var(--accent);">(scene, config, seed)</code> → byte-identical SHA-256 witness across browsers, OSes, transports.</p>
|
||||
<p>Reference: <span style="font-family: var(--mono); font-size: 10.5px; color: var(--accent-3);">cc8de9b01b0ff5bd…</span></p>
|
||||
<p>Try the Witness tab on the right — it re-derives the hash live in this browser and compares against the published reference.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>Privacy, ethics, legal</h2>
|
||||
<div class="ethics">
|
||||
<h3>This is the open-source version. Same physics, opposite governance.</h3>
|
||||
<ul>
|
||||
<li><b>Civilian opt-in only</b> — search-and-rescue, elder-care, occupancy, ICU vitals. Not surveillance.</li>
|
||||
<li><b>No directional pursuit</b> — no beam-steering, target-following, or remote person-of-interest tracking.</li>
|
||||
<li><b>Data minimisation</b> — fused output is <code>(presence, HR, BR, pose, p_alive)</code>; raw streams discarded at the edge.</li>
|
||||
<li><b>PII gates</b> (ADR-040) block identifying biometric streams from leaving the local mesh without consent.</li>
|
||||
<li><b>Adversarial-signal detection</b> flags physically-impossible signal patterns from compromised mesh nodes.</li>
|
||||
<li><b>No export-controlled hardware</b> — RuView targets < $50 COTS. ITAR/EAR sub-THz coherent radars and shielded NV ensembles are out of scope.</li>
|
||||
</ul>
|
||||
<p style="font-size: 11.5px; color: var(--ink-3); margin: 10px 0 0;">
|
||||
RuView is not affiliated with the United States government, the CIA, Lockheed Martin,
|
||||
or any classified program. References to "Ghost Murmur" in this view refer
|
||||
exclusively to the publicly-reported program of that name as covered in the open
|
||||
press in April 2026.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<h2>Cross-references</h2>
|
||||
<div class="card">
|
||||
<p style="font-size: 12px; color: var(--ink-2); line-height: 1.7; margin: 0;">
|
||||
<b>ADRs:</b> 014 (signal) · 021 (vitals) · 024 (AETHER) · 027 (MERIDIAN) ·
|
||||
028 (witness audit) · 029 (RuvSense) · 040 (PII gates) · 086 (ESP32 RaBitQ) ·
|
||||
<b>089 (nvsim, Accepted)</b> · 090 (Lindblad, Proposed-conditional) ·
|
||||
091 (sub-THz radar research) · <b>092 (this dashboard)</b>.<br><br>
|
||||
<b>Primary physics:</b> Cohen 1970 · Bison 2009 · Wolf 2015 · Barry RMP 2020 · Doherty 2013 · Jackson 3e §5.6/§5.8.
|
||||
</p>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
/* Inspector — tabbed: Signal / Frame / Witness. */
|
||||
import { LitElement, html, css, svg } from 'lit';
|
||||
import { customElement, state } from 'lit/decorators.js';
|
||||
import { LitElement, html, css, svg, type PropertyValues } from 'lit';
|
||||
import { customElement, state, property } from 'lit/decorators.js';
|
||||
import { effect } from '@preact/signals-core';
|
||||
import {
|
||||
traceX, traceY, traceZ, stripBars, lastFrame,
|
||||
|
|
@ -13,6 +13,8 @@ type Tab = 'signal' | 'frame' | 'witness';
|
|||
@customElement('nv-inspector')
|
||||
export class NvInspector extends LitElement {
|
||||
@state() private tab: Tab = 'signal';
|
||||
/** When set by the parent, force the tab and pulse-highlight it. */
|
||||
@property({ attribute: false }) pinTab: Tab | null = null;
|
||||
|
||||
static styles = css`
|
||||
:host {
|
||||
|
|
@ -123,6 +125,16 @@ export class NvInspector extends LitElement {
|
|||
});
|
||||
}
|
||||
|
||||
override willUpdate(changed: PropertyValues): void {
|
||||
// Apply parent-driven tab pin during willUpdate so the new tab value
|
||||
// participates in this same render pass — avoids the "update after
|
||||
// update completed" Lit warning that would fire if we did this in
|
||||
// updated().
|
||||
if (changed.has('pinTab') && this.pinTab && this.tab !== this.pinTab) {
|
||||
this.tab = this.pinTab;
|
||||
}
|
||||
}
|
||||
|
||||
private async verify(): Promise<void> {
|
||||
const c = getClient(); if (!c) return;
|
||||
witnessVerified.value = 'pending';
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ export class NvRail extends LitElement {
|
|||
content: ''; position: absolute; left: -10px; top: 8px; bottom: 8px;
|
||||
width: 2px; background: var(--accent); border-radius: 2px;
|
||||
}
|
||||
.btn.ghost.active::before { background: var(--accent-3); }
|
||||
.spacer { flex: 1; }
|
||||
svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 1.8; }
|
||||
`;
|
||||
|
|
@ -69,16 +70,27 @@ export class NvRail extends LitElement {
|
|||
@click=${() => this.navigate('apps')}>
|
||||
<svg viewBox="0 0 24 24"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/></svg>
|
||||
</button>
|
||||
<button class="btn" title="Inspector" @click=${() => this.navigate('scene')}>
|
||||
<button class="btn ${this.view === 'inspector' ? 'active' : ''}" data-id="inspector-btn" title="Inspector"
|
||||
@click=${() => this.navigate('inspector')}>
|
||||
<svg viewBox="0 0 24 24"><circle cx="11" cy="11" r="7"/><line x1="21" y1="21" x2="16.6" y2="16.6"/></svg>
|
||||
</button>
|
||||
<button class="btn" title="Witness">
|
||||
<button class="btn ${this.view === 'witness' ? 'active' : ''}" data-id="witness-btn" title="Witness"
|
||||
@click=${() => this.navigate('witness')}>
|
||||
<svg viewBox="0 0 24 24"><path d="M9 12l2 2 4-4M21 12c0 4.97-4.03 9-9 9s-9-4.03-9-9 4.03-9 9-9 9 4.03 9 9z"/></svg>
|
||||
</button>
|
||||
<button class="btn ghost ${this.view === 'ghost-murmur' ? 'active' : ''}"
|
||||
data-id="ghost-murmur-btn" title="Ghost Murmur — research spec"
|
||||
@click=${() => this.navigate('ghost-murmur')}>
|
||||
<svg viewBox="0 0 24 24">
|
||||
<path d="M9 2C5.7 2 3 4.7 3 8v12l3-2 3 2 3-2 3 2 3-2 3 2V8c0-3.3-2.7-6-6-6H9z"/>
|
||||
<circle cx="9" cy="10" r="1.2" fill="currentColor"/>
|
||||
<circle cx="15" cy="10" r="1.2" fill="currentColor"/>
|
||||
</svg>
|
||||
</button>
|
||||
<div class="spacer"></div>
|
||||
<button class="btn" data-id="settings-btn" title="Settings"
|
||||
@click=${() => this.dispatchEvent(new CustomEvent('open-settings', { bubbles: true, composed: true }))}>
|
||||
<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 00.33 1.82l.06.06a2 2 0 010 2.83 2 2 0 01-2.83 0l-.06-.06A1.65 1.65 0 0015 19.4a1.65 1.65 0 00-1 1.51V21a2 2 0 01-4 0v-.09A1.65 1.65 0 009 19.4a1.65 1.65 0 00-1.82.33l-.06.06a2 2 0 01-2.83 0 2 2 0 010-2.83l.06-.06A1.65 1.65 0 004.6 15a1.65 1.65 0 00-1.51-1H3a2 2 0 010-4h.09A1.65 1.65 0 004.6 9a1.65 1.65 0 00-.33-1.82l-.06-.06a2 2 0 010-2.83 2 2 0 012.83 0l.06.06A1.65 1.65 0 009 4.6a1.65 1.65 0 001-1.51V3a2 2 0 014 0v.09A1.65 1.65 0 0015 4.6a1.65 1.65 0 001.82-.33l.06-.06a2 2 0 012.83 0 2 2 0 010 2.83l-.06.06A1.65 1.65 0 0019.4 9a1.65 1.65 0 001.51 1H21a2 2 0 010 4h-.09a1.65 1.65 0 00-1.51 1z"/></svg>
|
||||
<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 00.33 1.82l.06.06a2 2 0 010 2.83 2 2 0 01-2.83 0l-.06-.06A1.65 1.65 0 0015 19.4a1.65 1.65 0 00-1 1.51V21a2 2 0 01-4 0v-.09A1.65 1.65 0 009 19.4a1.65 1.65 0 00-1.82.33l-.06.06a2 2 0 01-2.83 0 2 2 0 010-2.83l.06-.06A1.65 1.65 0 004.6 15a1.65 1.65 0 00-1.51-1H3a2 2 0 010-4h.09A1.65 1.65 0 004.6 9a1.65 1.65 0 00-.33-1.82l-.06-.06a2 2 0 010-2.83 2 2 0 012.83 0l.06.06A1.65 1.65 0 009 4.6a1.65 1.65 0 001-1.51V3a2 2 0 014 0v.09A1.65 1.65 0 0015 4.6a1.65 1.65 0 001.82-.33l.06.06a2 2 0 012.83 0 2 2 0 010 2.83l-.06.06A1.65 1.65 0 0019.4 9a1.65 1.65 0 001.51 1H21a2 2 0 010 4h-.09a1.65 1.65 0 00-1.51 1z"/></svg>
|
||||
</button>
|
||||
`;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue