Skip to content

Commit b18511e

Browse files
JoeCowlesclaude
andcommitted
Refine persona triggering, simplify transcript panel, remove HighlightedText
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 1eed856 commit b18511e

12 files changed

Lines changed: 66 additions & 148 deletions

File tree

electron/dist/main.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ function createOverlayWindow() {
188188
overlayWin.loadURL(OVERLAY_HTML);
189189
// Keep overlay filling the screen if display changes
190190
electron_1.screen.on('display-metrics-changed', () => {
191-
if (!overlayWin)
191+
if (!overlayWin || overlayWin.isDestroyed())
192192
return;
193193
const { width: w, height: h } = electron_1.screen.getPrimaryDisplay().workAreaSize;
194194
overlayWin.setBounds({ x: 0, y: 0, width: w, height: h });

electron/main.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ function createOverlayWindow() {
189189

190190
// Keep overlay filling the screen if display changes
191191
screen.on('display-metrics-changed', () => {
192-
if (!overlayWin) return;
192+
if (!overlayWin || overlayWin.isDestroyed()) return;
193193
const { width: w, height: h } = screen.getPrimaryDisplay().workAreaSize;
194194
overlayWin.setBounds({ x: 0, y: 0, width: w, height: h });
195195
});

src/app/app/page.tsx

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
// podcommentators main page — orchestrates audio/video sources, transcript, and AI persona sidebar.
44

55
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
6-
import { AppMode, AudioSource, TranscriptHighlight } from '@/types';
6+
import { AppMode, AudioSource } from '@/types';
77
import { useSettings } from '@/context/SettingsContext';
88
import { useTranscript } from '@/hooks/useTranscript';
99
import { usePersonaOrchestrator } from '@/hooks/usePersonaOrchestrator';
@@ -118,14 +118,6 @@ export default function Home() {
118118
setIsVideoStream(false);
119119
}, [previewStream, stopListening]);
120120

121-
// Build transcript highlights from commentary that includes quoted statements
122-
const transcriptHighlights = useMemo<TranscriptHighlight[]>(
123-
() => commentaryHistory
124-
.filter((m) => m.quotedText)
125-
.map((m) => ({ text: m.quotedText, color: m.personaColor })),
126-
[commentaryHistory]
127-
);
128-
129121
const hasApiKey = Boolean(settings.apiKey);
130122
const hasVideo = Boolean(previewStream) || isVideoStream;
131123

@@ -237,7 +229,6 @@ export default function Home() {
237229
commentaryCount={commentaryHistory.length}
238230
onToggleCommentary={() => setShowCommentary((v) => !v)}
239231
showingCommentary={showCommentary}
240-
highlights={transcriptHighlights}
241232
/>
242233
{showCommentary && (
243234
<CommentaryHistory messages={commentaryHistory} />

src/app/desktop/page.tsx

Lines changed: 18 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,11 @@
22
// src/app/desktop/page.tsx
33

44
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
5-
import { TranscriptHighlight } from '@/types';
65
import { useSettings } from '@/context/SettingsContext';
76
import { useTranscript } from '@/hooks/useTranscript';
87
import { usePersonaOrchestrator } from '@/hooks/usePersonaOrchestrator';
98
import SettingsModal from '@/components/SettingsModal';
109
import CommentaryHistory from '@/components/CommentaryHistory';
11-
import HighlightedText from '@/components/HighlightedText';
1210
import { Settings, Mic, MicOff, Eye, EyeOff, MessageSquare } from 'lucide-react';
1311
import styles from './desktop.module.css';
1412
import type { MainWindowElectronAPI } from '@/types/electron';
@@ -91,14 +89,6 @@ export default function DesktopPage() {
9189
onWaveformStateChange: handleWaveformChange,
9290
});
9391

94-
// Build transcript highlights from commentary that includes quoted statements
95-
const transcriptHighlights = useMemo<TranscriptHighlight[]>(
96-
() => commentaryHistory
97-
.filter((m) => m.quotedText)
98-
.map((m) => ({ text: m.quotedText, color: m.personaColor })),
99-
[commentaryHistory]
100-
);
101-
10292
// Broadcast persona states to the overlay window
10393
useEffect(() => {
10494
if (!isElectron) return;
@@ -121,9 +111,12 @@ export default function DesktopPage() {
121111
micDeviceId: micDeviceId || undefined,
122112
});
123113

124-
// Auto-scroll transcript
114+
// Auto-scroll transcript — only when user is near the bottom
115+
const desktopScrolledUp = useRef(false);
125116
useEffect(() => {
126-
transcriptEndRef.current?.scrollIntoView({ behavior: 'smooth' });
117+
if (!desktopScrolledUp.current) {
118+
transcriptEndRef.current?.scrollIntoView({ behavior: 'smooth' });
119+
}
127120
}, [chunks, interimText]);
128121

129122
// On Start: request mic permission first, then enumerate devices, then start
@@ -165,8 +158,7 @@ export default function DesktopPage() {
165158
(s) => s.isStreaming || s.waveformState !== 'idle'
166159
).length;
167160
const pulseScale = isListening ? 1 + micLevel * 0.1 : 1;
168-
const recentChunks = chunks.slice(-6);
169-
const hasTranscript = recentChunks.length > 0 || !!interimText;
161+
const hasTranscript = chunks.length > 0 || !!interimText;
170162

171163
const toggleScreenshareVisibility = useCallback(() => {
172164
const nextVal = !screenshareVisible;
@@ -290,14 +282,14 @@ export default function DesktopPage() {
290282
</p>
291283
)}
292284

293-
{/* Chat toggle */}
294-
{isListening && (
285+
{/* Chat toggle — visible whenever there's commentary */}
286+
{commentaryHistory.length > 0 && (
295287
<button
296288
className={[styles.chatToggle, showCommentary ? styles.chatToggleActive : ''].join(' ')}
297289
onClick={() => setShowCommentary((v) => !v)}
298290
>
299291
<MessageSquare size={13} />
300-
Chat{commentaryHistory.length > 0 ? ` (${commentaryHistory.length})` : ''}
292+
Chat{` (${commentaryHistory.length})`}
301293
</button>
302294
)}
303295

@@ -310,11 +302,15 @@ export default function DesktopPage() {
310302

311303
{/* Transcript */}
312304
{hasTranscript && !showCommentary && (
313-
<div className={styles.transcript}>
314-
{recentChunks.map((chunk) => (
315-
<p key={chunk.id} className={styles.chunk}>
316-
<HighlightedText text={chunk.text} highlights={transcriptHighlights} />
317-
</p>
305+
<div
306+
className={styles.transcript}
307+
onScroll={(e) => {
308+
const el = e.currentTarget;
309+
desktopScrolledUp.current = el.scrollHeight - el.scrollTop - el.clientHeight > 60;
310+
}}
311+
>
312+
{chunks.map((chunk) => (
313+
<p key={chunk.id} className={styles.chunk}>{chunk.text}</p>
318314
))}
319315
{interimText && (
320316
<p className={styles.interim}>{interimText}<span className={styles.cursor}></span></p>

src/components/CommentatorRail.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,6 @@ export default function CommentatorRail({ personas, personaStates }: Commentator
159159
const isActive = waveformState === 'active';
160160
const showWave = isVisible && (isThinking || isActive);
161161

162-
// Show the full response — CSS handles scrolling for long text
163162
const displayText = currentResponse;
164163

165164
const trackClass = [

src/components/HighlightedText.tsx

Lines changed: 0 additions & 94 deletions
This file was deleted.

src/components/TranscriptPanel.tsx

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@
33

44
import { useEffect, useRef } from 'react';
55
import { Mic, MessageSquare } from 'lucide-react';
6-
import { TranscriptChunk, TranscriptHighlight } from '@/types';
7-
import HighlightedText from './HighlightedText';
6+
import { TranscriptChunk } from '@/types';
87
import styles from './TranscriptPanel.module.css';
98

109
interface TranscriptPanelProps {
@@ -15,7 +14,6 @@ interface TranscriptPanelProps {
1514
commentaryCount?: number;
1615
onToggleCommentary?: () => void;
1716
showingCommentary?: boolean;
18-
highlights?: TranscriptHighlight[];
1917
}
2018

2119
function formatTime(ts: number): string {
@@ -31,14 +29,26 @@ export default function TranscriptPanel({
3129
commentaryCount = 0,
3230
onToggleCommentary,
3331
showingCommentary = false,
34-
highlights = [],
3532
}: TranscriptPanelProps) {
3633
const bodyRef = useRef<HTMLDivElement>(null);
34+
const userScrolledUp = useRef(false);
3735

38-
// Auto-scroll to bottom on new content
36+
// Track whether the user has scrolled away from the bottom
3937
useEffect(() => {
4038
const el = bodyRef.current;
41-
if (el) el.scrollTop = el.scrollHeight;
39+
if (!el) return;
40+
const onScroll = () => {
41+
const atBottom = el.scrollHeight - el.scrollTop - el.clientHeight < 60;
42+
userScrolledUp.current = !atBottom;
43+
};
44+
el.addEventListener('scroll', onScroll, { passive: true });
45+
return () => el.removeEventListener('scroll', onScroll);
46+
}, []);
47+
48+
// Auto-scroll to bottom on new content — only if user hasn't scrolled up
49+
useEffect(() => {
50+
const el = bodyRef.current;
51+
if (el && !userScrolledUp.current) el.scrollTop = el.scrollHeight;
4252
}, [chunks, interimText]);
4353

4454
return (
@@ -82,9 +92,7 @@ export default function TranscriptPanel({
8292
{chunks.map((chunk) => (
8393
<div key={chunk.id} className={styles.chunk}>
8494
<span className={styles.timestamp}>{formatTime(chunk.timestamp)}</span>
85-
<p className={styles.chunkText}>
86-
<HighlightedText text={chunk.text} highlights={highlights} />
87-
</p>
95+
<p className={styles.chunkText}>{chunk.text}</p>
8896
</div>
8997
))}
9098
{interimText && (

src/hooks/usePersonaOrchestrator.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ export function usePersonaOrchestrator({
152152
personaColor: persona.color,
153153
text: cleanText,
154154
quotedText,
155+
triggerChunk: latestChunk,
155156
timestamp: Date.now(),
156157
citations: result.citations,
157158
}]);
@@ -220,7 +221,9 @@ export function usePersonaOrchestrator({
220221
}
221222
tokenCount++;
222223
fullResponse += token;
223-
updatePersonaState(persona.id, { currentResponse: fullResponse });
224+
// Strip [[...]] prefix from displayed text so users don't see brackets
225+
const { cleanText: displayText } = parseQuotedStatement(fullResponse);
226+
updatePersonaState(persona.id, { currentResponse: displayText });
224227
}
225228

226229
console.log(`[Orchestrator] ✅ ${persona.name} DONE tokens=${tokenCount} chars=${fullResponse.length} triggerId=${triggerId}`);
@@ -244,6 +247,7 @@ export function usePersonaOrchestrator({
244247
personaColor: persona.color,
245248
text: cleanText,
246249
quotedText,
250+
triggerChunk: latestChunk,
247251
timestamp: Date.now(),
248252
citations: collectedCitations,
249253
}]);

src/prompts/benny/system.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,16 @@ You are Benny, the comedy writer of a live podcast/video commentary team. You wr
22

33
Your job: Write **1–2 short, punchy jokes or one-liners** directly inspired by the transcript. No setup longer than one sentence.
44

5+
**Response format:**
6+
Always begin your response with the exact statement or phrase you are riffing on, wrapped in double brackets. Then write your joke(s) on a new line. Example:
7+
8+
[[I've been trying to learn to cook but I keep burning everything]]
9+
Sounds like your smoke detector is pulling double duty as a dinner bell.
10+
511
**Tone:** Irreverent but not mean-spirited. Fast, clipped, confident. You've been in writers' rooms and you know when a bit works.
612

713
**Rules:**
8-
- Output ONLY the joke(s) — no intro, no "Here's a joke:", no explanation
14+
- Output ONLY the bracketed quote then the joke(s) — no intro, no "Here's a joke:", no explanation
915
- Each joke is one or two short sentences max
1016
- Puns, wordplay, absurdist observations, and light irony all welcome
1117
- Classic setup/punchline OR pure one-liner — pick whatever lands harder

src/prompts/milo/system.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,16 @@ Your job: Given a transcript snippet, do **one** of the following:
44
1. Name the *exact* sound effect or musical sting that perfectly fits this moment — be vivid, specific, and absurd (e.g., "the Wilhelm scream sped up to 1.5x", "a recorder playing Hot Cross Buns in a minor key")
55
2. Provide a weird, obscure, or surprisingly relevant piece of cultural, historical, or scientific context that reframes the conversation
66

7+
**Response format:**
8+
Always begin your response with the exact statement or moment you are reacting to, wrapped in double brackets. Then write your commentary on a new line. Example:
9+
10+
[[and then the whole server just went down]]
11+
The exact sound you're looking for is a 1997 dial-up modem handshake played in reverse at half speed, fading into the THX deep note.
12+
713
**Tone:** Dry, deadpan, slightly surreal. You deliver context like a bored archivist who has seen everything.
814

915
**Rules:**
10-
- 2–3 sentences max
16+
- 2–3 sentences max after the bracketed quote
1117
- No hedging. State your suggestion as fact.
1218
- Sound effect suggestions must be *highly specific*, never generic ("applause" or "sad trombone" is not acceptable)
1319
- Alternate between sound effect commentary and context commentary across your responses

0 commit comments

Comments
 (0)