feat(web): voice input — record, check the recognised text, send
CI / ruff · pytest (pull_request) Successful in 2m24s
CI / ruff · pytest (pull_request) Successful in 2m24s
Microphone button in the chat composer and in free-text steps. The browser
records 16 kHz mono WAV (no server-side ffmpeg), /me/{token}/stt transcribes,
the text lands in the field for the patient to check and edit — never sent
automatically, so safety and scrub() apply on send. Hidden when the server has
voice input off or the browser lacks a microphone API. nginx: 4 MB body limit.
Refs #117
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1566,6 +1566,106 @@ button.row-card:hover:not(:disabled) {
|
||||
color: var(--c-text-3);
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------- voice input (#117, slide 11) */
|
||||
|
||||
.voice-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: var(--s-2);
|
||||
justify-self: start;
|
||||
min-height: 44px;
|
||||
padding: 0 var(--s-4);
|
||||
border: 1.5px solid var(--c-border-strong);
|
||||
border-radius: 999px;
|
||||
background: var(--c-surface);
|
||||
color: var(--c-teal-strong);
|
||||
font-weight: 600;
|
||||
font-size: var(--fs-14);
|
||||
}
|
||||
|
||||
.voice-btn:hover:not(:disabled) {
|
||||
background: var(--c-teal-soft);
|
||||
}
|
||||
|
||||
.mic {
|
||||
flex: none;
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
border: 1.5px solid var(--c-border);
|
||||
border-radius: 50%;
|
||||
background: var(--c-surface);
|
||||
color: var(--c-teal-strong);
|
||||
}
|
||||
|
||||
.mic:hover:not(:disabled) {
|
||||
background: var(--c-teal-soft);
|
||||
}
|
||||
|
||||
.voice {
|
||||
display: grid;
|
||||
gap: var(--s-3);
|
||||
padding: var(--s-4);
|
||||
border: 1px solid var(--c-border);
|
||||
border-radius: var(--r-tile);
|
||||
background: #fbf6ee; /* warm, as on the slide */
|
||||
}
|
||||
|
||||
.voice--float {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: calc(100% + var(--s-2));
|
||||
box-shadow: 0 8px 24px rgb(33 59 56 / 12%);
|
||||
}
|
||||
|
||||
.voice__hint {
|
||||
margin: 0;
|
||||
font-weight: 600;
|
||||
color: var(--c-text);
|
||||
}
|
||||
|
||||
.voice__wave {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
height: 44px;
|
||||
}
|
||||
|
||||
.voice__wave span {
|
||||
flex: 1;
|
||||
max-width: 6px;
|
||||
border-radius: 3px;
|
||||
background: #b5452f;
|
||||
transition: height 90ms linear;
|
||||
}
|
||||
|
||||
.voice__row {
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr 1.4fr;
|
||||
align-items: center;
|
||||
gap: var(--s-2);
|
||||
}
|
||||
|
||||
.voice__time {
|
||||
font-variant-numeric: tabular-nums;
|
||||
font-weight: 600;
|
||||
color: var(--c-text-2);
|
||||
}
|
||||
|
||||
.voice__row .btn {
|
||||
min-height: 44px;
|
||||
padding: 0 var(--s-3);
|
||||
font-size: var(--fs-14);
|
||||
}
|
||||
|
||||
.voice__privacy {
|
||||
margin: 0;
|
||||
font-size: var(--fs-12);
|
||||
color: var(--c-text-3);
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------- emergency */
|
||||
|
||||
.sosblock {
|
||||
|
||||
Reference in New Issue
Block a user