fix: full review pass (main process, voice pipeline, Hermes client, HUD) and e2e-validated local voice (v2.1.0.1)

Electron main
- utility-process channel: sherpa output copied into V8 buffers (Electron rejects external
  buffers), audio exchanged as base64; worker restart on timeout, identity-safe exit handling,
  deterministic native unload by restart
- webhook: loopback by default without secret, UTF-8-safe body assembly, clean restart, port validation
- files IPC: realpath-based root check, openPath allow-list (reveal-only outside document folders),
  Windows reserved names; store: debounced async atomic writes with backup of corrupt files;
  logger: streaming writes with rotation; log message size cap
- HTTP stream proxy: socket released on idle timeout / renderer destroyed, id validation
- model manager: inactivity timeout, retrying rm/rename (Windows locks), engine stopped before
  replacing a model; WAV decoder handles float/24-bit; IPC payload validation
- window: opaque rounded window on Windows, navigation lock-down, visibility events;
  Ctrl+Alt+Escape instead of the Task Manager shortcut; single-instance guard; EVEFLOW_USER_DATA

Voice pipeline
- abort semantics (SendHandle.aborted), abort before the stream opens, session id prefixes per transport
- hands-free re-arm after replies without speech, start/stop race, no chime on auto re-arm,
  no silence shipped to STT (400 ms pre-roll), no transcription of empty manual stops
- TTS: bounded prefetch, cancellable segments, non-interrupting notices, volume applied at play time
- SSE CRLF split, usage in chat completions, finish_reason length, phonetic regex hoisted

HUD
- core renderer: no canvas shadows, cached colours, reusable spectrum buffer, theme read on change,
  30 fps idle, stops when the window is hidden; ping flashes on send / tool / speech
- deltas coalesced per animation frame; stable auto-scroll; narrow selectors everywhere
- bundled fonts (offline), reduce-motion fix, error toasts, ops drawer below 1180 px,
  interim transcript and first-token latency in the core caption, Ctrl+K, dialog semantics,
  switch/aria roles, compact widget cleanup, Whisper small recommended for French
- docs/ROADMAP.md: audit results, e2e results and the plan towards a real JARVIS

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017Wn5VX9HNbJ7N54hR24u9Y
This commit is contained in:
Claude committed 2026-09-03 18:08:09 +00:00
1 parent b1b8d0d5d4
commit 5f0fa7a1e2
55 files changed
+1181 -433

No files matched your search

+54 -8
View File
@@ -7,6 +7,7 @@
grid-template-rows: 46px 1fr;
border-radius: 14px;
overflow: hidden;
background-clip: padding-box;
background:
radial-gradient(1200px 700px at 50% 40%, rgba(var(--accent-rgb), 0.08), transparent 60%),
linear-gradient(180deg, var(--bg-1) 0%, var(--bg-0) 100%);
@@ -51,6 +52,17 @@
grid-template-columns: minmax(340px, 1fr) minmax(380px, 1fr);
}
.hud-body > .ops-column {
position: absolute;
right: 14px;
top: 12px;
bottom: 14px;
width: min(360px, 60%);
z-index: 6;
background: var(--bg-1);
border-radius: var(--radius);
box-shadow: -20px 0 60px rgba(0, 0, 0, 0.5);
}
.hud-body > .ops-column:not(.open) {
display: none;
}
}
@@ -68,7 +80,6 @@
background: var(--panel);
border: 1px solid var(--line);
border-radius: var(--radius);
backdrop-filter: blur(14px);
display: flex;
flex-direction: column;
min-height: 0;
@@ -310,7 +321,8 @@
background: linear-gradient(180deg, rgba(var(--accent-rgb), 0.06), transparent);
-webkit-app-region: drag;
}
.topbar > * {
.topbar button,
.topbar .chip {
-webkit-app-region: no-drag;
}
.topbar .brand {
@@ -346,9 +358,6 @@
overflow: hidden;
-webkit-app-region: drag;
}
.topbar .status-strip > * {
-webkit-app-region: no-drag;
}
.topbar .clock {
font-family: var(--font-mono);
font-size: 13px;
@@ -421,6 +430,21 @@
.core-state.success {
color: var(--success);
}
.core-interim {
font-family: var(--font-ui);
font-size: 15px;
color: var(--ink-1);
max-width: 82%;
text-align: center;
opacity: 0.85;
min-height: 1.4em;
}
.core-latency {
font-family: var(--font-mono);
font-size: 10px;
letter-spacing: 0.2em;
color: var(--ink-3);
}
.core-sub {
font-family: var(--font-mono);
font-size: 12px;
@@ -438,10 +462,13 @@
right: 0;
text-align: center;
font-family: var(--font-display);
font-size: 22px;
font-size: clamp(13px, 2.2vw, 22px);
font-weight: 900;
letter-spacing: 0.5em;
padding-left: 0.5em;
letter-spacing: clamp(0.15em, 0.8vw, 0.5em);
padding: 0 12px 0 calc(12px + 0.4em);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
color: var(--ink-0);
text-shadow: 0 0 22px var(--accent-glow);
pointer-events: none;
@@ -474,6 +501,25 @@
margin-left: auto;
}
/* ── Toasts ────────────────────────────────────────────────────────────── */
.toast-stack {
position: absolute;
left: 50%;
bottom: 22px;
transform: translateX(-50%);
z-index: 18;
display: flex;
flex-direction: column;
gap: 8px;
width: min(560px, 80%);
pointer-events: none;
}
.toast-stack .error-banner {
pointer-events: auto;
box-shadow: 0 20px 50px rgba(0, 0, 0, 0.5);
animation: rise-in 200ms ease-out;
}
/* ── Overlay / modal ───────────────────────────────────────────────────── */
.overlay {
position: absolute;
-1
View File
@@ -11,7 +11,6 @@
display: flex;
flex-direction: column;
gap: 12px;
scroll-behavior: smooth;
}
.chat-list.compact {
padding: 8px 10px;
+10 -2
View File
@@ -7,7 +7,15 @@
background: linear-gradient(180deg, rgba(5, 12, 22, var(--compact-opacity, 0.92)), rgba(2, 6, 12, var(--compact-opacity, 0.92)));
border: 1px solid rgba(var(--accent-rgb), 0.3);
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.6), 0 0 30px rgba(var(--accent-rgb), 0.12);
backdrop-filter: blur(16px);
}
.compact-body .panel {
background: transparent;
border: none;
border-radius: 0;
}
.compact-body .panel.bracket::before,
.compact-body .panel.bracket::after {
display: none;
}
.compact-head {
display: flex;
@@ -29,7 +37,7 @@
}
.compact-core {
position: relative;
height: 220px;
height: clamp(140px, 38vh, 220px);
flex-shrink: 0;
}
.compact-core canvas {
+11
View File
@@ -104,3 +104,14 @@
background: linear-gradient(90deg, var(--accent), var(--success));
transition: width 60ms linear;
}
.switch {
width: 100%;
background: transparent;
border-left: none;
border-right: none;
border-top: none;
color: inherit;
text-align: left;
font: inherit;
}
+21 -3
View File
@@ -1,5 +1,3 @@
@import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@500;700;900&family=Rajdhani:wght@400;500;600;700&family=Share+Tech+Mono&display=swap');
:root {
--font-display: 'Orbitron', 'Rajdhani', 'Segoe UI', sans-serif;
--font-ui: 'Rajdhani', 'Segoe UI', 'Helvetica Neue', sans-serif;
@@ -137,7 +135,27 @@ textarea {
background: rgba(var(--accent-rgb), 0.45);
}
:root[data-motion='reduce'] * {
:root[data-motion='reduce'] *,
:root[data-motion='reduce'] *::before,
:root[data-motion='reduce'] *::after {
animation-iteration-count: 1 !important;
animation-duration: 0.001s !important;
transition-duration: 0.001s !important;
}
@media (prefers-reduced-motion: reduce) {
:root:not([data-motion='full']) *,
:root:not([data-motion='full']) *::before,
:root:not([data-motion='full']) *::after {
animation-iteration-count: 1 !important;
animation-duration: 0.001s !important;
transition-duration: 0.001s !important;
}
}
.error-banner span,
.activity-item .detail,
.row-item .desc,
.row-item .meta,
.core-sub,
.kv dd {
user-select: text;
}