feat: legibility — conditions, hints, kinder errors (Workstream B)
GET /api/conditions surfaces what the backend already computes: moon phase, veil thinness, geomagnetic state — junk lon degrades to moon-only instead of a 422, missing NOAA data means fewer lines, never an error. VeilConditions renders it in the séance side column, polling every 10 min. ModeHint: one in-fiction line per mode after 15s of an unused sensor, dismissed forever via localStorage. Error copy in evp/radio now detect-and-redirects (mic denied -> 'the board needs no ear'; no WebUSB -> try EVP) instead of dead-ending. No geolocation prompt from the conditions strip — asking for location from a passive readout would be hostile; ?lon= stays supported for callers that have it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> # Conflicts: # frontend/src/pages/SeancePage.tsx
This commit is contained in:
@@ -27,6 +27,8 @@ import { RitualPanel } from '../components/RitualPanel'
|
||||
import { DeviceWhisper } from '../components/DeviceWhisper'
|
||||
import { JudgmentPanel } from '../components/JudgmentPanel'
|
||||
import { TelemetryReadout } from '../components/TelemetryReadout'
|
||||
import { VeilConditions } from '../components/VeilConditions'
|
||||
import { ModeHint } from '../components/ModeHint'
|
||||
import { PlanchetteMachine } from '../lib/planchette'
|
||||
import type { PlanchetteSnapshot } from '../lib/planchette'
|
||||
import { EvpListener } from '../lib/evp'
|
||||
@@ -387,6 +389,7 @@ function SeanceSession({ username }: { username: string }) {
|
||||
onToggleOpen={() => setDossierOpen((o) => !o)}
|
||||
/>
|
||||
|
||||
<VeilConditions />
|
||||
<DeviceWhisper />
|
||||
<RitualPanel />
|
||||
<JudgmentPanel />
|
||||
@@ -503,6 +506,7 @@ function WirePanel() {
|
||||
})}
|
||||
</p>
|
||||
)}
|
||||
<ModeHint mode="wire" suppressed={state.passive} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -666,6 +670,7 @@ function EvpPanel() {
|
||||
>
|
||||
{listening ? t('seance.evp.stop') : t('seance.evp.start')}
|
||||
</button>
|
||||
<ModeHint mode="evp" suppressed={listening || pending} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -895,6 +900,7 @@ function RadioPanel() {
|
||||
>
|
||||
{phase === 'sweeping' ? t('seance.radio.stop') : t('seance.radio.start')}
|
||||
</button>
|
||||
<ModeHint mode="radio" suppressed={phase !== 'idle'} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -935,6 +941,7 @@ function OuijaPanel({ snapshot }: { snapshot: PlanchetteSnapshot | null }) {
|
||||
{snapshot && snapshot.spelled.length > 0 && (
|
||||
<p className="ouija-history dim">{snapshot.spelled.slice(-6).join(' · ')}</p>
|
||||
)}
|
||||
<ModeHint mode="ouija" suppressed={word !== null} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1193,6 +1200,7 @@ function EmfPanel() {
|
||||
{t('seance.emf.calibrate', { defaultValue: 'calibrate' })}
|
||||
</button>
|
||||
</div>
|
||||
<ModeHint mode="emf" suppressed={listening || pending} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user