Voice (dictate, read aloud) and Draw a picture over /v1, with the app's models scope
ci / ui (push) Waiting to run
ci / check (push) Failing after 3s
ci / start (push) Failing after 3s

The server kept voice and images on /v1 (models scope, which the app's token
already asks for); /me's services and scopes decide what is offered. Rust
sends the recording to /v1/audio/transcriptions, reads /v1/audio/speech back
as a data: URL (CSP media-src data:), and for /v1/images/generations takes
only the id of each of the person's own files from the answer and fetches
/v1/files/<id>/content from the app's own server. The microphone is allowed
only within seconds of the page's own Dictate (voice_arm); WebKitGTK's
media stream is switched on through its settings getter.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
HomerandClaude Opus 5.5 committed 2026-11-10 14:02:56 +00:00
1 parent 67ce6076c5
commit 2b87fa4c29
16 files changed
+1024 -4

No files matched your search

+5
View File
@@ -22,6 +22,8 @@ import { initAgent, link } from "./agent.js"
import { initScheduled, showScheduled, scheduledCommands } from "./scheduled.js"
import { initArchived, showArchived } from "./archived.js"
import { showMemory } from "./memory.js"
import { initVoice, voiceFor } from "./voice.js"
import { canDraw, openDraw } from "./draw.js"
import { closePopover } from "./popup.js"
import { noteSubagentsCap } from "./models.js"
@@ -94,6 +96,7 @@ async function startApp() {
try {
me = await get("/me")
noteMe(me)
voiceFor(me)
} catch (err) {
$("thread-note").textContent = why(err)
$("thread-note").hidden = false
@@ -233,6 +236,7 @@ function commands() {
{ label: t("Memory"), run: () => showMemoryPage() },
{ label: t("Settings"), run: () => showSettings(true) },
]
if (canDraw(me)) out.push({ label: t("Draw a picture"), run: openDraw })
if (openChatId()) out.push({ label: panelOpen() ? t("Close the activity panel") : t("Open the activity panel"), run: togglePanel })
if (terminalAvailable()) out.push({ label: terminalOpen() ? t("Close terminal") : t("Terminal"), run: toggleTerminal })
if (status?.linked) out.push({ label: status.agent_wanted ? t("Stop the agent") : t("Start the agent"), run: () => invoke("set_agent", { on: !status.agent_wanted }) })
@@ -280,6 +284,7 @@ async function boot() {
$("thread-note").hidden = !text
},
})
initVoice()
initChat({
status: () => status,
thisDevices,