Skip to content

Commit b592675

Browse files
committed
fix format
1 parent 4409459 commit b592675

File tree

2 files changed

+86
-85
lines changed

2 files changed

+86
-85
lines changed

app/page.tsx

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import { CloseIcon } from "@/components/CloseIcon";
44
import { NoAgentNotification } from "@/components/NoAgentNotification";
5+
import { PushToTalkButton } from "@/components/PushToTalkButton";
56
import TranscriptionView from "@/components/TranscriptionView";
67
import {
78
BarVisualizer,
@@ -16,7 +17,6 @@ import { AnimatePresence, motion } from "framer-motion";
1617
import { Room, RoomEvent } from "livekit-client";
1718
import { useCallback, useEffect, useState } from "react";
1819
import type { ConnectionDetails } from "./api/connection-details/route";
19-
import { PushToTalkButton } from "@/components/PushToTalkButton";
2020

2121
export default function Page() {
2222
const [room] = useState(new Room());
@@ -155,20 +155,20 @@ function ControlBar(props: { onConnectButtonClicked: () => void }) {
155155
</AnimatePresence>
156156
<AnimatePresence>
157157
{agentState !== "disconnected" && agentState !== "connecting" && (
158-
<motion.div
159-
initial={{ opacity: 0, top: "10px" }}
160-
animate={{ opacity: 1, top: 0 }}
161-
exit={{ opacity: 0, top: "-10px" }}
162-
transition={{ duration: 0.4, ease: [0.09, 1.04, 0.245, 1.055] }}
163-
className="flex h-8 absolute left-1/2 -translate-x-1/2 justify-center items-center gap-3"
164-
>
165-
<VoiceAssistantControlBar controls={{ leave: false }} />
166-
<PushToTalkButton />
167-
<DisconnectButton>
168-
<CloseIcon />
169-
</DisconnectButton>
170-
</motion.div>
171-
)}
158+
<motion.div
159+
initial={{ opacity: 0, top: "10px" }}
160+
animate={{ opacity: 1, top: 0 }}
161+
exit={{ opacity: 0, top: "-10px" }}
162+
transition={{ duration: 0.4, ease: [0.09, 1.04, 0.245, 1.055] }}
163+
className="flex h-8 absolute left-1/2 -translate-x-1/2 justify-center items-center gap-3"
164+
>
165+
<VoiceAssistantControlBar controls={{ leave: false }} />
166+
<PushToTalkButton />
167+
<DisconnectButton>
168+
<CloseIcon />
169+
</DisconnectButton>
170+
</motion.div>
171+
)}
172172
</AnimatePresence>
173173
</div>
174174
);

components/PushToTalkButton.tsx

Lines changed: 71 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
1-
import {
2-
useLocalParticipant,
3-
useParticipants,
4-
} from "@livekit/components-react";
1+
import { useLocalParticipant, useParticipants } from "@livekit/components-react";
52
import { motion } from "framer-motion";
6-
import { useCallback, useEffect, useRef, useState, MouseEvent as ReactMouseEvent } from "react";
3+
import { MouseEvent as ReactMouseEvent, useCallback, useEffect, useRef, useState } from "react";
74

85
export function PushToTalkButton() {
96
const { localParticipant } = useLocalParticipant();
@@ -13,9 +10,7 @@ export function PushToTalkButton() {
1310
const lastActionTime = useRef(0);
1411

1512
// find agent participant that supports PTT
16-
const agent = participants.find(
17-
(p) => p.attributes?.["push-to-talk"] === "1"
18-
);
13+
const agent = participants.find((p) => p.attributes?.["push-to-talk"] === "1");
1914

2015
useEffect(() => {
2116
// start with microphone disabled for PTT agents
@@ -25,25 +20,28 @@ export function PushToTalkButton() {
2520
}, [localParticipant, agent]);
2621

2722
// when user presses the button
28-
const handleMouseDown = useCallback(async (e: ReactMouseEvent<HTMLButtonElement>) => {
29-
e.preventDefault(); // prevent default browser behavior
30-
31-
if (!agent || !localParticipant) return;
32-
33-
console.log("starting turn");
34-
try {
35-
// await localParticipant.setMicrophoneEnabled(true);
36-
await localParticipant.performRpc({
37-
destinationIdentity: agent.identity,
38-
method: "start_turn",
39-
payload: "",
40-
});
41-
setIsPressed(true);
42-
setIsOutside(false);
43-
} catch (error) {
44-
console.error("Failed to start turn:", error);
45-
}
46-
}, [agent, localParticipant]);
23+
const handleMouseDown = useCallback(
24+
async (e: ReactMouseEvent<HTMLButtonElement>) => {
25+
e.preventDefault(); // prevent default browser behavior
26+
27+
if (!agent || !localParticipant) return;
28+
29+
console.log("starting turn");
30+
try {
31+
// await localParticipant.setMicrophoneEnabled(true);
32+
await localParticipant.performRpc({
33+
destinationIdentity: agent.identity,
34+
method: "start_turn",
35+
payload: "",
36+
});
37+
setIsPressed(true);
38+
setIsOutside(false);
39+
} catch (error) {
40+
console.error("Failed to start turn:", error);
41+
}
42+
},
43+
[agent, localParticipant]
44+
);
4745

4846
// when mouse leaves the button area while pressed
4947
const handleMouseLeave = useCallback(() => {
@@ -62,41 +60,47 @@ export function PushToTalkButton() {
6260
}, [isPressed, isOutside]);
6361

6462
// shared function to end turn with specified method
65-
const endTurnWithMethod = useCallback(async (method: string) => {
66-
if (!agent || !localParticipant || !isPressed) return;
67-
68-
// Prevent multiple actions within 100ms
69-
const now = Date.now();
70-
if (now - lastActionTime.current < 100) return;
71-
lastActionTime.current = now;
72-
73-
console.log(`ending turn with method: ${method}`);
74-
try {
75-
// await localParticipant.setMicrophoneEnabled(false);
76-
await localParticipant.performRpc({
77-
destinationIdentity: agent.identity,
78-
method: method,
79-
payload: "",
80-
});
81-
} catch (error) {
82-
console.error(`Failed to ${method}:`, error);
83-
} finally {
84-
setIsPressed(false);
85-
setIsOutside(false);
86-
}
87-
}, [agent, localParticipant, isPressed]);
63+
const endTurnWithMethod = useCallback(
64+
async (method: string) => {
65+
if (!agent || !localParticipant || !isPressed) return;
66+
67+
// Prevent multiple actions within 100ms
68+
const now = Date.now();
69+
if (now - lastActionTime.current < 100) return;
70+
lastActionTime.current = now;
71+
72+
console.log(`ending turn with method: ${method}`);
73+
try {
74+
// await localParticipant.setMicrophoneEnabled(false);
75+
await localParticipant.performRpc({
76+
destinationIdentity: agent.identity,
77+
method: method,
78+
payload: "",
79+
});
80+
} catch (error) {
81+
console.error(`Failed to ${method}:`, error);
82+
} finally {
83+
setIsPressed(false);
84+
setIsOutside(false);
85+
}
86+
},
87+
[agent, localParticipant, isPressed]
88+
);
8889

8990
// when user releases the mouse anywhere
90-
const handleMouseUp = useCallback((e: ReactMouseEvent) => {
91-
e.preventDefault(); // prevent default browser behavior
92-
93-
if (!isPressed) return;
94-
95-
// if mouse is outside the button on release, cancel the turn
96-
// otherwise, end the turn normally
97-
const method = isOutside ? "cancel_turn" : "end_turn";
98-
endTurnWithMethod(method);
99-
}, [isPressed, isOutside, endTurnWithMethod]);
91+
const handleMouseUp = useCallback(
92+
(e: ReactMouseEvent) => {
93+
e.preventDefault(); // prevent default browser behavior
94+
95+
if (!isPressed) return;
96+
97+
// if mouse is outside the button on release, cancel the turn
98+
// otherwise, end the turn normally
99+
const method = isOutside ? "cancel_turn" : "end_turn";
100+
endTurnWithMethod(method);
101+
},
102+
[isPressed, isOutside, endTurnWithMethod]
103+
);
100104

101105
// ensure turn is ended when component unmounts
102106
useEffect(() => {
@@ -114,9 +118,9 @@ export function PushToTalkButton() {
114118
handleMouseUp(e as unknown as ReactMouseEvent);
115119
};
116120

117-
window.addEventListener('mouseup', handleGlobalMouseUp);
121+
window.addEventListener("mouseup", handleGlobalMouseUp);
118122
return () => {
119-
window.removeEventListener('mouseup', handleGlobalMouseUp);
123+
window.removeEventListener("mouseup", handleGlobalMouseUp);
120124
};
121125
}
122126
}, [isPressed, handleMouseUp]);
@@ -139,16 +143,13 @@ export function PushToTalkButton() {
139143
: "#004085" // blue when speaking normally
140144
: "#007bff", // default blue
141145
scale: isPressed ? 0.95 : 1,
142-
boxShadow: isOutside && isPressed
143-
? "0 0 0 3px rgba(217, 83, 79, 0.5)"
144-
: "0 4px 6px rgba(0, 0, 0, 0.1)",
146+
boxShadow:
147+
isOutside && isPressed
148+
? "0 0 0 3px rgba(217, 83, 79, 0.5)"
149+
: "0 4px 6px rgba(0, 0, 0, 0.1)",
145150
}}
146151
>
147-
{isPressed
148-
? isOutside
149-
? "Release to Cancel"
150-
: "Speaking..."
151-
: "Press to Talk"}
152+
{isPressed ? (isOutside ? "Release to Cancel" : "Speaking...") : "Press to Talk"}
152153
</motion.button>
153154
);
154155
}

0 commit comments

Comments
 (0)