feat: listen mode + volume knob in chat; briefing discuss auto-send; fix LLM proactive note search

- ChatView: listen mode toggle (auto-reads new responses via TTS), volume popup
  with range slider persisted per-device in localStorage via GainNode
- useVoiceAudio: shared module-level _volume ref with localStorage persistence,
  GainNode for volume control, exported setVoiceVolume()
- tts.py: pre-warm all Kokoro voices at pipeline load to eliminate HuggingFace
  HEAD requests at synthesis time (reduces TTS latency)
- BriefingView: discuss article button now auto-sends instead of just filling input;
  prompt capped to 15 sentences; send() accepts optional overrideText
- llm.py: instruct LLM not to proactively search notes or comment on note absence

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-31 16:52:11 -04:00
parent ab397e78f3
commit baeb0b14e5
5 changed files with 169 additions and 22 deletions
+8 -7
View File
@@ -160,8 +160,8 @@ watch(() => chatStore.streaming, async (streaming) => {
const input = ref('')
const sending = ref(false)
async function send() {
const text = input.value.trim()
async function send(overrideText?: string) {
const text = (overrideText ?? input.value).trim()
if (!text || !todayConvId.value || chatStore.streaming || sending.value) return
if (chatStore.currentConversation?.id !== todayConvId.value) {
await chatStore.fetchConversation(todayConvId.value)
@@ -175,13 +175,14 @@ async function send() {
}
}
function discussArticle(item: NewsItem) {
async function discussArticle(item: NewsItem) {
const snippet = item.snippet ? `\n\n"${item.snippet.slice(0, 400)}${item.snippet.length > 400 ? '…' : ''}"` : ''
input.value = `Can you summarize and discuss this article for me?\n\n**${item.title}**${snippet}\n\nSource: ${item.source}`
// Scroll the chat panel into view on narrow layouts
nextTick(() => {
const text = `Please give me a brief summary and key takeaways for this article (15 sentences or fewer):\n\n**${item.title}**${snippet}\n\nSource: ${item.source}`
// Scroll the chat panel into view on narrow layouts, then send
await nextTick(() => {
document.querySelector('.briefing-chat')?.scrollIntoView({ behavior: 'smooth', block: 'nearest' })
})
await send(text)
}
function onKeydown(e: KeyboardEvent) {
@@ -490,7 +491,7 @@ onMounted(async () => {
</button>
<button
class="btn-send"
@click="send"
@click="send()"
:disabled="!input.trim() || chatStore.streaming || sending"
aria-label="Send"
>&uarr;</button>